• Welcome to Battlezone Universe.
 

News:

Welcome to the BZU Archive dated December 24, 2009. Topics and posts are in read-only mode. Those with accounts will be able to login and browse anything the account had access granted to at the time. No changes to permissions will be made to be given access to particular content. If you have any questions, please reach out to squirrelof09/Rapazzini.

Main Menu

Uncle Avatar's Scriptor Time...

Started by Avatar, April 22, 2008, 06:41:05 PM

Previous topic - Next topic

Avatar

This is my thread for discussing my own philosophies about BS-er's DLL Scriptor.  I think I have a unique perspective given that I was basically handed 25 stories and had to figure out how to duplicate them in the Scriptor (talking about the BZ1 SP missions, if you've been living in a cave for the last few years...)

I'm going to start with basic philosophies and techniques, getting into specific code later on...

MAIN

My Scripts always starts with a 'Main' routine...  [routine,Main,1,true]   My background started with linear programming in Basic and I usually fall back on that to begin a project.  I've also found it helps with debugging/testing, and with keeping things straight as far as having a place to return to as the mission progresses if you keep one 'master' routine and have it control the rest.

Yes, yes... "One routine to rule them all, and in the darkness bind them..."

I use 'Main' to first set up the pieces present in the mission.  Being part of the 1.3 patch process and building/tweaking hundreds of units for eight races I learned the very very very VERY hard way that you NEVER just drop a unit in a map and save it.   This will make you cry when something changes, whether it's the patch or you tweaking the model or ODF, and the map no longer loads.

There are also things that don't change easily once a unit is saved inside a map, like ammo/hull/addammo...  where you'll find they keep their initial values even after you change the ODF, and only newly created tanks take on the new ODF values.  Far better to spawn everything needed via the Script.

So rule number one is NEVER drop a unit in a map.  Instead, spawn it via a position or path point.  I prefer path points but some people aren't comfortable in the Shift-F9 screen so make a list of positions instead.

Back to the Scriptor....  in order to speed things up when setting up your map I initially run Main at a very high speed...

[routine,Main,9999,true]
//my stuff
   createp,MyRecy,"avrecy",1,"myrecyspawn"
   createp,MyGuard,"avtank",1,"guard1"
   createp,MyGuard,"avtank",1,"guard2"

//bad guys
  createp,TheirRecy,"svrecy",2,"theirrecyspawn"

etc....  I do have a set of pathpoint names but they've changed over the course of doing the missions, and usually now I default to the ODF name and a number.  I use paper maps to decide what goes where, and the pathpoint name helps me make sure I don't spawn the wrong item.  Set up your stuff, enemy stuff, nav beacons, set pieces, animals, whatever you need, then reset the run speed...

   runspeed,Main,1,true

Back when I started scripting I'd set up other routines with a delay before kicking in, to give Main time to create everything.  Remember, all of the routines marked as active start all at the same time, so you could easily have a routine start up before whatever it's watching has even been created.  The easiest way to avoid this issue is to set up all of the sub-routines as [routine,subroutine,0,false] initially, and turn them on in Main when the time is right.

   runspeed,WatchMyRecy,1,true
   runspeed,WatchTheirRecy,1,true

Now you want to get into the guts of an SP mission.  That means giving out objectives at the least, and playing a full blown cutscene at the most.  Do that right in Main, and then start up whatever supporting routine you need.

  Clear
  Display,Obj1,white   //"Retrieve the Relic at Nav 1"
  StartSound,"Mission1-A.wav"  //"Commander, get out there and get that relic!"
  runspeed,WatchRelic,1,true

Now, there are some tricks you can do if you want to keep things strictly inside of Main, such as shutting it off at this point.

  runspeed,Main,0,false

You can then have the WatchRelic routine turn it back on and it'll pick up where it left off.  I tend to avoid this, though, as it's a sure way of having nothing further happen when some yahoo decides to do something odd and break your script, never restarting Main...   :)

At this point you might want to wait a bit and then send the enemy after the Relic.

  Wait,60
  runspeed,EnemyToRelic,1,true

