====== Adding a new flag ======
===== Overview =====
Even before making any modifications to the randomisation logic itself, there are a few things that need to be done to have generation recognise your new flag and make it available for use in any logic updates. The most important of these is ''flagspec.txt'', although you will also likely want to update ''uispec.txt'' to make the new flag available on the website.
===== flagspec.txt =====
''flagspec.txt'', present in the ''FreeEnt'' directory, sets out the schema for flag strings. There are three major sections to ''flagspec.txt'' - ''SPEC'', ''IMPLICIT'', and ''SLUGS''.
==== SPEC ====
Each line in ''SPEC'' contains a list of (usually) related flags as they appear in the flag string. As an example, here is the single line showing the K flags:
Kvanilla Kmain Ksummon Kmoon Kmiab Knofree Kunsafe Kforce:magma/Kforce:hook
The / indicates flags that are directly mutually exclusive.
"Numeric" flags are sets of separate internal flags; as an example of this, here is one of the sets of C flags:
Cj:spells Cj:abilities Cnekkie Cnodupes Cparty:1/Cparty:2/Cparty:3/Cparty:4 Cbye Cpermajoin Cpermadeath/Cpermadeader Chero
We can see that Cparty is actually four separate flags.
=== Objective specs ===
Note that ''SPEC'' contains an ''AUTO_OBJECTIVES'' subsection which maps conventional objectives to each of the O1: through O8: flagsets. This section is automatically generated from the separate ''objective_spec.txt'' by the usual process of updating autogenerated files documented below. In most cases you won't have to worry about this, particularly since "mode" objectives are handled largely separately from this file (except in cases where the objective doesn't line up with an already extant objective, as you can see from the ''internal_dkmatter'' objective present in ''objective_spec.txt'' for ''Omode:dkmatter'' purposes).
==== IMPLICIT ====
''IMPLICIT'' specifies additional flag logic, usually indicating that certain flags cannot be active when certain flags are specified. As an example, here is a line related to ''Tvanilla'':
Tvanilla : ~Tshuffle & ~Tstandard & ~Tpro & ~Twild & ~Twildish & ~Tempty
The ~ indicates "not", and the & indicates "and"; the above line therefore specifies that if ''Tvanilla'' is active that none of ''Tshuffle'', ''Tstandard'', ''Tpro'', ''Twild'', ''Twildish'', or ''Tempty'' can be active.
Regular expressions are also supported to an extent, per the O and G lines which are respectively:
Onone : ~/^O(mode|\d+|random):/
Gnone : ~/^G(?!none)/
respectively indicating that if ''Onone'' is active that no ''Omode'' or ''Orandom'' can be active and that if ''Gnone'' is active that no other ''G'' flags can be active.
==== SLUGS ====
''SLUGS'' maps flag string names to their respective identifiers in the randomiser code. As an example, here is the line for ''Omode:classicforge'':
''Omode:classicforge objective_mode_classicforge''
This indicates that if the ''Omode:classicforge'' flag is set then this will be indicated by ''env.options.flags'' containing ''objective_mode_classicforge'' and also allowing the flag to be checked by that name in f4c scripts. Here is an example from ''adamant.f4c'':
//%flag objective_mode_classicforge on%
text(map #SmithyHouseMainFloor message $05) {
It's done!
__I'm gonna keep it.
}
//%end%
And one from ''core_rando.py'':
if env.options.flags.has('objective_mode_classicforge'):
keyitem_assigner.item_tier(3).append(ItemReward('#item.Excalibur'))
===== flagsetcore.py =====
''flagsetcore.py'' (in the ''FreeEnt'' directory) contains much of the logic for parsing flagsets; it is of particular interest here because of the ''fix'' function which contains additional logic that cannot be captured by ''flagspec.txt''. To illustrate, here are the first two such checks in ''fix'':
if flagset.has_any('Ksummon', 'Kmoon', 'Kmiab') and not flagset.has('Kmain'):
flagset.set('Kmain')
self._lib.push(log, ['correction', 'Advanced key item randomizations are enabled; forced to add Kmain'])
if flagset.has('Kvanilla'):
self._simple_disable(flagset, log, 'Key items not randomized', ['Kunsafe'])
If your flag has advanced interactions with other flags, you will need to incorporate that here.
===== uispec.txt =====
''uispec.txt'' (in the ''FreeEnt/server'' directory) specifies how your flag is displayed on the website. As an example, here is the ''KEY ITEMS'' section:
== KEY ITEMS
<>* Key Items
.[Kvanilla]- No key item randomization
..[Knofree]! No free key item in Toroia
.[Kmain] Randomize key items
..[Ksummon]! Mix with summon quest rewards
..[Kmoon]! Mix with moon boss rewards
..[Kmiab]! Mix with monster-in-a-box chests
..[Knofree]! No free key item in Toroia
..<> Underworld access
...(Kforce:magma) Guarantee underworld access via Magma Key
...(Kforce:hook)! Force underworld access via Hook route
..[Kunsafe]! No safety checks
The . indicates level of indentation; indented items appear as a dropdown below their parent item. <> indicates a section or subsection that is not itself a flag. [] maps a line to its associated flag as an individually selectable item. () at the same level of indentation indicates mutually exclusive flags. ! indicates a red (dangerous) flag.
===== uispec_flagdescriptions.txt =====
''uispec_flagdescriptions.txt'' (in the ''FreeEnt/server'' directory) maps flag names to their descriptions on the website.
===== Updating the autogenerated files =====
You may notice that just updating all of the above files is insufficient to have the generator and website recognise your new flag. This is because they depend upon information that is compiled / transpiled from these files as opposed to the files directly.
For convenience, there is a ''compile_all_specs.bat'' (Windows) / ''compile_all_specs.sh'' (other operating systems) script available that runs all of the steps necessary to update the compiled files from the base specs. Once you've made all of the changes you need to the above files, simply run this script to make your flag available on the website and to the generator.