Difference between revisions of "Development:Missions"

From VsWiki
Jump to: navigation, search
Line 12: Line 12:
 
Vega Strike has a powerful missions scripting interface that allows you to nearly modify Vega Strike at whatever level you choose, from the AI scripts (and the physics thereof) to the missions... to the overall plot and goal of the game.
 
Vega Strike has a powerful missions scripting interface that allows you to nearly modify Vega Strike at whatever level you choose, from the AI scripts (and the physics thereof) to the missions... to the overall plot and goal of the game.
  
==Basic XML integration==
+
==[[HowTo:Edit_Missions:XML_integration|Basic XML integration]]==
First let me start by explaining how to run a python class... in this case we start with the prebuild "privateer.py" script to run our mission. Open up mission/explore_universe.mission First come the variables that designate how the mission should normally start. By normally I mean if they don't have any save game present.
 
  
===Variables Section===
+
==[[HowTo:Edit_Missions:Python:Missions|Python Inheritance In Missions]]==
It loads Crucible/Cephid_17.system as the star system (which is in XML and stores all present planets) After this, comes the actors in the mission, the flightgroups of fighters. If a mission has more than one player, then each player is the leader (first ship) in each respective flightgroup.
 
 
 
Notice that so far there are a lot of values you don't see for long in the default vegastrike game. Credits, System, first flightgroup fighter, difficulty--these are all initial values but can change.... since there is specified a savegame. The savegame makes the mission more of a campaign type mission where things may be saved to disk and reloaded at a later point.  If no savegame variable is specified (this is where the autosave goes) then the mission will not save and will be a one time play mission.
 
 
 
<pre>
 
<mission>
 
<settings>
 
<origin  planet="earth" x="0000" y="100" z="000.0"/>
 
</settings>
 
 
 
<variables>
 
<!-- <var name="continuousterrain" value="continuousterrain.txt"/>-->
 
<var name="credits" value="13500"/>
 
<var name="defaultplayer" value="blue"/>
 
<var name="mission_name" value="Privateer Mission" />
 
<var name="difficulty" value=".05"/><!--starting difficulty-->
 
<var name="system" value="Crucible/Cephid_17" />
 
<var name="description" value="Like, as if." />
 
<var name="savegame" value="explore_universe_difficulty" />
 
</variables>
 
</pre>
 
 
 
 
 
===Flightgroup Section===
 
The rot flag is ignored, and the position specifies the x,y,z coords of the Shlimazel flightgroup, which is of type llama.begin.
 
 
 
<pre>
 
        <flightgroups>
 
                <flightgroup name="Shlimazel" faction="privateer" type="llama.begin"
 
                                  ainame="default" waves="1" nr_ships="1">
 
<pos x="-95124024.543917" y="412089916.256812"
 
                            z="-110779667.398050"/> 
 
<rot x="180.0" y="180.0" z="180.0"/>
 
<order order="tmptarget" target="omikron"/>
 
<order priority="0" order="superiority" target="enemy"/>
 
<order priority="1" order="bomber" target="omikron"/>
 
<order priority="2" order="escort" target="blue"/>
 
</flightgroup>
 
        </flightgroups>
 
</pre>
 
 
 
===Python Section===
 
Future note: If you wanted to add a campaign to the privateer mission you would most likely modify privateer.py to have a campaign module get loaded.... so you wouldn't necessarily need to modify explore_universe.mission just to add a campaign... lets dig further into the meat of the scripting.
 
 
 
If a mission has python tags...then it is a python mission and may have some embedded python in it. In this case the python makes a new python object of type privateer in the privateer.py module in the modules/ directory.
 
 
 
 
 
<pre>
 
<python>
 
                from privateer import privateer
 
                my_obj=privateer(8000,40000,500,3,2,.6,.25,.1,400000,2000)
 
</python>
 
</mission>
 
</pre>
 
 
 
You have to be careful about newlines, etc in the XML, but it shoudl usually work just dandy. You notice that the mission may pass in arguments to the privateer module that can make slight changes in teh gameplay. This allows many cargo missions to use the same module in order to change the parameters of the cargo missions. In this case the values I have selected appear to work relatively well.
 
 
 
==Python Inheritance In Missions==
 
 
 
Ok lets dig further into the privateer.py module:
 
 
 
It is a very short module written in more or less pure python... I say more or less for a very specific reason: it inherits from a C++ class!
 
 
 
Director is a C++ class exported to python. All missions must inherit from director.... in this case the mission is very short as it uses other (pure python) modules. random_encounters.random_encounters, difficulty.difficulty and ,trading.trading all do NOT inherit from C++ as this class takes care of the C++ inheritance.
 
 
 
The constructor __init__ takes the arguments that we passed in from the XML file and passes them on to the various submodules. The only really really important thing you do in the constructor is call
 
 
 
  Director.Mission.__init__ (self)
 
If you do not do this, Vegastrike will not load your module and will silently fail (this calls the C++ constructor, which actually triggers the binding of your module to the python mission).
 
 
 
Each mission module must have an execute function so that something can happen every physics frame. In this case it just calls execute on all the submodules... not very interesting.
 
 
 
Missions also may have 2 functions:
 
 
 