Then you may want to wait a bit before upping the pressure...

  Wait,60
  runspeed,ExtraAttackWaves,1,true

And finally, set up a timer to end the mission.
 
  Wait,120
  Clear
  Display,Obj2,white   //"Enemy reinforcements arrive in:"
  starttimer,360,240,120
  StartSound,"Mission1-B.wav"  //"Commander, things look dark"
  Wait,360
  Runspeed,Armaggedon,1,true


Now you've got the basic framework for whatever will happen in your mission.  It's laid out in such a way that you can go down it and fill in the missing routines and, when testing, jump around from section to section easily.

For example, you may not want to play the first 5 minutes over and over and over as you test the mission.  Easy enough then to put a

  jumpTo,TestPhase2

in there right after everything is set up, to jump you right to turning the enemy loose to get the Relic...  saves a LOT of time when you do this, as you can perfect each section and jump right to what you need to test, one after another.

So there you have the basic philosophy I use when scripting.  Make the general outline happen in Main, setting up the pieces, giving out the orders, playing the voiceovers and cutscenes, and controlling the flow such that you can jump forward in sections for testing.


***

So, was this helpful?  It only took a few minutes to type (love the spellcheck in Firefox... :) ) and if it's helpful at all I'll keep going...

-Av-

General BlackDragon

Yes

Post more  :-)

I think I'll use your BZS files as examples when ever I learn to do stuff in the scriptor...



*****General BlackDragon*****

Sonic

Don't know if anyone cares but I've uploaded a variety of scripts that I have written over the years including scripts for my Mods, potential Starfleet MOD scripts and several other mods I was writting scripts for. I personally learn from examples which is why I'm uploading these so others can learn from them as examples.

http://www.starfleetplatoon.com/users/sonic/bz2/bz2_dll_scripts.7z
"Linux is user friendly...
...it's just very selective about who its friends are."

bigbadbogie

you actually did ITS??

i thought it had been abandoned...
Others would merely say it was good humour.


My BZ2 mods:

QF2: Essence to a Thief - Development is underway.

Fleshstorm 2: The Harvest - Released on the 6th of November 2009. Got to www.bz2md.com for details.

QF Mod - My first mod, finished over a year ago. It can be found on BZ2MD.com

Avatar

Ack, this thread got STICKY stuff all over it, and it's just starting out...   :-P

Quote from: Sonic on April 22, 2008, 08:30:20 PM
Don't know if anyone cares but I've uploaded a variety of scripts that I have written over the years including scripts for my Mods, potential Starfleet MOD scripts and several other mods I was writting scripts for. I personally learn from examples which is why I'm uploading these so others can learn from them as examples.

http://www.starfleetplatoon.com/users/sonic/bz2/bz2_dll_scripts.7z


You made those available long ago and I can't thank you enough for them.  They really helped me get started when I first dove into the Scriptor.  Many of the Scriptor routines are dead simple, but many need a little more in the lines of actual scripts to really show what they can do, and your examples do that very well.

OK, well, I'll do some more tonight then...

-Av-


BNG Da BZ Fool

Tanks AV, we do appreciate helpful information relating to modding for BZII. I personally don't have much practice with putting together scripts and this kind of info is like coming across a pot of gold. I think I've edited maybe 2 scripts just to get the feel of it, but love the thought that someone cares enough to actually share such jewels with other modders in a somewhat dry information landscape and it's like find an oassis in the middle of a very dry desert. Kudos AV!
When I'm not in hot water with the community I'm usually making models for BZII. I've made a few models for other peeps. BNG.

Avatar

#6
OK, let's step back a second and get into prepping the map for the Script.

