• 🏆 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!

[Release] WurstScript - A Wurst to Jass Compiler & IDE

Status
Not open for further replies.
Level 14
Joined
Jun 27, 2008
Messages
1,325
I just adjusted my testproject to use the new inits and it works perfectly.

Good job peq! I really like the new init rules, strict but simple. Here is a "deliver" penguin for you:
151697-albums6077-picture80083.gif


However about "init all globals with zero or null to avoid init crashes (commit: 4f7a483cee08cab32063c802a93cea86b0e4fc81)":
Im not sure if i like the idea of all globals getting initialized automatically. When all globals are initialized automatically its harder to spot bugs in the initialization order as accessing non-initialized globals wont crash the entire init thread anymore but instead just gives wrong results (if the desired user-defined init value differs from the default one). Sometimes a crashing initthread is very helpful because it tells you that you did something wrong instead of covering it up :>
 

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
However about "init all globals with zero or null to avoid init crashes (commit: 4f7a483cee08cab32063c802a93cea86b0e4fc81)":
Im not sure if i like the idea of all globals getting initialized automatically. When all globals are initialized automatically its harder to spot bugs in the initialization order as accessing non-initialized globals wont crash the entire init thread anymore but instead just gives wrong results (if the desired user-defined init value differs from the default one). Sometimes a crashing initthread is very helpful because it tells you that you did something wrong instead of covering it up :>

Debugging such crashes is always annoying. Java does does it in a similar way, but as Javas init order is determined at runtime, it only happens in very rare cases.

Maybe you are right, and I should just let it crash. I could add a check to the mapscript, which verifies that the init was executed competely. This could be implemented by starting a 1-second timer at the beginning of the main function. Then after the initialization of every package, I would increment an integer by one. The check-function called by the timer would then verify whether the integer has the correct value and if that is not the case, it could print a message about which package failed to initialize.
 
There's nothing wrong about initializing globals at map init. Actually, it avoids a lot of other stupid bugs in the WC3 engine aswell. So I say go for it!
Just make sure to split it up into multiple threads, so that the operation limit is a non-issue.
The increased loading time will be negligible.

And it's a lot cleaner than that timer approach.


EDIT: Then again, I think muzzel has a point here. Initializing globals might mask certain user errors.
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
The timer is a good idea even if globals get initialized automatically. There are other reasons why a package initializer could break the init thread, including op limit. Its always good to know if all the init code was executed successfully.
 

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
eclipse itself has 200MB filesize x.x

Is the problem disc usage or a slow Internet connection? Is this a reason fir you not to try Wurst?

Initially, we were planning to do a standalone editor, but there was just too much stuff we had to implement from scratch and which is already provided by editors like eclipse.
 

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
I recently changed the Wurst parser from jflex+cup to antlr4. I hope the new parser produces better error messages, and that it has a better error handling, which should make autocomplete work better in cases where there are some syntax errors.

I also used the parser rewrite to slightly change some rules of the language:


  • It is now possible to indent a block by more than one tab. This is often nicer, when using anonymous functions.
  • The rules for newlines have changed. Before the change, all newlines inside parenthesis were ignored. Now a newline is ignored, when there is a certain token before or after the newline. For example when a line begins with a comma, then the newline before the comma is ignored. The exact rules are in the manual.
    This idea is stolen from the Go programming language. I think with this change it is also possible to remove the ugly begin-end syntax for anonymous functions.
  • Some ugly corner cases of the grammar are now fixed, and the grammar is much more readable: old grammar and new grammar


yes, I have problem with my connection :>

btw, I really wanted to try wurst from the first place..

So after searching for one hour I found the download link for a smaller Eclipse download (60Mb): http://archive.eclipse.org/eclipse/downloads/drops4/R-4.3.2-201402211700/#PlatformRuntime
This small version is called "Platform Runtime Binary" and contains only the most basic plugins.
 

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
Update: I have implemented mutually recursive functions in the compiler. The compiler can now take something like this:

Wurst:
function foo(int x,real y) returns real
	return 1+bar(y, x)
function bar(real a, int b) returns real
	if a>0
		return a
	else
		return foo(1, 2.*b)

And turn it into that:

JASS:
function cyc_foo takes integer funcChoice, integer x, real y, integer b returns nothing
	if funcChoice == 0 then
		call cyc_foo(1, 0, y, x)
		set tempReturn_real = 1. + tempReturn_real
		return
	elseif funcChoice == 1 then
		if y > 0. then
			set tempReturn_real = y
			return
		else
			call cyc_foo(0, 1, 2. * b, 0)
			set tempReturn_real = tempReturn_real
			return
		endif
	endif