Pickle (self) which returns a string with a XML mission file that will load the mission properly as well as a string representing the mission in serialized form... if the mission were reloaded that mission would be loaded and its corresponding python would be run.
 
Then Unpickle (self, str) would be called (with str being a string) and the expectation that the mission would be reloaded. data/newmission.py has an example of this.
 
You may also choose to save data in a float by float format as follows (the advantage to this format is that it is saved on a PER PLAYER basis... so that each player may have a different value):
 
 
 
There are 4 functions at your disposal for this older way to save missions:
 
 
 
This function adds the key for later retrieval (even after VS quits ) and pushes the float to the end of the hashtable indexed by stringkey:
 
 
 
<pre>
 
  myindex=Director.pushSaveData(int whichplayer, stringkey,floatVal);
 
</pre>
 
 
 
This function modifies a index in a key that has already been pushed back at one point:
 
 
 
<pre>
 
  Director.putSaveData (intWhichPlayer, stringkey, myIndex, floatVal);
 
</pre>
 
 
 
This function gets the number of float values for a given player:
 
 
 
<pre>
 
  Director.getSaveDataLength (int whichplayer, string key)
 
</pre>
 
 
 
This function gets save data saved at that number:
 
 
 
<pre>
 
  Director.getSaveData(int whichplayer, stringkey, int num)
 
</pre>
 
 
 
You can make up stringkeys and save only 1 float per key if you wish... each key is allowed to have an vector of possible values. Use as you wish! For the privateer mission I save things with key 31337ness and the one value in there is the difficulty that I write out.... If a campaign is more global rather than player specific you may just choose to pickle it...however if something is on a per-player basis, it may be best going in these SaveData float vectors.
 
 
 
With the exception of AI modules from here on out it's straight python. I will go over some of the functions you can call inside Vega Strike in a minute.
 
  
 
==Python Inheritance with AI Scripts ==
 
==Python Inheritance with AI Scripts ==

Revision as of 07:08, 21 March 2005

arrow_left.png Add Conversation arrow_up.png HowTo Edit News arrow_right.png

EDITING MISSIONS

Introduction

Vega Strike has a powerful missions scripting interface that allows you to nearly modify Vega Strike at whatever level you choose, from the AI scripts (and the physics thereof) to the missions... to the overall plot and goal of the game.

Basic XML integration

Python Inheritance In Missions

Python Inheritance with AI Scripts

Let me talk about AI scripts briefly.

You'll notice a nice AI script named printhello.py in the data/printhello.py. It is very similar to a mission with a few notable differences.

  • First of all the AI class you make must inherit from the class VS.PythonAI.
  • Secondly instead of having an __init__ function it must have an init() function. The reasons for this have something to do with how inheritance works from C++, but this init function takes in self of course and the unit to which thie AI script belongs. __init__ may start off some AI scripts from the get go, or you may wait until you execute each frame. in this case printhello loads the XML script ++turntowards.xml (which is the hard coded C++ equivalent to ai/scripts/turntowards.xml). After loading that AI script it replaces its parents order with the last AI script, in this case the XML script. In the Execute function this AI script calls Execute on the higher level (which takes care of responding to communications) and then prints h to the console for no apparent reason.
  • Lastly and most importantly, the AI script makes a new class for itself. So when the C++ code executes printhello.py it will call the C++ constructor which will take care of binding it. So when you make a new unit like so:
 
VS.launch (fgname,type,faction,"unit","printhello.py",nr_ships,nr_waves,vec,logo)

it will have this python AI attached to it. However if you are too lazy to write AI's you can use the most excellent default AI

VS.launch (fgname,type,faction,"unit","default",nr_ships,nr_waves,vec,logo)

Summary about python-Vegastrike class relationships

  • Missions must inherit from Director.Mission
  • AI's must inherit from VS.PythonAI
  • Missions must have an __init__ function with any number of arguments that calls Director.Mission.__init__ (self); to trigger the C++ binding
  • AI's must have an init(self, un) function with those exact 2 arguments... the second one is the parent Unit. I don't recommend you save the unit currently--I'm not sure if it will garbage collect the unit in that case... just call the self.GetParent() function as the AI script.
  • Both AI's and Missions may have an Execute(self) function that does somethign each frame depending on the disired result.
  • Missions may have a Pickle and Unpickle function in order to save and load the state of a campaign (this can be incredibly complex!)

Python - Vega Strike Bindings

Vegastrike Utility Functions

Now that you can run complex python scripts that could even play minesweeper at the prompt, you probably want to interact with Vegastrike directly and "do" stuff like make ships, set objectives, and generally give the player a hard time.

First of all let me talk about the "stubs". We have had a clever idea to make missions somewhat testable outside the framework of vegastrike by autogenerating stub functions. The way you make stub functions is by using the C++ processor in 2 steps...

cd src/python gcc -E -DPYTHON_STUB=1 unit_wrapper.cpp > /data/modules/stub/VS.py

then edit that file and replace with (newline)(space)(space) and you have a sample python stub so you can find out every mission that vegastrike exports.

we try to keep these stubs as up to date as possible. Anyhow this lets you test your missions at the prompt by adding the two directories

 import sys
 sys.path = sys.path + ['/home/blah/data/modules']
 sys.path = sys.path + ['/home/blah/data/modules/stub']
 import VS
 ...

