2014-07-02 23:11:36 -05:00
|
|
|
|
namespace Ficdown.Parser.Player
|
|
|
|
|
{
|
2015-07-19 17:19:40 -05:00
|
|
|
|
using System;
|
2014-07-26 23:54:50 -05:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2014-08-10 11:10:21 -05:00
|
|
|
|
using Model.Player;
|
2014-07-02 23:11:36 -05:00
|
|
|
|
using Model.Story;
|
2014-07-26 23:54:50 -05:00
|
|
|
|
using Parser;
|
2015-07-19 17:19:40 -05:00
|
|
|
|
using Action = Model.Story.Action;
|
2014-07-02 23:11:36 -05:00
|
|
|
|
|
2014-08-09 15:18:31 -05:00
|
|
|
|
internal class GameTraverser : IGameTraverser
|
2014-07-02 23:11:36 -05:00
|
|
|
|
{
|
2014-08-09 15:18:31 -05:00
|
|
|
|
private StateManager _manager;
|
|
|
|
|
private Queue<StateQueueItem> _processingQueue;
|
|
|
|
|
private IDictionary<string, PageState> _processed;
|
|
|
|
|
private IDictionary<string, PageState> _compressed;
|
2014-08-10 11:10:21 -05:00
|
|
|
|
private IDictionary<int, Action> _actionMatrix;
|
2015-07-19 17:19:40 -05:00
|
|
|
|
private bool _wasRun = false;
|
2014-07-02 23:11:36 -05:00
|
|
|
|
|
2014-08-09 15:18:31 -05:00
|
|
|
|
private Story _story;
|
|
|
|
|
public Story Story
|
2014-07-02 23:11:36 -05:00
|
|
|
|
{
|
2014-08-09 15:18:31 -05:00
|
|
|
|
get { return _story; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_story = value;
|
2014-08-10 11:10:21 -05:00
|
|
|
|
_actionMatrix = _story.Actions.ToDictionary(a => a.Value.Id, a => a.Value);
|
2014-08-09 15:18:31 -05:00
|
|
|
|
_manager = new StateManager(_story);
|
|
|
|
|
_processingQueue = new Queue<StateQueueItem>();
|
|
|
|
|
_processed = new Dictionary<string, PageState>();
|
|
|
|
|
_compressed = new Dictionary<string, PageState>();
|
|
|
|
|
}
|
2014-07-26 23:54:50 -05:00
|
|
|
|
}
|
|
|
|
|
|
2015-07-19 17:19:40 -05:00
|
|
|
|
public IEnumerable<Scene> OrphanedScenes
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if(!_wasRun) throw new Exception("Call Enumerate() before getting orphans");
|
|
|
|
|
return _story.Scenes.SelectMany(l => l.Value, (l, s) => s).Where(s => !s.Visited);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IEnumerable<Action> OrphanedActions
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if(!_wasRun) throw new Exception("Call Enumerate() before getting orphans");
|
|
|
|
|
return _actionMatrix.Values.Where(a => !a.Visited);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-26 23:54:50 -05:00
|
|
|
|
public IEnumerable<PageState> Enumerate()
|
|
|
|
|
{
|
2015-07-19 17:19:40 -05:00
|
|
|
|
if(_wasRun) throw new Exception("Can't call Enumerate() more than once");
|
|
|
|
|
_wasRun = true;
|
|
|
|
|
|
2014-07-26 23:54:50 -05:00
|
|
|
|
// generate comprehensive enumeration
|
|
|
|
|
|
2014-07-27 00:43:32 -05:00
|
|
|
|
var initial = _manager.InitialState;
|
|
|
|
|
_processingQueue.Enqueue(new StateQueueItem
|
|
|
|
|
{
|
|
|
|
|
Page = initial,
|
|
|
|
|
AffectedStates = new List<State> {initial.AffectedState}
|
|
|
|
|
});
|
2014-07-26 23:54:50 -05:00
|
|
|
|
while (_processingQueue.Count > 0)
|
2014-07-02 23:11:36 -05:00
|
|
|
|
{
|
2014-07-26 23:54:50 -05:00
|
|
|
|
var state = _processingQueue.Dequeue();
|
2014-07-27 00:43:32 -05:00
|
|
|
|
if (!_processed.ContainsKey(state.Page.UniqueHash))
|
2014-07-26 23:54:50 -05:00
|
|
|
|
{
|
2014-07-27 00:43:32 -05:00
|
|
|
|
_processed.Add(state.Page.UniqueHash, state.Page);
|
2014-07-26 23:54:50 -05:00
|
|
|
|
ProcessState(state);
|
|
|
|
|
}
|
2014-07-02 23:11:36 -05:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-10 15:25:20 -05:00
|
|
|
|
// make sure every page gets affected data on every page that it links to
|
|
|
|
|
foreach (var pageTuple in _processed)
|
|
|
|
|
{
|
|
|
|
|
foreach (var linkTuple in pageTuple.Value.Links)
|
|
|
|
|
{
|
|
|
|
|
for (var i = 0; i < _processed[linkTuple.Value].AffectedState.PlayerState.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
pageTuple.Value.AffectedState.PlayerState[i] |=
|
|
|
|
|
_processed[linkTuple.Value].AffectedState.PlayerState[i];
|
|
|
|
|
}
|
|
|
|
|
for (var i = 0; i < _processed[linkTuple.Value].AffectedState.ScenesSeen.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
pageTuple.Value.AffectedState.ScenesSeen[i] |=
|
|
|
|
|
_processed[linkTuple.Value].AffectedState.ScenesSeen[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-26 23:54:50 -05:00
|
|
|
|
// compress redundancies
|
2014-08-09 15:18:31 -05:00
|
|
|
|
foreach (var row in _processed)
|
|
|
|
|
{
|
|
|
|
|
if (!_compressed.ContainsKey(row.Value.CompressedHash))
|
|
|
|
|
{
|
|
|
|
|
var scene = row.Value;
|
|
|
|
|
var links = scene.Links.Keys.ToArray();
|
|
|
|
|
foreach (var link in links)
|
|
|
|
|
{
|
|
|
|
|
scene.Links[link] = _processed[scene.Links[link]].CompressedHash;
|
|
|
|
|
}
|
|
|
|
|
_compressed.Add(row.Value.CompressedHash, row.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-02 23:11:36 -05:00
|
|
|
|
|
2014-08-09 15:18:31 -05:00
|
|
|
|
return _compressed.Values;
|
2014-07-26 23:54:50 -05:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-10 11:10:21 -05:00
|
|
|
|
private IEnumerable<Action> GetActionsForPage(PageState page)
|
|
|
|
|
{
|
|
|
|
|
var actions = new List<Action>();
|
|
|
|
|
for (var i = 0; i < page.State.ActionsToShow.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (page.State.ActionsToShow[i]) actions.Add(_actionMatrix[i + 1]);
|
|
|
|
|
}
|
|
|
|
|
return actions;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-27 00:43:32 -05:00
|
|
|
|
private void ProcessState(StateQueueItem currentState)
|
2014-07-02 23:11:36 -05:00
|
|
|
|
{
|
2015-07-19 17:19:40 -05:00
|
|
|
|
currentState.Page.Scene.Visited = true;
|
|
|
|
|
|
2014-07-26 23:54:50 -05:00
|
|
|
|
var states = new HashSet<string>();
|
2014-07-27 00:43:32 -05:00
|
|
|
|
|
2015-07-19 15:51:10 -05:00
|
|
|
|
var anchors = Utilities.GetInstance(currentState.Page.Scene.Name, currentState.Page.Scene.LineNumber).ParseAnchors(currentState.Page.Scene.Description).ToList();
|
2014-08-10 11:10:21 -05:00
|
|
|
|
foreach (var action in GetActionsForPage(currentState.Page))
|
2015-07-19 17:19:40 -05:00
|
|
|
|
{
|
|
|
|
|
action.Visited = true;
|
2015-07-19 15:51:10 -05:00
|
|
|
|
anchors.AddRange(Utilities.GetInstance(action.Toggle, action.LineNumber).ParseAnchors(action.Description));
|
2015-07-19 17:19:40 -05:00
|
|
|
|
}
|
2014-07-27 00:43:32 -05:00
|
|
|
|
var conditionals =
|
|
|
|
|
anchors.SelectMany(
|
|
|
|
|
a => a.Href.Conditions != null ? a.Href.Conditions.Select(c => c.Key) : new string[] {})
|
|
|
|
|
.Distinct()
|
|
|
|
|
.ToArray();
|
|
|
|
|
var hasFirstSeen = RegexLib.BlockQuotes.IsMatch(currentState.Page.Scene.Description);
|
|
|
|
|
|
|
|
|
|
foreach (var affected in currentState.AffectedStates)
|
|
|
|
|
{
|
|
|
|
|
// signal to previous scenes that this scene's used conditionals are important
|
2014-08-10 11:10:21 -05:00
|
|
|
|
if(currentState.Page.Scene.Conditions != null)
|
|
|
|
|
foreach (var conditional in currentState.Page.Scene.Conditions)
|
|
|
|
|
_manager.ToggleStateOn(affected, conditional.Key);
|
2014-07-27 00:43:32 -05:00
|
|
|
|
foreach (var conditional in conditionals) _manager.ToggleStateOn(affected, conditional);
|
|
|
|
|
|
|
|
|
|
// signal to previous scenes if this scene has first-seen text
|
|
|
|
|
if (hasFirstSeen) _manager.ToggleSeenSceneOn(affected, currentState.Page.Scene.Id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var anchor in anchors.Where(a => a.Href.Target != null || a.Href.Toggles != null))
|
2014-07-02 23:11:36 -05:00
|
|
|
|
{
|
2014-08-10 17:32:13 -05:00
|
|
|
|
// don't follow links that would be hidden
|
|
|
|
|
if (anchor.Href.Conditions != null &&
|
|
|
|
|
string.IsNullOrEmpty(
|
2015-07-19 15:51:10 -05:00
|
|
|
|
Utilities.GetInstance(currentState.Page.Scene.Name, currentState.Page.Scene.LineNumber).ParseConditionalText(anchor.Text)[
|
|
|
|
|
Utilities.GetInstance(currentState.Page.Scene.Name, currentState.Page.Scene.LineNumber).ConditionsMet(StateResolver.GetStateDictionary(currentState.Page),
|
2014-08-10 17:32:13 -05:00
|
|
|
|
anchor.Href.Conditions)])) continue;
|
|
|
|
|
|
2014-07-27 00:43:32 -05:00
|
|
|
|
var newState = _manager.ResolveNewState(anchor, currentState.Page);
|
|
|
|
|
if (!currentState.Page.Links.ContainsKey(anchor.Original))
|
|
|
|
|
currentState.Page.Links.Add(anchor.Original, newState.UniqueHash);
|
|
|
|
|
|
2014-07-26 23:54:50 -05:00
|
|
|
|
if (!states.Contains(newState.UniqueHash) && !_processed.ContainsKey(newState.UniqueHash))
|
2014-07-02 23:11:36 -05:00
|
|
|
|
{
|
2014-07-26 23:54:50 -05:00
|
|
|
|
states.Add(newState.UniqueHash);
|
2014-07-27 00:43:32 -05:00
|
|
|
|
var newAffected = new List<State>(currentState.AffectedStates);
|
|
|
|
|
newAffected.Add(newState.AffectedState);
|
|
|
|
|
_processingQueue.Enqueue(new StateQueueItem {Page = newState, AffectedStates = newAffected});
|
2014-07-02 23:11:36 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-19 15:51:10 -05:00
|
|
|
|
}
|