endfunction

Why is this useful?

When using the object-oriented features of Wurst (dynamic dispatch), a function which looks simply recursive can actually be mutually recursive with the function responsible for the dynamic dispatch. An example is the function showDirect in HotN: https://github.com/Crigges/HotN/blob/master/HotN/wurst/UIStuff/Menu.wurst#L57
 
Can you try to tackle that problem with timer-called functions not being able to take arguments?
That would improve readability of scripts a lot.

It could compile into a simple timer-attachment via GetHandleId(timer) and a constant (un-accessable for the user) hashtable, which has proven to be almost (not quite) as efficient as the fastest timer systems available.

But cleaner code and readability trumps speed imho. And I recall that being a central Wurst-mantra.
 

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
Can you try to tackle that problem with timer-called functions not being able to take arguments?
That would improve readability of scripts a lot.

It could compile into a simple timer-attachment via GetHandleId(timer) and a constant (un-accessable for the user) hashtable, which has proven to be almost (not quite) as efficient as the fastest timer systems available.

But cleaner code and readability trumps speed imho. And I recall that being a central Wurst-mantra.

With closures you can already do something similar using the ClosureTimers package:

Wurst:
// call foo after 10 seconds:
doAfter(10.0, () -> a.foo(3,x,y))

Still, having a built-in support for such timer attachments would be more efficient, since the closure solution has some unnecessary overhead.
 

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
A small new feature for eclipse:

IlSgHwx.png


You can right-click on a map and export all the custom text triggers into Jurst files. The contents of the files will not be converted to Wurst, it just extracts the text. The files will be stored in the wurst/exported/ folder.

This might be useful if you want to convert a vjass map to Jurst or Wurst as the first step is usually to put all the scripts into files.
 

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
I wish this had a stable DDS system like looking_for_help's.

I will try to port this to Wurst.

edit: So here is a port of the DamageEvent library to Jurst: http://peeeq.de/code.php?id=7433 (not tested)
The only things changed are public/private keywords, initialization, static ifs, use of an enum, and removed variable events.

But I would not use this as a standard library in Wurst. The API is not very wursty. For example one has to change a global variable to change the amount of damage done.

Also, we already have a much simpler DamageDetection package in the standard library. It cannot distinguish between physical and spell damage, but that could be build on top.
 
Last edited:
Level 8
Joined
Feb 3, 2013
Messages
277
I will try to port this to Wurst.

edit: So here is a port of the DamageEvent library to Jurst: http://peeeq.de/code.php?id=7433 (not tested)
The only things changed are public/private keywords, initialization, static ifs, use of an enum, and removed variable events.

But I would not use this as a standard library in Wurst. The API is not very wursty. For example one has to change a global variable to change the amount of damage done.

Also, we already have a much simpler DamageDetection package in the standard library. It cannot distinguish between physical and spell damage, but that could be build on top.

yes, i've seen the damage detection and closure events - I tried to copy some dds systems into wurst but I failed lol - maybe I'll try again.

I'm sorry but can you explain what jurst is? it's not in the manual anywhere. does it just make it compatible for wurst to call jass like functions? The code you gave me give me errors in eclipse.
 

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
I'm sorry but can you explain what jurst is? it's not in the manual anywhere. does it just make it compatible for wurst to call jass like functions? The code you gave me give me errors in eclipse.

Jurst is still work in progress, that's why it is not documented. Basically it is Wurst with a vJass like Syntax, so that it is less effort to port existing vJass libraries.

You can create files with the ending ".jurst" instead of ".wurst" to use Jurst.
 
Level 8
Joined
Feb 3, 2013
Messages
277
okay thanks
I'd like to point out is that the modified DamageEvent library you gave me is a little buggy. It detects damage and changes the amount properly, but doesn't detect damage type properly. Also, this isn't your fault at all but i should mention damageUnitTargetEx is kinda funky, it has problems killing a unit and dealing initial damage in my map.