and then you can run your mission and see if it at least sort of works.

the stub file also gives you a good idea about what functions are available for you in your missions.

I will try to give a brief description here...

Feel free to add to the descritptions or to ask me about any that are unclear. I give you as follows the VS functions C++ names so you get type information as well! Note that these comments are also included in src/universe_util.h


Universe_util.h

//this function sets the "current" system to be "name"  where name may be something like
 //"sol_sector/sol"  or "vega_sector/vega"   this function may take some time if the system
 //has not been loaded before

        void pushSystem (string name);

 //this function restores the active system.... there must be an equal number of pushSystems
 //and popSystems or else Vega Strike may behave unpredictably
        void popSystem ();
 //This function gets the current system's official name
        string getSystemFile();
 //this function gets the current system's nickname (not useful)
        string getSystemName();
 //this function gets an iterator into the units in the current system... do NOT keep an iterator
 //across a frame--it may get deleted!
        un_iter getUnitList();
 //This function gets a unit given a number (how many iterations to go down in the iterator)
        Unit *getUnit(int index);
 //this function launches a wormhole or ajump point.
        Unit *launchJumppoint(string name_string,
                        string faction_string,
                        string type_string,
                        string unittype_string,
                        string ai_string,
                        int nr_of_ships,
                        int nr_of_waves,
                        QVector pos,
                        string squadlogo,
                        string destinations);
 //this function launches a normal fighter  the name is the flightgroup name, the type is the ship type,
 //the faction is who it belongs to, the unittype is usually "unit" unless you want to make asteroids or
 //nebulae or jump points or planets.  the aistring is either a python filename or "default"  the nr of
 //ships is the number of ships to be launched with this group, the number of waves is num reinforcements...
 //the position is a tuple (x,y,z) where they appear and the squadlogo is a squadron image...you can leave
 //this the empty string '' for the default squadron logo.
        Unit* launch (string name_string,string type_string,string faction_string,string unittype,
                string ai_string,int nr_of_ships,int nr_of_waves, QVector pos, string sqadlogo);
 //this gets a random cargo type (useful for cargo missions) from either any category if category is ''
 //or else from a specific category  'Contraband'  comes to mind!
        Cargo getRandCargo(int quantity, string category);
 //This gets the faction name of an index
        string GetFactionName(int index);
 //this gets an index given a faction name
        int GetFactionIndex(string name);
 //this gets a relationship between two factions
        float GetRelation(string myfaction,string theirfaction);
 //this changes the relaationship based on the rank and how mad or happy they are (the relationship
 //is between 0 and 1... so a adjustment of .01 with a rank of 1 is QUITE significant.
        void AdjustRelation(string myfaction,string theirfaction, float factor, float rank);
 //this gets the number of factions in game
        int GetNumFactions ();
 //this gets the current time in seconds
        float GetGameTime ();
 //this sets the time compresison value to zero
        void SetTimeCompression ();
 //this gets a string which has in it a space delimited list of neighmoring systems
        string GetAdjacentSystem (string str, int which);
 //this gets a specific property of this system as found in universe/milky_way.xml
        string GetGalaxyProperty (string sys, string prop);
 //this gets the number of systems adjacent to the sysname
        int GetNumAdjacentSystems (string sysname);
 //this adds a playlist to the music and may be triggered with an int
        int musicAddList(string str);
 //this plays a specific song
        void musicPlaySong(string str);
 //this plays msuci from a given list (where the int is what was returned by musicAddList)
        void musicPlayList(int which);
 //this gets the difficutly of the game... ranges between 0 and 1... many missions depend on it
 //never going past .99 unless it's always at one.
        float GetDifficulty ();
 //this sets the difficulty
        void SetDifficulty (float diff);
 //this plays a sound at a location...if the sound has dual channels it will play in the center
        void playSound(string soundName, QVector loc, Vector speed);
 //this plays an image (explosion or warp animation) at a location
        void playAnimation(string aniName, QVector loc, float size);
 //this ends the mission with either success or failure
        void terminateMission(bool term);
 //this gets the player belonging to this mission
        Unit *getPlayer();
 //this gets a player number (if in splitscreen mode)
        Unit *getPlayerX(int which);
 //this gets the number of active players
        int getNumPlayers ();
 //this adds an objective for the cockpit to view ("go here and do this')
        int addObjective(string objective);
 //this sets the objective's completeness (the int was returned by add objective)
        void setObjective(int which, string newobjective);
 //this sets the completeness of a particular objective... chanigng the color onscreen
        void setCompleteness(int which, float completeNess);
 //this gets that completeness
        float getCompleteness(int which);
 //this sets the owner of a completeness
        void setOwner(int which,Unit *owner);
 //this gets an owner of a completeness (NULL means all players can see this objective)
        Unit* getOwner(int which);
 //this sends an IO message... I'm not sure if delay currently works, but from, to and message
 //do :-) ... if you want to send to the bar do "bar" as the to string... if you want to make
 //news for the news room specify "news"
    void IOmessage(int delay,string from,string to,string message);
 //this gets a unit with 1 of each cargo type in it
        Unit *GetMasterPartList ();
 //this gets a unit with a faction's contraband list... may be null (check with isNull)
        Unit *GetContrabandList (string faction);
 //this sets whether or not a player may autopilot.  Normally they are both 0 and the autopiloting
 //is allowed based on if enemies are near... if you pass in 1 then autopilot will be allowed no
 //matter who is near... if you set -1 then autopilot is never allowed.  global affects all
 //players... player just affects the player who accepted the mission.
        void SetAutoStatus (int global_auto, int player_auto);

