Difference between revisions of "Development:Campaigns"
m (Switched the example campaign URL from SVN to GH) |
|||
(27 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
− | |||
− | |||
{{NAV_Manual | | {{NAV_Manual | | ||
− | | previous=[[HowTo: | + | | previous=[[HowTo:Edit faction relationships|Edit faction relationships]] |
| up=[[HowTos]] | | up=[[HowTos]] | ||
− | | next=[[ | + | | next=[[Development:Missions|Edit Missions]] |
}} | }} | ||
---- | ---- | ||
− | = | + | =Editing Campaigns= |
+ | |||
+ | '''''Note:''' The information contained here is useful only to those willing to learn a minimal amount of python code (the language does not need to be learned, only the formatting restrictions imposed). A GUI based campaign editor is in development, but work has stalled due to lack of time on the developer's part. If anyone wishes to assist, please contact [[User:dandandaman|dandandaman]].'' | ||
+ | |||
+ | An example campaign can be found under the following [https://github.com/vegastrike/Assets-Production/blob/master/modules/campaigns.py link] | ||
+ | |||
+ | To write a campaign, you'll require missions. The mission system is what is used by both the mission bbs, and the campaign. As a start, it is good to plan to use only the mission types available, as writing your own can be a pain, and requires a degree of proficiency with python and vegastrike which you may not have for a little while. If you are developing a campaign and do require something specific that isn't available, just ask, and we'll see what can be done. | ||
+ | |||
+ | Since you are then limited to the missions we have, you'll want a nice reference about what each of the variables for each mission are ... and what they do. We don't actually have one ;-) but we have something that is almost as good: verify_missions.py At the bottom of this file is a list of the mission types, and slightly more descriptive labels for the arguments (their meanings should be relatively easy to glean from that info, but ask if you're unsure :-) ). | ||
+ | |||
+ | That is unfortunately the easy part. The harder part (easy once you understand it) is then writing the campaign. The campaign for Vega Strike is stored in campaigns.py. | ||
+ | |||
+ | Before we proceed I think it's best if you look at this stuff and try to understand a little bit yourself. At the top of campaigns.py is a whole bunch of text, the dialog and strings used by all the various campaign 'nodes.' | ||
+ | |||
+ | A 'node' is a branch of a campaign, it is where fixers get created and the decision to accept or deny a mission occurs, or where the campaign is branched automatically (depending on some conditions that you might want to specify. I.e. you might have a branching storyline depending on whether a flight group still exists or not). | ||
+ | |||
+ | Typically, each node contains a mission ... Cargo missions are a special case, they have a special type of node wrapper, and are probably the easiest to start with. Anyway, get yourself a text editor with syntax highlighting for python, and have a look at the nodes in the function ''LoadTestCampaign'' (this is a short test campaign that I made to demonstrate a bug). | ||
+ | |||
+ | There was more documentation somewhere, but it appears to have been vanquished. So, ask the inevitable questions! | ||
+ | |||
+ | =Campaign Structure= | ||
+ | |||
+ | ==Campaign Integration== | ||
+ | |||
+ | You will need to create a new campaign python file (e.g. ''campaign_mycamp.py''), which then will be integrated into the ''campaigns.py'' with the following additions: | ||
+ | <code><pre> | ||
+ | import campaign_mycampaign | ||
+ | |||
+ | campaignsloaders = [ ... | ||
+ | lambda:campaign_mycamp.LoadMyCampaign() | ||
+ | ] | ||
+ | </pre></code> | ||
+ | |||
+ | The basic structure of your new ''campaign_mycamp.py'' file will then be: | ||
+ | |||
+ | <code><pre> | ||
+ | import universe | ||
+ | import campaign_lib | ||
+ | from campaign_lib import * | ||
+ | |||
+ | ### define node dialogs | ||
+ | ... | ||
+ | |||
+ | ### define fixer sprites | ||
+ | ... | ||
+ | |||
+ | def LoadMyCampaign(): | ||
+ | |||
+ | ### define nodes | ||
+ | ... | ||
+ | |||
+ | # savegame_variable (can't contain spaces) | ||
+ | vs = Campaign("campaign_mycamp") | ||
+ | # the starting node. | ||
+ | vs.Init(FirstNode) | ||
+ | |||
+ | ### define node missions | ||
+ | ... | ||
+ | |||
+ | # finalize campaign definition | ||
+ | return vs | ||
+ | </pre></code> | ||
+ | |||
+ | |||
+ | = Campaign Missions = | ||
+ | |||
+ | ==Nodes== | ||
+ | |||
+ | There are 2 types of nodes: | ||
+ | * ''CampaignClickNode(args)'' - here you have to talk to a fixer and accept or reject the proposed mission. | ||
+ | * ''CampaignNode(args)'' - creates an action, like adding reward or adjusting a faction relation, without fixer intervention. | ||
+ | * ''CampaignChoiceNode(args)'' | ||
+ | |||
+ | Nodes must be initialized first: | ||
+ | |||
+ | <code><pre> | ||
+ | ThisNode = CampaignClickNode() # initialize the node | ||
+ | ActionNode = CampaignNode() | ||
+ | </pre></code> | ||
+ | |||
+ | The nodes are then connected with the last four arguments in the ''MakeMission'' function: | ||
+ | <code><pre>MakeMission(vs, ... | ||
+ | RejectNode, # If you reject the mission twice. "None" means that he continues asking you forever until you accept | ||
+ | LoseNode, # Node if you lose the mission | ||
+ | WinNode, # Node if you win the mission | ||
+ | ThisNode) # The node for this mission | ||
+ | </pre></code> | ||
+ | |||
+ | The complete mission statement and the different types of mission nodes that can be created are described in the following section. Further down, you will find the singular structure of the arguments. | ||
+ | |||
+ | ==MakeMission Node== | ||
+ | <code><pre>MakeMission(savegame_variable, fixer_sprite, start_location, end_location, click_script, | ||
+ | mission_script, mission_arguments, completion_script, dialog_dictionary, reject_node, lose_node, | ||
+ | win_node, this_node)</pre></code> | ||
+ | |||
+ | ==MakeCargoMission Node== | ||
+ | <code><pre>MakeCargoMission(savegame_variable, fixer_sprite, start_location, end_location, | ||
+ | click_script, mission_script, mission_arguments, completion_script, dialog_dictionary, | ||
+ | reject_node, lose_node, win_node, this_node)</pre></code> | ||
+ | |||
+ | ==MakeNoFailureCargoMission Node== | ||
+ | <code><pre>MakeNoFailureCargoMission(savegame_variable, fixer_sprite, start_location, end_location, | ||
+ | click_script, mission_script, mission_arguments, completion_script, dialog_dictionary, | ||
+ | reject_node, lose_node, win_node, this_node)</pre></code> | ||
+ | |||
+ | ==Init Node== | ||
+ | <code><pre>NodeName.Init(savegame_variable, start_location, dialog_dictionary, fixer_sprite, | ||
+ | subnode_script, completion_script, next_node)</pre></code> | ||
+ | |||
+ | ==Mission Arguments== | ||
+ | |||
+ | * '''savegame_variable''' - usually ''vs'' | ||
+ | * '''fixer_sprite''' - sprite file for the fixer in the form (sprite_path, display_name, full_screen_sprite), e.g. ''("campaign/captain.sprite","Talk_To_The_Captain","campaign/heads/captain.sprite")'' | ||
+ | * '''start_location''' - a command for location checking containing a tuple with (sector/system, base), e.g. ''[InSystemCondition("Crucible/Cephid_17","Serenity")]'' | ||
+ | * '''end_location''' - same format as start_location | ||
+ | * '''click_script''' - script to be run as you click on the fixer. A common use is to ''AddCredits()'' for the previous mission. | ||
+ | * '''mission_script''' - script to be run to start the mission (usually ''None'' if you don't have a script, but ambush is also common.) | ||
+ | * '''mission_arguments''' - depend on the mission. E.g. for cargo mission you will give the loaded cargo as arguments: ''("Recycled_Plastics",50,False)'' | ||
+ | * '''completion_script''' - script to be set on completion (-1=Failure, 0=Not Accepted, 1=Succeed, 2=In progress), often just ''vs.name+"_mission"'' | ||
+ | * '''dialog_dictionary''' - the lines that the fixer says: a dictionary with {dialog_type, dialog_lines_list}, see structure description further down | ||
+ | * '''reject_node''' - name of the node if you reject the mission twice. "None" means that fixer continues asking you forever until you accept | ||
+ | * '''lose_node''' - next node if you are unsuccessful in the mission | ||
+ | * '''win_node''' - next node if you successfully complete the mission | ||
+ | * '''this_node''' - name of the node for this mission, e.g. ''FirstNode'' | ||
+ | |||
+ | = Subnodes = | ||
+ | |||
+ | * TrueSubnode | ||
+ | * GoToSubnode | ||
+ | * GoToSubnodeIfTrue | ||
+ | * TrueBackwardsSubnode | ||
+ | |||
+ | = Conditions = | ||
+ | |||
+ | * SaveVariableCondition | ||
+ | * HaveCredits | ||
+ | * InSystemCondition | ||
+ | * HasUndocked | ||
+ | * CargoSpaceCondition | ||
+ | * AtMostActiveMissionsCondition | ||
+ | * AtLeastActiveMissionsCondition | ||
+ | * OrCondition | ||
+ | * AndCondition | ||
+ | * InvertCondition | ||
+ | |||
+ | = Scripts = | ||
+ | |||
+ | Scripts and script arguments can be seen in ''data/modules/campaign_lib.py''. | ||
+ | The usual arguments are: (scriptarguments, nextscript), where the next script can be omitted and is nested as the last script argument. | ||
+ | |||
+ | * AddCredits(numcreds,nextscript) | ||
+ | * AddCargo(name,num,missionflag,nextscript) | ||
+ | * RemoveCargo(name,num,missionflag,nextscript) | ||
+ | * SetSaveVariable(varname,varvalue,nextscript) | ||
+ | * IncSaveVariable(varname,nextscript) | ||
+ | * AddTechnology(technology,nextscript) | ||
+ | * AdjustRelation(us,them,change,nextscript) | ||
+ | * ClearFactionRecord(fac,newrelation,nextscript) | ||
+ | * ClearRecord(nextscript) | ||
+ | * PushRelation(faction,nextscript) | ||
+ | * PopRelation(faction,nextscript) | ||
+ | * LaunchWingmen(faction,shiptype,num,nextscript) | ||
+ | * ChangeSystemOwner(system,faction,nextscript) | ||
+ | * SaveVariableGreaterScript(var,val,nextscript) | ||
+ | * DisplayTextIfTrueScript(text,nextscript) | ||
+ | * RemoveCredits(numcreds,nextscript) | ||
+ | * SetCredits(numcreds,nextscript) | ||
+ | * PushCredits(nextscript) | ||
+ | * PopCredits(nextscript) | ||
+ | * PushNews(story,nextscript) | ||
+ | * LoadMission(name,missionname,missionargs,nextscript=None,briefing='',briefing_done='',vars=None,vars_done=None) | ||
+ | * AddSprite(name,sprite,pos,nextscript) | ||
+ | * AddPythonSprite(name,sprite,center_position,widthheight,text,python,nextscript) | ||
+ | * AddRemovingSprite | ||
+ | * AddConversationStoppingSprite | ||
+ | * GoToSubnodeIfTrue | ||
+ | * TrueSubnode | ||
+ | * TrueBackwardsSubnode | ||
+ | * GoToSubnode | ||
+ | |||
+ | Remarks: | ||
+ | * relation is saved as two bidirectional values ranging from -0.5 to 1.0 in a variable "Relation_to_''factionname''" with two float values appended. One is the players relation to that faction, the second is the faction relation to the player. | ||
− | + | = Dialogs = | |
− | + | The structure for the ''dialog_dictionary'' is as follows: | |
− | |||
− | + | <code><pre> | |
+ | dialog_dictionary = { | ||
+ | "intro": [conversation], | ||
+ | "reject1": [conversation], | ||
+ | "reconsider":[conversation], | ||
+ | "reject2": [conversation], | ||
+ | "accept": [conversation], | ||
+ | "accept2": [conversation], | ||
+ | "reminder": [conversation], | ||
+ | "failure": [conversation], | ||
+ | } | ||
− | + | with: | |
− | + | conversation = (speaker1, line1, ...), (speaker2, line1, ...), ..., soundfile | |
− | + | </pre></code> | |
− | |||
− | |||
− | |||
− | |||
− | + | For Init Nodes you will supply only one ''[conversation]''. | |
− | |||
− | |||
− | + | = Examples = | |
− | |||
− | |||
− | |||
− | + | ==The first major campaign written (Jenek)== | |
− | |||
− | + | See also the [http://forums.vega-strike.org/viewtopic.php?t=3246 forum thread] (comments & feedback) when this mission was first put into SVN. | |
− | + | {{Fixme}} Improve/expand it? | |
− | |||
− | |||
− | |||
− | |||
− | + | ==Ending Node with AddCredits and AdjustRelation== | |
+ | <code><pre> | ||
+ | FinalNode.Init(vs, # savegame variable | ||
+ | [], #start location | ||
+ | [], #dialog | ||
+ | None, # fixer sprite | ||
+ | TrueSubnode(AddCredits(100000,AdjustRelation("privateer","pirates",0.01))), # subnode script | ||
+ | None, # completion script | ||
+ | [CampaignEndNode(vs)]) # continue with next mission | ||
+ | </pre></code> | ||
− | * [[ | + | =See Also= |
+ | * [[Development:Missions|Editing Missions]] | ||
* [[HowTo:Add_Conversations|Adding Conversations]] | * [[HowTo:Add_Conversations|Adding Conversations]] | ||
---- | ---- | ||
{{NAV_Manual | | {{NAV_Manual | | ||
− | | previous=[[HowTo: | + | | previous=[[HowTo:Edit faction relationships|Edit faction relationships]] |
| up=[[HowTos]] | | up=[[HowTos]] | ||
− | | next=[[ | + | | next=[[Development:Missions|Edit Missions]] |
}} | }} | ||
[[Category:HowTos|Edit Campaigns]] | [[Category:HowTos|Edit Campaigns]] | ||
+ | [[Category:Development|Edit Campaigns]] |
Latest revision as of 17:39, 22 March 2020
Edit faction relationships | HowTos | Edit Missions |
Contents
Editing Campaigns
Note: The information contained here is useful only to those willing to learn a minimal amount of python code (the language does not need to be learned, only the formatting restrictions imposed). A GUI based campaign editor is in development, but work has stalled due to lack of time on the developer's part. If anyone wishes to assist, please contact dandandaman.
An example campaign can be found under the following link
To write a campaign, you'll require missions. The mission system is what is used by both the mission bbs, and the campaign. As a start, it is good to plan to use only the mission types available, as writing your own can be a pain, and requires a degree of proficiency with python and vegastrike which you may not have for a little while. If you are developing a campaign and do require something specific that isn't available, just ask, and we'll see what can be done.
Since you are then limited to the missions we have, you'll want a nice reference about what each of the variables for each mission are ... and what they do. We don't actually have one ;-) but we have something that is almost as good: verify_missions.py At the bottom of this file is a list of the mission types, and slightly more descriptive labels for the arguments (their meanings should be relatively easy to glean from that info, but ask if you're unsure :-) ).
That is unfortunately the easy part. The harder part (easy once you understand it) is then writing the campaign. The campaign for Vega Strike is stored in campaigns.py.
Before we proceed I think it's best if you look at this stuff and try to understand a little bit yourself. At the top of campaigns.py is a whole bunch of text, the dialog and strings used by all the various campaign 'nodes.'
A 'node' is a branch of a campaign, it is where fixers get created and the decision to accept or deny a mission occurs, or where the campaign is branched automatically (depending on some conditions that you might want to specify. I.e. you might have a branching storyline depending on whether a flight group still exists or not).
Typically, each node contains a mission ... Cargo missions are a special case, they have a special type of node wrapper, and are probably the easiest to start with. Anyway, get yourself a text editor with syntax highlighting for python, and have a look at the nodes in the function LoadTestCampaign (this is a short test campaign that I made to demonstrate a bug).
There was more documentation somewhere, but it appears to have been vanquished. So, ask the inevitable questions!
Campaign Structure
Campaign Integration
You will need to create a new campaign python file (e.g. campaign_mycamp.py), which then will be integrated into the campaigns.py with the following additions:
import campaign_mycampaign
campaignsloaders = [ ...
lambda:campaign_mycamp.LoadMyCampaign()
]
The basic structure of your new campaign_mycamp.py file will then be:
import universe
import campaign_lib
from campaign_lib import *
### define node dialogs
...
### define fixer sprites
...
def LoadMyCampaign():
### define nodes
...
# savegame_variable (can't contain spaces)
vs = Campaign("campaign_mycamp")
# the starting node.
vs.Init(FirstNode)
### define node missions
...
# finalize campaign definition
return vs
Campaign Missions
Nodes
There are 2 types of nodes:
- CampaignClickNode(args) - here you have to talk to a fixer and accept or reject the proposed mission.
- CampaignNode(args) - creates an action, like adding reward or adjusting a faction relation, without fixer intervention.
- CampaignChoiceNode(args)
Nodes must be initialized first:
ThisNode = CampaignClickNode() # initialize the node
ActionNode = CampaignNode()
The nodes are then connected with the last four arguments in the MakeMission function:
MakeMission(vs, ...
RejectNode, # If you reject the mission twice. "None" means that he continues asking you forever until you accept
LoseNode, # Node if you lose the mission
WinNode, # Node if you win the mission
ThisNode) # The node for this mission
The complete mission statement and the different types of mission nodes that can be created are described in the following section. Further down, you will find the singular structure of the arguments.
MakeMission Node
MakeMission(savegame_variable, fixer_sprite, start_location, end_location, click_script,
mission_script, mission_arguments, completion_script, dialog_dictionary, reject_node, lose_node,
win_node, this_node)
MakeCargoMission Node
MakeCargoMission(savegame_variable, fixer_sprite, start_location, end_location,
click_script, mission_script, mission_arguments, completion_script, dialog_dictionary,
reject_node, lose_node, win_node, this_node)
MakeNoFailureCargoMission Node
MakeNoFailureCargoMission(savegame_variable, fixer_sprite, start_location, end_location,
click_script, mission_script, mission_arguments, completion_script, dialog_dictionary,
reject_node, lose_node, win_node, this_node)
Init Node
NodeName.Init(savegame_variable, start_location, dialog_dictionary, fixer_sprite,
subnode_script, completion_script, next_node)
Mission Arguments
- savegame_variable - usually vs
- fixer_sprite - sprite file for the fixer in the form (sprite_path, display_name, full_screen_sprite), e.g. ("campaign/captain.sprite","Talk_To_The_Captain","campaign/heads/captain.sprite")
- start_location - a command for location checking containing a tuple with (sector/system, base), e.g. [InSystemCondition("Crucible/Cephid_17","Serenity")]
- end_location - same format as start_location
- click_script - script to be run as you click on the fixer. A common use is to AddCredits() for the previous mission.
- mission_script - script to be run to start the mission (usually None if you don't have a script, but ambush is also common.)
- mission_arguments - depend on the mission. E.g. for cargo mission you will give the loaded cargo as arguments: ("Recycled_Plastics",50,False)
- completion_script - script to be set on completion (-1=Failure, 0=Not Accepted, 1=Succeed, 2=In progress), often just vs.name+"_mission"
- dialog_dictionary - the lines that the fixer says: a dictionary with {dialog_type, dialog_lines_list}, see structure description further down
- reject_node - name of the node if you reject the mission twice. "None" means that fixer continues asking you forever until you accept
- lose_node - next node if you are unsuccessful in the mission
- win_node - next node if you successfully complete the mission
- this_node - name of the node for this mission, e.g. FirstNode
Subnodes
- TrueSubnode
- GoToSubnode
- GoToSubnodeIfTrue
- TrueBackwardsSubnode
Conditions
- SaveVariableCondition
- HaveCredits
- InSystemCondition
- HasUndocked
- CargoSpaceCondition
- AtMostActiveMissionsCondition
- AtLeastActiveMissionsCondition
- OrCondition
- AndCondition
- InvertCondition
Scripts
Scripts and script arguments can be seen in data/modules/campaign_lib.py. The usual arguments are: (scriptarguments, nextscript), where the next script can be omitted and is nested as the last script argument.
- AddCredits(numcreds,nextscript)
- AddCargo(name,num,missionflag,nextscript)
- RemoveCargo(name,num,missionflag,nextscript)
- SetSaveVariable(varname,varvalue,nextscript)
- IncSaveVariable(varname,nextscript)
- AddTechnology(technology,nextscript)
- AdjustRelation(us,them,change,nextscript)
- ClearFactionRecord(fac,newrelation,nextscript)
- ClearRecord(nextscript)
- PushRelation(faction,nextscript)
- PopRelation(faction,nextscript)
- LaunchWingmen(faction,shiptype,num,nextscript)
- ChangeSystemOwner(system,faction,nextscript)
- SaveVariableGreaterScript(var,val,nextscript)
- DisplayTextIfTrueScript(text,nextscript)
- RemoveCredits(numcreds,nextscript)
- SetCredits(numcreds,nextscript)
- PushCredits(nextscript)
- PopCredits(nextscript)
- PushNews(story,nextscript)
- LoadMission(name,missionname,missionargs,nextscript=None,briefing=,briefing_done=,vars=None,vars_done=None)
- AddSprite(name,sprite,pos,nextscript)
- AddPythonSprite(name,sprite,center_position,widthheight,text,python,nextscript)
- AddRemovingSprite
- AddConversationStoppingSprite
- GoToSubnodeIfTrue
- TrueSubnode
- TrueBackwardsSubnode
- GoToSubnode
Remarks:
- relation is saved as two bidirectional values ranging from -0.5 to 1.0 in a variable "Relation_to_factionname" with two float values appended. One is the players relation to that faction, the second is the faction relation to the player.
Dialogs
The structure for the dialog_dictionary is as follows:
dialog_dictionary = {
"intro": [conversation],
"reject1": [conversation],
"reconsider":[conversation],
"reject2": [conversation],
"accept": [conversation],
"accept2": [conversation],
"reminder": [conversation],
"failure": [conversation],
}
with:
conversation = (speaker1, line1, ...), (speaker2, line1, ...), ..., soundfile
For Init Nodes you will supply only one [conversation].
Examples
The first major campaign written (Jenek)
See also the forum thread (comments & feedback) when this mission was first put into SVN. FIXME Improve/expand it?
Ending Node with AddCredits and AdjustRelation
FinalNode.Init(vs, # savegame variable
[], #start location
[], #dialog
None, # fixer sprite
TrueSubnode(AddCredits(100000,AdjustRelation("privateer","pirates",0.01))), # subnode script
None, # completion script
[CampaignEndNode(vs)]) # continue with next mission
See Also
Edit faction relationships | HowTos | Edit Missions |