Also, in TimedLoop module where can i access the ANIMATION_PERIOD? is there a way i can override this variable in my classes for flexibility?
so currently i have something like this
JASS:
package Lacerate

	import DamageEvent
	import Maths
	import Colors
	import TimedLoop
	
	constant int abilID = 'A000'
	constant int illuID = 'e001'
	constant colorA cStart = colorA(150, 235, 235, 255)
	constant colorA cEnd = colorA(0, 0, 0, 0)
	constant real cTime = 1.0
	constant string fxPath = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
	
	class FadeUnit
		use TimedLoop
		constant real ANIMATION_PERIOD = 0.02
		colorA start
		colorA finish
		real fTime
		real cTime
		boolean kill
		private boolean done
		unit u
		
		override function onTimedLoop()
			if this.done
				if this.kill 
					this.u.remove()
				this.stopTimedLoop()
				return
				
			this.cTime = this.cTime + ANIMATION_PERIOD
			colorA newC = this.start.mix(this.finish, this.cTime/this.fTime)
			this.u.setVertexColor(newC)
			
			if this.cTime >= this.fTime
				this.done = true
				
			
		construct (unit u, colorA start, colorA finish, real time, boolean kill)
			this.u = u
			this.start = start
			this.finish = finish
			this.fTime = time
			this.cTime = 0.
			this.kill = kill
			this.done = false
			
			this.startTimedLoop()
			
			
	class Lacerate
		use TimedLoop
		unit cast
		unit targ
		int count
		real dmg
		
		constant real ANIMATION_PERIOD = 0.1500
		
		override function onTimedLoop()
			unit d
			real x
			real y
			angle ang
		
				
			if this.count == 0 or not this.targ.isAlive()
				this.stopTimedLoop()
			else
				ang = angleBetweenCoords(this.cast.getX(), this.cast.getY(), this.targ.getX(), this.targ.getY())
				x = polarProjectionX(this.targ.getX(), -95., ang)
				y = polarProjectionY(this.targ.getY(), -95., ang)
				d = CreateUnit(GetOwningPlayer(this.cast), illuID, x, y, ang.degrees())
				d.setAnimation("attack")
				// this.cast.damageTarget(this.targ, this.dmg)
				unitDamageTargetEx(this.cast, this.targ, this.dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
				AddSpecialEffectTarget(fxPath, this.targ, "origin").destr()
				d.queueAnimation("stand")
				new FadeUnit(d, cStart, cEnd, cTime, true)
				this.count = this.count - 1
			
		construct (unit u, unit t, real amt)
			this.dmg = amt * .35
			this.cast = u
			this.targ = t
			this.count = 4
		
			this.startTimedLoop()
			
	function onAttack()
		if source.getAbilityLevel(abilID) > 0 and damageType == DamageType.PHYSICAL
			new Lacerate(source, target, amount)
			amount = 0.
			
	init
		addDamageHandler(function onAttack)

if I use damageTargetUnitEx, the caster has problems dealing damage on the first attack and killing attack. If a unit has 5 health and an attacker deals 50 damage it will not kill it directly. But if i change targets from the low hp unit to another unit, then the low hp unit will die.
If I use unit.damageTarget, damageEvent still identifies the damageType as physical - even though it's universal damage.


edit: solved
 

Attachments

  • Test.w3x
    34 KB · Views: 59
Last edited:
Level 19
Joined
Aug 8, 2007
Messages
2,765
How do you use predefind units with this? when i try


var u = gg_unit_Hblm_0001

it gives me a translation error the first run than a null error message every subsequent run.

Also, is there a documetation for the language?

e/ I get a null error message every subsequent run, no matter what the error message. Even without gg_unit_Hblm_0001, i still get

Error in de.peeeq.jmpq.JMpqError : Error in delete file : File not found

2e/ and WurstUpdater gets stuck on updating checksums. What a shame.
 
Last edited:
Level 19
Joined
Aug 8, 2007
Messages
2,765
Okay?

I'm still getting error message null when I try to run from eclipse. Either that or "could not find type angle"

E/ also gives me

Could not find imported package Handles
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
pretty sure mine is done right...

in wurst.dependencies i have

D:\Programs\Wurst SDK\Wurstpack\wurstscript\lib

which, in windows, brings me to a screen with 9 folders, 3 .wurst files, and a .project file
 
Level 8
Joined
Nov 20, 2011
Messages
202
pretty sure mine is done right...

in wurst.dependencies i have

D:\Programs\Wurst SDK\Wurstpack\wurstscript\lib

which, in windows, brings me to a screen with 9 folders, 3 .wurst files, and a .project file

Well maybe you got some outdated wurstpack, try this updater and redownload wurst with it: https://dl.dropboxusercontent.com/u/103859688/wurstpack updater.jar

Otherwise join the inwc irc and ask peq or me for help. But at the current point i argree with frotty it seems like you got some wrong depen file.
 

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
Just updated wurstpack. wurstupdater freezes on generating checksums

I don't know how this can happen. Usually generating the checksums only takes one second. Maybe Crigges has an idea, he wrote the updater. Maybe you can try to remove or rename the wurstpack folder and run the updater again, so that it will download a new, fresh copy.

Regarding the MPQ-error: I don't know what is causing this problem. I think the "error 32" you get means that the map file is being used by another program. I think Crigges also got this error some times, but it happens very rarely. So far I have no idea how I can debug or even reproduce this problem.
Is this a problem you have every time you save or does it happen only occasionally?
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
Is there a way to call functions by name dynamically? I wanted to make a library that makes it easy to call vJass functions from Wurst

e.g.

JASS:
function call(string number)
    print$number$(number)

where number is a constant
 

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
Is there a way to call functions by name dynamically? I wanted to make a library that makes it easy to call vJass functions from Wurst

e.g.

JASS:
function call(string number)
    print$number$(number)

where number is a constant

Why would it be easier to use call("5") instead of print5("5")? Why do you want to call vJass functions dynamically from Wurst?
 
Level 23
Joined
Jan 1, 2009
Messages
1,610
In other news:

  • Wurst now supports sized array-members
    Wurst:
    class Example
    	int array[10] ints
    with sizes up to 8191 while also allowing 8191 class instances​
  • Wurstpack now uses a native mpq implementation instead of 3rd-party editor
  • Cyclic imports now have to be handled manually with the 'initlater' keyword
  • Stdlib-additions, improvements & bugfixes - as usual
 
Level 23
Joined
Jan 1, 2009
Messages
1,610
Cool. How is this implemented in JASS (underlying code)?

Wurst creates SIZE amount of arrays and a set- and get-gunction.
All set/access expressions are replaced by those functions.
Inside the function the correct array is resolved with a binary search.
Tests have shown it to be way faster than a hashtable and the array limit is high enough to allow it.
 
Level 23
Joined
Jan 1, 2009
Messages
1,610
Arraying is always faster then hashtables, is it not?
Depends on the usage - if you had big sizes and wouldn't use binary search it would prolly be slower.

Interesting variation by the way.
?

Does it support everything the vanilla already did?
By the way as long as this is warcraft, technically every syntax is the same. Since you still have to convert it to JASS. Still looks more interesting then all those copies.

I suppose you mean WurstScript in general?
Vanilla Jass doesn't really support anything, what could be missing?
How is every "Syntax" the same? The Syntax is the main thing that differs from vanilla Jass, to improve readability, comfort and production speed.
Please read the first post and manual - that should answer all of your questions.
WurstScript has been around for almost 4 years - what copies are you talking about?

Wurst got very mature, this isn't just a preview, it works!
So don't just look, try it out :)
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Wurst creates SIZE amount of arrays and a set- and get-gunction.
All set/access expressions are replaced by those functions.
Inside the function the correct array is resolved with a binary search.
Tests have shown it to be way faster than a hashtable and the array limit is high enough to allow it.

Nice, this is a very interesting feature. But do I understand right that if I write something like:

Wurst:
class Example
	int array[8191] ints

that this will create 8191 global arrays? If so, won't that cause the map size to explode quite fast?
 
Level 23
Joined
Jan 1, 2009
Messages
1,610
Nice, this is a very interesting feature. But do I understand right that if I write something like:

Wurst:
class Example
	int array[8191] ints

that this will create 8191 global arrays? If so, won't that cause the map size to explode quite fast?

yes it will create 8191 arrays, the question is, when would you ever need such a big member-array ?
Realistic applications most rank below 20, 100 at most and the maximum amount of variables in a mapscript is about ~25k
Again, the mapscript doesn't bloat with proper sizes.
It is mostly for performance sake where you don't want to use a HashMap for just a few values inside a class.

I thought there was difficulties with a few functions as well such as DDS.

With closures/lambda-expressions DDS is actually more handy in wurst.
 
Level 1
Joined
Jun 17, 2014
Messages
3
Hi, thanks for the reply.

I feel like I'm missing something obvious. The problem I have is that I don't know what each package contains like an API. I am new to warcraft 3 scripting in general.
 
Level 23
Joined
Jan 1, 2009
Messages
1,610
Are you just new to scripting or also new to wc3 modding in general?
As I said, download the WurstPack
Here

Put it somewhere you want and update.
The stdlib will be inside the Wurstpack/wurstscript/lib folder.
Other than that you can of course use all wc3 natives.
The eclipse installation should be explain through the video :)
(I know it's a bit out of date, I think I'm gonna remake and continue the series soonish).

Anyways, if you have zero experience in programming/Jass you should maybe go through a basic scripting/jass tutorial to get a grasp of the basics.

If you have any tutorial suggestions feel free to post them and I might do something.
 
Status
Not open for further replies.
Top