VS.py

Many of these functions return Unit * (for python just Unit) However there is a very critical difference between a Python Unit and a C++ Unit *. If you have a Python Unit you may keep it across frames... it may turn null (check with isNull()), but you can still keep it.... (if it dies is when it turns null)... in C++ if you keep a Unit * across a frame you WILL cause random segfaults (bad things, hard to find) but ya...Python it's safe to keep Unit's returned by functions...just be aware that between frames they may die and then bet tested Null with isNull().

The following comments are done with the python class as the arguments are clearly listed in modules/stub/VS.py function :-)

class Unit:
 #don't call this one :-) it's just there in the stub ... really make units with the VS.launch above
  def __init__(self):
    print 'Unit constructor called with (self)'

 # 1 "python_unit_wrap.h" 1

 #make this unit warp to "un" and possibly ignore friendly targets. when computing if allowed
  def AutoPilotTo(self,un,ignore_friendlies):
   print "AutoPilotTo"
   return 0
 #sets the turret AI to actually fire a turret
  def SetTurretAI(self):
   print "SetTurretAI"
 #tells teh turret AI never to fire
  def DisableTurretAI(self):
   print "DisableTurretAI"
 #drains power of a unit
  def leach(self,XshieldPercent,YrechargePercent,ZenergyPercent):
   print "leach"
 #figures out what rank in the flightgroup is the unit (-1 if no flightgroup or unit dead)
  def getFgSubnumber(self):
   print "getFgSubnumber"
   return -1
 #gets the ID of the flightgroup
  def getFgID(self):
   print "getFgID"
   return string()
 #sets the unit's nickname
  def setFullname(self,name):
   print "setFullname"
 #gets the unit's nickname
  def getFullname(self):
   print "getFullname"
   return string()
 #debug
  def getFullAIDescription(self):
   print "getFullAIDescription"
   return string()
 #not sure!!
  def setTargetFg(self,primary,secondary,tertiary):
   print "setTargetFg"
 #not sure again..perhaps targets a flihgtgroup? probably slow
  def ReTargetFg(self,which_target):
   print "ReTargetFg"
 #if self is a starship as opposed to planet, asteroid, etc
  def isStarShip(self):
   print "isStarShip"
   return 0
 #if the self is a planet
  def isPlanet(self):
   print "isPlanet"
   return 0
 #if the self is a jump point
  def isJumppoint(self):
   print "isJumppoint"
   return 0
 #if the other is an enemey
  def isEnemy(self,other):
   print "isEnemy"
   return 0
 #if the enemy is a friend
  def isFriend(self,other):
   print "isFriend"
   return 0
 #if enemy is neutral
  def isNeutral(self,other):
   print "isNeutral"
   return 0
 #get the numerical relation btw -1 and 1 of this with other (may not be the same as other and this)
  def getRelation(self,other):
   print "getRelation"
   return 0
 #switches missile
  def ToggleWeapon(self,Missile):
   print "ToggleWeapon"
 #turns on all missiles
  def SelectAllWeapon(self,Missile):
   print "SelectAllWeapon"
 #splits a unit into many shrapnel... not 100% sure this works
  def Split(self,level):
   print "Split"
 #don't call
  def Init(self):
   print "Init"
 #turns on the jump drive (provided energy) this will cause the unit to teleport upon hitting a jump point
 #this also causes the default AI to go for targetted jump points like there's no tomorrow.
 #Desintation should be 0 for now
  def ActivateJumpDrive(self,destination):
   print "ActivateJumpDrive"
 #this turns off an active jump drive
  def DeactivateJumpDrive(self):
   print "DeactivateJumpDrive"
 #destroys this unit with explosion...preferrable to deliver damage or to just plain call Kill()
  def Destroy(self):
   print "Destroy"
 #finds the local coordinates of another unit in this unit's space
  def LocalCoordinates(self,un):
   print "LocalCoordinates"
   return (0,0,0)
 #is another unit in range of this
  def InRange(self,target,cone,cap):
   print "InRange"
   return 0
 #is this unit visible?
  def CloakVisible(self):
   print "CloakVisible"
   return 0
 #Please cloak this unit
  def Cloak(self,cloak):
   print "Cloak"
 #debug I think!!
  def RemoveFromSystem(self):
   print "RemoveFromSystem"
 #find this unit's position if a unit at local_posit fired a shot at speed "speed"
  def PositionITTS(self,local_posit,speed):
   print "PositionITTS"
   return (0,0,0)
 #the actual position of this unit
  def Position(self):
   print "Position"
   return (0,0,0)
 #position of this unit based on its owning unit (i.e. if it is a turret) or else space for most units
  def LocalPosition(self):
   print "LocalPosition"
   return (0,0,0)
 #the unit that is threatening this unit
  def Threat(self):
   print "Threat"
   return Unit()
 #set the turrets to target the target passed in
  def TargetTurret(self,targ):
   print "TargetTurret"
 #get an iterator to the subunits
  def getSubUnits(self):
   print "getSubUnits"
   return 0
 #threaten  the target (placing this unit in the Threat() variable)
  def Threaten(self,targ,danger):
   print "Threaten"
 #set the threat level to zero
  def ResetThreatLevel(self):
   print "ResetThreatLevel"
 #fire guns or (if missile is 1) missiles
  def Fire(self,Missile):
   print "Fire"
 #turn off beams
  def UnFire(self):
   print "UnFire"
 #how far have our missiles locked
  def computeLockingPercent(self):
   print "computeLockingPercent"
   return 0
 #what percent is our shield
  def FShieldData(self):
   print "FShieldData"
   return 0
  def RShieldData(self):
   print "RShieldData"
   return 0
  def LShieldData(self):
   print "LShieldData"
   return 0
  def BShieldData(self):
   print "BShieldData"
   return 0
 #how much fuel have we
  def FuelData(self):
   print "FuelData"
   return 0
 #how much energy
  def EnergyData(self):
   print "EnergyData"
   return 0
 #how much hull
  def GetHull(self):
   print "GetHull"
   return 0
 #how big is this unit
  def rSize(self):
   print "rSize"
   return 0
 #how far away is this unit from a point
  def getMinDis(self,pnt):
   print "getMinDis"
   return 0
 #is a beam (starting at start, ending at end) going through a bubble around this unit
  def querySphere(self,start,end,my_unit_radius):
   print "querySphere"
   return 0
 #is a beam starting at origin with direction going through this unit
  def queryBoundingBox(self,origin,direction,err):
   print "queryBoundingBox"
   return 0
 #reset all orders to "sitting duck"
  def PrimeOrders(self):
   print "PrimeOrders"
 #load an AI script given the name
  def LoadAIScript(self,aiscript):
   print "LoadAIScript"
 #load the last script that was constructed from VS.PythonAI
  def LoadLastPythonAIScript(self):
   print "LoadLastPythonAIScript"
   return 0
 #enqueue that script into the end of the given scripts
  def EnqueueLastPythonAIScript(self):
   print "EnqueueLastPythonAIScript"
   return 0
 #set the position to be pos
  def SetPosition(self,pos):
   print "SetPosition"
 #set the current position (causing the unit to streak over to that position in a number of frames)
  def SetCurPosition(self,pos):
   print "SetCurPosition"
 #sets all positions to be that position
  def SetPosAndCumPos(self,pos):
   print "SetPosAndCumPos"
 #rotation on that axis
  def Rotate(self,axis):
   print "Rotate"
  def ApplyForce(self,Vforce):
   print "ApplyForce"
  def ApplyLocalForce(self,Vforce):
   print "ApplyLocalForce"
  def Accelerate(self,Vforce):
   print "Accelerate"
  def ApplyTorque(self,Vforce,Location):
   print "ApplyTorque"
  def ApplyBalancedLocalTorque(self,Vforce,Location):
   print "ApplyBalancedLocalTorque"
  def ApplyLocalTorque(self,torque):
   print "ApplyLocalTorque"
 #deals damage to the hull based on a local coordinate point
  def DealDamageToHull(self,pnt,Damage):
   print "DealDamageToHull"
   return 0
 #finds a thrust vector based on the thrust passed in (given the fact that thrusters are finite powered)
  def ClampThrust(self,thrust,afterburn):
   print "ClampThrust"
   return (0,0,0)
 #thrust in a direction
  def Thrust(self,amt,afterburn):
   print "Thrust"
 #give lateral thrust
  def LateralThrust(self,amt):
   print "LateralThrust"
  def VerticalThrust(self,amt):
   print "VerticalThrust"
  def LongitudinalThrust(self,amt):
   print "LongitudinalThrust"
 #clamp the velocity of a unit to its max speed
  def ClampVelocity(self,velocity,afterburn):
   print "ClampVelocity"
   return (0,0,0)
 #clamp a rotation of the unit to max yaw/pitch/roll
  def ClampAngVel(self,vel):
   print "ClampAngVel"
   return (0,0,0)
 #clamp angular acceleration to a vector
  def ClampTorque(self,torque):
   print "ClampTorque"
   return (0,0,0)
 #sets the orientation given the up and fore vector (q and r respectiveoly)
  def SetOrientation(self,q,r):
   print "SetOrientation"
 #translate from the previous space to this space (without translation)
  def UpCoordinateLevel(self,v):
   print "UpCoordinateLevel"
   return (0,0,0)
 #translate from this space down to the world space  (without translation)
  def DownCoordinateLevel(self,v):
   print "DownCoordinateLevel"
   return (0,0,0)
 #goes from world space to this coordinates including translation_
  def ToLocalCoordinates(self,v):
   print "ToLocalCoordinates"
   return (0,0,0)
 #goes from this coordinates to world coordinates including translation
  def ToWorldCoordinates(self,v):
   print "ToWorldCoordinates"
   return (0,0,0)
 #gets the angular velocity
  def GetAngularVelocity(self):
   print "GetAngularVelocity"
   return (0,0,0)
 #gets velocity
  def GetVelocity(self):
   print "GetVelocity"
   return (0,0,0)
  def SetVelocity(self,v):
   print "SetVelocity"
  def SetAngularVelocity(self,v):
   print "SetAngularVelocity"
  def GetMoment(self):
   print "GetMoment"
   return 0
  def GetMass(self):
   print "GetMass"
   return 0
 #is a missile locked?
  def LockMissile(self):
   print "LockMissile"
   return 0
 #enject cargo with that index (-1 is ejector seat)
  def EjectCargo(self,index):
   print "EjectCargo"
 #what would this unti pay for the cargo string passed in)
  def PriceCargo(self,s):
   print "PriceCargo"
   return 0
 #how cargos does this unit have
  def numCargo(self):
   print "numCargo"
   return 0
 #is the docking unit cleared with this one
  def IsCleared(self,dockingunit):
   print "IsCleared"
   return 0
 #get an entire category from teh master part list and add it to the hold
  def ImportPartList(self,category,price,pricedev,quantity,quantdev):
   print "ImportPartList"
   #docking unit wants to request clearence to self
  def RequestClearance(self,dockingunit):
   print "RequestClearance"
   return 0
 #is the unit docked already?
  def isDocked(self,dockingUnit):
   print "isDocked"
   return 0
 #try to dock (if in white square) with the unittoDockWith otherwise return false
  def Dock(self,unitToDockWith):
   print "Dock"
   return 0
 #try to undock with the unitToDockWith...on success return 1
  def UnDock(self,unitToDockWith):
   print "UnDock"
   return 0
 #get number of gunso n this ship (and missiles)
  def GetNumMounts(self):
   print "GetNumMounts"
   return 0
 #teleport to that other system (as a string) loads if necessary
  def JumpTo(self,systemstring):
   print "JumpTo"
   return 0
 #gets the faction name of this unit
  def getFactionName(self):
   print "getFactionName"
   return ''
 #gets the faciton index
  def getFactionIndex(self);
   print "getFactionIndex"
   return 0
 #sets the faction name (who ownz this unit)
  def setFactionName(self,strname):
   print "setFactionName"
   #who owns this
  def setFactionIndex(self,factionindex):
   print "setFactionIndex"
  def getName(self):
   print "getName"
   return ''
  def getFlightgroupName(self):
   print "getFlightgroupName"
   return ''
  def getFgDirective(self):
   print "getFgDirective"
   return ''
 #gets a unit who is the leader of this flightgroup...returns this if no flightgroup
  def getFlightgroupLeader(self):
   print "getFlightgroupLeader"
   return Unit()
 #gimme (this unit) money (can be negative)
  def addCredits(self,floatcache):
   print "addCredits"
   #switch to another lfightgroup
  def switchFg(self,fgname):
   print "switchFg"
   #how much cache do I have (money)
  def getCredits(self):
   print "getCredits"
   return 0
 #set the leader
 def setFlightgroupLeader(self,leader):
   print "setFlightgroupLeader"
   return 0
 #not sure
  def setFgDirective(a=None,b=None,c=None,d=None,e=None,f=None,g=None,h=None,i=None,j=None):
   print "setFgDirective"
   return 0
 #how leaderful am I
  def getFgSubnumber(self):
   print "getFgSubnumber"
   return -1
 #is this unit a significant object (base, jump point, planet)
  def isSignificant(self):
   print "isSignificant"
   return 0

  def isSun(self):
   print "isSun"
   return 0
 #communicate to other unit
  def communicateTo(self,other):
   print "communicateTo"
   return 0
 #maek comm animation appear on screen of target
  def commAnimation(self, stringanimation):
   print "commAnimation"
   return 0
 #remove a crago from this unit
  def removeCargo(self, stringcargo,quantity):
   print "removeCargo"
   return 0
 #add another units stats to my own... in the upgrades/unitfile/unitfile
  def upgrade(self, unitfile,force, subunitoffset, mountoffset):
   print "upgrade"
   return 0
 #add cargo to my unit
  def addCargo(self,Cargo carg):
   print "addCargo"
   return 0
 #get distance (minus radii) between self and other
  def getDistance(self, other):
   print "getDistance"
   return 0
 #increment random cargo
  def incrementCargo(a=None,b=None,c=None,d=None,e=None,f=None,g=None,h=None,i=None,j=None):
   print "incrementCargo"
   return 0
 #decrement random cargo
  def decrementCargo(a=None,b=None,c=None,d=None,e=None,f=None,g=None,h=None,i=None,j=None):
   print "decrementCargo"
   return 0
 #get distance plus atmospheric size if large planet (to prevent players from coming too close
 #to surface when leaving autopilot
  def getSignificantDistance(self,other):
   print "getSignificantDistance"
   return 0
 #is this unit a player starship...if not return -1  else return player number (starting at 0)
  def isPlayerStarship(a=None,b=None,c=None,d=None,e=None,f=None,g=None,h=None,i=None,j=None):
   print "isPlayerStarship"
   return -1
 #does this unit have some cargo
  def hasCargo(self,mycarg):
   print "hasCargo"
   return 0
 #retuns cargo if the unit has it by that name else returns cargo with 0 quantity
  def GetCargo(self,cargoname):
   print "GetCargo"
   return Cargo("","",1,1,1,1)
 # 351 "unit_wrapper.cpp" 2
 #is this unti equal to another
  def __eq__(self,oth):
   print "__eq__"
   return 0;
 #is this unit notequal
  def __ne__(self,oth):
   print "__ne__"
   return 1;
 #remove unit from game the correct way
  def Kill(self):
   print "Kill";
   #set this unit to be null (doesnt' affect unit itself, only this pointer)
  def setNull(self):
   print "setNull";
   #is this unit not null
  def __nonzero__(self):
   print "__nonzero__"
   return random.randrange(0,2);
 #is this unit NULL (i.e. dead or killed)
  def isNull(self):
   print "isNull"
   return random.randrange(0,2);
 #set this units' target to un
  def SetTarget(self,un):
   print "SetTarget";
   #get this unit's current target
  def GetTarget(self):
   print "GetTarget"
   return Unit();
 #get the unit that this unit is matching speed with
  def GetVelocityReference(self):
   print "GetVelocityReference"
   return Unit()
 #set the unit this unit is matching speed with
  def SetVelocityReference(self,un):
   print "SetVelocityReference";
   #get orientation of this unit
  def GetOrientation(self):
   print "GetOrientation"
   return ((1,0,0),(0,1,0),(0,0,1))
 #is the beam inside this ship
  def queryBSP(self,start,end):
   print "queryBSP"
   return (un,(0,0,1),0)
 #how far off is this unit to another unit with guns that have speed and range) (returns a tuple
 #with a cosine and a range normalized between 0 and 1)
  def cosAngleToITTS(self,un,speed,range):
   print "cosAngleToITTS"
   return (.95,10000)
 #same as above but assuming guns are instant
  def cosAngleTo(self,un):
   print "cosAngleTo"
   return (.93,10000)
 #more specific to this ship and very slow
  def cosAngleFromMountTo(self,un):
   print "cosAngleFromMountTo"
   return (.93,10000)
 #gets the gun speed of this ship
  def getAverageGunSpeed(self):
   print "getAverageGunSpeed"
   return (200,10000)
 #is another unit inside this unit
  def InsideCollideTree(self,un):
   print "InsideCollideTree"
   return ((0,0,0),(0,0,1),(0,0,0),(0,1,0))
 #get a particular turret
  def getSubUnit(self,which):
   print "getSubUnit"
   return Unit()

 #this is the unit iterator class...  it is returned in a number of functions... DO NOT keep this
 #across a frame (i.e. save it after Execute()
