A fixer is a sprite that can be placed in rooms around the base (as long as the rooms have specified "locations" for the fixers to exist in) to allow player interaction in campaigns and/or missions. Most commonly, fixers are found in the bar to offer you various types of missions. Adding a fixer of your own was a long and tedious process, and the results were often less than ideal.
Recently, a new fixer interface was implemented that allows the easy creation of fixers for scripted campaigns or missions. The new system, in keeping with the aim of the simulated universe, is dynamic, and can be made to change depending on the situation of the universe. In addition, script writers are no longer limited to a simple question/answer interaction. A whole "conversation" can be scripted, much like the interactions in games such as the Kings Quest series.
This new system is what I will be teaching you to use in this document.
The classes you will be using to construct your fixer are found in the bases/fixers.py file. The Conversation, Node, RootNode, and SubNode classes define the fixer and the interactions possible.
This class defines the objects that contain the content of any conversation. Each object can be created with content:
sn = SubNode("Ahh, that's right. You're here about the jump drive!", [str(TRIGGER_SAVE) + '#' + str(TRIGGER_VALUE)], ["bases/fixers/no.spr|#\nimport quest_intro\nquest_intro.interactWithJenek(\"nojump\")|Sorry, what else do you have?", "bases/fixers/yes.spr|#\nimport quest_intro\nquest_intro.interactWithJenek(\"yesjump\")|Yeah, I'm interested."], "bases/fixers/merchant.spr", "Talk to Jenek about the jump drive.")Or each object can be created empty, and the content added later:
sn = SubNode() sn.text = "Ahh, that's right. You're here about the jump drive!" sn.conditions = [str(TRIGGER_SAVE) + '#' + str(TRIGGER_VALUE)] sn.choices = ["bases/fixers/no.spr|#\nimport quest_intro\nquest_intro.interactWithJenek(\"nojump\")|Sorry, what else do you have?", "bases/fixers/yes.spr|#\nimport quest_intro\nquest_intro.interactWithJenek(\"yesjump\")|Yeah, I'm interested."] sn.sprite = "bases/fixers/merchant.spr" sn.motext = "Talk to Jenek about the jump drive."
str(TRIGGER_SAVE) + '#' + str(TRIGGER_VALUE)An alternative form is also available, to allow for more complete condition checking. If the condition string is preceded by a '#' character, then the string is assumed to be python code and executed. After the execution, the value stored in the value "result" (this must be created in the executed string) is taken to be the result (True/False) of the condition.
"#\nimport quest_intro\nresult = quest_intro.buyDrive()==%s"%str(DRV_SUCCESS)
The Node class requires significantly less understanding than the SubNode class, as the data for the fixer has already been specified.
The SubNodes belonging to the node can be referenced in a list when the object is created, or added later using the addNode method.
The RootNode class inherits from the Node
class. At the time of writing the only difference is an extra
method, getInitialInfo method. As a result, the RootNode
class has the requirement that any SubNode child
must have valid
The Conversation class requires additional data
about the conversation not presented in the SubNode.
In addition to the list of nodes, the conversation class must
have a valid name string (to reference the fixer), general
preconditions (whether or not the fixer displays at all), and
a string that contains the code to be executed if/when the fixer
is clicked. The conversation object must be accessible through
the reference
Well, other than what I've told you so far, and looking at examples, there isn't mush more that can be tought without doing. That said, there are some details left out of the above descriptions that might come in useful to know ;-)
These fixers, at least at this stage, are currently limited to being created by python code in various parts, most likely quests. To help scripters manage their fixers, there are some functions in the fixers module to help.
queueFixer(playernum, name, scripttext, overwrite=0)The name is simply the name given to identify this fixer, and the scripttext is the string to be executed to retrieve the fixer. overwrite is more of a testing argument (may be removed in future!) that allows you to overwrite any fixer with the same name. If overwrite is not set to 1 and a fixer is already queued with the same name, an error is outputted to the console and the fixer is not updated.
eraseCFixer(playernum, name)This is a lot easier to explain. It deletes the first fixer with the matching name in the fixer queue.
At last, the easy bit to write! This is actually quite simple. Assuming the fixer has been queued using the appropriate functions, when you dock, and the base screens are created, the following will occur.
First each conversation in the queue is retrieved (by executing the string stored with the name), and the fixer objects are created. Then, until the loading room is full (ie the bar has room for 3 fixers), each fixer has it's conversation preconditions evaluated, and if they are good the fixer is drawn using the information retrieved from the RootNode (which is mandatory.
"But wait!" I hear you say. "Aren't we able to add multiple possibilities for the fixers display via SubNodes?" The answer of course is yes. In the display of each node, and this includes the RootNode, each of the SubNodes will have their conditions evaluated, with the first SubNode with all conditions returning true being used. If none of the SubNodes in the Node evaluate as true, the last in the list will be used as a default.
The last question I am able to think of at this late hour is, "How do I end a conversation?" This too is easy, simply make the last subnode used have no choice buttons created!
Now to explain how to do the strings that hold code to be executed. ie the 'alternative' conditions, the choices, and the code referencing the Conversation object.
The format of these strings is simple. Each must be preceded by a # (hash) character. Each must have each line of code seperated by the \n character, and each other special character (quotes, backslash) must also be escaped. In addition, if used as a 'condition,' the result must be stored in a variable called result:
"#import module\nresult = module.testMethod()"If the string is used to retrieve the Conversation object, the object must be stored in a variable called conversation:
"#import quest_module\nconversation = quest_module.getCon()"
See quest_into.py for an example.