• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Best of the Wurst 5

Status
Not open for further replies.
Level 23
Joined
Jan 1, 2009
Messages
1,610


1.29 AND PTR

The wurst team is excited and curious to see what the new PTR changes will mean for the wc3 modding community. We are anticipating the release to the stable battle.net realms, and will add support to the wurst toolchain as soon as possible.

At this time, we do not plan to have a separate branch to support PTR installs. However, if the classic team continues to make extensive use of the testing realm, we may consider that option in the future.

We are actively thinking about what the PTR changes will mean for us and how it should affect 2018's timeline. For example, now that 24-player maps are coming, we will prioritize support for editing map player metadata from vscode.


24 unique player-colors, in wurst!


UPDATES

We are quite a bit late with our blog post this month, as there were no major updates to present and we were focusing on quality work as well as keeping an eye on Wurst's stability. That being said, in march we revamped the lambda syntax for more flexibility and cleaner code. Additionally we again have received a number of contributions from the community.

  • Type inference plus new block handling for lambda expressions and closures
  • We resolved several opened issues (#44, #45, #48) and merged 7 pull requests (#47, #49, #50, #51, #51, #54, #55, #56) for the standard library.
  • The Wurst Setup received several stability updates and we are working on further integration with the wurst tool suite.

 
Last edited by a moderator:
Level 23
Joined
Jan 1, 2009
Messages
1,610

NEW LAMBDA SHENANIGANS

Types are now optional when writing lambda expressions:
Wurst:
interface UnitFunction
    function run(unit u)
  
function group.foreach(UnitFunction closure)
    ...

// Before:
g.foreach((unit u) -> print(u.getName()))
// Now also possible:
g.foreach(u -> print(u.getName()))
// Works for multiple params too:
list.foldl((x,y) -> x+y)
Additionally, you can now get rid of begin and end keywords for statement lambdas if the lambda is the last argument in the current line. The lambda is them moved behind the function call like so:
Wurst:
// Before:
doAfter(10.0, () -> begin
    u.kill()
    createNiceExplosion()
    doMoreStuff()
end)

// Now also possible:
doAfter(10.0) ->
    u.kill()
    createNiceExplosion()
    doMoreStuff()

With the new syntax the indentation defines the block. This allows one to define customized control flow, which looks very similar to a construct built into the language.

The begin and end syntax will of course remain for backwards compatibility and for when the lambda isn't the last argument.



WURST SPELL EXCURSION

Spells are a very popular choice to script in wc3, and most maps have at least some scripted abilities.

Here we will show a concise example of how to combine object-editing, event listening and the use of closures to create a decent spell without much hassle.

In this case we are making a timed explosion spell called "Conflagration". The explosion applies a burn debuff to hit enemies, which causes successive conflagration hits to deal bonus damage. We aim to make the spell easily configurable and in the end upload it to github so it can be easily imported into a wurst project.

CONFIGURATION PACKAGE

To make a nicely configurable spell, it can sometimes help to think about the configuration package first. Based on the concept, we know in what way we want to allow the user to customize the ability, like:

- base damage, bonus damage
- spell radius, effect, duration
- buff duration, effect

To put this into wurst code, we create a new package with the variables from the list. In order for them to be configurable by the user, we must annotate them with @configurable.

Wurst:
package ConflagrationConstants
import public Assets

@configurable public let BASE_DAMAGE = 50.
@configurable public let BONUS_DAMAGE = 50.

@configurable public let SPELL_EFFECT_PATH = Units.infernalBirth
@configurable public let BONUS_EFFECT_PATH = Abilities.fireBallMissile
@configurable public let SPELL_EFFECT_DURATION = .75
@configurable public let SPELL_RADIUS = 256.

@configurable public let SPELL_ICON = Icons.bTNBreathOfFire
@configurable public let SPELL_NAME = "Conflagration"
@configurable public let SPELL_TT_NORMAL = "Cast Conflagration"
@configurable public let SPELL_TT_EXTENDED = "Conjures a firey explosion at the " +
    "target, damaging nearby enemies and applying a buff, which will cause them to " +
    "take more damage on succeeding hits of this spell."

@configurable public let BUFF_DURATION = 7.5
@configurable public let BUFF_EFFECT_PATH = Abilities.incinerateBuff

@configurable public let BUFF_NAME = "Conflagrated"
@configurable public let BUFF_TT = "This unit has weakness to fire"
Clearly, the above paints a picture of a quick, unit-style spell with fairly low damage and duration.

SPELL OBJECTS

Next we'll generate the required objects for our spell. We will be needing an ability object for the unit that uses the spell, and a buff object that displays the presence of conflagration visually and as status.

Thus we create a new package:
Wurst:
package ConflagrationObjects
import public ConflagrationConstants
import public BuffObjEditing
import ChannelAbilityPreset

public let SPELL_ID = compiletime(ABIL_ID_GEN.next())
public let BUFF_OBJ = compiletime(createDummyBuffObject(BUFF_NAME, BUFF_TT,    Icons.bTNFireBolt, Abilities.incinerateBuff, "chest"))

@compiletime function genObj()
    new ChannelAbilityPreset(SPELL_ID, 4, true)
    ..setName(SPELL_NAME)
    ..presetTooltipNormal((int lvl) -> SPELL_TT_NORMAL)
    ..presetTooltipNormalExtended((int lvl) -> SPELL_TT_EXTENDED)
    ..presetIcon(SPELL_ICON)
    ..presetButtonPosNormal(0, 0)
    ..presetManaCost((int lvl) -> 0)
    ..presetCooldown((int lvl) -> 4.)
    ..presetHotkey("Q")
    ..presetTargetTypes(Targettype.POINT)
    ..presetAreaofEffect((int lvl) -> SPELL_RADIUS)
    ..presetOption(Option.TARGETIMAGE, true)


SPELL EFFECT

Finally we create the actual effect. At the map's initialization we register a spell event listener, which will fire when any unit casts our generated spell.

Wurst:
package Conflagration
import ConflagrationObjects
import ClosureTimers
import ClosureForGroups
import RegisterEvents
import EventHelper
import HashMap

let buffId = BUFF_OBJ.abilId
let buffMap = new HashMap<unit, CallbackSingle>()
init
    registerSpellEffectEvent(SPELL_ID) ->
        let caster = GetTriggerUnit()
        let tpos = getSpellTargetPos()
        flashEffect(SPELL_EFFECT_PATH, tpos)
        doAfter(SPELL_EFFECT_DURATION) ->
            forUnitsInRange(tpos, SPELL_RADIUS)  u ->
                if u.hasAbility(buffId)
                    caster.damageTarget(u, BONUS_DAMAGE)
                    flashEffect(BONUS_EFFECT_PATH, tpos)

                caster.damageTarget(u, BASE_DAMAGE)
                u.addAbility(buffId)
                if buffMap.has(u)
                    destroy buffMap.get(u)
                let cb = doAfter(BUFF_DURATION) ->
                    if buffMap.has(u)
                        buffMap.remove(u)
                        u.removeAbility(buffId)
                buffMap.put(caster, cb)


vSmdcaS.gif

The spell in action


CREATING A REPOSITORY FOR OUR PROJECT

Finally, let's create a git dependency from our wurst project. We will be using GitHub and it's corresponding desktop app which requires a free account. If you don't already have one, please create an account and setup the app.

Once you have the app setup, we can create a new git repo inside our project folder by choosing File -> Add Local Repository. Select the root of your project and then select create a repository:

Hkti2j7.png


Leave all settings as is and create the repository:

JGyhA9g.png


Hooray, our repository is created. All our existing files have been added to an initial commit. Files that shouldn't be versioned are automatically ignored by the configuration generated by the setup tool. We can now publish our repository:

EUi3tRA.png


If everything went well, you repository is now published. You can view the repository we created in this blog post here: Frotty/ConflagrationSpell

Note If you want to know more about the git workflow, you can checkouts everal tutorials online, like this one.

USING THE REPOSITORY AS WRUST DEPENDENCY

Open the setup tool and import the wurst.build file of some other project. Under Advanced you can now enter the link to the repository that we created above. Hit Update Project to update the dependencies.

vE4d08I.png


noIBCgD.png


Reload VSCode and you should be able to import the spell as necessary.




And that's it! Thanks for reading another Best of the Wurst blog-thing.

Cheers
Frotty & Cokemonkey11
 
Last edited by a moderator:

Deleted member 219079

D

Deleted member 219079

omg the landba features are cool they are cool!
upload_2018-3-26_22-32-6.gif
 
Level 23
Joined
Jan 1, 2009
Messages
1,610
I can see myself becoming a WurstScript user if I happen to start modding WC3 again. :)
Long time no see Robbe :)
Maybe with the next patches it becomes more interesting again.

Kinda unrelated but, are there plans of adding a better way of combining strings?

in C#
$"{u.getName()}'s owner is {u.getOwner().toString()}"

in Javascript
`${u.getName()}'s owner is ${u.getOwner().toString()}`

or maybe it exists and I am just unaware.

Yea we have thought about adding "You got $(goldVal) gold" but it's not a priority right now.
If you want Java-like auto usage of .toString() you could overload the + operator for those types. Though not really recommended.
There also exist the .format() functions.
 
Last edited:
Level 3
Joined
Jul 8, 2008
Messages
28
That Looks so Easy Now , as _OBJ Spell && Effect
(if someone doesnt know what is OBJ , go search in the WEB for Coders what is OBJ and how can i use it)
 
Status
Not open for further replies.
Top