class un_iter:
  #dont' call this...stub only
  def __init__(self):
    print 'un_iter constructor called with (self)'
 #the unit this iterator is pointing to (may well be null...that's how you know to stop checking)
  def current(self):
   print "current"
   return Unit()
 #advance this iterator (check to see if null first!!)
  def advance(self):
   print "advance"
   #remove this unit from the collection in wchih it is in
  def remove(self):
   print "remove"
   #add a unit to this collection
  def preinsert(self,un):
   print "preinsert"

class Cargo:
  def __init__ (self,a,b,c,d,e,f):
    print 'Cargo constructor called with (self,%s,%s,%f,%d,%f,%f)' % (a,b,c,d,e,f)
  def SetPrice(self,price):
   print "SetPrice"
  def GetPrice(self):
   print "GetPrice"
   return 1
  def SetMass(self,mass):
   print "SetMass"
  def GetMass(self):
   print "GetMass"
   return 1
  def SetVolume(self,volume):
   print "SetVolume"
  def GetVolume(self):
   print "GetVolume"
   return 1
  def SetQuantity(self,quantity):
   print "SetQuantity"
  def GetQuantity(self):
   print "GetQuantity"
   return 1
  def SetContent(self,content):
   print "SetContent"
  def GetContent(self):
   print "GetContent"
   return "weapons"
  def SetCategory(self,category):
   print "SetCategory"
  def GetCategory(self):
   print "GetCategory"
   return "contraband"
  def SetMissionFlag(self,flag):
   print "SetMissionFlag"
  def GetMissionFlag(self):
   print "GetMissionFlag"
   return 0

  def GetCategory(self):
   print "GetCategory"
   return "contraband"
  def GetDescription(self):
   print "GetDescription"
   return ""

