Writing Twee code with Naracea

Recently I’ve written short interactive story (in Czech, of course) using twee (while I like idea of Twine’s user interface, I’m text/code person, so I prefer to use twee). I used my own text editor Naracea, because, obviously, it fits all of my needs, and it integrates with twee quite nicely (as I will show below).

Naracea supports branching, which can be used either to branch text to different versions inside of single file, or it can base multiple branches on initial state of the first branch and thus create multiple “notebooks” inside of one document. When writing story, I was experimenting with this for a while, and after couple of passages I realized, that keeping everything in single branch (or single text file), leads to horrible mess which I’m not able to manage.

So I’ve split whole story to multiple branches (around sixty when it was done) with most branches having only one passage in it.

That was nice and all, but there were two problems. One is necessity to setup each branch manually when it was created. I didn’t foresee this as a problem when I was using the editor for usual things like writing blog posts etc., but here it quickly became a major pain point. Fortunately all settings in Naracea are hierarchical, so when branch doesn’t define e.g. spell checker language, it will look in document’s settings (and then in global application settings).

Recently I added plugin model and possibility to script Naracea with IronPython, so it was quite easy to write little script to handle the problem:

import clr
# Get some types we need to use
clr.AddReference("Common")
clr.AddReference("Core")
from Naracea.Common import ( Settings )
from Naracea.Core.Spellcheck import ( DictionaryDescriptor )
from System import ( Exception )  

# Check whether a document is open and active.
document = PluginHost.Application.ActiveDocument  
if document == None:
	raise Exception("No document")

# Get spellcheck language from current branch. This way we don't need to import any further types.
# Just set spelling language on one branch from app UI.
currentSpellingLanguage = document.ActiveBranch.Settings.Get[DictionaryDescriptor](Settings.SpellcheckLanguage)
print currentSpellingLanguage.DictionaryName

# Now apply settings on document level.
# Since settings are hierarchical, when nothing is set on branch, document value is used.
# We set spellchecker, export format + extension and syntax highlighter.
document.Settings.Set(Settings.SpellcheckLanguage, currentSpellingLanguage)
document.Settings.Set(Settings.BranchAutoExportFormat, "TXT")
document.Settings.Set(Settings.BranchAutoExportExtension, ".tw")
document.Settings.Set(Settings.ViewTextEditorSyntaxHighlighting, "Twee")

# If there are existing branches in document, setup their exporting.
for branch in document.Branches:
	branch.Settings.Set(Settings.BranchAutoExportFormat, "TXT");
	branch.Settings.Set(Settings.BranchAutoExportExtension, ".tw");
	branch.Editor.Settings.Set(Settings.ViewTextEditorSyntaxHighlighting, "Twee");

Now I just setup one branch and run the script propagate settings to document (and all existing branches). (Also remember to close and open document again to see the changes, because I wasn’t planning things like this when I was writing those parts of Naracea — I’ll fix this in some future release.)

Nice.

The second problem was, that when all source text was kept in single branch, I was able to setup autoexport for a branch and get whole twee file (and then run the twee from command line to process it into HTML), now I have tens of branches which need to be merged.

So I created another script to fix this issue:

# CLR is needed
import clr   
# Some references to assemblies
clr.AddReference("Plugin")
clr.AddReference("System.Threading")

# Import classes we are going to use
from System.Threading import ( AutoResetEvent )
from System.Diagnostics import ( Process, ProcessStartInfo )   
from System.IO import ( Path )   
from System import ( Exception )    
from Naracea.Plugin.Messages import ( BranchShiftingFinished )

# No document? Report error
if PluginHost.Application.ActiveDocument == None:  
     raise Exception("No document open.")

# Here will be the merged story text
mergedText = ""

