Difference between revisions of "Development:Campaigns"

From VsWiki
Jump to: navigation, search
m
m (Switched the example campaign URL from SVN to GH)
 
(23 intermediate revisions by 5 users not shown)
Line 1: Line 1:
=EDITING Campaigns=
+
{{NAV_Manual |
 +
| previous=[[HowTo:Edit faction relationships|Edit faction relationships]]
 +
| up=[[HowTos]]
 +
| 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 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]].''
+
'''''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]].''
  
==Campaign Structure Overview==
+
An example campaign can be found under the following [https://github.com/vegastrike/Assets-Production/blob/master/modules/campaigns.py link]
 
 
An example campaign can be found under the following [http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/vegastrike/data4.x/modules/campaigns.py?rev=HEAD&content-type=text/vnd.viewcvs-markup 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.
 
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.
Line 13: Line 17:
 
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.
 
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 dialogue and strings used by all the various campaign 'nodes.'
+
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. ie you might have a branching storyline depending on whether a flightgroup still exists or not).
+
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 ... CargoMissions 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).
+
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!
 
There was more documentation somewhere, but it appears to have been vanquished. So, ask the inevitable questions!
  
==See Also==
+
=Campaign Structure=
* [[HowTo:Edit_Missions|Editing Missions]]
+
 
 +
==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 |
 +
| previous=[[HowTo:Edit faction relationships|Edit faction relationships]]
 +
| up=[[HowTos]]
 +
| next=[[Development:Missions|Edit Missions]]
 +
}}
 +
 +
[[Category:HowTos|Edit Campaigns]]
 +
[[Category:Development|Edit Campaigns]]

Latest revision as of 18:39, 22 March 2020

arrow_left.png Edit faction relationships arrow_up.png HowTos Edit Missions arrow_right.png

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


arrow_left.png Edit faction relationships arrow_up.png HowTos Edit Missions arrow_right.png