class PythonAI:
  def init(self,un):
   print "init"
  def Execute(self):
   print "Execute"
  def GetParent(self):
   print "GetParent"
   return Unit()
  def __init__(self):
    print 'PythonAI constructor called with (self)'
    self.init(Unit())
  def AddReplaceLastOrder(self,replace):
   print "AddReplaceLastOrder"
  def ExecuteLastScriptFor(self,time):
   print "ExecuteLastScriptFor"
  def FaceTarget(self,end):
   print "FaceTarget"
  def FaceTargetITTS(self,end):
   print "FaceTargetITTS"
  def MatchLinearVelocity(self,terminate,vec,afterburn,local):
   print "MatchLinearVelocity"
  def MatchAngularVelocity(self,terminate,vec,local):
   print "MatchAngularVelocity"
  def ChangeHeading(self,vec):
   print "ChangeHeading"
  def ChangeLocalDirection(self,vec):
   print "ChangeLocalDirection"
  def MoveTo(self,Targ,afterburn):
   print "MoveTo"
  def MatchVelocity(self,terminate,vec,angvel,afterburn,local):
   print "MatchVelocity"
  def Cloak(self,enable,seconds):
   print "Cloak"
  def FormUp(self,pos):
   print "FormUp"
  def FaceDirection(self,distToMatchFacing,finish):
   print "FaceDirection"
  def XMLScript(self,script):
   print "XMLScript"
  def LastPythonScript(self):
   print "LastPythonScript"

