I personally think the Scriptor and/or DLL creation could use their own sub-boards. I know there's been a lot of help given here and there, but it would be nice to have it all in one place.
Anyway, as a contribution to anyone wanting to learn more about the phenomenally easy to use DLL Scriptor by BS-er, here's some code that reads the difficulty level the Player has selected in the Options/Play screen and runs different code depending on that choice. This is something I'd been trying to figure out for some time and am now incorporating into BZClassic to make the 'Hard' setting take you beyond the original BZ1 missions.
To read the Difficulty setting:
IFaceGetInt,"options.instant.int1" //reads the difficulty they chose
StoreResult,Difficulty //stores the result in the variable "Difficulty"
Add,Difficulty,0 //The Scriptor can't compare two values directly (no "if A = B")
IfEQ,0,EASY //"Easy" reads as zero, if Difficulty + 0 = 0 Goto EASY
IfEQ,1,MEDIUM //"Medium" reads as one, if Difficulty + 0 = 1 Goto MEDIUM
IfEQ,2,HARD //"Hard" reads as two, if Difficulty + 0 = 2 Goto HARD
EASY: //Go here if they chose '0' or 'Easy'.
//place your code here, like this line that makes a Warrior
createp,warrior1,"fvtank",5,"spawnpoint1"
GoTo,ENDDIFFICULTY //When done with your code jump over the rest
MEDIUM: //Go here if they chose '1' or 'Medium'
//again, your code for medium goes here, like making TWO Warriors
createp,warrior1,"fvtank",5,"spawnpoint1"
createp,warrior2,"fvtank",5,"spawnpoint2"
GoTo,ENDDIFFICULTY //Again, you have to jump over the rest when done
HARD:
//your code for hard goes here, like making THREE Warriors
createp,warrior1,"fvtank",5,"spawnpoint1"
createp,warrior2,"fvtank",5,"spawnpoint2"
createp,warrior3,"fvtank",5,"spawnpoint3"
ENDDIFFICULTY: //and here we are, all done.
Now, if all you were doing was making warriors you can condense it all down:
IFaceGetInt,"options.instant.int1" //reads the difficulty they chose
StoreResult,Difficulty //stores the result in the variable "Difficulty"
Add,Difficulty,0 //The Scriptor can't compare two values directly (no "if A = B")
IfEQ,0,EASY //"Easy" reads as zero, if Difficulty + 0 = 0 Goto EASY
IfEQ,1,MEDIUM //"Medium" reads as one, if Difficulty + 0 = 1 Goto MEDIUM
IfEQ,2,HARD //"Hard" reads as two, if Difficulty + 0 = 2 Goto HARD
HARD: //'hard' will make three warriors
createp,warrior3,"fvtank",5,"spawnpoint3"
MEDIUM: //'medium' will make two warriors
createp,warrior2,"fvtank",5,"spawnpoint2"
EASY: //'easy' will make one warrior
createp,warrior1,"fvtank",5,"spawnpoint1"
//and here we are, all done.
Keep in mind that the DLL is run progressively, and the setting read at the time that the code is run. That means with this code they can play on 'Hard' until they cry, and then drop down to 'Easy' to get through the tough part, going back to 'Hard' again to say they made it to the end on 'Hard'... :)
-Av-
Or in my Python scriptor:
# Direct code:
difficulty = IFace_GetInteger("options.instant.int1")
if difficulty==0:
# Do easy
elif difficulty==1
# Do medium
elif difficulty==2
# Do Hard
# Could also define these somewhere in the main script area:
Easy = 0
Medium = 1
Hard = 2
# So you can do this instead (more readable):
difficulty = IFace_GetInteger("options.instant.int1")
if difficulty is Easy: # Could still do difficulty==EASY is so inclined, but this is more readable and appropriate for this case
# Do easy
elif difficulty is Medium
# Do medium
elif difficulty is Hard
# Do Hard
Or in C++:
const int difficulty = IFace_GetInteger("options.instant.int1")
if(difficulty==0)
{
// Do Easy
}
else if(difficulty==1)
{
// Do Medium
}
else if(difficulty==2)
{
// Do Hard
}
// Or to be more readable, put this somewhere in a main section or header or what-not (and yes, for any of you real programmers, I would normally put constants, in both C++ and Python all in caps, but this is easier to look at for non-programmers):
const int Easy = 0;
const int Medium = 1;
const int Hard = 2;
// Then this:
const int difficulty = IFace_GetInteger("options.instant.int1")
if(difficulty==Easy)
{
// Do Easy
}
else if(difficulty==Medium)
{
// Do Medium
}
else if(difficulty==Hard)
{
// Do Hard
}
this is another thread that could do with being stickied , but we have so many stickies up there already I'm a bit reluctant to add another one .......
Should probably add it to a Wiki, this is not really the kind of stuff to belong in a forum post. :)
First off, I don't mean any slight to your Scriptor OM, it's just the BSer was there first and his is a bit more english-intuitive, or at least IMHO easier for a non-programmer to pick up and do something with. It's also very well documented and Sonic has made a ton of examples available to boot.
So not to leave you out in the cold here, and it's excellent that you post a 'translation' to both your Python and VC++. Makes this sort of a 'Rosetta Stone' for scripting... :)
As to a Wiki that would be cool. I'd love to see examples of code posted, as well as some place people could post code they need help with OR just questions and such.
-Av-
I'll pass the thought of having a DLL Scriptor Forum to CmptrWz. I would say thought it won't happen till the site is moved to the new server.
I say bite the bullet and learn C++. Well worth it.
Quote from: Red Devil on January 22, 2008, 12:05:45 AM
I say bite the bullet and learn C++. Well worth it.
I dunno, C++ works great for things like MPIs, but the DLL Scriptor provides lineage which is great for story based missions. I find it alot easier to write a single player mission in the scriptor when I'm worried about having the player do x than y than z than to write a C++ DLL. I find it easier in C++ however to do things like do x every 10 minutes, check y every run, suprise the player when z happens.
without the dll scriptor i bet that many mods would never have been made - fleshstorm and FE would be pipedreams
i looked at some c++ coding a while ago and vowed to never, ever learn how to do that - because my life would vanish
Personaly that C++ looks more at home to me...
It could use adding to the BZU wiki (whereever it is). My BZWIKI is not so technicly minded ATM, it is more conserned about story (that and I'm moving sites). :roll:
Let's see, with my python version (mostly going to Sonic there), mine has linearity (you can even Sleep(time) and it will pause that tasklet, can make it very linear, not to mention tasklets are vastly useful in simplifying *so* much code). The code tends to be a great deal shorter then BS-er's scriptor and C++ (while being more readable in my opinion, I can give examples if you give me large swathes of scriptor code for me to convert :) ).
And doing things every so often, in my scriptor is like this:
def DoEveryTenMinutes():
while True:
AddToMessagesBox("This is sent once every ten minutes"
Sleep(10*60*10) # 10 ticks in a second, 60 seconds in a minute, for 10 minutes, could just write 6000, I could even make conversions functions so you could write something like: Sleep(MinutesToTicks(10))
def RunEveryTick()
while True:
AddToMessagesBox("This will spam your message box every single tick (ten times a second)")
Sleep()
# This decorator does not exist, but I could make it in, oh, a few seconds...
@RunWhenIsBuilt(ODF="UberTank", Team=HumanTeam)
def UberTankBuiltByHumans(uberTank):
while uberTank: # While uberTank is alive in other words), could also do while uberTank.IsAlive():
BuildObject("UberTankKiller", ComputerTeam, UberTankKillerSpawnPoint).Attack(uberTank, 1000) # Focusing on killing the ubertank, at the expense of all else
Sleep(10*60) # Wait 60 seconds, then retest if uberTank is alive, if so, it will build another killer, if not, then the killers will be idle and soon be picked up by the idle dispatcher, would also be simple to keep a list of them and send them off to do something else once the uberTank is dead)
@RunWhenPlayerDies(Team=HumanTeam)
def PlayerDied(player):
FailMission(100, "PlayerDiedLostMessage.txt")
@RunWhenIsDestroyed(ODF="UberTank", Team=HumanTeam)
def UberTankDied(uberTank):
FailMission(100, "UberTankDiedLostMessage.txt")
@RunWhenIsDestroyed(ODF="AiRecy", Team=ComputerTeam)
def ComputerRecyclerDied(recy):
SucceedMission(100, "ComputerKilledWinMessage.txt")
def SomethingLinearForPlayerToDo():
player = GetLocalPlayer()
uberTankFactory = GetUberTankFactory()
AddObjective("Goto Uber-Tank Factory", WHITE)
SetObjectiveOn(uberTankFactory)
while not player.IsWithin(uberTankFactory, 50):
Sleep()
ClearObjectives()
AddObjective("Goto Uber-Tank Factory", GREEN)
AddObjective("Slaughter the enemy before they slaughter your Uber-Tank", WHITE)
SetBestGroup(BuildObject("UberTank", HumanTeam, uberTankFactory))
AddToMessagesBox("Computer: GAGH, I WILL KILL YOU FOR THAT!", RGB(255, 64, 64))
And yes, those decorated functions (when something dies and so forth, I really should make those, would be easy, and it is powerful as seen... hmm... next version perhaps) can be turned on or off at will, but they are on by default. and not all parameters are necessary, for example if Team is omitted then it will be called anytime anyone builds/kills one, regardless of Team, and could be other options too.
How would something like this work in the scriptor, I know it would be pretty simple too for 'some' of those functions, and in C++ pretty much all of that is... well... impossible as is, would need a lot of code to set things up and so forth, or make real threads with locks.
You slay me, dude. I can see you and Nathan and Ken sitting on the back porch in your old age enjoying a good sherry reminiscing about the classes you've built and the bad pointers you found.
Programming is life. :)
Literally though, you should hear me when someone asks what I have been up to coding recently (usually they are relatively new around me and just know I 'program', but do not know how involved I get), I will start and go for hours if I am not stopped, talking about implementation details I discovered were useful, little bugs I ran into, etc... etc... I am not normally asked again. :p
Only for those that cannot program. :P
true 8-)
I figured yours also had a linear style, but I simply choose to stay on topic with DLL Scriptor since its the one I've used for 4 mods and several FE missions.
I am still interested in the scriptor equivalent of what I posted. I could do the C++ equiv but it would be rather nasty for some of those.
I can do some off the top of my head, but I'm at work and can't make sure it's 100% true code:
[text]
spammessage1,"This is sent once every ten minutes."
spammessage2,"This will spam your message box every single tick (ten times a second)"
routine, everyten,1,true
Startover:
wait,600 //60seconds per minute * 10 minutes
display,spammessage1
Jumpto,Startover
routine, everytick,1,true
Startover:
display,spammessage2
Jumpto,Startover
routine,killubertank,1,true
checkagain:
getbylabel,"unnamed_ubertank1",ubertank
isaround,ubertank
ifeq,false,checkagain
getteam,ubertank
ifNE,1,checkagain
createp,"ivutankkill",uberkiller,2
attack,ubertank,uberkiller,5000
wait,60
jumpto,checkagain
routine,checkplayer,1,true
checkagain:
getplayer,theplayer
isaround, theplayer
ifeq,true,checkagain
fail,youdied.des,10
routine,checkubertank,1,true
checkagain:
getbylabel,"unnamed_ubertank1",ubertank
isaround,ubertank
ifeq,true,checkagain
fail,uberdied.des,10
routine,checkenemyrec,1,true
checkagain:
getbyTS,1,1,enemyrec
isaround,enemyrec
ifeq,true,checkagain
win,"youkilledem.des",10
routine,playertask,1,true
getbylabel,"unnamed_ubertankfactory1",enemyfactory
display,"go to the enemy factory",white
beaconon,enemyfactory
checkagain:
getplayer,theplayer
distobject,theplayer,enemyfactory
ifGT,50,checkagain
clear
display,"go to the enemy factory",green
display,"Slaughter the enemy before they slaughter your Uber-Tank!",white
and, I'm not sure what the last few lines of your code do, OM, but it would be easy enough to watch for the ubertankfactory to die...
I can ready youre code easily enough OM, and would gladly use it, but it would take some good documentation before I felt comfortable doing so. As it is I can fumble my way through Scriptor code, even at work, with minimal errors, even though it's been some months since I last did a lot of code.
***
Where the Scriptor fall short is in comparisons, conditional loops, arrays, and I have trouble assigning proper labels to units that are created ingame.
For instance, it would be great to be able to make an array of all of the enemy Scavs created during the game and loop through them easily to check their distance from your base. You can do this in the Scriptor but it's not very intuitive, or efficient, and it's hard to shuffle the groups down, and and and... the code GSH gave me to do it in VC++ for MP is small and elegant, where the Scriptor equivalent fills a page and a half...
Anyway, I'd gladly go with your Scriptor but you're going to have to bite the bullet and make some sort of help file outlining how it all works. :)
-Av-
which are you using for bzc av??
On things like "routine, everytick,1,true", shouldn't that 1 actually be equal to the number of commands in the routine to be fully synced, because like that it will only execute one command a tick, instead of the whole loop every tick, thus meaning it will actually only print once every 2 to 3 ticks? The others have the same issue.
And yea the last few lines:
# This will create an "UberTank.odf" unit for the human at the location of the
# uberTankFactory, then give it a group so the human can control it
SetBestGroup(BuildObject("UberTank", HumanTeam, uberTankFactory))
# The above line could be written like this too if you needed to do more with it...
uberTank = BuildObject("UberTank", HumanTeam, uberTankFactory)
SetBestGroup(uberTank)
# This just displays a message in the message box, so probably something
# like display,spammessage3 if spammessage3 is set to the message.
# Does the scriptor support colorizing the message box text, because that
# is what that RGB does, just a lightish red is what I had it do.
AddToMessagesBox("Computer: GAGH, I WILL KILL YOU FOR THAT!", RGB(255, 64, 64))
To do something like looping over a list of enemy scavs, you could do something like this (with example):
class EnemyScavDistanceWatcher():
def __init__(self):
self.__enemyScavs = [] # Two underscores in front of a variable name makes it private, like the private keyword in C++
@RunWhenIsBuilt(GOClass=("CLASS_SCAVENGER", "CLASS_SCAVENGERH"), NotTeam=HumanTeam)
def NonHumanScavCreated(self, scav):
self.__enemyScavs.append(scav)
@RunWhenIsDestroyed(GOClass=("CLASS_SCAVENGER", "CLASS_SCAVENGERH"), NotTeam=HumanTeam)
def NonHumanScavDestroyed(self, scav):
self.__enemyScavs.remove(scav)
def GetClosestAIScavAndDistance(self, from):
closest = None
dist = 2**30
for scav in self.__enemyScavs:
d = scav.GetDistance(from)
if d<dist:
dist = d
closest = scav
return (closest, dist)
# Then from your mission class, just inherit from this as well, you could just
# put it inside the class directly and save a little bit of code, but this way is
# reusable across projects and so forth :)
class MyMission(BZ2Mission, EnemyScavDistanceWatcher):
# etc... and other things...
# This is just some function that checks to see if the nearest scav is within
# a certain distance of the main base, and if so permenently marks it on the
# hud for the player to attack
def WatchForEnemyScavsAndMark(self):
humanBase = GetHumanMainBuilding()
while humanBase:
scav, dist = self.GetClosestAIScavAndDistance(humanBase)
if dist<200.0:
scav.SetObjectiveOn()
Sleep(10*30) # Wait one-half minute before marking again...
else:
Sleep(10) # Test once a second
And yea, I know, need to create documentation...
I had the scripts done using BSer's Scriptor before OM's came out... I've got a LOT of time and effort into the missions and starting over with a new utility, no matter how much more powerful, isn't going to happen...
To me the main attraction of OM's version is MP support. To have something like the Scriptor, that will do MP capable scripts so we can make online co-op missions (like DuneCommand's), would be a dream...
SO... when BZC is out and the stock missions in the can I might turn to OM's Scriptor to do the Ancients Campaign. It would be cool if they could all be played out as MPI's, as that means a more realistic level of difficulty.
***
The 'wait,600' will make it wait 600 seconds, and yes maybe there'll be 1/10th of a second between the end of the wait and the printout, but I'd call it close enough. :)
The other only actually has one command, the print command. The label doesn't take a tick, the jump might but it doesn't seem that it does, as it just sets the next run point, so you get one line printed each 1/10th of a second.
I've never worried about time and the number of lines run, as "wait,30" seems to wait for 30 seconds to me. Maybe it's off by a second or two in some scripts but I've never really timed it that closely. (If the Golem comes and eats me at roughly the time I told it to I'm happy...).
-Av-
I can barely find the right link to OM's Scriptor.............. (yes,very sad)....................
To BBB..........
Those who are programatically (yes, play on words i suppose) illiterate will suffer in this age of time.......... I am among that group........so dont be like me....... :-D
Actually my experience is that there's a divide forming.
There are those who understand how and why a program works, and while they know the code ins and outs they often aren't even that good at using the program. Technically brilliant they often aren't the best source for info on making the program useful in real life situations.
Then there are those who don't have a clue as to what's under the hood, but can be absolute adepts at using it. The whole thing is magic to them, but they can make it dance and sing.
There are few who bridge the gap nowadays. We're becoming Morlock and Eloi right before our eyes...
-Av-
exactly av -- i am the second class you mentioned - as are most modders
BSer bridges the gap - he made the dll scriptor and several FE missions
I worked a tiny bit on the documentation (a tiny bit meaning almost 50 functions and so forth, just a drop in the huge amount there is). I also, really need to get a new version out, the new one has more capabilities, which are always a good thing. :)
If I ever make missions, I wont be able to beat them... I am one of those good at modding bad at playing. Then again, I don't give myself enough credit.
The mark of a good mission is that it won't play exactly the same each time, so if they catch you napping you're toast even if you were the one making the script. There should be an ebb and flow, a mix of tactics and styles, and a touch of randomness to make it fun.
:)
-Av-
Then you are going to love qf mod
Quote from: Avatar on January 23, 2008, 09:41:26 PM
The mark of a good mission is that it won't play exactly the same each time, so if they catch you napping you're toast even if you were the one making the script. There should be an ebb and flow, a mix of tactics and styles, and a touch of randomness to make it fun.
:)
-Av-
My MPI DLL takes randomness into account which can be damn evil. The Reinforcements routine will randomly spit things out at a random point. The evil of it is its called at a fix time variable, called when the AI's Anti-Siege is called and when the AI's Recycler has taking moderate damage. The results one time was we have 4 Siege Tanks show up from all directions smashing down our defenses, which included Rave Titans mind you.
Quote from: Avatar on January 23, 2008, 09:41:26 PM
The mark of a good mission is that it won't play exactly the same each time, so if they catch you napping you're toast even if you were the one making the script. There should be an ebb and flow, a mix of tactics and styles, and a touch of randomness to make it fun.
:)
-Av-
I take it we are randomizing with the computer clock miliseconds as the seed?
BZ2 supplies a couple random functions that are synced across the network (assuming they are called an equal number of times of course), probably that.
Um... remember, it's me... the storyteller. I'm not talking about MP, I'm talking about SP/IA.
I'd need either serious DLL help or OM's Scriptor for MP missions as I wouldn't even know where to start right now. The only way the BZC MP DLL worked at all was with a ton of hand holding by OM and GSH, I couldn't have done it properly on my own.
-Av-