The first thing I do when making an SP mission is set up the Terrain.  Sculpt it, paint it, (use Winter, can't stress enough how cool that is) and then lay out what is where...  set up the pools, loose scrap, and at least the edge_path.

Now, on paper, I usually lay out where the enemy base is, where my base is, where I need the player to go and why, that sort of thing.  Make a mission outline complete with map.

I then start up the empty map and use the editor to lay out the enemy base, my possible base, and place any ruins, relics, that sort of thing.  I know, I know, I told you NOT to just drop things in the map, but this is different.

Now that I've got the actual objects in my map I go into the shift-f9 screen and place pathpoints for them all.  This lets you get the alignment right, and usually makes sure you can build on those squares.  It also shows me if I need to smooth the terrain out a bit more, or give them more elbow room.

Once the pathpoints are laid out (and recorded on my paper map) I erase all of the objects I've placed and save the map.  This sounds like a lot of work, but you can always just skip placing the scrap and set off DaBomb if you're too lazy to delete everything...  :)    Using this method I've never once had a situation where the AI couldn't build what I told them to, where I told them to build it.

Finally, you set the name of the Script it's going to be using in the PATHS section and save.

NOW, you're ready to begin your script.   :)

Again, as I stated earlier I usually set up the skeleton of the mission as the 'Main' routine.  You can then run through it if you want, to get a feel of the timing, as it's sometimes important to actually DRIVE there when you're under a timer...

So, we've got the basics:

Map terrain complete with pathpoints. 

Script that very quickly places all of the pieces on the map:

  [routine,Main,10000,true]
   //set up my stuff
   createp,SvAPC,"svapcempty7",1,"svapc"  (create at a point, Handle (name you use in the script), ODF name, Team, Pathpoint Name)

     setGroup,SvAPC,0    (place it in the first Fkey)
   createp,Svrecy,"Svrecy_nd",0,"svrecy"

   //bad guys
   createp,Wpower1,"abwpow",2,"abwpow1"
     createp,Wpower2,"abwpow",2,"abwpow2"
   createp,Abtowe1,"abtowe",2,"abtowe1"
   createp,Absilo1,"absilo",2,"absilo1"
   createp,Absilo2,"absilo",2,"absilo2"
   createp,Prison,"abbarr",2,"prison"
     setName,Prison,"Military Prison"   (change the name seen when targeted)
     beaconOn,Prison                      (light it up in the HUD so you can see it from anywhere)
     SetMaxHealth,Prison,400
   createp,Amrecy,"avrecy",2,"avrecy"
   createp,Avturr1,"avturr",2,"avturr1"
   createp,Avturr2,"avturr",2,"avturr2"

Places and names the navs:

        //set up navs
   createp,BaseNav,"abnav",1,"abasenav"
      setname,BaseNav,"Nav"

Resets 'Main' to a normal speed and turns on the first set of routines needed to watch things:

       runspeed,Main,1,true
       runspeed,WatchMyRec,1,true
       runspeed,WatchTheirRec,1,true


Now, THOSE routines are fairly simple.  You just watch for the existance of an object and do something if it's destroyed.  This is used usually for mission critical objects like your Recycler, or something you're supposed to guard.

You can do this by watching the object the whole time the mission is going, or you can add something to the ON DELETED routine to watch for that particular object to be destroyed.   Let's talk about that...

There are two routines that are tricky with the Scriptor.  The ON NEW OBJECT, and ON DELETED OBJECT routines run when something is added or removed from the map.  This can be VERY powerful as it lets you watch, say, the enemy build units (or the enemy watch YOU build units), but they're also very tricky when you consider how many things are being added or removed from a map all the time.  Ships, scrap, pilots, powerups, etc. all trigger these when created, used, or destroyed.

In Other Words it's best to save those for the more important things you can't do without them.  Even running very fast in the Scriptor these routines can still miss things when there's a ton of fighting going on, what with things being destroyed and created constantly.  Best to save those for later discussion.

So, to just watch something, especially something you've already placed on the map, it's easiest to just set up a routine to keep checking for its existence:

[routine,WatchMyRecy,0,false]
CHECKAGAIN:
    GetByTS,MyRecy,1,1
    IsAround,MyRecy
    IfEQ,TRUE,CHECKAGAIN
   //uh oh, it's gone...
    StartSound,"Yousuck.wav"
    Fail,20,"yousuck.des"