# We need to iteratre over all branches, get their text and add it to the mergedText variable.
# However some branches might not be activated yet (Naracea lazy loads branches to save 
# memory and improve file opening times), so we need to ensure they are all active before 
# getting text out of them.
document = PluginHost.Application.ActiveDocument  
activeBranch = document.ActiveBranch
# We will sync on this event
waitEvent = AutoResetEvent(False)
# Subscribe for message signalling branch was successfully initialized
PluginHost.Bus.Subscribe[BranchShiftingFinished](lambda msg: waitEvent.Set());
# Iterate all branches
for branch in document.Branches:
	if not branch.IsActivated:
		# Branch is not active, we need to activate it by setting it as active branch
		document.ActiveBranch = branch
		# And now we need to wait till branch is initialized
		waitEvent.WaitOne();
	
	# It is safe to get text now
	mergedText += branch.Editor.Text + "\n\n"

# Restore last active branch
document.ActiveBranch = activeBranch

# Log merged text to console if you need it
# print mergedText

# Now we prepare save path
# File might not be saved yet, and we will use temp path in such case
sourcePath = PluginHost.Application.ActiveDocument.Filename  
if sourcePath != None:  
     sourcePath = Path.GetDirectoryName(sourcePath)       
else:  
     sourcePath = Path.GetTempPath()  
       
# Exported file name building happens here
branchExportFilename = "tweenExport.tw"  
sourceFile = Path.Combine(sourcePath, branchExportFilename)
print sourceFile

# Open file and save text as UTF8
outFile = open(sourceFile, "w")
outFile.write(mergedText.encode('utf8'))
outFile.close()

# Now generate target filename 
targetFile = Path.Combine(sourcePath, Path.GetFileNameWithoutExtension(sourceFile)) + ".html"   
print targetFile   
  
# Following calls twee and executes conversion
compiler = Process()   
compiler.StartInfo.FileName = "cmd"   
compiler.StartInfo.WorkingDirectory = sourcePath   
compiler.StartInfo.Arguments = "/c c:\\devtools\\python27\\python.exe c:\\devtools\\twee\\twee -t sugarcane \"{0}\" > \"{1}\"".format(sourceFile, targetFile)   
compiler.StartInfo.UseShellExecute = False   
compiler.StartInfo.CreateNoWindow = True  
compiler.StartInfo.RedirectStandardOutput = True 
compiler.Start()   
output = compiler.StandardOutput.ReadToEnd()   
compiler.WaitForExit()   
print output 

# Open converted HTML file in default browser
Process.Start(ProcessStartInfo(targetFile))   

This will merge all branches in one text, save it to disk, call twee to generate HTML and open exported file in default web browser.

I guess this makes Naracea nice alternative for developing Twine/twee stories on Windows:

Twee code

Interaktivní fikce?

Tak tedy. Po všech těch letech jsem zase začal tak trochu psát.

Uvědomil jsem si, že když už jsem naprogramoval textový editor který používám jen já sám, měl bych jej použít alespoň k něčemu pořádnému.

A protože všude ve (anglicky hovořícím) světě, právě vrcholí cosi, čemu se říká Twine revoluce (a všímají si toho i mainstreamová média), rozhodl jsem se, že místo abych objevoval kolo, použiji tentokrát už hotový nástroj (ale protože jsem ještě pořád dost programátor, místo graficky orientovaného Twine používám příkazově řádkované twee a nějaké vlastní skripty v Pythonu).

V rámci své vlastní osvěty jsem se snažil dohledat nějaké české interaktivní fikce psané v Twine (nebo podobných nástrojích, jako je třeba Inklewriter nebo Undum) a… neuspěl jsem. Existuje vlastně jen jeden pokus o interaktivní fikci nového střihu který jsem našel — jmenuje se Díra a je to… hmmm… postkatastrofický příběh z Prahy po atomové válce, nebo něco v tom stylu (eh, pamatuji si, jak jsem na konci devadesátých let napsal postkatastrofický příběh z Prahy po atomové válce a už tehdy to asi nebyl úplně nejlepší nápad). Technologicky Díra vypadá jako produkt Inklewriteru, což není špatné, ale můj pokus o čtení záhy skončil, protože to prostě nebyl můj šálek kávy. Což je naprosto v pořádku, proto jsou všechny ty volně dostupné nástroje pro tvorbu IF tak důležité: každý může tvořit pro svou niché a všichni jsou spokojeni.

