• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

Wurst resources

Status
Not open for further replies.
Level 2
Joined
Feb 27, 2019
Messages
14
Hi Hive,
I'm looking for some pointers with picking up Wurst syntax.
How to iterate over all players in the game and spawn starting units for them?

I've read most of the documentation on WurstScript • Home including WurstScript • Manual (#for-loops) however I'm still painfully stuck.
Here's a pastebin of my current typical problems (wurstbin) and here's an example from it

Code:
@test function testUnitTesting()
    let a = 5 * 2
    a.assertEquals(10) // passes

@test function testGetPlayersUnitTest()
    let a = GetPlayers() + 100
    a.assertGreaterThan(0)
    // fails with compiler errors!
    // where to put that @compiletime ?

init
    for player p in players
        print(p.getName()) // compiler error

I know C++ (UnrealEngine) and web development (JavaScript), so I know what a variable is, how to loop, and other general programming concepts and I'm a diehard unit tester. But I've never used Java or Python, so Wurst is pretty foreign.

  • I'm looking for code examples and other resources (I'm not really used to coding without the glorious stackoverflow...)
  • Specifically answers to questions from that wurstbin (9 questions in total). Also, I'll buy you a cup of coffee for some chat time via some IM.

I have no idea how Jass works, but am somewhat ok with GUI triggers (see for yourself below).
Wurst is really pretty, so I would indeed like to pick it up.

Generally, I belive if I could retype what I've done thusfar in GUI I would be ok.
E.g. below is my creep wave spawner.



  • -------- Wave Increment --------
  • Actions
    • -------- Run wave --------
    • Trigger - Run Wave Load <gen> (checking conditions)
    • -------- Increment for next wave to total or reset to WaveDifficulty --------
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • Wave Less than WavesTotal
      • Then - Actions
        • Set Wave = (Wave + 1)
      • Else - Actions
        • Set Wave = (Min(WaveDifficulty, WavesTotal))
        • Set WaveDifficulty = (WaveDifficulty + 1)
    • -------- Start timer --------
    • Countdown Timer - Start Timer as a One-shot timer that will expire in TimerDuration seconds
  • -------- Trigger Wave Load --------
  • Actions
    • -------- Type A --------
    • Set SpwnCount = (SpwnCountA[Wave] + WaveDifficulty)
    • Set SpwnUnitType = SpwnUnitTypeA[Wave]
    • Trigger - Run Wave Spawn <gen> (checking conditions)
    • -------- Type B and C (nested if statements) --------
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • SpwnCountB[Wave] Greater than 0
      • Then - Actions
        • -------- Type B --------
        • Set SpwnCount = (SpwnCountB[Wave] + WaveDifficulty)
        • Set SpwnUnitType = SpwnUnitTypeB[Wave]
        • Trigger - Run Wave Spawn <gen> (checking conditions)
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • SpwnCountC[Wave] Greater than 0
          • Then - Actions
            • -------- Type C --------
            • Set SpwnCount = (SpwnCountC[Wave] + WaveDifficulty)
            • Set SpwnUnitType = SpwnUnitTypeC[Wave]
            • Trigger - Run Wave Spawn <gen> (checking conditions)
          • Else - Actions
      • Else - Actions
    • -------- Finish --------
    • Unit Group - Remove all units of SpwndGroups[0] from SpwndGroups[0]
    • Unit Group - Add all units of SpwnGroup to SpwndGroups[0]
    • Hashtable - Save Handle OfSpwndGroups[0] as 1 of 1 in Camps
    • Unit Group - Remove all units of SpwnGroup from SpwnGroup
    • -------- Testing --------
    • Game - Display to (All players) the text: (array + (String((Number of units in SpwndGroups[0]))))
    • Game - Display to (All players) the text: (single + (String((Number of units in SpwnGroup))))
    • Trigger - Run All Camps to Red <gen> (checking conditions)
  • -------- Trigger Wave Spawn --------
  • Actions
    • Unit - Create SpwnCount SpwnUnitType for Player 9 (Gray) at (Center of (Load 0 of 1 in Camps)) facing 0.00 degrees
    • Unit Group - Add all units of (Last created unit group) to SpwnGroup

Cheers
 