CHECKAGAIN: is the label, it allows the routine to jump back to that spot

GetByTS is "Get By Team Slot".  It allows you to grab things like the Recy, Factory, etc. by finding out what's in that slot for that team.  In this case it's Team 1, Slot 1 for the Recy.  The Factory would be: GetByTS,MyFactory,1,2   for Team 1, Slot 2.

IsAround checks to see if the object now called MyRecy is still in the map.

IFEQ,TRUE,CHECKAGAIN says if the previous 'IsAround' came back TRUE, then jump back to the CHECKAGAIN label.

So every time it does this, and finds the Recy, it just jumps back to CHECKAGAIN.  If it DOESN'T find a Recycler in your slot 1 it knows it's been destroyed and the IFEQ statement just lets the code drop through to the next line.

StartSound,"yousuck.wav" plays that wave file.

Fail,20,"yousuck.des" ends the mission in 20 seconds, and if it's been set up as an actual SP mission it'll display the yousuck.des file (text) after the mission ends.  If you're just running the mission that statement won't do anything, it has to be run through the SP mission selection in the game, but you'll want to put it in now no matter how you're running the mission for testing.  I use a shortcut to jump right into the mission, rather than running the whole game, but that's up to you.

I should also mention that it looks like that DES or TXT file has to have a name that's not more than 11 characters long with the extension.  My testing showed that longer names don't get displayed.  This may have changed in the 1.3 patch, but it's still a good idea to keep things as simple as you can.  I name my missions things like "NSDF07", so that des file would be "NSDF07F1.des", or something simple like that.

So now you have a fully functional routine that watches for your Recycler and ends the game when it gets destroyed...

You may ask, if earlier I'm doing this:

   createp,MyRecy,"avrecy",1,"avrecyspawn"

Then why do I have to use the teamslot function to get what my Recy is?  It's already called "MyRecy" when I placed it...

The reason is that some objects change their states during the game.  When the Recycler deploys it's actually being removed, and a building put in its place.  This can 'break' the handle, so that the name is no longer associated with the object.   If you just rely on the name you gave it when you placed it on the map the routine will run the minute you deploy.

The same sort of thing happens to the Player.  In a lot of missions all I have to do to stop an attack wave from coming is hop out of my tank when I know they're on the way.  Suddenly their target has disappeared and they don't know who to attack...  This stalls them for the rest of the game, unless you deliberately re-order them OR have an AIP file active so the Idle Dispatcher can send them to their doom. 

So,when in doubt, re-aquire your object by using one of the methods the scriptor gives us to find objects in the game.   For things like the Recycler you can use the GetByTS method, but there's also:

GetByLabel - gets by the name of the object.  Can be tricky as many objects are given a number on the end, and most end up called "unnamed_thisobject".  Eyedropping a unit in the editor will help you figure out the names of objects if you can't guess it...

GetPlayer - gets the Player.  I call this over and over for attack waves, in case the sneaky player jumps out of their tank...

[routine,AttackWave,1,true]
CHECKAGAIN:
   GetPlayer,ThePlayer
   Attack,Tank1,ThePlayer,9999
   Wait,10
   JumpTo,CHECKAGAIN

This routine will find the Player, assign that object to the handle "ThePlayer", send a tank to attack him at a high priority (9999), and then wait 10 seconds to do it all over again.  This will keep that attack wave on track despite the Player changing tanks or trying to stall it by hopping out.

***

Whenever you're not sure how to lay out a command just call it up in the HELP menu.  It'll outline just what you need to state to make the command work, and sometimes give you an example of that command working with another command:

EvalPos, position, integer, variable*
Evaluate the integer component of the position. If integer is 0, then the X component is the result. If integer is 1, then the Y component is the result, if integer is 2, then the Z component is the result. The result can be used by the statement that comes after it. If the optional variable is present, the result will be stored in that variable.
Example: evalpos,playerpos,1
Could be followed by: iflt,100,NOT_HIGH_ENOUGH



***

