Advanced Features: Professional-Grade Capabilities
Professional-grade capabilities for complex dice mechanics, deterministic systems, statistical analysis, and enterprise-level game development requirements. These features leverage the full power of the 29-script framework architecture.
Table of Contents
Seeded Random Systems
Deterministic Rolling with SeededRandom
The SeededRandom class provides deterministic random number generation for reproducible results across sessions:
Deterministic Roll Implementation
// Initialize seeded random for deterministic results
DiceAPI.InitializeSeed("GameSession_2024_01_15");
// All subsequent rolls are deterministic and reproducible
int roll1 = DiceAPI.Roll(20, 5); // Always same result for this seed
int roll2 = DiceAPI.Roll(6, 2); // Next in deterministic sequence
int roll3 = DiceAPI.Roll(10); // Continues sequence
// Reset to same seed to replay exact sequence
DiceAPI.InitializeSeed("GameSession_2024_01_15");
int replayRoll1 = DiceAPI.Roll(20, 5); // Identical to roll1
Debug.Log($"Original: {roll1}, Replay: {replayRoll1}"); // Should be identical
// Return to system random for normal gameplay
DiceAPI.InitializeSystemRandom();
Network Synchronization
Using seeded random for multiplayer dice synchronization:
Multiplayer Synchronization Pattern
public class NetworkedDiceManager : MonoBehaviour
{
public void ExecuteNetworkRoll(int sides, int modifier, string playerName)
{
// Generate network seed from game state + timestamp
string networkSeed = $"{Time.time}_{playerName}_{Random.Range(1000, 9999)}";
// Server executes seeded roll
DiceAPI.InitializeSeed(networkSeed);
int result = DiceAPI.Roll(sides, modifier);
// Send seed and result to all clients for verification
SendNetworkRoll(sides, modifier, networkSeed, result, playerName);
}
private void SendNetworkRoll(int sides, int modifier, string seed, int expected, string player)
{
// Network RPC or similar to synchronize all clients
foreach (var client in NetworkManager.ConnectedClients)
{
client.VerifyNetworkRoll(sides, modifier, seed, expected, player);
}
}
public void VerifyNetworkRoll(int sides, int modifier, string seed, int expected, string player)
{
// Client verifies result using same seed
DiceAPI.InitializeSeed(seed);
int clientResult = DiceAPI.Roll(sides, modifier);
if (clientResult == expected)
{
Debug.Log($"{player} rolled {clientResult} - Verified");
ProcessVerifiedRoll(clientResult, player);
}
else
{
Debug.LogError($"Network desync! Expected {expected}, got {clientResult}");
}
}
}
Procedural Content Generation
Using seeded generation for consistent procedural content:
Seeded Procedural Generation
public class SeededDungeonGenerator : MonoBehaviour
{
public GeneratedDungeon CreateDungeon(string seedString, int roomCount)
{
// Initialize with seed for reproducible generation
DiceAPI.InitializeSeed(seedString);
var dungeon = new GeneratedDungeon();
dungeon.rooms = new List<DungeonRoom>();
for (int i = 0; i < roomCount; i++)
{
var room = new DungeonRoom();
// Room size using drop dice for consistent distribution
var sizeResult = DiceAPI.RollDrop(6, 4, 1, DropType.Lowest, 2); // 4d6 drop lowest + 2
room.size = sizeResult.totalResult;
// Room type using weighted distribution
var typeConfig = ScriptableObject.CreateInstance<DiceConfiguration>();
typeConfig.sides = 100;
typeConfig.weightConfig.enabled = true;
typeConfig.weightConfig.algorithm = WeightAlgorithm.BiasLow; // Common rooms more likely
int typeRoll = DiceAPI.Roll(typeConfig);
room.roomType = DetermineRoomType(typeRoll);
// Monster population using pool system
if (room.roomType == RoomType.MonsterDen)
{
var monsterPool = DiceAPI.RollPool(6, 8, 4); // 8d6, count 4+ as monsters
room.monsterCount = monsterPool.totalResult;
}
// Treasure using exploding dice for rare high-value finds
if (room.roomType == RoomType.TreasureRoom)
{
var treasureRoll = DiceAPI.RollExploding(10, 3); // d10 exploding max 3 times
room.treasureValue = treasureRoll.totalResult * 100; // Scale up value
}
dungeon.rooms.Add(room);
}
// Reset to system random after generation
DiceAPI.InitializeSystemRandom();
return dungeon;
}
// Validation method for testing consistency
public bool ValidateDungeonGeneration(string seed, GeneratedDungeon expected)
{
var testDungeon = CreateDungeon(seed, expected.rooms.Count);
return CompareDungeons(testDungeon, expected);
}
}
Advanced Conditional Rules
77 Rule Combinations
The framework provides 11 trigger types and 7 action types for 77 total rule combinations:
Complete Rule Matrix
Trigger Types (11)
- OnMax: Natural maximum die result
- OnMin: Natural minimum die result
- OnValue: Specific die result
- OnRange: Result within range
- OnSuccess: Meeting target number
- OnFailure: Missing target number
- OnMargin: Exceeding target by amount
- OnSequence: Consecutive results pattern
- OnPattern: Specific result patterns
- OnFlag: Conditional flag presence
- OnModifier: Session modifier conditions
Action Types (7)
- RollBonus: Add bonus dice rolls
- AddValue: Add fixed value to result
- MultiplyResult: Multiply result by factor
- SetFlag: Set conditional flags
- AddModifier: Add session modifier
- Reroll: Force reroll with conditions
- Cascade: Trigger additional rules
Rule Chaining and Priority
Complex rule systems with proper priority and chaining:
Advanced Rule Chain Implementation
public ConditionalRule[] CreateSpellcastingRules()
{
var rules = new List<ConditionalRule>();
// Rule 1: Critical Success (Priority 1 - Execute first)
var criticalRule = ScriptableObject.CreateInstance<ConditionalRule>();
criticalRule.ruleName = "Critical Success";
criticalRule.priority = 1;
criticalRule.enabled = true;
criticalRule.chainable = true;
criticalRule.trigger.type = TriggerType.OnMax;
criticalRule.action.type = ActionType.SetFlag;
criticalRule.action.flagName = "Critical";
rules.Add(criticalRule);
// Rule 2: Spell Damage on Success (Priority 5)
var damageRule = ScriptableObject.CreateInstance<ConditionalRule>();
damageRule.ruleName = "Spell Damage";
damageRule.priority = 5;
damageRule.enabled = true;
damageRule.chainable = true;
damageRule.trigger.type = TriggerType.OnSuccess;
damageRule.trigger.targetNumber = 15; // Spell DC
damageRule.action.type = ActionType.RollBonus;
damageRule.action.diceConfig = new BonusDiceConfiguration
{
count = 2,
sides = 6,
modifier = 0,
rollType = RollType.Normal
};
rules.Add(damageRule);
// Rule 3: Critical Damage Enhancement (Priority 10 - After damage)
var critDamageRule = ScriptableObject.CreateInstance<ConditionalRule>();
critDamageRule.ruleName = "Critical Damage";
critDamageRule.priority = 10;
critDamageRule.enabled = true;
critDamageRule.chainable = false;
critDamageRule.trigger.type = TriggerType.OnFlag;
critDamageRule.trigger.flagName = "Critical";
critDamageRule.action.type = ActionType.MultiplyResult;
critDamageRule.action.multiplier = 2.0f;
rules.Add(critDamageRule);
return rules.ToArray();
}
// Usage with rule processing
public RollResult CastSpell(DiceConfiguration spellConfig)
{
var rules = CreateSpellcastingRules();
var (result, nextModifier) = DiceAPI.RollWithRules(spellConfig, rules);
// Apply accumulated modifier to modifier session
if (nextModifier > 0)
{
ModifierSession.AddModifier(nextModifier, "Spell Momentum");
}
return result;
}
Dynamic Rule Processing
Context-aware rule activation and state-dependent processing:
Dynamic Rule System
public class DynamicRuleProcessor : MonoBehaviour
{
private Dictionary<string, object> gameState = new Dictionary<string, object>();
private List<ConditionalRule> availableRules = new List<ConditionalRule>();
public RollResult ProcessDynamicRoll(DiceConfiguration baseConfig, string context = "")
{
// Filter rules based on current context and game state
var applicableRules = GetApplicableRules(context);
// Execute roll with context-filtered rules
var (result, accumulatedModifier) = DiceAPI.RollWithRules(baseConfig, applicableRules);
// Update game state based on result
UpdateGameState(result, context);
// Handle accumulated modifiers
if (accumulatedModifier != 0)
{
ModifierSession.AddModifier(accumulatedModifier, $"Dynamic Rule - {context}");
}
return result;
}
private ConditionalRule[] GetApplicableRules(string context)
{
var applicable = new List<ConditionalRule>();
foreach (var rule in availableRules)
{
if (IsRuleApplicable(rule, context))
{
applicable.Add(rule);
}
}
// Sort by priority for proper execution order
return applicable.OrderBy(r => r.priority).ToArray();
}
private bool IsRuleApplicable(ConditionalRule rule, string context)
{
// Context-based filtering
if (context.Contains("combat") && !rule.ruleName.ToLower().Contains("combat"))
{
return false;
}
// Game state filtering
if (rule.ruleName.Contains("Night") && !gameState.ContainsKey("IsNight"))
{
return false;
}
if (rule.ruleName.Contains("Magic") && !(bool)gameState.GetValueOrDefault("MagicEnabled", false))
{
return false;
}
// Resource-based filtering
if (rule.trigger.type == TriggerType.OnModifier &&
ModifierSession.GetTotalModifier() <= 0)
{
return false;
}
return rule.enabled;
}
private void UpdateGameState(RollResult result, string context)
{
// Update state based on roll outcome
if (result.totalResult >= 18)
{
gameState["LastRollExceptional"] = true;
}
// Context-specific state updates
if (context.Contains("spell") && result.naturalResult == 20)
{
gameState["SpellSurge"] = true;
}
// Track roll history for sequence-based rules
if (!gameState.ContainsKey("RecentRolls"))
{
gameState["RecentRolls"] = new Queue<int>();
}
var recentRolls = (Queue<int>)gameState["RecentRolls"];
recentRolls.Enqueue(result.naturalResult);
if (recentRolls.Count > 5) // Keep last 5 rolls
{
recentRolls.Dequeue();
}
}
}
Statistical Analysis
Monte Carlo Simulation
The ProbabilityEngine provides sophisticated statistical analysis through Monte Carlo methods:
Comprehensive Statistical Analysis
public class StatisticalAnalyzer : MonoBehaviour
{
public void AnalyzeWeaponBalance(DiceConfiguration weaponConfig)
{
// Perform Monte Carlo analysis with 50,000 iterations
var probability = DiceAPI.CalculateProbability(
weaponConfig,
15, // Target AC
ComparisonType.GreaterEqual,
50000
);
Debug.Log("=== Weapon Balance Analysis ===");
Debug.Log($"Configuration: {weaponConfig.displayName}");
Debug.Log($"Hit Rate vs AC 15: {probability.percentage:F2}%");
Debug.Log($"Average Roll: {probability.average:F2}");
Debug.Log($"Standard Deviation: {probability.standardDeviation:F2}");
Debug.Log($"Range: {probability.minimum} - {probability.maximum}");
Debug.Log($"Median: {probability.median:F2}");
// Analyze damage output with same weapon
if (weaponConfig.rollType != RollType.Pool) // Don't analyze pool dice as damage
{
var damageAnalysis = AnalyzeDamageDistribution(weaponConfig, 10000);
DisplayDamageAnalysis(damageAnalysis);
}
}
private DamageDistribution AnalyzeDamageDistribution(DiceConfiguration config, int iterations)
{
var results = new List<int>();
for (int i = 0; i < iterations; i++)
{
var result = DiceAPI.RollDetailed(config);
results.Add(result.totalResult);
}
results.Sort();
return new DamageDistribution
{
average = results.Average(),
minimum = results.Min(),
maximum = results.Max(),
percentile25 = results[results.Count / 4],
percentile75 = results[(3 * results.Count) / 4],
median = results[results.Count / 2],
standardDeviation = CalculateStandardDeviation(results),
distribution = BuildDistributionMap(results)
};
}
private Dictionary<int, float> BuildDistributionMap(List<int> results)
{
var distribution = new Dictionary<int, float>();
var total = results.Count;
foreach (var result in results)
{
if (!distribution.ContainsKey(result))
{
distribution[result] = 0;
}
distribution[result]++;
}
// Convert to percentages
var percentageDistribution = new Dictionary<int, float>();
foreach (var kvp in distribution)
{
percentageDistribution[kvp.Key] = (kvp.Value / total) * 100f;
}
return percentageDistribution;
}
}
Probability Engine Usage
Advanced probability calculations for game balance:
Balance Validation System
public class GameBalanceValidator : MonoBehaviour
{
public BalanceReport ValidateGameBalance(DiceConfiguration[] configs, int[] targetNumbers)
{
var report = new BalanceReport();
report.configurations = new List<ConfigurationAnalysis>();
foreach (var config in configs)
{
var configAnalysis = new ConfigurationAnalysis();
configAnalysis.configName = config.displayName;
configAnalysis.targetAnalysis = new List<TargetAnalysis>();
foreach (var target in targetNumbers)
{
// Analyze success probability for each target
var probability = DiceAPI.CalculateProbability(
config, target, ComparisonType.GreaterEqual, 25000);
var targetAnalysis = new TargetAnalysis
{
targetNumber = target,
successRate = probability.percentage,
averageRoll = probability.average,
recommendation = GenerateRecommendation(probability.percentage, target)
};
configAnalysis.targetAnalysis.Add(targetAnalysis);
}
// Overall balance score
configAnalysis.balanceScore = CalculateOverallBalance(configAnalysis.targetAnalysis);
report.configurations.Add(configAnalysis);
}
// Cross-configuration comparison
report.relativeBalance = AnalyzeRelativeBalance(report.configurations);
report.recommendations = GenerateGlobalRecommendations(report);
return report;
}
private string GenerateRecommendation(float successRate, int target)
{
var idealRange = GetIdealSuccessRange(target);
if (successRate < idealRange.min)
{
return $"Too difficult - consider reducing target or adding bonuses";
}
else if (successRate > idealRange.max)
{
return $"Too easy - consider increasing target or reducing bonuses";
}
else
{
return $"Well balanced - {successRate:F1}% success rate is appropriate";
}
}
private (float min, float max) GetIdealSuccessRange(int targetNumber)
{
// Different target numbers have different ideal success rates
return targetNumber switch
{
<= 10 => (70f, 90f), // Easy targets
<= 15 => (55f, 75f), // Moderate targets
<= 20 => (35f, 55f), // Hard targets
_ => (15f, 35f) // Very hard targets
};
}
}
Game Balance Validation
Automated balance testing and validation workflows:
Automated Balance Testing
[System.Serializable]
public class BalanceTestSuite
{
public string testName;
public DiceConfiguration[] testConfigurations;
public int[] testTargets;
public BalanceTestCriteria criteria;
}
public class AutomatedBalanceTester : MonoBehaviour
{
[SerializeField] private BalanceTestSuite[] testSuites;
[ContextMenu("Run All Balance Tests")]
public void RunAllBalanceTests()
{
var allResults = new List<BalanceTestResult>();
foreach (var testSuite in testSuites)
{
Debug.Log($"=== Running Balance Test: {testSuite.testName} ===");
var result = RunBalanceTestSuite(testSuite);
allResults.Add(result);
LogTestResults(result);
}
// Generate comprehensive report
GenerateBalanceReport(allResults);
}
private BalanceTestResult RunBalanceTestSuite(BalanceTestSuite testSuite)
{
var result = new BalanceTestResult();
result.testSuiteName = testSuite.testName;
result.configResults = new List<ConfigBalanceResult>();
foreach (var config in testSuite.testConfigurations)
{
var configResult = new ConfigBalanceResult();
configResult.configName = config.displayName;
configResult.targetResults = new List<TargetBalanceResult>();
foreach (var target in testSuite.testTargets)
{
// Run probability analysis
var probability = DiceAPI.CalculateProbability(
config, target, ComparisonType.GreaterEqual,
testSuite.criteria.iterations);
var targetResult = new TargetBalanceResult
{
targetNumber = target,
successRate = probability.percentage,
meetsExpectations = IsWithinExpectedRange(
probability.percentage,
testSuite.criteria.GetExpectedRange(target)),
deviation = CalculateDeviation(
probability.percentage,
testSuite.criteria.GetExpectedRange(target))
};
configResult.targetResults.Add(targetResult);
}
configResult.overallBalance = CalculateOverallBalanceScore(configResult.targetResults);
result.configResults.Add(configResult);
}
result.passed = result.configResults.All(cr => cr.overallBalance >= testSuite.criteria.minimumScore);
return result;
}
private void GenerateBalanceReport(List<BalanceTestResult> results)
{
var report = new StringBuilder();
report.AppendLine("=== AUTOMATED BALANCE TEST REPORT ===");
report.AppendLine($"Generated: {System.DateTime.Now}");
report.AppendLine();
int passedTests = results.Count(r => r.passed);
int totalTests = results.Count;
report.AppendLine($"Overall Results: {passedTests}/{totalTests} test suites passed");
report.AppendLine();
foreach (var result in results)
{
report.AppendLine($"Test Suite: {result.testSuiteName} - {(result.passed ? "PASSED" : "FAILED")}");
foreach (var configResult in result.configResults)
{
report.AppendLine($" {configResult.configName}: {configResult.overallBalance:F2} balance score");
foreach (var targetResult in configResult.targetResults)
{
string status = targetResult.meetsExpectations ? "OK" : "FAIL";
report.AppendLine($" Target {targetResult.targetNumber}: {targetResult.successRate:F1}% ({status})");
}
}
report.AppendLine();
}
// Write report to file
string reportPath = Path.Combine(Application.dataPath, "BalanceReports",
$"balance_report_{System.DateTime.Now:yyyyMMdd_HHmmss}.txt");
Directory.CreateDirectory(Path.GetDirectoryName(reportPath));
File.WriteAllText(reportPath, report.ToString());
Debug.Log($"Balance report saved to: {reportPath}");
}
}
Advanced Session Management
Complex Multi-Dice Sessions
Sophisticated session management with dependency handling:
Complex Combat Session
public class AdvancedCombatManager : MonoBehaviour
{
public CombatResult ExecuteFullCombatRound(Character attacker, Character defender)
{
var combatResult = new CombatResult();
// Phase 1: Initiative and Setup
var initiativeSession = CreateInitiativeSession(attacker, defender);
var initiativeResult = DiceAPI.RollSession(initiativeSession);
var attackerInit = initiativeResult.GetResult("Attacker Initiative");
var defenderInit = initiativeResult.GetResult("Defender Initiative");
// Determine turn order
bool attackerFirst = attackerInit.totalResult >= defenderInit.totalResult;
// Phase 2: Attack Resolution
if (attackerFirst)
{
combatResult.attackerTurn = ExecuteAttackTurn(attacker, defender);
if (!combatResult.attackerTurn.targetDefeated)
{
combatResult.defenderTurn = ExecuteAttackTurn(defender, attacker);
}
}
else
{
combatResult.defenderTurn = ExecuteAttackTurn(defender, attacker);
if (!combatResult.defenderTurn.targetDefeated)
{
combatResult.attackerTurn = ExecuteAttackTurn(attacker, defender);
}
}
return combatResult;
}
private AttackTurnResult ExecuteAttackTurn(Character attacker, Character defender)
{
var turnResult = new AttackTurnResult();
// Create comprehensive attack session
var attackSession = DiceAPI.CreateSession($"{attacker.name} Attack Turn");
attackSession.UseSessionModifiers = true;
// Add all relevant dice to session
attackSession.AddDice(attacker.weaponConfig, "Attack Roll");
attackSession.AddDice(attacker.damageConfig, "Base Damage");
// Add conditional dice based on character abilities
if (attacker.HasAbility("Sneak Attack"))
{
attackSession.AddDice(attacker.sneakAttackConfig, "Sneak Attack");
}
if (attacker.HasAbility("Divine Strike"))
{
attackSession.AddDice(attacker.divineStrikeConfig, "Divine Strike");
}
// Execute session with conditional rules
var sessionRules = GetApplicableCombatRules(attacker, defender);
var sessionResult = DiceAPI.RollSessionWithRules(attackSession, sessionRules);
// Process results
var attackRoll = sessionResult.GetResult("Attack Roll");
turnResult.hit = attackRoll.totalResult >= defender.armorClass;
if (turnResult.hit)
{
turnResult.totalDamage = CalculateTotalDamage(sessionResult);
turnResult.critical = attackRoll.naturalResult == 20;
// Apply damage with resistance
turnResult.finalDamage = ApplyDamageResistance(
turnResult.totalDamage, defender.resistances);
// Check for defeat
defender.currentHitPoints -= turnResult.finalDamage;
turnResult.targetDefeated = defender.currentHitPoints <= 0;
}
return turnResult;
}
private DiceSession CreateInitiativeSession(Character char1, Character char2)
{
var session = DiceAPI.CreateSession("Initiative Roll");
var initiativeConfig = ScriptableObject.CreateInstance<DiceConfiguration>();
initiativeConfig.sides = 20;
initiativeConfig.rollType = RollType.Normal;
var char1Config = Object.Instantiate(initiativeConfig);
char1Config.modifier = char1.initiativeBonus;
var char2Config = Object.Instantiate(initiativeConfig);
char2Config.modifier = char2.initiativeBonus;
session.AddDice(char1Config, "Attacker Initiative");
session.AddDice(char2Config, "Defender Initiative");
return session;
}
}
Modifier Session Management
Advanced modifier accumulation and tracking:
Modifier Session Usage
public class ModifierManager : MonoBehaviour
{
public void DemonstrateModifierSession()
{
// Clear any existing modifiers
ModifierSession.ClearModifiers();
// Add various modifiers from different sources
ModifierSession.AddModifier(2, "Bless Spell");
ModifierSession.AddModifier(1, "Magic Weapon");
ModifierSession.AddModifier(-1, "Cursed");
Debug.Log($"Total accumulated modifiers: {ModifierSession.GetTotalModifier()}");
Debug.Log($"Modifier sources: {string.Join(", ", ModifierSession.GetModifierHistory())}");
// Roll with accumulated modifiers (automatically applied)
int rollWithModifiers = DiceAPI.Roll(20); // Includes +2 from accumulated modifiers
Debug.Log($"Roll result with modifiers: {rollWithModifiers}");
// Conditional rules can add more modifiers
var ruleWithModifier = ScriptableObject.CreateInstance<ConditionalRule>();
ruleWithModifier.trigger.type = TriggerType.OnMax;
ruleWithModifier.action.type = ActionType.AddModifier;
ruleWithModifier.action.modifierValue = 3;
ruleWithModifier.action.modifierSource = "Critical Momentum";
var config = ScriptableObject.CreateInstance<DiceConfiguration>();
config.sides = 20;
var (result, nextModifier) = DiceAPI.RollWithRules(config, new[] { ruleWithModifier });
if (nextModifier > 0)
{
Debug.Log($"Rule added {nextModifier} to modifier session");
}
// Advanced modifier management
ManageModifierExpiration();
}
private void ManageModifierExpiration()
{
// Custom modifier expiration logic
var expiredSources = new[] { "Temporary Buff", "Combat Bonus" };
foreach (var source in expiredSources)
{
if (ModifierSession.HasModifierFromSource(source))
{
ModifierSession.RemoveModifierFromSource(source);
Debug.Log($"Expired modifier from {source}");
}
}
}
// Method to save/restore modifier state
public ModifierSessionState SaveModifierState()
{
return new ModifierSessionState
{
totalModifier = ModifierSession.GetTotalModifier(),
sources = ModifierSession.GetModifierHistory().ToArray(),
timestamp = System.DateTime.Now
};
}
public void RestoreModifierState(ModifierSessionState state)
{
ModifierSession.ClearModifiers();
// Re-apply saved modifiers (you'd need to store individual values)
foreach (var source in state.sources)
{
// This would require extending ModifierSession to store individual values
// ModifierSession.AddModifier(value, source);
}
}
}
Template-Based Sessions
Using the template generation system for quick session creation:
Template-Based Session Creation
public class SessionTemplateManager : MonoBehaviour
{
[SerializeField] private DiceTemplateData diceTemplates;
public DiceSession CreateSessionFromTemplate(string templateName)
{
switch (templateName.ToLower())
{
case "dnd_combat":
return CreateDnDCombatSession();
case "skill_challenge":
return CreateSkillChallengeSession();
case "saving_throws":
return CreateSavingThrowSession();
case "damage_breakdown":
return CreateDamageBreakdownSession();
default:
Debug.LogWarning($"Unknown template: {templateName}");
return CreateBasicSession();
}
}
private DiceSession CreateDnDCombatSession()
{
var session = DiceAPI.CreateSession("D&D Combat Round");
session.Description = "Complete D&D 5e combat resolution";
// Attack roll
var attackConfig = ScriptableObject.CreateInstance<DiceConfiguration>();
attackConfig.displayName = "Attack Roll";
attackConfig.sides = 20;
attackConfig.rollType = RollType.Normal; // Can be changed to Advantage/Disadvantage
attackConfig.modifier = 0; // Set based on character
attackConfig.targetConfig.enabled = true;
attackConfig.targetConfig.value = 15; // Target AC
attackConfig.targetConfig.comparison = ComparisonType.GreaterEqual;
session.AddDice(attackConfig, "Attack");
// Base weapon damage
var weaponDamage = ScriptableObject.CreateInstance<DiceConfiguration>();
weaponDamage.displayName = "Weapon Damage";
weaponDamage.sides = 8; // Longsword
weaponDamage.modifier = 0; // Strength/Dex modifier
session.AddDice(weaponDamage, "Base Damage");
return session;
}
private DiceSession CreateSkillChallengeSession()
{
var session = DiceAPI.CreateSession("Skill Challenge");
session.Description = "Multi-skill challenge resolution";
string[] skillNames = { "Investigation", "Perception", "Athletics", "Persuasion" };
int[] skillBonuses = { 5, 3, 2, 4 }; // Example bonuses
for (int i = 0; i < skillNames.Length; i++)
{
var skillConfig = ScriptableObject.CreateInstance<DiceConfiguration>();
skillConfig.displayName = $"{skillNames[i]} Check";
skillConfig.sides = 20;
skillConfig.modifier = skillBonuses[i];
skillConfig.targetConfig.enabled = true;
skillConfig.targetConfig.value = 15; // Standard DC
skillConfig.targetConfig.comparison = ComparisonType.GreaterEqual;
session.AddDice(skillConfig, skillNames[i]);
}
return session;
}
private DiceSession CreateSavingThrowSession()
{
var session = DiceAPI.CreateSession("Saving Throws");
session.Description = "All saving throw types";
string[] saveTypes = { "Fortitude", "Reflex", "Will" };
int[] saveBonuses = { 4, 2, 6 }; // Example bonuses
for (int i = 0; i < saveTypes.Length; i++)
{
var saveConfig = ScriptableObject.CreateInstance<DiceConfiguration>();
saveConfig.displayName = $"{saveTypes[i]} Save";
saveConfig.sides = 20;
saveConfig.modifier = saveBonuses[i];
saveConfig.targetConfig.enabled = true;
saveConfig.targetConfig.value = 16; // Save DC
saveConfig.targetConfig.comparison = ComparisonType.GreaterEqual;
session.AddDice(saveConfig, $"{saveTypes[i]} Save");
// Set as disabled initially - enable as needed
session.SetDiceEnabled(saveTypes[i] + " Save", false);
}
return session;
}
}
Configuration Management
JSON Import/Export System
The ImportExportSystem provides comprehensive configuration serialization:
Configuration Import/Export
public class ConfigurationManager : MonoBehaviour
{
public void ExportConfiguration(DiceConfiguration config, string filePath)
{
try
{
string json = ImportExportSystem.ExportConfiguration(config);
File.WriteAllText(filePath, json);
Debug.Log($"Configuration exported to: {filePath}");
}
catch (System.Exception e)
{
Debug.LogError($"Export failed: {e.Message}");
}
}
public DiceConfiguration ImportConfiguration(string filePath)
{
try
{
string json = File.ReadAllText(filePath);
var config = ImportExportSystem.ImportConfiguration(json);
Debug.Log($"Configuration imported from: {filePath}");
return config;
}
catch (System.Exception e)
{
Debug.LogError($"Import failed: {e.Message}");
return null;
}
}
// Batch operations
public void ExportMultipleConfigurations(DiceConfiguration[] configs, string directoryPath)
{
Directory.CreateDirectory(directoryPath);
foreach (var config in configs)
{
string fileName = $"{config.displayName.Replace(" ", "_")}.json";
string fullPath = Path.Combine(directoryPath, fileName);
ExportConfiguration(config, fullPath);
}
Debug.Log($"Exported {configs.Length} configurations to {directoryPath}");
}
public DiceConfiguration[] ImportMultipleConfigurations(string directoryPath)
{
var configs = new List<DiceConfiguration>();
var jsonFiles = Directory.GetFiles(directoryPath, "*.json");
foreach (var filePath in jsonFiles)
{
var config = ImportConfiguration(filePath);
if (config != null)
{
configs.Add(config);
}
}
Debug.Log($"Imported {configs.Count} configurations from {directoryPath}");
return configs.ToArray();
}
// Cross-project configuration sharing
public void ShareConfigurationSet(string setName, DiceConfiguration[] configs)
{
var sharedPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"DiceRollToolkit", "SharedConfigs", setName);
ExportMultipleConfigurations(configs, sharedPath);
// Create metadata file
var metadata = new ConfigurationSetMetadata
{
setName = setName,
createdDate = System.DateTime.Now,
configCount = configs.Length,
configNames = configs.Select(c => c.displayName).ToArray(),
createdBy = System.Environment.UserName
};
string metadataPath = Path.Combine(sharedPath, "_metadata.json");
string metadataJson = JsonUtility.ToJson(metadata, true);
File.WriteAllText(metadataPath, metadataJson);
Debug.Log($"Configuration set '{setName}' shared successfully");
}
}
Template Generation System
Advanced template generation for AI integration and documentation:
Template Generation Usage
public class TemplateManager : MonoBehaviour
{
[ContextMenu("Generate Master Template")]
public void GenerateMasterTemplate()
{
var masterTemplate = TemplateGenerationSystem.GenerateMasterTemplate();
string templatePath = Path.Combine(Application.dataPath, "Templates", "MasterTemplate.json");
Directory.CreateDirectory(Path.GetDirectoryName(templatePath));
string json = JsonUtility.ToJson(masterTemplate, true);
File.WriteAllText(templatePath, json);
Debug.Log($"Master template generated: {templatePath}");
Debug.Log($"Template contains {masterTemplate.diceTemplates.Length} dice configurations");
Debug.Log($"Template contains {masterTemplate.ruleTemplates.Length} rule combinations");
}
[ContextMenu("Generate Dice Template Set")]
public void GenerateDiceTemplateSet()
{
var diceTemplate = TemplateGenerationSystem.GenerateDiceTemplate();
string templatePath = Path.Combine(Application.dataPath, "Templates", "DiceTemplate.json");
Directory.CreateDirectory(Path.GetDirectoryName(templatePath));
string json = JsonUtility.ToJson(diceTemplate, true);
File.WriteAllText(templatePath, json);
Debug.Log($"Dice template generated: {templatePath}");
Debug.Log($"Contains templates for all {System.Enum.GetNames(typeof(RollType)).Length} roll types");
}
[ContextMenu("Generate Rule Template Set")]
public void GenerateRuleTemplateSet()
{
var ruleTemplate = TemplateGenerationSystem.GenerateRuleTemplate();
string templatePath = Path.Combine(Application.dataPath, "Templates", "RuleTemplate.json");
Directory.CreateDirectory(Path.GetDirectoryName(templatePath));
string json = JsonUtility.ToJson(ruleTemplate, true);
File.WriteAllText(templatePath, json);
Debug.Log($"Rule template generated: {templatePath}");
Debug.Log($"Contains all 77 rule combinations");
}
// Custom template creation
public void CreateCustomGameTemplate(string gameName, GameTemplateSpec spec)
{
var customTemplate = new CustomGameTemplate
{
gameName = gameName,
gameType = spec.gameType,
diceConfigs = CreateConfigsForGame(spec),
commonRules = CreateRulesForGame(spec),
sessionTemplates = CreateSessionsForGame(spec)
};
string templatePath = Path.Combine(Application.dataPath, "Templates", "CustomGames", $"{gameName}.json");
Directory.CreateDirectory(Path.GetDirectoryName(templatePath));
string json = JsonUtility.ToJson(customTemplate, true);
File.WriteAllText(templatePath, json);
Debug.Log($"Custom template for {gameName} created: {templatePath}");
}
private DiceConfiguration[] CreateConfigsForGame(GameTemplateSpec spec)
{
var configs = new List<DiceConfiguration>();
// Generate configurations based on game specifications
foreach (var dieSpec in spec.requiredDice)
{
var config = ScriptableObject.CreateInstance<DiceConfiguration>();
config.displayName = dieSpec.name;
config.sides = dieSpec.sides;
config.rollType = dieSpec.rollType;
config.modifier = dieSpec.baseModifier;
configs.Add(config);
}
return configs.ToArray();
}
}
Cross-Project Configuration Sharing
Sharing configurations between projects and teams:
Configuration Sharing System
public class ConfigurationSharingManager : MonoBehaviour
{
private const string SHARED_CONFIGS_FOLDER = "DiceRollToolkit_SharedConfigs";
public void PublishConfigurationPack(string packName, ConfigurationPack pack)
{
string sharedPath = GetSharedConfigsPath();
string packPath = Path.Combine(sharedPath, packName);
Directory.CreateDirectory(packPath);
// Export all configurations
foreach (var config in pack.diceConfigurations)
{
string configPath = Path.Combine(packPath, $"{config.displayName}.json");
string json = ImportExportSystem.ExportConfiguration(config);
File.WriteAllText(configPath, json);
}
// Export rules
foreach (var rule in pack.conditionalRules)
{
string rulePath = Path.Combine(packPath, "Rules", $"{rule.ruleName}.json");
Directory.CreateDirectory(Path.GetDirectoryName(rulePath));
string json = ImportExportSystem.ExportRule(rule);
File.WriteAllText(rulePath, json);
}
// Export sessions
foreach (var session in pack.diceSessions)
{
string sessionPath = Path.Combine(packPath, "Sessions", $"{session.sessionName}.json");
Directory.CreateDirectory(Path.GetDirectoryName(sessionPath));
string json = ImportExportSystem.ExportSession(session);
File.WriteAllText(sessionPath, json);
}
// Create pack manifest
var manifest = new ConfigurationPackManifest
{
packName = packName,
version = pack.version,
author = pack.author,
description = pack.description,
createdDate = System.DateTime.Now,
diceConfigCount = pack.diceConfigurations.Length,
ruleCount = pack.conditionalRules.Length,
sessionCount = pack.diceSessions.Length,
tags = pack.tags
};
string manifestPath = Path.Combine(packPath, "manifest.json");
string manifestJson = JsonUtility.ToJson(manifest, true);
File.WriteAllText(manifestPath, manifestJson);
Debug.Log($"Configuration pack '{packName}' published successfully");
}
public string[] GetAvailableConfigurationPacks()
{
string sharedPath = GetSharedConfigsPath();
if (!Directory.Exists(sharedPath))
{
return new string[0];
}
var packDirs = Directory.GetDirectories(sharedPath);
var packNames = new List<string>();
foreach (var dir in packDirs)
{
string manifestPath = Path.Combine(dir, "manifest.json");
if (File.Exists(manifestPath))
{
packNames.Add(Path.GetFileName(dir));
}
}
return packNames.ToArray();
}
public ConfigurationPack ImportConfigurationPack(string packName)
{
string packPath = Path.Combine(GetSharedConfigsPath(), packName);
if (!Directory.Exists(packPath))
{
Debug.LogError($"Configuration pack '{packName}' not found");
return null;
}
// Load manifest
string manifestPath = Path.Combine(packPath, "manifest.json");
if (!File.Exists(manifestPath))
{
Debug.LogError($"Invalid configuration pack: missing manifest");
return null;
}
string manifestJson = File.ReadAllText(manifestPath);
var manifest = JsonUtility.FromJson<ConfigurationPackManifest>(manifestJson);
var pack = new ConfigurationPack
{
packName = manifest.packName,
version = manifest.version,
author = manifest.author,
description = manifest.description
};
// Import dice configurations
var diceConfigs = new List<DiceConfiguration>();
var diceFiles = Directory.GetFiles(packPath, "*.json")
.Where(f => !Path.GetFileName(f).Equals("manifest.json"));
foreach (var diceFile in diceFiles)
{
string json = File.ReadAllText(diceFile);
var config = ImportExportSystem.ImportConfiguration(json);
if (config != null)
{
diceConfigs.Add(config);
}
}
pack.diceConfigurations = diceConfigs.ToArray();
// Import rules
string rulesPath = Path.Combine(packPath, "Rules");
if (Directory.Exists(rulesPath))
{
var rules = new List<ConditionalRule>();
var ruleFiles = Directory.GetFiles(rulesPath, "*.json");
foreach (var ruleFile in ruleFiles)
{
string json = File.ReadAllText(ruleFile);
var rule = ImportExportSystem.ImportRule(json);
if (rule != null)
{
rules.Add(rule);
}
}
pack.conditionalRules = rules.ToArray();
}
// Import sessions
string sessionsPath = Path.Combine(packPath, "Sessions");
if (Directory.Exists(sessionsPath))
{
var sessions = new List<DiceSession>();
var sessionFiles = Directory.GetFiles(sessionsPath, "*.json");
foreach (var sessionFile in sessionFiles)
{
string json = File.ReadAllText(sessionFile);
var session = ImportExportSystem.ImportSession(json);
if (session != null)
{
sessions.Add(session);
}
}
pack.diceSessions = sessions.ToArray();
}
Debug.Log($"Imported configuration pack '{packName}' successfully");
return pack;
}
private string GetSharedConfigsPath()
{
return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
SHARED_CONFIGS_FOLDER);
}
}