Last edited:
Level 23
Joined
Jan 1, 2009
Messages
1,610
edit: Btw this is wrong forum. Should be trigger & scripts.

I don't think you have problems with syntax, rather with understanding the underlying basics. They are more touched upon in beginner guide and blog posts.

1-2. If the compiler tells you something, it's most likely correct. You don't need the imports because they are part of the auto imports.

3. Only for group closures. You can ctrl + left click all references to jump into them and just check the package out to see what's inside (taught in beginner's guide)

4. If you want to convert GUI, use the convert function in editor and check the emitted jass. Everything that exists in Jass exists in Wurst, though you should usually use wrappers.
It just takes a little getting used to, and heavy usage of autocomplete and search. I don't know what you mean by MAP_RESOURCE_TRADING_ALLIES_ONLY. What you are doing there is simply defining global variables.

5. "What is the command for compile in VSCode when using wurst?" it's "run map", what a surprise. You most likely simply aren't saving your files (ctrl + s) thus your code might appear without errors, but the on disk variant still has them. Make sure to save before running. This isn't really related to wurst.

6. Tests are compiletime - only (compiletime is the time you run a run/build task in vscode). This means wurst interprets your code on your machine instead of Warcraft 3.
This also means that only natives, that have been implemented in the wurst compiler, can be run. Exactly what the error tells you "The native <GetPlayers> has not been implemented for compiletime!"
You can only make this test pass if the native were to be implemented - but even then those tests would require a map to read that data from.
Thus I think it is pointless in the first place. It is nearly impossible to usefully test code that relies on wc3 intrinsics, since we don't know how a lot of things internally work.

7. RTFM! you said you read the website, but obviously you didn't: WurstScript • Closure For Groups more on closures: WurstScript • Get close with closures
Also, "u.GetUnitName()" is very incorrect. All extension functions start lowercase. If a function starts with uppercase, it's a native. Therefore no business even calling with dot.
Also if you are looking for the extension func for something like "GetUnitName", the "unit" part is implied and therefore omitted in the wrapper name.
What you're looking for is "u.getName()" - again, use autocomplete via ctrl + space.

8. No. Just make multiple init blocks if you want that, but it seems like an anti-pattern to me.

9. No. You can convert your triggers to Jass and start from there. But as said in every Jass tutorial, GUI is inherently different from Jass, and you will just need to learn and adapt.

I think this is mostly stuff which is taught in beginner's guide. If you have specific suggestions for improvements I will gladly add them.

I'm looking for code examples and other resources

Did you check the showcase? All my maps are open source in github and linked there, also my Frentity lib for bootstrapped map dev. It has a package which basically initializes players like you are trying:
Frotty/Frentity


Generally, I belive if I could retype what I've done thusfar in GUI I would be ok.

You can, but it will result in very sub par code. Also since wurst is OOP you should foolow the paradigm and encapsulate your code. In my defense map I use a wave classes and a spawner class e.g.
This is a freehand example without imports I just hacked together: wurstbin
Often you also want to couple spawning with wurst object generation.

Btw a few tips for our chat: We are mostly EU ppl with dayjob. If you write something at 2am or during normal working hours, and then don't stay still evening, we normally can't answer you.
Also you should highlight someone, e.g. me, by writing their name.

Cheers
 
Last edited:
Level 2
Joined
Feb 27, 2019
Messages
14
4. If you want to convert GUI, use the convert function in editor and check the emitted jass. Everything that exists in Jass exists in Wurst, though you should usually use wrappers.
Yess! Thanks a bunch, this is exactly what I was missing. (That and showcase maps, which I've checked briefly, but I am mining through now.) I can now create trigger in Warcraft 3 standard GUI editor, go under Edit > Convert to JASS while the trigger is selected, then copy all of this to a dummy copy.j file (so I get jass syntax highlight in VSCode) and then Ctrl + Click function names to find required syntax. (And don't forget to delete / comment out / whatever this copy.j before running). Helped a TON man with this!

Still, wondering if there's a quicker way to do this, because with a little script... I've created one for myself using windows macro and I'll come back to this later, maybe give new people an easier way once I can no longer call myself a complete newbie.