Nicméně mě zarazilo, že když existuje tolik nástrojů, zrovna v českém prostředí, které je rozhodně minoritní a úzce specializované, se neprojevuje více autorů, kteří by ty nástroje využili. Když se v Česku hovoří o narativních formách založených na textu s různým stupněm interaktivity, končí diskuse u vzpomínek na staré zlaté časy na Spektru, bla, bla, bla textovky. A to je podle mě dost depresivní. Dokonce jsem kdesi narazil na blog post z letošního roku (2013), který zcela vážně pojednával o tvoření engine pro parserovou textovou hru. Uh.

Tak tedy: Kde jsi, moderní česká interaktivní fikce? Kde jsou autoři z minorit, kteří by chtěli sdílet s ostatními své problémy, svůj pohled na svět? A nemusí to být zrovna příběhy o hormonální terapii transgenderových jedinců. Může to být něco docela banálního. Co já vím… třeba nějaká krátká SF nebo fantasy povídka, u které by bylo škoda neprozkoumat všechny možné konce. Nebo něco, bez stupidních inventářových puzzlů, co by se dalo přečíst/zahrát na tabletu nebo na telefonu jen prostým tapáním na hypertextové odkazy.

Och, ne že bych zrovna já chtěl být nějakým evangelizátorem, chraň Bůh. Já jsem jen člověk, který nedokáže odolat volání technologie, a přemýšlí co by s ní mohl udělat. A tak trochu mě zarazilo, že nemůžu najít nikoho jiného, kdo by se pohyboval ve stejné části právě tohoto dlouhého chvostu. Pokud nějaká česká komunita interaktivních fikcařů existuje, je dobře utajená (někde za druhou stránkou výsledků vyhledávání výrazu “interaktivní fikce” na Google) a možná je to škoda.

Tak, tak.

Chci říct: to psaní mě i po těch letech docela baví, i když jsou v celém tom procesu dvě místa, která mně dost vadí: za prvé revidování a editování. Už jsem zapomněl, jak pracné je pilovat i jen relativně krátkou povídku do podoby, kdy jsem s ní spokojen. Ta aktuální mě stála řekněme šest, sedm čtení a souvisejících úprav a nejspíš by snesla ještě další tři, ale chci se už věnovat něčemu jinému, takže… i když možná časem budou další verze, rozhodně ne brzy. Alespoň myslím.

A za druhé, je tu celá ta věc publikováním. Sice jsem se na rozdíl od většiny twinařů, nedopustil nějaké hluboké intimní zpovědi, ale stejně… I po těch letech, je to pořád stejně těžké.

Nicméně. Pokud máte jednu nebo dvě hodiny času, můžete je třeba zkusit strávit s tímhle.

Poslední přesun

Ach. Tak tedy opět. Ale snad už naposledy.

Ukázalo se, že udržovat více než dvě instalace WordPressu je pro mě neúnosné, a proto jsem se rozhodl konsolidovat své internetové impérium do jediného URL, kde bude všechno. Tedy ne že bych měl těch aktivit tolik, ale je to nesmírně obtěžující muset obejít i těch několik málo webů které mám a nebo bych časem chtěl mít. Tak proto tedy přesun.

Původně jsem se snažil vymyslet nějaké opravdu nápadité jméno pro doménu, kde bych se uhnízdil, ale ukázalo se, že na to už je pozdě. Proto tedy jménopříjmení.com. Časem bych rád publikoval nějaké krátké texty i v… angličtině — ano, dá se to tak nazvat — proto ne .cz. Navíc: nic vám nepřinese víc komentářového spamu a marných pokusů o přihlášení do administrace WordPressu jako doména .com.

Tak tedy: směle do toho.

PS: Původně jsem sem přenesl většinu starších postů z festivalu a nulové obtížnosti. Nicméně se ukázalo, že mé psaní nestárne úplně nejlépe, a především: vytáhnout posty z jejich původního kontextu způsobí, že se z nich ztratí dost z původního smyslu. Takže jsem je zase všechny smazal.