Hi,
I am looking for an example of some very advanced Galaxy script.
For example a complete map like Tower Defense Tycoon, if it was scripted manually, which it probably wasn't.
I just need something to learn from, some script to read so that I can understand how to do basic things in Galaxy, if they are even possible.
More specifically I want to know the best/cleanest way to
- repeatedly do many dynamic things if condition but stop it at some different condition
(I understand TriggerAddEventTimeElapsed and such, but is it good to have 50 of those running or should I make a big one that handles all of them, even if the things happen at different intervals? If so, should I just have a |timer| running that I check for elapsed time with a big if/switch block? Should I create an array of structrefs, that contain a time and funcref, that I loop through and trigger at the right time?)
- can I have anonymous functions / create functions on the fly
- is it possible to use static functions for CreateTrigger or must it be a global one
- can I create unit Behaviors through Galaxy and bind them
- create a hash-type map like map["key"] = value
I will probably have more questions as I go, which is why I'd rather have access to an already-existing advanced map from which I could just deduce these things myself. But it's hard to find one.
After reading the forums here for a while I recognize many names of whom I'd like to read their code, but I can't find their maps through the editor.
That probably means they have obfuscated the code right? Or locked their maps, that is.
Also, how can I get the ID of a unit that I select with my mouse in the World Editor?
The code I am working with references UnitFromId(123), but how can I find that unit in the terrain view?
there a programs to extract the downloaded maps from the sc2 folders. then get an mpq-editor, open the map with it and extract the galaxy script, so you can see the script of any map you have downloaded, just needs some googleing.
1. i dont think so
2. you can use any function for CreateTrigger (as far as i know)
3. behaviours must be created with data-editor
- repeatedly do many dynamic things if condition but stop it at some different condition (I understand TriggerAddEventTimeElapsed and such, but is it good to have 50 of those running or should I make a big one that handles all of them, even if the things happen at different intervals? If so, should I just have a |timer| running that I check for elapsed time with a big if/switch block? Should I create an array of structrefs, that contain a time and funcref, that I loop through and trigger at the right time?)
I'm not sure which method is the best in terms of perfomance, but i think every of those suggested are pretty close to each other. As long as the limit is 50.
It's better though to use a single trigger. As every one would create a new context with separate stack.
So have an single trigger with multiple events tied to it. Every event would have an own timer. To detect which timer has been triggered you must store all of them inside an array then loop through all of them and match the right one using:
nativetimerEventTimer();// it will return the timer that has intiated the trigger// other natives you need:nativetimerTimerCreate();nativevoidTimerStart(timert,fixedduration,boolperiodic,inttimeType);nativetimerTimerLastStarted();nativevoidTriggerAddEventTimer(triggerinTrigger,timerinTimer);
Quote:
- can I have anonymous functions / create functions on the fly
Nope. TriggerCreate and funcref's is all we have available for that type of things.
Quote:
- is it possible to use static functions for CreateTrigger or must it be a global one
I believe so. As long as function matches the
boolfn(bool,bool)
pattern it will accept anything. There's really no difference between static and non static functions though. You just can't reference them outside of the current script file where they were defined. They are still global for my understanding - you can't redeclare another static function with same name in different file.
Quote:
- can I create unit Behaviors through Galaxy and bind them
It's not possible to create any static data entries like behaviors via galaxy. All you can do is adjust some values of already created entries with:
Galaxy doesn't support associative arrays natively. You would need to implement some sort of library by yourself. But as it would require some sort of hasing system it might not be worth the effort.. and even be slower than other methods.
The simpliest way is just to have an array of structs. And to fetch them by name use one of those 2 methods:
1) Store the name inside an struct and loop through all the entries untill it's matched.
2) Use dynamic data tables and store the key id to an array inside them.
After reading the forums here for a while I recognize many names of whom I'd like to read their code, but I can't find their maps through the editor. That probably means they have obfuscated the code right? Or locked their maps, that is.
Pretty much. Editor is listing only maps marked as public. Private maps aren't necessarily obfuscated - it's optional. Also only auto-generated code from triggers built via editor GUI is being obfuscated. If someone has wrote its code by hand it will be untouched - default obfuscator from editor cannot handle that type of things..
Private maps can still be downloaded as Funky said. Just not directly through editor.
Quote:
Also, how can I get the ID of a unit that I select with my mouse in the World Editor? The code I am working with references UnitFromId(123), but how can I find that unit in the terrain view?
I don't know any way. Atleast not a simple way that doesn't involve some tedious workarounds.
What i do is instead of preplacing units on the map i preplace points. Then i dynamically create units at position of the point. Points can by fetched with PointFromName(string name).
It's a mix of GUI triggers and custom scripts.. core libraries were written in galaxy and are exposed into GUI via custom API.
Although it's pretty dirty i must say. I would have done many things different if i would be about starting new map. There's some inconsistency etc.. So it's not an good project to base things on but you might find something useful after all.
There's also cortex engine (library for role play maps) https://github.com/90-proof/cortex-forge/tree/master/Cortex initially created by Motive.
And i must say i don't know any other public maps that use pure galaxy. Most poeple just built stuff with blocks in GUI..
Thank you. That is an awesome answer, very thorough.
If I make this map work then my code will be available for everyone to use.
If I can't make it work through pure script then I will discontinue it, so we will see.
The fact that I can't redeclare a static function in another file completely ruins the keyword then. Grrrr.
And I suppose namespacing is impossible as well, then.
Before reading your answer about preplacing points I had already started doing something similar but using regions instead.
But I need some metadata on them, and some help :-)
If I have a
regionx=RegionFromId(10);
How can I get the regions name? I know about RegionFromName but I can't enumerate them.
With RegionFromId I can just do a while loop until it returns nothing to enumerate all regions.
Or at least I assume so, provided the editor keeps the id numbers going upwards an replaces unused ones.
Perhaps I will end up traversing from 0-1000 and just skipping empty ones.
Or, alternatively, can I enumerate all regions in the map?
Is it possible to set custom values on a Region through the editor, and if so can I get them through script?
I already checked and the max length of a Regions name when created through the editor is 80, so I can use that to store some metadata if I can just get to it through script.
The fact that I can't redeclare a static function in another file completely ruins the keyword then. Grrrr. And I suppose namespacing is impossible as well, then.
Yeah, no namespaces available.. i find it hard to deal with too.. Everything you declare is basically global. Unless it's in context of function.
Coding in galaxy is overall very similiar to coding in embedded C. Lots of static linking, no dynamic memory allocations etc.
Quote:
How can I get the regions name?
You can't. There's no native to do that.
What about using some kind of naming convention for regions? Like Namespace_1.
Quote:
Is it possible to set custom values on a Region through the editor, and if so can I get them through script? I already checked and the max length of a Regions name when created through the editor is 80, so I can use that to store some metadata if I can just get to it through script.
There are no custom values. You can indeed store some data in the name itself but it doesn't sound like a good way :)
Quote:
Or, alternatively, can I enumerate all regions in the map?
I don't think so.
If you really need that then viable apporach would be to write a custom script/app (whatever) that would read Regions file inside an map (its an XML file that looks like that) . From that data create .galaxy file which would fill the data into array at map initialization. I did similiar thing for my map to scan terrain texture cells and write it down to .galaxy file: https://bitbucket.org/Talv/untitled-escape/raw/776ffaa1e25a35754cab0d08f3549e03ef4ee916/map.SC2Map/TextureMap.galaxy
It's pretty unhandy though. As you obviously need to regenerate that file every time you edit regions.
Okay so tell me, how crazy am I here?
So as you might have guessed, I will do the galaxy coding while a real mapper will do the layout and world stuff.
I think my next step is creating a test map which means I have to fiddle with the world editor and learn some things there, too.
// ZZZ Build a table of User Data in the SC2 Editor with the name and// fields defined below.// Can name the table anything as long as the id is "EvilRegions"staticconststring_cpCoreRegionListKey="EvilRegions"// Region name as defined in world editor. Put all buildings that should// autospawn inside this region.// Type: String// Amount: 1staticconststring_cpCoreRegionFieldName="Region Name"// How many resources this evil base region has access to per wave.// We multiply this based on difficulty (for example * 4 is very hard,// * 0.6 is easy and * 1 is normal).// Type: Integer// Amount: 1staticconststring_cpCoreRegionFieldMinerals="Resources";// Can also trigger this base to start spawning from the regions// defined here. And yes, I know you can combine regions.// Note also that we don't look for spawning buildings in these, that's just in// the main region.// Type: String// Amount: 10staticconststring_cpCoreRegionFieldTriggerRegions="Trigger Regions";// What should happen when a player enters this base?// Type: String// Amount: 1staticconststring_cpCoreRegionFieldAction="Action";// Can be "Spawn X", where X is seconds between waves// We need to figure out the other action types// Make a new User Data table with these names and fields.staticconststring_cpCoreSettingsListKey="CPSettings";// If these unit types enter a region, do not start events.// Type: String// Amount: 1staticconststring_cpCoreSettingsIgnore="ignoreUnitTypes";// Spawn units from these kinds of buildings// Type: String// Amount: 1staticconststring_cpCoreSettingsSpawners="spawnFromTypes";staticstringgetSetting(stringsettingKey){returnUserDataGetString(_cpCoreSettingsListKey,settingKey,"Key",1);}staticstructSETTINGS{stringignoreUnitTypes;stringspawners;}// XXX Defaults, init them somewhere// SETTINGS.ignoreUnitTypes = "Medivac Battlecruiser Raven Banshee VoidRay VikingFighter ProtossRelic"// SETTINGS.spawners = "BanelingNest EvolutionChamber GreaterSpire Hatchery Hive HydraliskDen InfestationPit Lair LurkerDen RoachWarren SpawningPool Spire UltraliskCavern"staticconstint_maxEvilRegions=100;structRegionData{stringname;regionregionRef;intresources;region[10]triggers;stringaction;}// Map of region + trigger region to master region namestructRegionRef{regionref;stringname;}staticint_cpRegionRefAmount=0;staticRegionRef[1000]_cpRegionRefs;staticRegionData[_maxEvilRegions]evilRegions;// always use index=1 if the field is not an arraystaticstringgetStr(stringinstance,stringfield,intindex){returnUserDataGetString(_cpCoreRegionListKey,instance,field,index)}staticstringgetInt(stringinstance,stringfield,intindex){returnUserDataGetInt(_cpCoreRegionListKey,instance,field,index)}staticbool_cpCoreHandleUnitEntersTrigger(boolcheck,booldo){regiontriggeredByRegion=EventUnitRegion();unittriggeredByUnit=EventUnit();inti=0;// xxx check if triggeredByUnit should be ignored first because it would be faster I assume// xxx we check if the unit belongs to a player and that it's not in SETTINGS.ignoreUnitTypes// find spawners for this regionunitgroupspawners;for(;i<_cpRegionRefAmount;i+=1){if(_cpRegionRefs[i].ref==triggeredByRegion){spawners=DataTableGetUnitGroup(true,_cpRegionRefs[i].name);}}if(!spawners){returntrue;}// XXX START SPAWNING UNITS HOLY SHIT IT SHOULD WORK?!TriggerDestroy(TriggerGetCurrent());returntrue;}staticvoidbuildRegionData(){stringinstance;inti;intj;stringtriggerRegionName;stringregionName;regionregionTmp;intregionCount=UserDataInstanceCount(_cpCoreRegionListKey);triggerregionTrigger;for(;i<=regionCount;i+=1){instance=IntToString(i);regionName=getStr(instance,_cpCoreRegionFieldName,1);regionTmp=RegionFromName(regionName);if(!regionTmp){// XXX not sure what RegionFromName would return here, check it// XXX debugmessage that region doesn't exist and break}regionTrigger=TriggerCreate("_cpCoreHandleUnitEntersTrigger");TriggerAddEventUnitRegion(regionTrigger,null,regionTmp,true);evilRegions[i].name=regionName;evilRegions[i].resources=getInt(instance,_cpCoreRegionFieldMinerals,1);evilRegions[i].action=getStr(instance,_cpCoreRegionFieldAction,1);evilRegions[i].regionRef=regionTmp;_cpRegionRefs[_cpRegionRefAmount].ref=regionTmp;_cpRegionRefs[_cpRegionRefAmount].name=regionName;_cpRegionRefAmount+=1;for(;j<10;j+=1){triggerRegionName=getStr(instance,_cpCoreRegionFieldTriggerRegions,j);if(triggerRegionName!=""){regionTmp=RegionFromName(triggerRegionName);evilRegions[i].triggers[j]=regionTmp;TriggerAddEventUnitRegion(regionTrigger,null,regionTmp,true);_cpRegionRefs[_cpRegionRefAmount].ref=regionTmp;// ZZZ Master region name, not trigger region.// ZZZ The unitgroup of spawners is stored in datatable referenced by master region._cpRegionRefs[_cpRegionRefAmount].name=regionName;_cpRegionRefAmount+=1;}}}}staticvoidfindSpawners(stringregionName,regionr){unitgroupunits=UnitGroup(null,-1,r,null,0);unitgroupspawners=UnitGroupEmpty();unitcurrent;UnitGroupLoopBegin(units);while(!UnitGroupLoopDone()){current=UnitGroupLoopCurrent();// Do we spawn units from this type?if(StringFind(SETTINGS.spawnFromTypes,UnitGetType(current))){UnitGroupAdd(spawners,current);}UnitGroupLoopStep();}UnitGroupLoopEnd();DataTableSetUnitGroup(true,regionName,spawners);}staticvoidinitializeRegions(){inti;buildRegionData();for(;i<_maxEvilRegions;i+=1){// XXX String values default to "" right? Or null? Check this.if(evilRegions[i].name==""){break;}findSpawners(evilRegions[i].name,evilRegions[i].regionRef);}}
Good use of User Data Types! I should've suggested that earlier, but wasn't sure whether it will satistify you as they don't provide field for direct link to region.
TriggerDestroy(TriggerGetCurrent());
It is used correct in your code, but i think you might use some description on how it behaves as it wasn't obvious for me at begining.
TriggerDestroy will indeed destroy trigger but it won't stop already runing context. For example if you would put that at the top of the function it would still execute to the end.
There's also TriggerStop. But it won't instantly kill runing trigger as one might think. It must be first released before it can be killed by scheduler. Releasing runing trigger is archived by calling Wait function. Then scheduler jumps to next trigger and can kill the previous one.
If region with given name doesn't exist it will return null, so you should do null comparison.
As far as i remember galaxy overall doesn't like those logical comparisons without clear condition, on complex types like region etc. And usually just throws an error.
// XXX String values default to "" right? Or null? Check this.if(evilRegions[i].name=="")
Every declared string is initialized to "" (empty string) by galaxy VM. So that condition is correct.
There are some natives that can return null strings though. Like TriggerGetFunction called on non-existing trigger will return null. Trying to compare null string to empty string would throw a warning.
Thanks for your most excellent guidance.
I've attached the latest version of my crap.
If anyone would be so kind as to tell me where my code really sucks, that would be awesome!
In the Data editor in the Evil Regions table, you can change "Action" to things from here:
http://www.sc2mapster.com/paste/12941/
If you want to play. But just load the map up, start it, and move the tank to one of the 3 signs at the bottom. They all do the same thing.
Note that if you stagger so much that the timer will go over the specified repeat, I have not tested that and don't care, really.
There are also probably some other things you could do to break it.
I think there is one bug. Some times, and I have not tested this enough to verify how or when, when you enter either the first or the last of the additional 3 trigger regions with your tank, it will not start the spawns.
EDIT: Okay so I already found a few bugs. For example you can only have Repeat set to 30 in the attached version. Anyway that's what you get when coding for one test case, and it's not really important.
I am more interested in the level of shitty in my code.
Someone tell me how bad it runs please, and that it needs to be optimized in 5 different ways.
And then enumerate those ways :-P Thank you!
Okay so I could really use some help making this stuff to a library.
I have renamed the library ID to "Spwn" and tried adding it to the "CGame" GameplaySettings object TriggerLibraries thing as both
"Spwn" + ""
"Spwn" + "Base.SC2Data/LibSpwn"
( for some reason I did not try with LibSpwn.galaxy yet but now I've been fiddling with this crap for HOURS and I give up until tomorrow )
Then I publish it to BNet and add as dependency in a new map.
But it never runs the LibSpwn.galaxy file. How can I make it?
It should be so that when you add the library in another map, the library gets loaded right? What the hell is going on?
And how come it's called "CGame" when every other map and mod that I see has it listed in the Gameplay Data tab as "Default SC2 Gameplay Settings"?
And why is it sometimes blue, sometimes brown, sometimes gray?
Okay so I could really use some help making this stuff to a library. I have renamed the library ID to "Spwn" and tried adding it to the "CGame" GameplaySettings object TriggerLibraries thing as both "Spwn" + "" "Spwn" + "Base.SC2Data/LibSpwn" ( for some reason I did not try with LibSpwn.galaxy yet but now I've been fiddling with this crap for HOURS and I give up until tomorrow ) Then I publish it to BNet and add as dependency in a new map.
But it never runs the LibSpwn.galaxy file. How can I make it? It should be so that when you add the library in another map, the library gets loaded right? What the hell is going on?
And how come it's called "CGame" when every other map and mod that I see has it listed in the Gameplay Data tab as "Default SC2 Gameplay Settings"?
And why is it sometimes blue, sometimes brown, sometimes gray?
To make sure a library is loaded, right click on the library and select "Change Library ID". In that dialog, there is a checkbox called "Auto Load Library in Game". Checking this box will add the appropriate entry into the Trigger Libraries.
CGame is the core definition and can not be changed and is not used. The Default SC2 Gameplay settings entry is the only one used (has an ID of dflt), and if you wish to make alterations, you must do it to that one.
As for the colors, these are defined in the color preferences, you can change them if you wish. What they mean is indeed important. I will attempt to summarize, but if you wish a more in depth explaination, I will happily provide one. Every data entry is defined somewhere, either in the Core dependency (basically the base elements of the game), in a Blizzard controlled dependency (The Liberty and Swarm ones), in a dependency of your own making, or in the current document. One of the key features of the data editor is that you can define an object in one dependency and then override it in a higher dependency. The game takes the lowest defined dependency (Starting with Core, and working down the list in your dependency definitions, and down the lists of the dependencies listed in other dependencies), to determine the final data that is used. This permits one to define a data object to be used in many maps, but override fields for specific maps. The colors are used to reflect where the current data is drawn from. If you switch to XML mode, you will see the data listed for your current map, but you will also see a listing on top of each dependency. You can select and see what the data for that given data type is in the selected dependency.
That's the VERY short version. Frankly, I should write a guide, and I may, time allowing.
You seem to worry a lot about quality of your code :) It looks good to me as a concept.
One good side about so many limitations in galaxy is that it's hard to write a broken code.
Do you use some external editor for coding? I noticed that you have all your stuff in a single custom script block. I hope you are not typing this inside a SC2 editor? Do you? :)
And script itself is already too big for me to go through every piece to be honest. I'm not even entirely sure what's the goal of your map.
You could also organize it a bit - split into files etc.
Have you resolved your library issue yet?
Because looking at your map i don't see any library created :) I think mentioned data entries are to export library outside of the mod so that it can be used in maps. But firstly you need to actually create a library inside a trigger editor ("New -> Library").
If you want a live example checkout "GAx3", public mod on bnet. It has a library that is exported into maps that use the mod as a depedency. Plus it is partly based on custom script which is included from file inside the mod itself.
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
Hi, I am looking for an example of some very advanced Galaxy script. For example a complete map like Tower Defense Tycoon, if it was scripted manually, which it probably wasn't. I just need something to learn from, some script to read so that I can understand how to do basic things in Galaxy, if they are even possible.
More specifically I want to know the best/cleanest way to
I will probably have more questions as I go, which is why I'd rather have access to an already-existing advanced map from which I could just deduce these things myself. But it's hard to find one.
After reading the forums here for a while I recognize many names of whom I'd like to read their code, but I can't find their maps through the editor. That probably means they have obfuscated the code right? Or locked their maps, that is.
Also, how can I get the ID of a unit that I select with my mouse in the World Editor? The code I am working with references UnitFromId(123), but how can I find that unit in the terrain view?
there a programs to extract the downloaded maps from the sc2 folders. then get an mpq-editor, open the map with it and extract the galaxy script, so you can see the script of any map you have downloaded, just needs some googleing.
1. i dont think so
2. you can use any function for CreateTrigger (as far as i know)
3. behaviours must be created with data-editor
I'm not sure which method is the best in terms of perfomance, but i think every of those suggested are pretty close to each other. As long as the limit is 50. It's better though to use a single trigger. As every one would create a new context with separate stack.
So have an single trigger with multiple events tied to it. Every event would have an own timer. To detect which timer has been triggered you must store all of them inside an array then loop through all of them and match the right one using:
Nope. TriggerCreate and funcref's is all we have available for that type of things.
I believe so. As long as function matches the
pattern it will accept anything. There's really no difference between static and non static functions though. You just can't reference them outside of the current script file where they were defined. They are still global for my understanding - you can't redeclare another static function with same name in different file.
It's not possible to create any static data entries like behaviors via galaxy. All you can do is adjust some values of already created entries with:
Galaxy doesn't support associative arrays natively. You would need to implement some sort of library by yourself. But as it would require some sort of hasing system it might not be worth the effort.. and even be slower than other methods.
The simpliest way is just to have an array of structs. And to fetch them by name use one of those 2 methods:
1) Store the name inside an struct and loop through all the entries untill it's matched.
2) Use dynamic data tables and store the key id to an array inside them.
Pretty much. Editor is listing only maps marked as public. Private maps aren't necessarily obfuscated - it's optional. Also only auto-generated code from triggers built via editor GUI is being obfuscated. If someone has wrote its code by hand it will be untouched - default obfuscator from editor cannot handle that type of things..
Private maps can still be downloaded as Funky said. Just not directly through editor.
I don't know any way. Atleast not a simple way that doesn't involve some tedious workarounds.
What i do is instead of preplacing units on the map i preplace points. Then i dynamically create units at position of the point. Points can by fetched with PointFromName(string name).
For other things you can check out my map (Back To Brood Ice Escape, on arcade):
https://bitbucket.org/Talv/untitled-escape
Can be downloaded here: https://bitbucket.org/Talv/untitled-escape/get/master.zip
It's a mix of GUI triggers and custom scripts.. core libraries were written in galaxy and are exposed into GUI via custom API. Although it's pretty dirty i must say. I would have done many things different if i would be about starting new map. There's some inconsistency etc.. So it's not an good project to base things on but you might find something useful after all.
There's also cortex engine (library for role play maps)
https://github.com/90-proof/cortex-forge/tree/master/Cortex initially created by Motive.
And i must say i don't know any other public maps that use pure galaxy. Most poeple just built stuff with blocks in GUI..
Thank you. That is an awesome answer, very thorough. If I make this map work then my code will be available for everyone to use. If I can't make it work through pure script then I will discontinue it, so we will see.
The fact that I can't redeclare a static function in another file completely ruins the keyword then. Grrrr. And I suppose namespacing is impossible as well, then.
Before reading your answer about preplacing points I had already started doing something similar but using regions instead. But I need some metadata on them, and some help :-)
If I have a
How can I get the regions name? I know about RegionFromName but I can't enumerate them. With RegionFromId I can just do a while loop until it returns nothing to enumerate all regions. Or at least I assume so, provided the editor keeps the id numbers going upwards an replaces unused ones. Perhaps I will end up traversing from 0-1000 and just skipping empty ones.
Or, alternatively, can I enumerate all regions in the map?
Is it possible to set custom values on a Region through the editor, and if so can I get them through script? I already checked and the max length of a Regions name when created through the editor is 80, so I can use that to store some metadata if I can just get to it through script.
Yeah, no namespaces available.. i find it hard to deal with too.. Everything you declare is basically global. Unless it's in context of function.
Coding in galaxy is overall very similiar to coding in embedded C. Lots of static linking, no dynamic memory allocations etc.
You can't. There's no native to do that.
What about using some kind of naming convention for regions? Like Namespace_1.
There are no custom values. You can indeed store some data in the name itself but it doesn't sound like a good way :)
I don't think so.
If you really need that then viable apporach would be to write a custom script/app (whatever) that would read Regions file inside an map (its an XML file that looks like that) . From that data create .galaxy file which would fill the data into array at map initialization. I did similiar thing for my map to scan terrain texture cells and write it down to .galaxy file:
https://bitbucket.org/Talv/untitled-escape/raw/776ffaa1e25a35754cab0d08f3549e03ef4ee916/map.SC2Map/TextureMap.galaxy
It's pretty unhandy though. As you obviously need to regenerate that file every time you edit regions.
Thank you, between your answers here, your map, and Cortex, I have all the code I need to inspect. Well done, good work.
Okay so tell me, how crazy am I here? So as you might have guessed, I will do the galaxy coding while a real mapper will do the layout and world stuff.
I think my next step is creating a test map which means I have to fiddle with the world editor and learn some things there, too.
Good use of User Data Types! I should've suggested that earlier, but wasn't sure whether it will satistify you as they don't provide field for direct link to region.
It is used correct in your code, but i think you might use some description on how it behaves as it wasn't obvious for me at begining.
TriggerDestroy will indeed destroy trigger but it won't stop already runing context. For example if you would put that at the top of the function it would still execute to the end.
There's also TriggerStop. But it won't instantly kill runing trigger as one might think. It must be first released before it can be killed by scheduler. Releasing runing trigger is archived by calling Wait function. Then scheduler jumps to next trigger and can kill the previous one.
There's no real multi-threading in galaxy.
If region with given name doesn't exist it will return null, so you should do null comparison.
As far as i remember galaxy overall doesn't like those logical comparisons without clear condition, on complex types like region etc. And usually just throws an error.
Every declared string is initialized to "" (empty string) by galaxy VM. So that condition is correct.
There are some natives that can return null strings though. Like TriggerGetFunction called on non-existing trigger will return null. Trying to compare null string to empty string would throw a warning.
Thanks for your most excellent guidance. I've attached the latest version of my crap.
If anyone would be so kind as to tell me where my code really sucks, that would be awesome!
In the Data editor in the Evil Regions table, you can change "Action" to things from here: http://www.sc2mapster.com/paste/12941/ If you want to play. But just load the map up, start it, and move the tank to one of the 3 signs at the bottom. They all do the same thing.
Note that if you stagger so much that the timer will go over the specified repeat, I have not tested that and don't care, really. There are also probably some other things you could do to break it.
You can call
To manually trigger.
I think there is one bug. Some times, and I have not tested this enough to verify how or when, when you enter either the first or the last of the additional 3 trigger regions with your tank, it will not start the spawns.
EDIT: Okay so I already found a few bugs. For example you can only have Repeat set to 30 in the attached version. Anyway that's what you get when coding for one test case, and it's not really important.
I am more interested in the level of shitty in my code. Someone tell me how bad it runs please, and that it needs to be optimized in 5 different ways. And then enumerate those ways :-P Thank you!
All suggestions welcome!
Okay so I could really use some help making this stuff to a library. I have renamed the library ID to "Spwn" and tried adding it to the "CGame" GameplaySettings object TriggerLibraries thing as both "Spwn" + "" "Spwn" + "Base.SC2Data/LibSpwn" ( for some reason I did not try with LibSpwn.galaxy yet but now I've been fiddling with this crap for HOURS and I give up until tomorrow ) Then I publish it to BNet and add as dependency in a new map.
But it never runs the LibSpwn.galaxy file. How can I make it? It should be so that when you add the library in another map, the library gets loaded right? What the hell is going on?
And how come it's called "CGame" when every other map and mod that I see has it listed in the Gameplay Data tab as "Default SC2 Gameplay Settings"?
And why is it sometimes blue, sometimes brown, sometimes gray?
To make sure a library is loaded, right click on the library and select "Change Library ID". In that dialog, there is a checkbox called "Auto Load Library in Game". Checking this box will add the appropriate entry into the Trigger Libraries.
CGame is the core definition and can not be changed and is not used. The Default SC2 Gameplay settings entry is the only one used (has an ID of dflt), and if you wish to make alterations, you must do it to that one.
As for the colors, these are defined in the color preferences, you can change them if you wish. What they mean is indeed important. I will attempt to summarize, but if you wish a more in depth explaination, I will happily provide one. Every data entry is defined somewhere, either in the Core dependency (basically the base elements of the game), in a Blizzard controlled dependency (The Liberty and Swarm ones), in a dependency of your own making, or in the current document. One of the key features of the data editor is that you can define an object in one dependency and then override it in a higher dependency. The game takes the lowest defined dependency (Starting with Core, and working down the list in your dependency definitions, and down the lists of the dependencies listed in other dependencies), to determine the final data that is used. This permits one to define a data object to be used in many maps, but override fields for specific maps. The colors are used to reflect where the current data is drawn from. If you switch to XML mode, you will see the data listed for your current map, but you will also see a listing on top of each dependency. You can select and see what the data for that given data type is in the selected dependency.
That's the VERY short version. Frankly, I should write a guide, and I may, time allowing.
You seem to worry a lot about quality of your code :) It looks good to me as a concept.
One good side about so many limitations in galaxy is that it's hard to write a broken code.
Do you use some external editor for coding? I noticed that you have all your stuff in a single custom script block. I hope you are not typing this inside a SC2 editor? Do you? :)
And script itself is already too big for me to go through every piece to be honest. I'm not even entirely sure what's the goal of your map.
You could also organize it a bit - split into files etc.
Have you resolved your library issue yet?
Because looking at your map i don't see any library created :) I think mentioned data entries are to export library outside of the mod so that it can be used in maps. But firstly you need to actually create a library inside a trigger editor ("New -> Library").
If you want a live example checkout "GAx3", public mod on bnet. It has a library that is exported into maps that use the mod as a depedency. Plus it is partly based on custom script which is included from file inside the mod itself.