So now you should be able to take a blank map, add a few points, spawn some tanks there and have them attack the player...

-Av-


       


lucky_foot

YES!!!!!

Tutorial information for Issue #5 of BZmag!!!!!!

:D
Jonathan S.



Avatar

LOL...   knock yourself out, and be prepared to edit for brevity...  I sometimes go on for longer than I should...

-Av-

Nielk1

It's not like we're using real paper here at BZMAG!
*If I had a really good printer that could print the entire margin I would make a hard copy of the mags though. I wonder if I can get my cover art as posters.*

Click on the image...

mrtwosheds

#10
Having created a recycler vehicle by script.
   Createp,myrecy,">ivrecy_me",1,"recycler"
and wishing it to be controlled by aip
   setplan,"relaip1.aip",1

How can I make it deploy without being told to by the player?
The deploy script command does not make a recycler deploy.
I am trying to make the player a pilot, not a commander, for the first part of this mission.


General BlackDragon

Well, i think the stock AIP's should have that info, in IA / MPI the cpu recy starts out as avehicle and deploys on a pathpoint.



*****General BlackDragon*****

Avatar

As far as I know the DLL can't do that directly.  It may be that the AIP has a plan for this...  I'd like to know that myself as something tells the AI Recycler to deploy in an IA/MPI game...

It can be faked in the Scriptor, though...

[routine,FakeRecyDeploy,1,true]
     Createp,myrecy,">ivrecy_me",1,"recycler"      create recy at pathpoint
     Wait,20         
     GoTo,myrecy,"recydeploy",9999                   send recy to deploy point at high priority
     Wait,5
     SetAnimation,myrecy,"deploy",1                   set it to run 'deploy' once
     StartAnimation,myrecy                               run the animation
     Wait,5
     Replace,myrecy,"abrecy",true                      replace the deployed vehicle with the building


No blue lines, or fading in/out of parts, but it IS faking it, after all...   You could place the building first, then remove the vehicle, which would look a bit more like the actual deploy, but you'd have to grab the orientation of the recy manually first.

A lot of things can be 'faked', like undeploying recyclers, with animations and 'replace'...  :)

-Av-
   

Red Devil

From PB2:


[Plan#]
planType = "Deployer" // New planType.
buildLoc = "pathname" // Required: Pathpoint name. Plan shuts down if not valid.
DeployClass = "ivrecy" // Required: GameObjectClass of what to send. Plan shuts down if not valid.

   If the buildLoc or DeployClass are invalid/not found, then this
plan will go into a safety mode and do nothing. ["pathname" and
"ivrecy" above are only examples, the true defaults are empty strings,
i.e. do nothing] Note that all the other usual common/shared items in
a [Plan#] can be added. Things like conditions, buildIfNoIdle, and the
like can be specified as well. You'll probably want to at least put a
NotExists condition on the plan to ensure it doesn't keep building and
sending things to a destination. Also, this doesn't apply to just
'recyclers' -- it sends a 'deploy at' command to its target object.

   Also, added a new pair of conditions to all AIP chunks
[Plan#]
planCondition = "PathBuildingExists"
planConditionPath = "pathname" // Required: pathpoint name. Condition ignored if invalid or not on current map

[Plan#]
planCondition = "NotPathBuildingExists"
planConditionPath = "pathname" // Required: pathpoint name. Condition ignored if invalid or not on current map

   Of course, these should work in the other condition slots, e.g.
planCondition2, planConditionPath2 .. planCondition16,
planConditionPath16. A PathBuildingExists condition is satisfied when
there is a terrain-owning building (of *any* type) at the path. A
NotPathBuildingExists condition is satisfied when there is *no*
terrain-owning building (of *any* type) at the path. This type of
condition shold probably be used with the "Deployer" plan above, for
fairly obvious reasons -- cutting down on overbuild. It can be used on
other planTypes as well.

   All this is pretty much untested. If it blows up, send me a save
with assets. [NM]
What box???

Avatar

And there you have it... no need to fake it, just add the Deployer plan to your AIP...

Thanks RD!

-Av-