I guess I need to learn JASS and then come back to wurst. I' currently rocking through Beginning JASS Tutorial Series
Wurst:
function setWildsPerRegion(int i, rect r) // int i should be refactored to be called region
    let g = GetUnitsInRectOfPlayer(r, wild) // wild is a player set to players[20] (player 21 coal; because neutral hostiles need to be reissued commands periodically because they return to their camps)
    ForGroup(g, function saveUserData)
    wilds.saveGroupHandle(i, KEY_WILDS_GROUPWILD, g)
    // KEY_WILDS_GROUPWILD is my int constant referring to a key, i.e. child key in hashtable wilds)

function saveUserData()
     GetEnumUnit().setUserData(data)
    // I'd like to save integer (referring to a hashtable row, i.e. parent key)
    // similar to timer example attaching data?
Now, if you've used GUI before, you'll probably be wanting to do something like "Pick Every Unit in (group) and do actions", but there's a problem with that. In order to use that function (it's know as ForGroup in JASS), you'll need a separate function that you'll be unable to pass any values to. Now you could use global variables to transfer information, but then the spell wouldn't be able to be used by more than 1 unit at a time. You could possibly think of other ways around this, but they'd take more time than it would be worth. I'll give you a hint on how I do it, I use a loop. "Loop?" you might be thinking, "how can I use a loop?" because if you've used GUI before, the loops would run for a fixed length (for integer # through #, do actions), unlike in JASS where a loop will only end when a certain condition is met. And that condition will be that all enemies in the enemy group have been hit.

Question – Wurst is really simple to understand and seems GREAT as compared to call call call in JASS, but I guess I'll continue having this problems until I understand some specific JASS workings. Or?
Wurst:
function setWildsInRegion(int i, rect r)
    let g = GetUnitsInRectOfPlayer(r, wild)
    for unit u in g
        u.setUserData(i)
    wilds.saveGroupHandle(i, KEY_WILDS_GROUPWILD, g)
I keep reading about "leaks" though I don't know if that's because of GUI, poor programming practices or built into some, in the end, JASS things? Like arrays not being sized (only in classes, but that's a wurst thing). Would this be a leak, am I iterating over 12783612836 or how many is there, empty units in that group?

Question – Another thing I'm missing sorely is GUI unit (ability etc) icons compared to 'hmil' for Militia. I've found UnitIds.wurst but don't know how can I write full names instead of codes. <solved: import UnitIds and then call UnitIds class var with UnitIds.militia (I though I've tried that... maybe didn't import)>

Presumably this would on compile time convert to pure integers anyway? I've read that you avoid using editor and instead write everything in wurst (e.g. by creating all important bits directly in code), but still, those icons are so nice. Currently I do it by setting dummy variables to unit types in GUI and copying that file to wurst (as described above) every time I need it, having edited data in GUI. Is there a better way to do this; or would you insist again that it's faulty because coding would be easier? Thanks.

It is nearly impossible to usefully test code that relies on wc3 intrinsics, since we don't know how a lot of things internally work.
I get by with print("created " + i.toString()) with rerunning the map everytime :/

Wurst:
function spawn()
    let wave = waves.pop()
    doPeriodicallyCounted(0.5, wave.amount) cb ->
        createUnit(.., wave.unitId, start, ..)
        ..issuePointOrder(OrderIds.attack, target)
Wow, thanks for that example. What do .. in createUnit do (i.e. what is it called and where can I read about it)?

-------------------------------------

5. "What is the command for compile in VSCode when using wurst?" it's "run map", what a surprise. You most likely simply aren't saving your files (ctrl + s) thus your code might appear without errors, but the on disk variant still has them. Make sure to save before running. This isn't really related to wurst.
War3 was running, therefore VSCode autosave couldn't save. I didn't notice because I was in fullscreen VSCode. Stupid mistake but that's how it is when you're learning something new man... Just hearing from another person that the code is fine, it's something else helps so much.. Yes I have RTFM (yes including the beginners section :p, but not closure events that much because it said advanced :( ), but it's hard when you've never used that specific manual and help from other people still helps ! And sorry for irking you with pushing, I didn't mean to demand help in any way. Thanks for the help, really. <3
 
Last edited:
Status
Not open for further replies.
Top