Writing Add On Adventures

Honestly you merely need a basic understanding of python in order to craft your own add on adventures. But I decided to write a framework for consistently and speedily adding adventures to the general exploration of Vega Strike.

I will define "adventure" as follows: A minature mission that gets triggered by a player who goes in its system. This mission may either be persistent or nonpersistent. A persistent mission will reload the next time the triggering player launched it until the player beats the mission.

THE "QUEST" CLASS: THE ACTUAL QUEST LOGIC GOES HERE

The quest, quest_factory and lastly the adventure modules take care of most of the dirty work. All an quest class has to do is the following:

class quest_my(quest.quest):
      def __init__ (self):
          #do anything you need here
      def Execute (self):
          #do anything you need here...return 1 if you wish to execute again, 0 if you wish to
          #terminate yourself

That's it.... though there are some useful functions you may wish to call in your superclass

self.removeQuest() #this prevents your player from EVER encountering the quest again in his life
self.makeQuestPersistent() #this causes the quest to be loaded the next time your player jumps in after rebooting vegastrike

Generally before returing 0 (terminating self) in a mission you may wish to make the quest either persistent (comes back next time he starts game) or removed. Though you could also leave the quest the way it is and the next time the player goes to the system after rebooting vegastrike he'll get the same quest again :-) But in order for your quest to be complete you must have two more components

THE "QUEST FACTORY" CLASS: A CLASS THAT RETURNS THE QUEST NEEDED

Each quest must have a factory that inherits from quest.quest_factory. and must define both an init, and a create function. Optionally there's a conditional function called precondition.... That returns a boolean whether or not the quest is ripe for creation (when a player encounters it in its native system that is)

class quest_my_factory (quest.quest_factory):
    def __init__ (self):
        quest.quest_factory.__init__ (self,"quest_my")
    def create (self):
        return quest_my()
    def precondition(self,playernum):
        return Director.getSaveData(playernum,"talked_to_sandra",0)>0

The factory must have 2 and can have all 3 of the above functions:

  • is the init function (__init__) .. you must call your superclass's init with your name.
  • is the create function... this must simply return the quest class you painstakingly created above
  • is the precondition. This is optional (by default it returns 1...always true)
    • the precondition can look in the save variables and see if you have done a task (set by some other quest perhaps) and only then return 1... :-) in that case when a player encounters it for the first time it will check the precondition before launching the quest. Note the precondition does NOT get checked once the mission has been turned into a persistent mission. This is because the mission already made the decision to make itself persistent I don't expect many missions to need to be persistent. I expect more lighthearted adventures that don't drag out if a player quits, etc.

Anyhow... a good example is the persistent quest known as the quest_drone (quest_drone.py) That quest checks to see if you're near an unknown_derelect and if so it launches the badguy drone.... it makes the drone jump and follow you wherever you go until it is destroyed...it is persistent and launches the drone every time you rerun the game. Luckily if you're close to the derelect chances are you'll find the gun that you can use to kill the drone.... which brings me right smack into my next point: location location location!

THE LOCATION AND PERSISTENCE OF A GIVEN QUEST (i.e. HOOKING IT INTO privateer.py)

adventure.py has the master list of all possible quests in vega strike. Right now there are two (but one is stupid and will be removed soon enough...that's the default one)

adventures = {"gemini_sector/delta_prime":quest_drone.quest_drone_factory(),
              "sol_sector/celeste":quest.quest_factory("default_quest",0)}

persistent_adventures = [[quest_drone.quest_drone_factory()] These are the only 2 lists that will ever need to be changed in the modules that are already there.

  • "adventures" contains a map of location to adventure. Note that there can be only 1 adventure per location (the players need to be encouraged to actually EXPLORE)
  • The second list lists all possible "persistent adventures" (though they are only loaded from if the given adventure has gotten around to calling self.makeQuestPersistent)

Anyhow just adding more cool missions to this list will make it a lot more fun to explore around and encounter strang epeople, strange news, and a bunch 'o cash :-P

Note that we should eventually make the news reports line up with the quests... :-) I have some ideas how to do that. Basically you can call

VS.IOmessage ("game","news","A drone was sighted in the delta prime...blah blah blah")

and then people will see it in the game when they click on GNN likewise if you send it to

VS.IOmessage ("game","bar","A drone was sighted in the delta prime...blah blah blah")

then you can hear it when you talk to the bartender :-)

Conclusion

Well this is the best I can do so far. Please read this documentation over and let me know if you have any suggestions or clarifications. And best yet give it a shot and try it out. And at least look at modules/cargo_mission.py (which is called from a number of missions I think) as well as modules/privateer.py (called by mission/exploration/explore_universe.mission) And certainly try to understand modules/quest_drone.py before trying to write a quest (or while trying to write one)

See also


arrow_left.png Add Conversation arrow_up.png HowTo Edit News arrow_right.png