├── .circleci └── config.yml ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── wurst.build ├── wurst ├── Wurst.wurst ├── _handles │ ├── Ability.wurst │ ├── Boolexpr.wurst │ ├── Camera.wurst │ ├── Destructable.wurst │ ├── Dialog.wurst │ ├── Effect.wurst │ ├── Fogmodifier.wurst │ ├── Force.wurst │ ├── ForceTests.wurst │ ├── Framehandle.wurst │ ├── GameCache.wurst │ ├── GameCacheTests.wurst │ ├── Group.wurst │ ├── GroupTests.wurst │ ├── Hashtable.wurst │ ├── HashtableTests.wurst │ ├── Image.wurst │ ├── Item.wurst │ ├── Lightning.wurst │ ├── Multiboard.wurst │ ├── Player.wurst │ ├── Playercolor.wurst │ ├── Quest.wurst │ ├── QuestItem.wurst │ ├── QuestItemTests.wurst │ ├── QuestTests.wurst │ ├── Rect.wurst │ ├── Region.wurst │ ├── Sound.wurst │ ├── Texttag.wurst │ ├── Timer.wurst │ ├── TimerDialog.wurst │ ├── Trigger.wurst │ ├── Unit.wurst │ ├── Weather.wurst │ ├── Widget.wurst │ ├── _Handles.wurst │ └── primitives │ │ ├── Boolean.wurst │ │ ├── Integer.wurst │ │ ├── PrimitivesTests.wurst │ │ ├── Real.wurst │ │ ├── Reference.wurst │ │ ├── String.wurst │ │ ├── StringTests.wurst │ │ └── _Primitives.wurst ├── _wurst │ ├── Annotations.wurst │ ├── Basics.wurst │ ├── ErrorHandling.wurst │ ├── MagicFunctions.wurst │ ├── Reflection.wurst │ ├── TypeCasting.wurst │ ├── Wurstunit.wurst │ └── assets │ │ ├── Abilities.wurst │ │ ├── AbilityIds.wurst │ │ ├── Assets.wurst │ │ ├── AttachmentPoints.wurst │ │ ├── BuffIds.wurst │ │ ├── Buildings.wurst │ │ ├── Doodads.wurst │ │ ├── Environment.wurst │ │ ├── FramehandleNames.wurst │ │ ├── Icons.wurst │ │ ├── ItemIds.wurst │ │ ├── Objects.wurst │ │ ├── OrderIds.wurst │ │ ├── Orders.wurst │ │ ├── PathingMaps.wurst │ │ ├── Sounds.wurst │ │ ├── Soundsets.wurst │ │ ├── Textures.wurst │ │ ├── Tiles.wurst │ │ ├── UI.wurst │ │ ├── UnitAnimations.wurst │ │ ├── UnitIds.wurst │ │ ├── Units.wurst │ │ └── WeatherEffects.wurst ├── closures │ ├── ClosureEvents.wurst │ ├── ClosureEventsTests.wurst │ ├── ClosureForGroups.wurst │ ├── ClosureFrames.wurst │ ├── ClosureKeyPresses.wurst │ ├── ClosureTimers.wurst │ └── Execute.wurst ├── data │ ├── BitSet.wurst │ ├── HashList.wurst │ ├── HashListTests.wurst │ ├── HashMap.wurst │ ├── HashMapTests.wurst │ ├── HashSet.wurst │ ├── HashSetTests.wurst │ ├── LinkedList.wurst │ ├── LinkedListModule.wurst │ ├── LinkedListModuleTests.wurst │ ├── LinkedListTests.wurst │ └── Table.wurst ├── dummy │ ├── DummyCaster.wurst │ ├── DummyDamage.wurst │ ├── DummyRecycler.wurst │ ├── Fx.wurst │ ├── Fx2.wurst │ └── InstantDummyCaster.wurst ├── event │ ├── DamageDetection.wurst │ ├── DamageEvent.wurst │ ├── EventHelper.wurst │ ├── LastOrder.wurst │ ├── OnUnitEnterLeave.wurst │ └── RegisterEvents.wurst ├── file │ ├── Base64.wurst │ ├── Base64Tests.wurst │ ├── ByteBuffer.wurst │ ├── ChunkedString.wurst │ ├── FileIO.wurst │ ├── GameStatus.wurst │ ├── SaveLoadData.wurst │ ├── Serializable.wurst │ └── SyncSimple.wurst ├── math │ ├── Angle.wurst │ ├── BigNum.wurst │ ├── Bitwise.wurst │ ├── BitwiseInit.wurst │ ├── BitwiseTests.wurst │ ├── Interpolation.wurst │ ├── Maths.wurst │ ├── Matrices.wurst │ ├── MatricesTests.wurst │ ├── Quaternion.wurst │ ├── QuaternionTests.wurst │ ├── Raycast.wurst │ └── Vectors.wurst ├── objediting │ ├── AbilityObjEditing.wurst │ ├── BuffObjEditing.wurst │ ├── DestructableObjEditing.wurst │ ├── ItemObjEditing.wurst │ ├── ObjEditingCommons.wurst │ ├── ObjEditingNatives.wurst │ ├── ObjectIds.wurst │ ├── TargetsAllowed.wurst │ ├── UnitObjEditing.wurst │ ├── UpgradeObjEditing.wurst │ └── presets │ │ ├── ChannelAbilityPreset.wurst │ │ ├── HeroPreset.wurst │ │ ├── ObjectIdGenerator.wurst │ │ └── OrderStringFactory.wurst └── util │ ├── Board.wurst │ ├── Cinematic.wurst │ ├── Colors.wurst │ ├── DialogBox.wurst │ ├── EffectUtils.wurst │ ├── GameTimer.wurst │ ├── GroupUtils.wurst │ ├── Knockback3.wurst │ ├── MapBounds.wurst │ ├── Preloader.wurst │ ├── Printing.wurst │ ├── SafetyChecks.wurst │ ├── Simulate3dSound.wurst │ ├── SoundUtils.wurst │ ├── StandardTextTags.wurst │ ├── StringUtils.wurst │ ├── StringUtilsTests.wurst │ ├── TerrainUtils.wurst │ ├── Time.wurst │ ├── TimerUtils.wurst │ └── UnitIndexer.wurst └── wurst_run.args /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | build: 5 | 6 | docker: 7 | - image: frotty/wurstscript 8 | 9 | steps: 10 | - checkout 11 | 12 | - run: 13 | name: Update Wurst 14 | command: grill install wurstscript 15 | 16 | - run: 17 | name: Install Dependencies 18 | command: grill install 19 | 20 | - run: 21 | name: Test 22 | command: grill test 23 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.wurst] 2 | charset = utf-8 3 | indent_style = tab 4 | indent_size = 4 5 | end_of_line = lf 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [{.gitignore,.editorconfig}] 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | wurst.dependencies 3 | .vscode/settings.json 4 | *.w3x 5 | objectEditingOutput/ 6 | /archive -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: minimal 2 | 3 | services: 4 | - docker 5 | 6 | before_install: 7 | - docker pull frotty/wurstscript 8 | - docker run -it -d --name build frotty/wurstscript /bin/sh 9 | 10 | install: 11 | - docker exec build grill install wurstscript 12 | - docker exec build git clone https://github.com/$TRAVIS_REPO_SLUG.git target 13 | - docker exec build /bin/sh -c "mv -v target/* target/.* . 2>/dev/null; true" 14 | - docker exec build git checkout -qf $TRAVIS_COMMIT 15 | 16 | script: 17 | - docker exec build grill install 18 | - docker exec build grill test 19 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | The standard library has greatly flourished due to amazing feedback, pull requests and issue reports. Bugs and anomalies have been fixed and documented. We highly appreciate any PR with fixes or improvements. However a few points of advice: 4 | 5 | * Please keep PRs as small as possible to allow for easier review and faster merging 6 | * Make sure you follow the [Coding Conventions](https://wurstlang.org/manual.html#coding-conventions) 7 | * Anything that isn't too deeply linked to WC III game logic should be unit tested 8 | * Expect there to be requests for changes and suggestions by different members 9 | 10 | If further natives need to be implemented for compiletime, they can be requested via a Feature Request. 11 | 12 | Feel free to join our [Discord](https://discord.gg/mSHZpWcadz) if you would like to contribute. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://i.imgur.com/L3xfAs4.png) 2 | 3 | [![CircleCI](https://circleci.com/gh/wurstscript/WurstStdlib2.svg?style=svg)](https://circleci.com/gh/wurstscript/WurstStdlib2) 4 | # Wurst Standard Library 5 | 6 | This is the repository of the WurstScript standard library which provides a vast amount of useful packages to users starting out with Wurst. 7 | Many commonly used data structures, wc3 specific utility packages, Object Editing as well as extension wrappers for the blizzard natives have been implemented, are unit tested and therefore ready to use in production immediately. 8 | 9 | # Motivation 10 | 11 | Wurst aims to provide a better "out of the box" experience when it comes to warcraft III modding. Since Jass is very limited, developers have to implement basic data structures, like Lists, or Warcraft specific functionality, like damage detection, themselves. Before Wurst these resources had to be gathered and copied manually from modding forums across the web. Public code resources in forums threads are not only hard to maintain and keep up to date, but also often untested, interdependent on other resources and incompatible with other code. 12 | 13 | By introducing a standard library, we offer the developers everything they need to start focusing on creating content, rather than implementing basics to even get started. The frameworks provided by the standard library try to be lightweight and unintrusive, while still configurable for your needs. The streamlined API allows external packages to share code and work independently. 14 | 15 | # Contributing 16 | 17 | [View CONTRIBUTING.md](https://github.com/wurstscript/WurstStdlib2/blob/master/CONTRIBUTING.md) 18 | 19 | # Documentation 20 | 21 | https://wurstlang.org/stdlib 22 | 23 | 24 | -------------------------------------------------------------------------------- /wurst.build: -------------------------------------------------------------------------------- 1 | --- 2 | projectName: Wurst Standard Library 2 3 | dependencies: [] 4 | -------------------------------------------------------------------------------- /wurst/Wurst.wurst: -------------------------------------------------------------------------------- 1 | package Wurst 2 | import public _Handles 3 | import public Maths 4 | import public Vectors 5 | import public Printing 6 | import public MagicFunctions 7 | import public Basics 8 | import public Wurstunit 9 | import public TypeCasting 10 | import public Colors 11 | import public Annotations 12 | import public EventHelper 13 | -------------------------------------------------------------------------------- /wurst/_handles/Boolexpr.wurst: -------------------------------------------------------------------------------- 1 | package Boolexpr 2 | import NoWurst 3 | 4 | public function boolexpr.or_(boolexpr operandB) returns boolexpr 5 | return Or(this, operandB) 6 | 7 | public function boolexpr.and_(boolexpr operandB) returns boolexpr 8 | return And(this, operandB) 9 | 10 | public function boolexpr.destr() 11 | DestroyBoolExpr(this) 12 | -------------------------------------------------------------------------------- /wurst/_handles/Dialog.wurst: -------------------------------------------------------------------------------- 1 | package Dialog 2 | import NoWurst 3 | import Wurstunit 4 | 5 | /** 6 | Dialogs are big dialog boxes at the center of the screen. The player can choose one button to click. 7 | WARNING: Dialogs cannot be displayed at map init! Dialogs pause the game in single player mode! 8 | In multiplayer mode dialogs do not pause the game, but prevent players, who see the dialog from playing the game. 9 | **/ 10 | 11 | public function createDialog() returns dialog 12 | return DialogCreate() 13 | 14 | public function dialog.addButton(string buttonText) returns button 15 | return this.addButton(buttonText, 0) 16 | 17 | /** Hotkey: use ASCII numbers of the capital letter. */ 18 | public function dialog.addButton(string buttonText, int hotkey) returns button 19 | return DialogAddButton(this, buttonText, hotkey) 20 | 21 | /** Adds a quit button to this dialog. If it is clicked, it ends the game for that player. */ 22 | public function dialog.addQuitButton(boolean doScoreScreen, string buttonText) returns button 23 | return this.addQuitButton(doScoreScreen, buttonText, 0) 24 | 25 | /** Adds a quit button to this dialog. If it is clicked, it ends the game for that player. 26 | Hotkey: use ASCII numbers of the capital letter. */ 27 | public function dialog.addQuitButton(boolean doScoreScreen, string buttonText, int hotkey) returns button 28 | return DialogAddQuitButton(this, doScoreScreen, buttonText, hotkey) 29 | 30 | /** Removes all buttons from a dialog */ 31 | public function dialog.clear() 32 | DialogClear(this) 33 | 34 | public function dialog.destr() 35 | DialogDestroy(this) 36 | 37 | /** Toggles visibility of the dialog for a player. Dialogs are invisible by default 38 | Dialogs cannot be shown at map initialization */ 39 | public function dialog.display(player whichPlayer, boolean flag) 40 | DialogDisplay(whichPlayer, this, flag) 41 | 42 | public function dialog.setMessage(string messageText) 43 | DialogSetMessage(this, messageText) 44 | 45 | @Test 46 | function testDialog() 47 | let dia = createDialog() 48 | dia..addButton("text") 49 | ..addQuitButton(true, "test") 50 | ..display(Player(0), true) 51 | ..clear() 52 | ..destr() 53 | -------------------------------------------------------------------------------- /wurst/_handles/Fogmodifier.wurst: -------------------------------------------------------------------------------- 1 | package Fogmodifier 2 | import NoWurst 3 | import Vectors 4 | 5 | public function createFogModifier(player forWhichPlayer, fogstate whichState, vec2 center, real radius, bool useSharedVision, bool afterUnits) returns fogmodifier 6 | return CreateFogModifierRadius(forWhichPlayer, whichState, center.x, center.y, radius, useSharedVision, afterUnits) 7 | 8 | public function createBlackMask(player forWhichPlayer, vec2 center, real radius, bool useSharedVision, bool afterUnits) returns fogmodifier 9 | return CreateFogModifierRadius(forWhichPlayer, FOG_OF_WAR_MASKED, center.x, center.y, radius, useSharedVision, afterUnits) 10 | 11 | public function createFogOfWar(player forWhichPlayer, vec2 center, real radius, bool useSharedVision, bool afterUnits) returns fogmodifier 12 | return CreateFogModifierRadius(forWhichPlayer, FOG_OF_WAR_FOGGED, center.x, center.y, radius, useSharedVision, afterUnits) 13 | 14 | public function createVision(player forWhichPlayer, vec2 center, real radius, bool useSharedVision) returns fogmodifier 15 | return CreateFogModifierRadius(forWhichPlayer, FOG_OF_WAR_VISIBLE, center.x, center.y, radius, useSharedVision, false) 16 | 17 | public function createFogModifier(player forWhichPlayer, fogstate whichState, rect where, bool useSharedVision, bool afterUnits) returns fogmodifier 18 | return CreateFogModifierRect(forWhichPlayer, whichState, where, useSharedVision, afterUnits) 19 | 20 | public function createBlackMask(player forWhichPlayer, rect where, bool useSharedVision, bool afterUnits) returns fogmodifier 21 | return CreateFogModifierRect(forWhichPlayer, FOG_OF_WAR_MASKED, where, useSharedVision, afterUnits) 22 | 23 | public function createFogOfWar(player forWhichPlayer, rect where, bool useSharedVision, bool afterUnits) returns fogmodifier 24 | return CreateFogModifierRect(forWhichPlayer, FOG_OF_WAR_FOGGED, where, useSharedVision, afterUnits) 25 | 26 | public function createVision(player forWhichPlayer, rect where, bool useSharedVision) returns fogmodifier 27 | return CreateFogModifierRect(forWhichPlayer, FOG_OF_WAR_VISIBLE, where, useSharedVision, false) 28 | 29 | public function fogmodifier.destr() 30 | DestroyFogModifier(this) 31 | 32 | public function fogmodifier.start() 33 | FogModifierStart(this) 34 | 35 | public function fogmodifier.stop() 36 | FogModifierStop(this) 37 | -------------------------------------------------------------------------------- /wurst/_handles/Force.wurst: -------------------------------------------------------------------------------- 1 | package Force 2 | import NoWurst 3 | 4 | public function force.destr() 5 | DestroyForce(this) 6 | 7 | public function force.addPlayer(player whichPlayer) 8 | ForceAddPlayer(this, whichPlayer) 9 | 10 | public function force.addPlayers(vararg player players) 11 | for whichPlayer in players 12 | ForceAddPlayer(this, whichPlayer) 13 | 14 | public function force.removePlayer(player whichPlayer) 15 | ForceRemovePlayer(this, whichPlayer) 16 | 17 | public function force.removePlayers(vararg player players) 18 | for whichPlayer in players 19 | ForceRemovePlayer(this, whichPlayer) 20 | 21 | public function force.clear() 22 | ForceClear(this) 23 | 24 | public function force.enumPlayers(boolexpr filter) 25 | ForceEnumPlayers(this, filter) 26 | 27 | public function force.enumPlayersCounted(boolexpr filter, int countLimit) 28 | ForceEnumPlayersCounted(this, filter, countLimit) 29 | 30 | public function force.enumAllies(player whichPlayer, boolexpr filter) 31 | ForceEnumAllies(this, whichPlayer, filter) 32 | 33 | public function force.enumEnemies(player whichPlayer, boolexpr filter) 34 | ForceEnumEnemies(this, whichPlayer, filter) 35 | 36 | public function force.forEach(code callback) 37 | ForForce(this, callback) 38 | 39 | public function force.getRandomPlayer() returns player 40 | return ForcePickRandomPlayer(this) 41 | 42 | public function force.containsUnit(unit whichUnit) returns boolean 43 | return IsUnitInForce(whichUnit, this) 44 | 45 | public function force.containsPlayer(player whichPlayer) returns boolean 46 | return IsPlayerInForce(whichPlayer, this) 47 | 48 | /* Force iterator */ 49 | force iterForce 50 | 51 | /** Creates a new iterator for this group. */ 52 | public function force.iterator() returns force 53 | iterForce = CreateForce() 54 | ForForce(this, () -> iterForce.addPlayer(GetEnumPlayer())) 55 | return iterForce 56 | 57 | /** Returns whether the iterator has the next item */ 58 | public function force.hasNext() returns boolean 59 | return this.getRandomPlayer() != null 60 | 61 | /** Returns the next item from the iterator */ 62 | public function force.next() returns player 63 | let iterPlayer = this.getRandomPlayer() 64 | this.removePlayer(iterPlayer) 65 | return iterPlayer 66 | 67 | /** Closes the iterator, releaseing the group */ 68 | public function force.close() 69 | this.clear() 70 | this.destr() 71 | -------------------------------------------------------------------------------- /wurst/_handles/ForceTests.wurst: -------------------------------------------------------------------------------- 1 | package ForceTests 2 | import NoWurst 3 | import Execute 4 | import Wurstunit 5 | import Force 6 | 7 | @Test 8 | function testForce() 9 | let f = CreateForce() 10 | 11 | assertTrue(f != null) 12 | 13 | f.addPlayer(Player(0)) 14 | 15 | f.containsPlayer(Player(0)).assertTrue() 16 | 17 | var testInt = 0 18 | 19 | @Test 20 | function testForForce() 21 | testInt = 0 22 | execute() -> 23 | testInt.assertEquals(0) 24 | testInt = 10 25 | 26 | testInt.assertEquals(10) 27 | -------------------------------------------------------------------------------- /wurst/_handles/GameCache.wurst: -------------------------------------------------------------------------------- 1 | package GameCache 2 | import NoWurst 3 | import public Vectors 4 | 5 | public function gamecache.save() returns boolean 6 | return SaveGameCache(this) 7 | 8 | public function gamecache.saveInt(string missionKey, string key, int value) 9 | StoreInteger(this, missionKey, key, value) 10 | 11 | public function gamecache.saveReal(string missionKey, string key, real value) 12 | StoreReal(this, missionKey, key, value) 13 | 14 | public function gamecache.saveBoolean(string missionKey, string key, boolean value) 15 | StoreBoolean(this, missionKey, key, value) 16 | 17 | public function gamecache.saveUnit(string missionKey, string key, unit value) 18 | StoreUnit(this, missionKey, key, value) 19 | 20 | public function gamecache.saveString(string missionKey, string key, string value) 21 | StoreString(this, missionKey, key, value) 22 | 23 | public function gamecache.hasInt(string missionKey, string key) returns boolean 24 | return HaveStoredInteger(this, missionKey, key) 25 | 26 | public function gamecache.hasReal(string missionKey, string key) returns boolean 27 | return HaveStoredReal(this, missionKey, key) 28 | 29 | public function gamecache.hasBoolean(string missionKey, string key) returns boolean 30 | return HaveStoredBoolean(this, missionKey, key) 31 | 32 | public function gamecache.hasUnit(string missionKey, string key) returns boolean 33 | return HaveStoredUnit(this, missionKey, key) 34 | 35 | public function gamecache.hasString(string missionKey, string key) returns boolean 36 | return HaveStoredString(this, missionKey, key) 37 | 38 | public function gamecache.flush() 39 | FlushGameCache(this) 40 | 41 | public function gamecache.flushMission(string missionKey) 42 | FlushStoredMission(this, missionKey) 43 | 44 | public function gamecache.flushInt(string missionKey, string key) 45 | FlushStoredInteger(this, missionKey, key) 46 | 47 | public function gamecache.flushReal(string missionKey, string key) 48 | FlushStoredReal(this, missionKey, key) 49 | 50 | public function gamecache.flushBoolean(string missionKey, string key) 51 | FlushStoredBoolean(this, missionKey, key) 52 | 53 | public function gamecache.flushUnit(string missionKey, string key) 54 | FlushStoredUnit(this, missionKey, key) 55 | 56 | public function gamecache.flushString(string missionKey, string key) 57 | FlushStoredString(this, missionKey, key) 58 | 59 | public function gamecache.loadInt(string missionKey, string key) returns int 60 | return GetStoredInteger(this, missionKey, key) 61 | 62 | public function gamecache.loadReal(string missionKey, string key) returns real 63 | return GetStoredReal(this, missionKey, key) 64 | 65 | public function gamecache.loadBoolean(string missionKey, string key) returns boolean 66 | return GetStoredBoolean(this, missionKey, key) 67 | 68 | public function gamecache.restoreUnit(string missionKey, string key, player whichPlayer, vec2 pos, angle facing) returns unit 69 | return RestoreUnit(this, missionKey, key, whichPlayer, pos.x, pos.y, facing.degrees()) 70 | 71 | public function gamecache.loadString(string missionKey, string key) returns string 72 | return GetStoredString(this, missionKey, key) 73 | 74 | public function gamecache.syncInt(string missionKey, string key) 75 | SyncStoredInteger(this, missionKey, key) 76 | 77 | public function gamecache.syncReal(string missionKey, string key) 78 | SyncStoredReal(this, missionKey, key) 79 | 80 | public function gamecache.syncBoolean(string missionKey, string key) 81 | SyncStoredBoolean(this, missionKey, key) 82 | 83 | public function gamecache.syncUnit(string missionKey, string key) 84 | SyncStoredUnit(this, missionKey, key) 85 | 86 | public function gamecache.syncString(string missionKey, string key) 87 | SyncStoredString(this, missionKey, key) 88 | -------------------------------------------------------------------------------- /wurst/_handles/GameCacheTests.wurst: -------------------------------------------------------------------------------- 1 | package GameCacheTests 2 | import NoWurst 3 | import GameCache 4 | import Wurstunit 5 | 6 | 7 | @Test 8 | function testGameCache() 9 | let gc = InitGameCache("cool") 10 | let u = CreateUnit(Player(0), 0, 0, 0, 0) 11 | 12 | assertTrue(gc != null) 13 | 14 | gc.saveInt("a", "b", 1) 15 | gc.saveReal("a", "b", 2.) 16 | gc.saveBoolean("a", "b", true) 17 | gc.saveUnit("a", "b", u) 18 | gc.saveString("a", "b", "yes") 19 | 20 | gc.loadInt("a", "b").assertEquals(1) 21 | gc.loadReal("a", "b").assertEquals(2) 22 | gc.loadBoolean("a", "b").assertEquals(true) 23 | gc.loadString("a", "b").assertEquals("yes") 24 | 25 | assertTrue(u == gc.restoreUnit("a", "b", Player(0), vec2(0,0), (0).asAngleDegrees())) 26 | 27 | gc.flush() 28 | 29 | gc.loadInt("a", "b").assertEquals(0) 30 | 31 | -------------------------------------------------------------------------------- /wurst/_handles/Group.wurst: -------------------------------------------------------------------------------- 1 | package Group 2 | import NoWurst 3 | import Vectors 4 | import Player 5 | 6 | /** Use this group for your non-nested group enum calls 7 | and then iterate through the group via **for from** or 8 | clear the group after use */ 9 | public constant ENUM_GROUP = CreateGroup() 10 | 11 | public function group.enumUnitsInRange(vec2 pos, real range) 12 | GroupEnumUnitsInRange(this, pos.x, pos.y, range, null) 13 | 14 | public function group.enumUnitsInRange(vec2 pos, real range, boolexpr filter) 15 | GroupEnumUnitsInRange(this, pos.x, pos.y, range, filter) 16 | 17 | public function group.enumUnitsInRect(rect rec) 18 | this.enumUnitsInRect(rec, null) 19 | 20 | public function group.enumUnitsInRect(rect rec, boolexpr filter) 21 | GroupEnumUnitsInRect(this, rec, filter) 22 | 23 | public function group.enumUnitsSelected(player p, boolexpr filter) 24 | GroupEnumUnitsSelected(this, p, filter) 25 | 26 | public function group.enumUnitsOfPlayer(player p, boolexpr filter) 27 | GroupEnumUnitsOfPlayer(this, p, filter) 28 | 29 | public function group.enumUnitsOfType(string unitname, boolexpr filter) 30 | GroupEnumUnitsOfType(this, unitname, filter) 31 | 32 | public function group.enumUnitsOfType(int id, boolexpr filter) 33 | GroupEnumUnitsOfType(this, UnitId2String(id), filter) 34 | 35 | public function group.enumUnitsAll(boolexpr filter) 36 | for i = 0 to bj_MAX_PLAYER_SLOTS - 1 37 | ENUM_GROUP.enumUnitsOfPlayer(players[i], filter) 38 | this.add(ENUM_GROUP) 39 | ENUM_GROUP.clear() 40 | 41 | public function group.clear() 42 | GroupClear(this) 43 | 44 | /** Returns the number of added units. */ 45 | public function group.add(vararg unit units) returns int 46 | var i = 0 47 | for u in units 48 | if GroupAddUnit(this, u) 49 | i += 1 50 | return i 51 | 52 | /** Retruns the number of removed units. */ 53 | public function group.remove(vararg unit units) returns int 54 | var i = 0 55 | for u in units 56 | if GroupRemoveUnit(this, u) 57 | i += 1 58 | return i 59 | 60 | public function group.destr() 61 | DestroyGroup(this) 62 | 63 | public function group.has(unit u) returns bool 64 | return IsUnitInGroup(u, this) 65 | 66 | public function group.size() returns int 67 | return BlzGroupGetSize(this) 68 | 69 | public function group.isEmpty() returns bool 70 | return not this.hasNext() 71 | 72 | /** Returns a random unit from this group without removing it 73 | or null if the group is empty */ 74 | public function group.getRandom() returns unit 75 | let size = this.size() 76 | if size > 0 77 | return this.get(GetRandomInt(0, size - 1)) 78 | return null 79 | 80 | public function group.immediateOrder(string order) returns bool 81 | return GroupImmediateOrder(this, order) 82 | 83 | public function group.immediateOrderById(int order) returns bool 84 | return GroupImmediateOrderById(this, order) 85 | 86 | public function group.pointOrder(string order, vec2 point) returns bool 87 | return GroupPointOrder(this, order, point.x, point.y) 88 | 89 | public function group.pointOrderById(int order, vec2 point) returns bool 90 | return GroupPointOrderById(this, order, point.x, point.y) 91 | 92 | public function group.targetOrder(string order, widget targetWidget) returns bool 93 | return GroupTargetOrder(this, order, targetWidget) 94 | 95 | public function group.targetOrder(int order, widget targetWidget) returns bool 96 | return GroupTargetOrderById(this, order, targetWidget) 97 | 98 | public function group.add(vararg group groups) returns int 99 | var i = 0 100 | for g in groups 101 | i += BlzGroupAddGroupFast(g, this) 102 | return i 103 | 104 | public function group.remove(vararg group groups) returns int 105 | var i = 0 106 | for g in groups 107 | i += BlzGroupRemoveGroupFast(g, this) 108 | return i 109 | 110 | public function group.get(int index) returns unit 111 | return BlzGroupUnitAt(this, index) 112 | 113 | /* Group iterator */ 114 | 115 | group iterGroup 116 | 117 | /** Creates a new iterator for this group. */ 118 | public function group.iterator() returns group 119 | iterGroup = CreateGroup() 120 | iterGroup.add(this) 121 | return iterGroup 122 | 123 | /** Returns whether the iterator has the next item */ 124 | public function group.hasNext() returns bool 125 | return FirstOfGroup(this) != null 126 | 127 | /** Returns the next item from the iterator */ 128 | public function group.next() returns unit 129 | let iterUnit = FirstOfGroup(this) 130 | GroupRemoveUnit(this, iterUnit) 131 | return iterUnit 132 | 133 | /** Closes the iterator, releaseing the group */ 134 | public function group.close() 135 | this.clear() 136 | this.destr() 137 | -------------------------------------------------------------------------------- /wurst/_handles/GroupTests.wurst: -------------------------------------------------------------------------------- 1 | package GroupTests 2 | 3 | @Test 4 | function testGroupBasic() 5 | let g = CreateGroup() 6 | assertTrue(g != null) 7 | assertTrue(g.isEmpty()) 8 | 9 | let u = createUnit(Player(0), 'hfoo', vec2(0, 0), 0 .fromDeg()) 10 | assertTrue(u != null) 11 | g.add(u) 12 | g.size().assertEquals(1) 13 | 14 | var count = 0 15 | 16 | for i = 1 to 10 17 | count += g.add(createUnit(Player(0), 'hfoo', vec2(0, 0), 0 .fromDeg())) 18 | 19 | g.size().assertEquals(11) 20 | count.assertEquals(10) 21 | 22 | let u2 = createUnit(Player(0), 'hfoo', vec2(0, 0), 0 .fromDeg()) 23 | g.add(u2) 24 | assertTrue(g.get(0) == u) 25 | g.size().assertEquals(12) 26 | assertTrue(g.get(g.size()-1) == u2) 27 | 28 | for int i = 0 to g.size()-1 29 | print(g.get(i).getHandleId()) 30 | print("---") 31 | print(u2.getHandleId()) 32 | 33 | g.clear() 34 | assertTrue(g.isEmpty()) 35 | 36 | 37 | @Test 38 | function testGroupForLoops() 39 | let g = CreateGroup() 40 | assertTrue(g != null) 41 | assertTrue(not g.hasNext()) 42 | for i = 0 to 10 43 | g.add(createUnit(Player(0), 'hfoo', vec2(0, 0), 0 .fromDeg())) 44 | 45 | assertTrue(g.hasNext()) 46 | 47 | for u in g 48 | assertTrue(u != null) 49 | 50 | for _ from g 51 | skip 52 | 53 | assertTrue(not g.hasNext()) 54 | 55 | 56 | @Test 57 | function testRandom() 58 | let g = CreateGroup() 59 | 60 | for i = 0 to 10 61 | g.add(createUnit(Player(0), 'hfoo', vec2(0, 0), 0 .fromDeg())) 62 | 63 | let ru = g.getRandom() 64 | 65 | assertTrue(ru != null) 66 | 67 | assertTrue(g.has(ru)) 68 | 69 | g.remove(ru) 70 | 71 | for i = 1 to 10 72 | g.remove(g.getRandom()) 73 | 74 | assertTrue(g.isEmpty()) 75 | 76 | @Test 77 | function testHandleId() 78 | let g = CreateGroup() 79 | assertTrue(g.getHandleId() != 0) 80 | 81 | for i = 0 to 100 82 | let g2 = CreateGroup() 83 | assertTrue(g2.getHandleId() != 0) 84 | assertTrue(g2.getHandleId() != g.getHandleId()) 85 | 86 | @Test 87 | function testVararg() 88 | let g = CreateGroup() 89 | g.add(createUnit(Player(0), 'hfoo', vec2(0, 0), 0 .fromDeg()), createUnit(Player(0), 'hfoo', vec2(0, 0), 0 .fromDeg()), createUnit(Player(0), 'hfoo', vec2(0, 0), 0 .fromDeg())) 90 | g.size().assertEquals(3) 91 | 92 | unit array units 93 | var _count = 0 94 | 95 | for i = 0 to 9 96 | units[i] = createUnit(Player(0), 'hfoo', vec2(0, 0), 0 .fromDeg()) 97 | 98 | g.clear() 99 | 100 | _count = g.add(units[0], units[1], units[2], units[3]) 101 | _count.assertEquals(4) 102 | 103 | _count += g.add(units[2], units[3], units[4], units[5]) 104 | _count.assertEquals(6) 105 | 106 | g.clear() 107 | 108 | let a = CreateGroup() 109 | ..add(units[0], units[1], units[2], units[3], units[4], units[5]) 110 | let b = CreateGroup() 111 | ..add(units[4], units[5], units[6], units[7], units[8], units[9]) 112 | 113 | _count = g.add(a) 114 | _count.assertEquals(a.size()) 115 | 116 | _count = g.add(b) 117 | _count.assertEquals(4) 118 | 119 | g.clear() 120 | _count = g.add(a, b) 121 | _count.assertEquals(10) 122 | 123 | _count = g.remove(units[0], units[1], units[2]) 124 | _count.assertEquals(3) 125 | 126 | _count = g.remove(a, b) 127 | _count.assertEquals(7) 128 | assertTrue(g.isEmpty()) 129 | 130 | @Test 131 | function testGroupForGroups() 132 | let u = createUnit(Player(0), 'hfoo', vec2(0, 0), 0 .fromDeg()) 133 | let u2 = createUnit(Player(0), 'hfoo', vec2(0, 0), 0 .fromDeg()) 134 | let u3 = createUnit(Player(0), 'hfoo', vec2(0, 0), 0 .fromDeg()) 135 | 136 | let g = CreateGroup() 137 | g.add(u, u2) 138 | g.size().assertEquals(2) 139 | 140 | let g2 = CreateGroup() 141 | g2.add(g) 142 | g2.size().assertEquals(g.size()) 143 | g2.has(u).assertTrue() 144 | 145 | g2.add(u3) 146 | g2.remove(g) 147 | g2.size().assertEquals(1) 148 | g2.has(u3).assertTrue() 149 | g2.has(u2).assertFalse() 150 | g2.has(u).assertFalse() 151 | -------------------------------------------------------------------------------- /wurst/_handles/HashtableTests.wurst: -------------------------------------------------------------------------------- 1 | package HashtableTests 2 | 3 | @Test 4 | function testBasicDatatypes() 5 | let ht = InitHashtable() 6 | ht.saveBoolean(0, 0, true) 7 | ht.saveInt(0, 0, 1) 8 | ht.saveReal(0, 0, 2.) 9 | ht.saveString(0, 0, "1") 10 | 11 | assertTrue(ht.hasBoolean(0,0)) 12 | assertTrue(ht.hasInt(0,0)) 13 | assertTrue(ht.hasReal(0,0)) 14 | assertTrue(ht.hasString(0,0)) 15 | 16 | ht.loadInt(0,0).assertEquals(1) 17 | ht.loadString(0,0).assertEquals("1") 18 | assertTrue(ht.loadBoolean(0,0)) 19 | ht.loadReal(0,0).assertEquals(2.) 20 | 21 | @Test 22 | function testFlushing() 23 | let ht = InitHashtable() 24 | ht.saveReal(0, 0, 2.) 25 | ht.saveReal(1, 0, 4.) 26 | ht.flushChild(0) 27 | 28 | assertTrue(not ht.hasReal(0,0)) 29 | assertTrue(ht.hasReal(1,0)) 30 | 31 | ht.flush() 32 | 33 | assertTrue(not ht.hasReal(1,0)) 34 | 35 | @Test 36 | function testOverwrite() 37 | let ht = InitHashtable() 38 | ht.saveReal(0, 0, 2.) 39 | ht.saveReal(0, 0, 4.) 40 | 41 | assertTrue(ht.hasReal(0,0)) 42 | ht.loadReal(0,0).assertEquals(4.) 43 | 44 | @Test 45 | function testRemove() 46 | let ht = InitHashtable() 47 | ht.saveReal(0, 0, 2.) 48 | assertTrue(ht.hasReal(0,0)) 49 | 50 | ht.removeReal(0, 0) 51 | assertTrue(not ht.hasReal(0,0)) 52 | -------------------------------------------------------------------------------- /wurst/_handles/Image.wurst: -------------------------------------------------------------------------------- 1 | package Image 2 | import NoWurst 3 | import public Colors 4 | import Vectors 5 | import ErrorHandling 6 | import Player 7 | 8 | /** ImageLayer influences the order in which the images are drawn above one another: 9 | L1 ("Selection") is drawn above all other ImageLayers. 10 | L2 ("Indicator") is drawn above ImageLayer 4, but below 1 and 3. 11 | L3 ("Occlusion Mask") is drawn above ImageLayer 4 and 2 and below ImageLayer 1. 12 | L4 ("Ubersplat") is drawn below every other layer. Images of this layer are additionally affected by time of day and the fog of war (only for tinting). 13 | Multiple images with the same layer are drawn in their order of creation - first image below all others, last image above all others.*/ 14 | public enum ImageLayer 15 | L1 16 | L3 17 | L2 18 | L4 19 | 20 | /** Create an image at the target location, the Image has to be a blp, with transparent borders */ 21 | public function createImage(string path, vec2 pos, real sizeX, real sizeY) returns image 22 | return CreateImage(path, sizeX, sizeY, 0, pos.x, pos.y, 0, sizeX / 2, sizeY / 2, 0, 1) 23 | 24 | /** Create an image at the target location, the Image has to be a blp, with transparent borders. 25 | The layer specifies which image overlaps another: Level 1 is always on the top, and level 4 always in the background. Use the enums "ImageLayer.LX" */ 26 | public function createImage(string path, vec2 pos, real sizeX, real sizeY, ImageLayer layer) returns image 27 | return CreateImage(path, sizeX, sizeY, 0, pos.x, pos.y, 0, sizeX / 2, sizeY / 2, 0, layer castTo int + 1) 28 | 29 | /** Shows the image */ 30 | public function image.show() 31 | SetImageRenderAlways(this, true) 32 | 33 | /** Hides the image */ 34 | public function image.hide() 35 | SetImageRenderAlways(this, false) 36 | 37 | /** Shows the image for a specific player */ 38 | public function image.showForPlayer(player p) 39 | if localPlayer == p 40 | SetImageRenderAlways(this, true) 41 | 42 | /** Hides the image for a specific player */ 43 | public function image.hideForPlayer(player p) 44 | if localPlayer == p 45 | SetImageRenderAlways(this, false) 46 | 47 | /** Set the image 2D position */ 48 | public function image.setPos(vec2 pos) 49 | SetImagePosition(this, pos.x, pos.y, 0) 50 | 51 | /** Set the image 3D position */ 52 | public function image.setPos(vec3 pos) 53 | SetImagePosition(this, pos.x, pos.y, 0) 54 | SetImageConstantHeight(this, true, pos.z) 55 | 56 | /** Set the image height */ 57 | public function image.setHeight(real z) 58 | SetImageConstantHeight(this, true, z) 59 | 60 | /** Wrap the image on the terrain */ 61 | public function image.wrap() 62 | SetImageConstantHeight(this, false, 0) 63 | 64 | /** Set the image color by using the colorA tuple */ 65 | public function image.setColor(colorA col) 66 | SetImageColor(this, col.red, col.green, col.blue, col.alpha) 67 | 68 | /** Set the image color by using the color tuple */ 69 | public function image.setColor(color col) 70 | SetImageColor(this, col.red, col.green, col.blue, 255) 71 | 72 | /** Set the image layer (use the constants) */ 73 | public function image.setLevel(ImageLayer layer) 74 | SetImageType(this, layer castTo int + 1) 75 | 76 | /** Destroy the image */ 77 | public function image.remove() 78 | if this == null 79 | error("Nullpointer exeption by destroying an image") 80 | DestroyImage(this) 81 | 82 | -------------------------------------------------------------------------------- /wurst/_handles/Lightning.wurst: -------------------------------------------------------------------------------- 1 | package Lightning 2 | import NoWurst 3 | import Vectors 4 | import Colors 5 | 6 | public constant LIGHTNING_CHAIN_LIGHTNING_PRIMARY = "CLPB" 7 | public constant LIGHTNING_CHAIN_LIGHTNING_SEECONDARY = "CLSB" 8 | public constant LIGHTNING_DRAIN = "DRAB" 9 | public constant LIGHTNING_DRAIN_LIFE = "DRAL" 10 | public constant LIGHTNING_DRAIN_MANA = "DRAM" 11 | public constant LIGHTNING_FINGER_OF_DEATH = "AFOD" 12 | public constant LIGHTNING_FORKED_LIGHTNING = "FORK" 13 | public constant LIGHTNING_HEALING_WAWE_PRIMARY = "HWPB" 14 | public constant LIGHTNING_HEALING_WAWE_SEECONDARY = "HWSB" 15 | public constant LIGHTNING_LIGHTNING_ATTACK = "CHIM" 16 | public constant LIGHTNING_MAGIC_LEASH = "LEAS" 17 | public constant LIGHTNING_MANA_BURN = "MBUR" 18 | public constant LIGHTNING_MANA_FLARE = "MFPB" 19 | public constant LIGHTNING_SPIRIT_LINK = "SPLK" 20 | 21 | /** Acts as with vec3 version but with z equal to terrain height. */ 22 | public function addLightning(string codeName, bool checkVisibility, vec2 start, vec2 _end) returns lightning 23 | return AddLightning(codeName, checkVisibility, start.x, start.y, _end.x, _end.y) 24 | 25 | /** Warning, z value of vec3 should consider terrain height. 26 | (usually with z==0 the lightning spawns very low under ground) */ 27 | public function addLightning(string codeName, bool checkVisibility, vec3 start, vec3 _end) returns lightning 28 | return AddLightningEx(codeName, checkVisibility, start.x, start.y, start.z, _end.x, _end.y, _end.z) 29 | 30 | /** Acts as with vec3 version but with z equal to terrain height. */ 31 | public function lightning.move(bool checkVisibility, vec2 start, vec2 _end) returns bool 32 | return MoveLightning(this, checkVisibility, start.x, start.y, _end.x, _end.y) 33 | 34 | /** Warning, z value of vec3 should consider terrain height. 35 | (usually with z==0 the lightning spawns very low under ground) */ 36 | public function lightning.move(bool checkVisibility, vec3 start, vec3 _end) returns bool 37 | return MoveLightningEx(this, checkVisibility, start.x, start.y, start.z, _end.x, _end.y, _end.z) 38 | 39 | public function lightning.setColor(colorA c) returns boolean 40 | return SetLightningColor(this, c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255) 41 | 42 | public function lightning.setColor(color c) returns boolean 43 | return SetLightningColor(this, c.red / 255, c.green / 255, c.blue / 255, 1) 44 | 45 | /** Gets given lightning's color. 46 | Uses approximation since it's stored in reals. */ 47 | public function lightning.getColor() returns color 48 | return color((255 * this.getColorR() + .5).toInt(), (255 * this.getColorG() + .5).toInt(), (255 * this.getColorB() + .5).toInt()) 49 | 50 | /** Gets given lightning's color with alpha channel. 51 | Uses approximation since it's stored in reals. */ 52 | public function lightning.getColorWithA() returns colorA 53 | return this.getColor().withAlpha((255 * this.getColorA() + .5).toInt()) 54 | 55 | public function lightning.getColorR() returns real 56 | return GetLightningColorR(this) 57 | 58 | public function lightning.getColorG() returns real 59 | return GetLightningColorG(this) 60 | 61 | public function lightning.getColorB() returns real 62 | return GetLightningColorB(this) 63 | 64 | public function lightning.getColorA() returns real 65 | return GetLightningColorA(this) 66 | 67 | public function lightning.destr() returns bool 68 | return DestroyLightning(this) 69 | -------------------------------------------------------------------------------- /wurst/_handles/Playercolor.wurst: -------------------------------------------------------------------------------- 1 | package Playercolor 2 | import NoWurst 3 | import Force 4 | import Player 5 | import Wurstunit 6 | 7 | public constant PLAYER_COLOR_BLACK_AGGRESSIVE = ConvertPlayerColor(24) 8 | public constant PLAYER_COLOR_UNKNOWN1 = ConvertPlayerColor(25) 9 | public constant PLAYER_COLOR_UNKNOWN2 = ConvertPlayerColor(26) 10 | public constant PLAYER_COLOR_BLACK_PASSIVE = ConvertPlayerColor(27) 11 | 12 | constant LOOKUP = [ 13 | PLAYER_COLOR_RED, 14 | PLAYER_COLOR_BLUE, 15 | PLAYER_COLOR_CYAN, 16 | PLAYER_COLOR_PURPLE, 17 | PLAYER_COLOR_YELLOW, 18 | PLAYER_COLOR_ORANGE, 19 | PLAYER_COLOR_GREEN, 20 | PLAYER_COLOR_PINK, 21 | PLAYER_COLOR_LIGHT_GRAY, 22 | PLAYER_COLOR_LIGHT_BLUE, 23 | PLAYER_COLOR_AQUA, 24 | PLAYER_COLOR_BROWN, 25 | PLAYER_COLOR_MAROON, 26 | PLAYER_COLOR_NAVY, 27 | PLAYER_COLOR_TURQUOISE, 28 | PLAYER_COLOR_VIOLET, 29 | PLAYER_COLOR_WHEAT, 30 | PLAYER_COLOR_PEACH, 31 | PLAYER_COLOR_MINT, 32 | PLAYER_COLOR_LAVENDER, 33 | PLAYER_COLOR_COAL, 34 | PLAYER_COLOR_SNOW, 35 | PLAYER_COLOR_EMERALD, 36 | PLAYER_COLOR_PEANUT, 37 | PLAYER_COLOR_BLACK_AGGRESSIVE, 38 | PLAYER_COLOR_UNKNOWN1, 39 | PLAYER_COLOR_UNKNOWN2, 40 | PLAYER_COLOR_BLACK_PASSIVE] 41 | 42 | public function player.setColor(playercolor color) 43 | SetPlayerColor(this, color) 44 | 45 | public function player.getColor() returns playercolor 46 | return GetPlayerColor(this) 47 | 48 | public function int.toPlayerColor() returns playercolor 49 | return LOOKUP[this] 50 | 51 | public function playercolor.toInt() returns int 52 | var value = 0 53 | for i = 0 to bj_MAX_PLAYER_SLOTS - 1 54 | if LOOKUP[i] == this 55 | value = i 56 | break 57 | return value 58 | 59 | 60 | /** Returns the player that had this color *at the start of the game*. Does not 61 | respect SetPlayerColor. */ 62 | public function playercolor.getPlayer() returns player 63 | return players[this.toInt()] 64 | 65 | /** Returns the players who are currently using this player color. Respects calls 66 | to SetPlayerColor */ 67 | public function playercolor.getPlayers() returns force 68 | let matchingPlayers = CreateForce() 69 | for i = 0 to bj_MAX_PLAYER_SLOTS - 1 70 | if players[i].getColor() == this 71 | matchingPlayers.addPlayer(players[i]) 72 | return matchingPlayers 73 | 74 | 75 | @test public function testToInt() 76 | PLAYER_COLOR_RED.toInt().assertEquals(0) 77 | PLAYER_COLOR_PEANUT.toInt().assertEquals(23) 78 | 79 | @test public function testToPlayerColor() 80 | assertTrue((0).toPlayerColor() == PLAYER_COLOR_RED) 81 | assertTrue((23).toPlayerColor() == PLAYER_COLOR_PEANUT) 82 | 83 | @test public function testGetPlayer() 84 | assertTrue(PLAYER_COLOR_RED.getPlayer() == Player(0)) 85 | assertTrue(PLAYER_COLOR_PEANUT.getPlayer() == Player(23)) 86 | 87 | @test public function testGetPlayers() 88 | // Test some Player package methods that this file depends on 89 | (players[0] != null).assertTrue() 90 | players[0].setColor(PLAYER_COLOR_RED) 91 | (players[0].getColor() == PLAYER_COLOR_RED).assertTrue() 92 | // Test getting forces of players. 93 | let force1 = PLAYER_COLOR_RED.getPlayers() 94 | force1.containsPlayer(Player(0)).assertTrue() 95 | players[1].setColor(PLAYER_COLOR_BLUE) 96 | players[2].setColor(PLAYER_COLOR_BLUE) 97 | let force2 = PLAYER_COLOR_BLUE.getPlayers() 98 | force2.containsPlayer(Player(1)).assertTrue() 99 | force2.containsPlayer(Player(2)).assertTrue() 100 | force2.containsPlayer(Player(3)).assertFalse() 101 | 102 | let p = players[5] 103 | assertTrue(p.getColor() == null) 104 | p.setColor(PLAYER_COLOR_AQUA) 105 | assertTrue(p.getColor() == PLAYER_COLOR_AQUA) 106 | -------------------------------------------------------------------------------- /wurst/_handles/Quest.wurst: -------------------------------------------------------------------------------- 1 | package Quest 2 | import QuestItem 3 | 4 | public function quest.setRequired(boolean req) 5 | QuestSetRequired(this, req) 6 | 7 | public function quest.setTitle(string title) 8 | QuestSetTitle(this, title) 9 | 10 | public function quest.setDescription(string text) 11 | QuestSetDescription(this, text) 12 | 13 | public function quest.setIcon(string path) 14 | QuestSetIconPath(this, path) 15 | 16 | public function quest.completed(boolean b) 17 | QuestSetCompleted(this, b) 18 | 19 | public function quest.failed(boolean b) 20 | QuestSetFailed(this, b) 21 | 22 | public function quest.discovered(boolean b) 23 | QuestSetDiscovered(this, b) 24 | 25 | public function quest.isEnabled() returns boolean 26 | return IsQuestEnabled(this) 27 | 28 | public function quest.isCompleted() returns boolean 29 | return IsQuestCompleted(this) 30 | 31 | public function quest.remove() 32 | DestroyQuest(this) 33 | 34 | public function flashQuestDialogButton() 35 | FlashQuestDialogButton() 36 | 37 | public enum QuestState 38 | FAILED 39 | COMPLETED 40 | UNDISCOVERED 41 | DISCOVERED 42 | 43 | public class Quest 44 | quest q 45 | QuestState qState 46 | 47 | construct(boolean required) 48 | q = CreateQuest() 49 | q.setRequired(required) 50 | 51 | function setTitle(string title) 52 | q.setTitle(title) 53 | 54 | function setDescription(string title) 55 | q.setDescription(title) 56 | 57 | function setIcon(string path) 58 | q.setIcon(path) 59 | 60 | function isEnabled() returns boolean 61 | return q.isEnabled() 62 | 63 | function isCompleted() returns boolean 64 | return q.isCompleted() 65 | 66 | 67 | function getState() returns QuestState 68 | return qState 69 | 70 | function setState(QuestState state) 71 | qState = state 72 | switch state 73 | case state.COMPLETED 74 | q.completed(true) 75 | case state.FAILED 76 | q.failed(true) 77 | case state.UNDISCOVERED 78 | q.discovered(false) 79 | case state.DISCOVERED 80 | q.discovered(true) 81 | 82 | function addObjective(string text) returns QuestItem 83 | return new QuestItem(q, text) 84 | 85 | ondestroy 86 | q.remove() 87 | -------------------------------------------------------------------------------- /wurst/_handles/QuestItem.wurst: -------------------------------------------------------------------------------- 1 | package QuestItem 2 | 3 | public function questitem.setDescription(string text) 4 | QuestItemSetDescription(this, text) 5 | 6 | public function questitem.isCompleted() returns boolean 7 | return IsQuestItemCompleted(this) 8 | 9 | public function questitem.complete(boolean b) 10 | QuestItemSetCompleted(this, b) 11 | 12 | public class QuestItem 13 | questitem qi 14 | quest parent 15 | construct(quest q, string text) 16 | parent = q 17 | qi = QuestCreateItem(parent) 18 | qi.setDescription(text) 19 | 20 | function isCompleted() returns boolean 21 | return qi.isCompleted() 22 | 23 | function complete(boolean b) 24 | qi.complete(b) 25 | -------------------------------------------------------------------------------- /wurst/_handles/QuestItemTests.wurst: -------------------------------------------------------------------------------- 1 | package QuestItemTests 2 | import NoWurst 3 | import QuestItem 4 | import Wurstunit 5 | 6 | @test function testComplete() 7 | let qi = new QuestItem(CreateQuest(), "") 8 | qi.isCompleted().assertFalse() 9 | 10 | qi.complete(true) 11 | qi.isCompleted().assertTrue() 12 | 13 | qi.complete(false) 14 | qi.isCompleted().assertFalse() 15 | 16 | @test function testQuestItemNatives() 17 | // Exercise mocked out quest item native. 18 | // There's currently no way to validate the behavior of this native, 19 | // but this will detect a regression in mocked native presence. 20 | let qi = QuestCreateItem(CreateQuest()) 21 | 22 | qi.setDescription("") 23 | -------------------------------------------------------------------------------- /wurst/_handles/QuestTests.wurst: -------------------------------------------------------------------------------- 1 | package QuestTests 2 | import NoWurst 3 | import Quest 4 | import Wurstunit 5 | 6 | function QuestState.toString() returns string 7 | switch this 8 | case FAILED 9 | return "FAILED" 10 | case COMPLETED 11 | return "COMPLETED" 12 | case UNDISCOVERED 13 | return "UNDISCOVERED" 14 | case DISCOVERED 15 | return "DISCOVERED" 16 | default 17 | return "UNKNOWN" 18 | 19 | function QuestState.assertEquals(QuestState want) 20 | if this != want 21 | testFail("Expected <" + want.toString() + ">, Actual <" + this.toString() + ">") 22 | 23 | @test function testSetState() 24 | let q = new Quest(false /* required */) 25 | 26 | q.setState(QuestState.COMPLETED) 27 | q.getState().assertEquals(QuestState.COMPLETED) 28 | 29 | q.setState(QuestState.FAILED) 30 | q.getState().assertEquals(QuestState.FAILED) 31 | 32 | q.setState(QuestState.UNDISCOVERED) 33 | q.getState().assertEquals(QuestState.UNDISCOVERED) 34 | 35 | q.setState(QuestState.DISCOVERED) 36 | q.getState().assertEquals(QuestState.DISCOVERED) 37 | 38 | @test function testQuestNatives() 39 | // Exercise mocked out quest natives. 40 | // There's currently no way to validate the behavior of these natives, 41 | // but this will detect a regression in mocked native presence. 42 | let q = CreateQuest() 43 | 44 | q.setDescription("") 45 | q.setIcon("") 46 | q.setTitle("") 47 | -------------------------------------------------------------------------------- /wurst/_handles/Rect.wurst: -------------------------------------------------------------------------------- 1 | package Rect 2 | import NoWurst 3 | import Vectors 4 | import Wurstunit 5 | 6 | public function rect.randomPoint() returns vec2 7 | return vec2(GetRandomReal(this.getMinX(), this.getMaxX()), GetRandomReal(this.getMinY(), this.getMaxY())) 8 | 9 | public function vec2.isInRect(rect r) returns boolean 10 | return this.x > r.getMinX() and this.x < r.getMaxX() and this.y > r.getMinY() and this.y < r.getMaxY() 11 | 12 | public function rect.contains(vec2 r) returns boolean 13 | return r.x > this.getMinX() and r.x < this.getMaxX() and r.y > this.getMinY() and r.y < this.getMaxY() 14 | 15 | public function rect.getCenter() returns vec2 16 | return vec2(this.getCenterX(), this.getCenterY()) 17 | 18 | public function rect.getCenterX() returns real 19 | return GetRectCenterX(this) 20 | 21 | public function rect.getCenterY() returns real 22 | return GetRectCenterY(this) 23 | 24 | public function rect.getMaxX() returns real 25 | return GetRectMaxX(this) 26 | 27 | public function rect.getMaxY() returns real 28 | return GetRectMaxY(this) 29 | 30 | public function rect.getMinX() returns real 31 | return GetRectMinX(this) 32 | 33 | public function rect.getMinY() returns real 34 | return GetRectMinY(this) 35 | 36 | public function rect.getLeftTop() returns vec2 37 | return vec2(this.getMinX(), this.getMaxY()) 38 | 39 | public function rect.getRightTop() returns vec2 40 | return vec2(this.getMaxX(), this.getMaxY()) 41 | 42 | public function rect.getLeftBot() returns vec2 43 | return vec2(this.getMinX(), this.getMinY()) 44 | 45 | public function rect.getRightBot() returns vec2 46 | return vec2(this.getMaxX(), this.getMinY()) 47 | 48 | public function rect.moveTo(vec2 newCenter) 49 | MoveRectTo(this, newCenter.x, newCenter.y) 50 | 51 | public function rect.moveTo(real newCenterX, real newCenterY) 52 | MoveRectTo(this, newCenterX, newCenterY) 53 | 54 | public function rect.resize(vec2 min, vec2 max) 55 | SetRect(this, min.x, min.y, max.x, max.y) 56 | 57 | public function rect.width() returns real 58 | return this.getMaxX() - this.getMinX() 59 | 60 | public function rect.height() returns real 61 | return this.getMaxY() - this.getMinY() 62 | 63 | public function rect.remove() 64 | RemoveRect(this) 65 | 66 | public function rect.copy() returns rect 67 | return Rect(this.getMinX(), this.getMinY(), this.getMaxX(), this.getMaxY()) 68 | 69 | @Test 70 | function rectTest() 71 | let rekt = Rect(2, 2, 4, 4) 72 | 73 | rekt.width().assertEquals(2) 74 | rekt.height().assertEquals(2) 75 | 76 | rekt.getCenterX().assertEquals(3) 77 | rekt.getCenterY().assertEquals(3) 78 | 79 | rekt.moveTo(1, 1) 80 | 81 | rekt.width().assertEquals(2) 82 | rekt.height().assertEquals(2) 83 | 84 | rekt.getCenterX().assertEquals(1) 85 | rekt.getCenterY().assertEquals(1) 86 | 87 | rekt.resize(ZERO2, vec2(4, 4)) 88 | 89 | rekt.getCenterX().assertEquals(2) 90 | rekt.getCenterY().assertEquals(2) 91 | 92 | -------------------------------------------------------------------------------- /wurst/_handles/Region.wurst: -------------------------------------------------------------------------------- 1 | package Region 2 | import NoWurst 3 | import Vectors 4 | 5 | public function vec2.isInRegion(region whichRegion) returns boolean 6 | return IsPointInRegion(whichRegion, this.x, this.y) 7 | 8 | public function unit.isInRegion(region whichRegion) returns boolean 9 | return IsUnitInRegion(whichRegion, this) 10 | 11 | public function region.addRect(rect rct) 12 | RegionAddRect(this, rct) 13 | 14 | public function region.clearRect(rect rct) 15 | RegionClearRect(this, rct) 16 | 17 | public function region.addCell(vec2 point) 18 | RegionAddCell(this, point.x, point.y) 19 | 20 | public function region.clearCell(vec2 point) 21 | RegionClearCell(this, point.x, point.y) 22 | 23 | public function region.destr() 24 | RemoveRegion(this) 25 | -------------------------------------------------------------------------------- /wurst/_handles/Sound.wurst: -------------------------------------------------------------------------------- 1 | package Sound 2 | import NoWurst 3 | 4 | /** Sets the sound duration in milliseconds */ 5 | public function sound.setDuration(int dur) 6 | SetSoundDuration(this, dur) 7 | 8 | public function sound.play() 9 | StartSound(this) 10 | 11 | public function sound.stop(boolean killWhenDone, boolean fadeOut) 12 | StopSound(this, killWhenDone, fadeOut) 13 | 14 | public function sound.setChannel(int channel) 15 | SetSoundChannel(this, channel) 16 | 17 | public function sound.setPitch(real pitch) 18 | SetSoundPitch(this, pitch) 19 | 20 | /** Sets the sound's volume [0-100] */ 21 | public function sound.setVolume(int volume) 22 | SetSoundVolume(this, volume) 23 | 24 | public function sound.setConeAngles(real inside, real outside, int outsideVolume) 25 | SetSoundConeAngles(this, inside, outside, outsideVolume) 26 | 27 | public function sound.setCutoff(real cutoff) 28 | SetSoundDistanceCutoff(this, cutoff) 29 | 30 | /** Plays the sound, starting at the given position */ 31 | public function sound.playPosition(int millisecs) 32 | this.play() 33 | SetSoundPlayPosition(this, millisecs) 34 | 35 | /** Returns sound length in milliseconds. 36 | 37 | Beware that sound lengths of game assets may differ between different locales, 38 | and thus return a different duration. 39 | If you use a async duration in a synced manner, it will cause a desync. */ 40 | public function sound.getDuration() returns int 41 | return GetSoundDuration(this) 42 | 43 | public function sound.isPlaying() returns boolean 44 | return GetSoundIsPlaying(this) 45 | 46 | -------------------------------------------------------------------------------- /wurst/_handles/Texttag.wurst: -------------------------------------------------------------------------------- 1 | package Texttag 2 | import NoWurst 3 | import public Vectors 4 | import public Colors 5 | import Player 6 | import Unit 7 | 8 | public function texttag.setPos(real x, real y, real z) 9 | SetTextTagPos(this, x ,y , z) 10 | 11 | public function texttag.setPos(vec3 pos) 12 | SetTextTagPos(this, pos.x , pos.y , pos.z) 13 | 14 | public function texttag.setColor(int r, int g, int b, int a) 15 | SetTextTagColor(this, r, g , b, a) 16 | 17 | public function texttag.setSuspended(boolean flag) 18 | SetTextTagSuspended(this, flag) 19 | 20 | public function texttag.setColor(colorA col) 21 | SetTextTagColor(this, col.red, col.green , col.blue, col.alpha) 22 | 23 | public function texttag.setText(string text, real size) 24 | SetTextTagText(this, text, size * 0.0023) 25 | 26 | public function texttag.setLifespan(real span) 27 | SetTextTagLifespan(this, span) 28 | 29 | public function texttag.setPermanent(boolean flag) 30 | SetTextTagPermanent(this, flag) 31 | 32 | public function texttag.setFadepoint(real fadepoint) 33 | SetTextTagFadepoint(this, fadepoint) 34 | 35 | public function texttag.setAge(real age) 36 | SetTextTagAge(this, age) 37 | 38 | public function texttag.setVelocity(real xvel, real yvel) 39 | SetTextTagVelocity(this, xvel, yvel) 40 | 41 | public function texttag.setVelocity(vec2 vel) 42 | SetTextTagVelocity(this, vel.x, vel.y) 43 | 44 | public function texttag.setVisibility(boolean flag) 45 | SetTextTagVisibility(this, flag) 46 | 47 | /** Change the visibility based on whether the local player can see the given unit. 48 | This won't update if the unit moves or the local player gains vision of it. */ 49 | public function texttag.matchVisibility(unit target) 50 | this.setVisibility(localPlayer.hasVisibility(target)) 51 | 52 | /** Change the visibility based on whether the local player can see the given position. 53 | This won't update if local player gains vision of it. */ 54 | public function texttag.matchVisibility(vec2 pos) 55 | this.setVisibility(localPlayer.hasVisibility(pos)) 56 | 57 | /** Match the visibility and position of the texttag to the given unit. 58 | This won't update if the unit moves or the local player gains vision of it. */ 59 | public function texttag.match(unit target) 60 | this.match(target, vec2(0, 0)) 61 | 62 | /** Match the visibility and position with an offest of the texttag to the given unit. 63 | This won't update if the unit moves or the local player gains vision of it. */ 64 | public function texttag.match(unit target, vec2 offset) 65 | this.setPos((target.getPos() + offset).toVec3()) 66 | this.matchVisibility(target) 67 | 68 | public function texttag.destr() 69 | DestroyTextTag(this) 70 | 71 | public function createTTEx(vec3 pos, string message, real size) returns texttag 72 | return CreateTextTag() 73 | ..setPermanent(true) 74 | ..setText(message, size) 75 | ..setPos(pos) 76 | ..setVisibility(true) 77 | 78 | public function createTTEx(vec3 pos, string message, real size, colorA col) returns texttag 79 | return CreateTextTag() 80 | ..setPos(pos.toVec2().withZ(20.)) 81 | ..setText(message, size) 82 | ..setColor(col) 83 | ..setVisibility(true) 84 | 85 | public function createTTEx(vec3 pos, string message, real size, colorA col, player p) returns texttag 86 | return CreateTextTag() 87 | ..setPos(pos.toVec2().withZ(20.)) 88 | ..setText(message, size) 89 | ..setColor(col) 90 | ..setVisibility(localPlayer == p) 91 | 92 | public function createTTEx(vec3 pos, vec2 vel, string message, real size, real duration, colorA col) returns texttag 93 | return CreateTextTag() 94 | ..setPos(pos.toVec2().withZ(20.)) 95 | ..setText(message, size) 96 | ..setColor(col) 97 | ..setVelocity(vel.x, vel.y) 98 | ..setLifespan(duration) 99 | ..setFadepoint(.1) 100 | ..setPermanent(false) 101 | 102 | public function createTTEx(vec3 pos, vec2 vel, string message, real size, real duration, colorA col, player p) returns texttag 103 | return createTTEx(pos, vel, message,size,duration,col) 104 | ..setVisibility(localPlayer == p) 105 | 106 | -------------------------------------------------------------------------------- /wurst/_handles/Timer.wurst: -------------------------------------------------------------------------------- 1 | package Timer 2 | import NoWurst 3 | 4 | public function timer.destr() 5 | DestroyTimer(this) 6 | 7 | public function timer.getElapsed() returns real 8 | return TimerGetElapsed(this) 9 | 10 | public function timer.getHandleId() returns int 11 | return GetHandleId(this) 12 | 13 | public function timer.pause() 14 | PauseTimer(this) 15 | 16 | public function timer.getRemaining() returns real 17 | return TimerGetRemaining(this) 18 | 19 | public function timer.resume() 20 | ResumeTimer(this) 21 | 22 | public function timer.start(real time, code timerCallBack) 23 | TimerStart(this, time, false, timerCallBack) 24 | 25 | public function timer.startPeriodic(real time, code timerCallBack) 26 | TimerStart(this, time, true, timerCallBack) 27 | -------------------------------------------------------------------------------- /wurst/_handles/TimerDialog.wurst: -------------------------------------------------------------------------------- 1 | package TimerDialog 2 | import NoWurst 3 | import Colors 4 | 5 | public function timer.createTimerDialog() returns timerdialog 6 | return CreateTimerDialog(this) 7 | 8 | public function timerdialog.destr() 9 | DestroyTimerDialog(this) 10 | 11 | public function timerdialog.setTitle(string title) 12 | TimerDialogSetTitle(this, title) 13 | 14 | public function timerdialog.setTitleColor(int red, int green, int blue, int alpha) 15 | TimerDialogSetTitleColor(this, red, green, blue, alpha) 16 | 17 | public function timerdialog.setTitleColor(colorA colorA) 18 | TimerDialogSetTitleColor(this, colorA.red, colorA.green, colorA.blue, colorA.alpha) 19 | 20 | public function timerdialog.setTimeColor(int red, int green, int blue, int alpha) 21 | TimerDialogSetTimeColor(this, red, green, blue, alpha) 22 | 23 | public function timerdialog.setTimeColor(colorA colorA) 24 | TimerDialogSetTimeColor(this, colorA.red, colorA.green, colorA.blue, colorA.alpha) 25 | 26 | /** Multiplies the timer's tick rate and remaining time, so that the overall elapsed time stays the same. 27 | For example, timer was started with initial time 60 seconds. 28 | If speed is set to 2, then the time shown in dialog will be 120 seconds, but counting is two times faster. 29 | If speed is set to 0.5, then the time shown in dialog will be 30 seconds, but counting is two times slower. */ 30 | public function timerdialog.setSpeed(real speedMultFactor) 31 | TimerDialogSetSpeed(this, speedMultFactor) 32 | 33 | public function timerdialog.display(boolean display) 34 | TimerDialogDisplay(this, display) 35 | 36 | public function timerdialog.isDisplayed() returns boolean 37 | return IsTimerDialogDisplayed(this) 38 | 39 | public function timerdialog.setRealTimeRemaining(real timeRemaining) 40 | TimerDialogSetRealTimeRemaining(this, timeRemaining) 41 | 42 | -------------------------------------------------------------------------------- /wurst/_handles/Weather.wurst: -------------------------------------------------------------------------------- 1 | package Weather 2 | import NoWurst 3 | import public WeatherEffects 4 | 5 | /** For some strange reason, weathereffect-variables always contain 'null' value and return '0' from GetHandleId function but they can still be used and compared. */ 6 | public function addWeather(rect where, int effectID) returns weathereffect 7 | return AddWeatherEffect(where, effectID) 8 | 9 | public function weathereffect.enable() 10 | EnableWeatherEffect(this, true) 11 | 12 | public function weathereffect.disable() 13 | EnableWeatherEffect(this, false) 14 | 15 | public function weathereffect.remove() 16 | RemoveWeatherEffect(this) 17 | -------------------------------------------------------------------------------- /wurst/_handles/Widget.wurst: -------------------------------------------------------------------------------- 1 | package Widget 2 | import NoWurst 3 | import public Vectors 4 | 5 | public function widget.getLife() returns real 6 | return GetWidgetLife(this) 7 | 8 | public function widget.setLife(real newLife) 9 | SetWidgetLife(this, newLife) 10 | 11 | public function widget.getPos() returns vec2 12 | return vec2(this.getX(), this.getY()) 13 | 14 | public function widget.getX() returns real 15 | return GetWidgetX(this) 16 | 17 | public function widget.getY() returns real 18 | return GetWidgetY(this) 19 | 20 | public function widget.addEffect(string modelName, string attachment) returns effect 21 | return AddSpecialEffectTarget(modelName, this, attachment) 22 | 23 | /** Checks if the widget is alive by testing current life > .405 */ 24 | public function widget.isAliveTrick() returns bool 25 | return .405 < this.getLife() 26 | -------------------------------------------------------------------------------- /wurst/_handles/_Handles.wurst: -------------------------------------------------------------------------------- 1 | package _Handles 2 | import NoWurst 3 | import public _Primitives 4 | 5 | import public Ability 6 | import public Boolexpr 7 | import public Camera 8 | import public Destructable 9 | import public Effect 10 | import public Fogmodifier 11 | import public Force 12 | import public Framehandle 13 | import public GameCache 14 | import public Group 15 | import public Hashtable 16 | import public Image 17 | import public Item 18 | import public Lightning 19 | import public Multiboard 20 | import public Player 21 | import public Playercolor 22 | import public Rect 23 | import public Region 24 | import public Sound 25 | import public Texttag 26 | import public Timer 27 | import public TimerDialog 28 | import public Trigger 29 | import public Unit 30 | import public Weather 31 | import public Widget 32 | -------------------------------------------------------------------------------- /wurst/_handles/primitives/Boolean.wurst: -------------------------------------------------------------------------------- 1 | package Boolean 2 | import NoWurst 3 | 4 | /** Converts this boolean into a string */ 5 | public function boolean.toString() returns string 6 | return this ? "true" : "false" 7 | 8 | /** Converts this string into a boolean */ 9 | public function string.toBool() returns boolean 10 | return this == "1" or this == "true" ? true : false 11 | 12 | public function string.isBool() returns bool 13 | return this.toBool().toString() == this 14 | 15 | /** Converts this boolean into an int */ 16 | public function boolean.toInt() returns int 17 | return this ? 1 : 0 18 | 19 | /** Converts this int into a boolean */ 20 | public function int.toBool() returns boolean 21 | return this != 0 22 | 23 | 24 | public function xor(bool a, bool b) returns boolean 25 | return (a or b) and not (a and b) 26 | 27 | public function boolean.xor(bool other) returns boolean 28 | return xor(this, other) 29 | -------------------------------------------------------------------------------- /wurst/_handles/primitives/Integer.wurst: -------------------------------------------------------------------------------- 1 | package Integer 2 | import NoWurst 3 | import Real 4 | 5 | public constant INT_MAX = 2147483647 6 | public constant INT_MIN = -2147483648 7 | 8 | /** Returns the absolute value of this int */ 9 | public function int.abs() returns int 10 | return this < 0 ? -this : this 11 | 12 | /** Returns the square of this int */ 13 | public function int.squared() returns int 14 | return this * this 15 | 16 | /** returns the sign of the int */ 17 | public function int.sign() returns int 18 | return (this > 0 ? 1 : (this < 0 ? -1 : 0)) 19 | 20 | /** Limits this int to the given range */ 21 | public function int.clamp(int lowerBound, int higherBound) returns int 22 | return (this <= lowerBound ? lowerBound : (this >= higherBound ? higherBound : this)) 23 | 24 | /** Returns the int as real */ 25 | public function int.toReal() returns real 26 | return this * 1. 27 | 28 | /** Returns the string representation of this int */ 29 | public function int.toString() returns string 30 | return I2S(this) 31 | 32 | /** Returns this int to the power of the argument int */ 33 | public function int.pow(int x) returns int 34 | int result = 1 35 | for int i=1 to x 36 | result *= this 37 | return result 38 | 39 | /** Linear Interpolation with alphafactor(smoothness) */ 40 | public function int.lerp(int target, real alpha) returns int 41 | return ((this * (1 - alpha)) + (target * alpha)).round() 42 | 43 | /** Checks if this int is between low and high value */ 44 | public function int.isBetween(int low, int high) returns bool 45 | return this >= low and this <= high 46 | 47 | /** Returns the result of a bitwise AND operation performed on this int and the argument int. */ 48 | public function int.bitAnd(int other) returns int 49 | return BlzBitAnd(this, other) 50 | 51 | /** Returns the result of a bitwise OR operation performed on this int and the argument int. */ 52 | public function int.bitOr(int other) returns int 53 | return BlzBitOr(this, other) 54 | 55 | /** Returns the result of a bitwise exclusive OR operation performed on this int and the argument int. */ 56 | public function int.bitXor(int other) returns int 57 | return BlzBitXor(this, other) 58 | 59 | /** Checks whether this int is odd. */ 60 | public function int.isOdd() returns boolean 61 | return BlzBitAnd(this, 1) == 1 62 | 63 | /** Checks whether this int is even. */ 64 | public function int.isEven() returns boolean 65 | return not this.isOdd() 66 | -------------------------------------------------------------------------------- /wurst/_handles/primitives/PrimitivesTests.wurst: -------------------------------------------------------------------------------- 1 | package PrimitivesTests 2 | 3 | @Test 4 | function testClamp() 5 | (-1).toInt().clamp(0, 10).assertEquals(0) 6 | (11).toInt().clamp(0, 10).assertEquals(10) 7 | (5).toInt().clamp(0, 10).assertEquals(5) 8 | (-1.5).clamp(-0.5, 1.5).assertEquals(-0.5) 9 | (2.5).clamp(-0.5, 1.5).assertEquals(1.5) 10 | (1.0).clamp(-0.5, 1.5).assertEquals(1.0) 11 | 12 | @Test 13 | function testR2SW() 14 | (1.213124.toString()).assertEquals("1.213124") 15 | (1.213124.toString(3)).assertEquals("1.213") 16 | 17 | @Test 18 | function testArithmetics() 19 | (12314123 + 56235424).assertEquals(68549547) 20 | (99 * 99).assertEquals(9801) 21 | 22 | (1. / 100.).assertEquals(0.01) 23 | 24 | (-2.).abs().assertEquals(2) 25 | 26 | (4. .squared()).assertEquals(4.*4.) 27 | 28 | @Test 29 | function testBoolean() 30 | (true).assertEquals(true) 31 | (false).assertEquals(false) 32 | 33 | (true.toInt()).assertEquals(1) 34 | (false.toInt()).assertEquals(0) 35 | 36 | (true.toString()).assertEquals("true") 37 | (false.toString()).assertEquals("false") 38 | 39 | (42 .toBool()).assertEquals(true) 40 | (0 .toBool()).assertEquals(false) 41 | 42 | ("true".toBool()).assertEquals(true) 43 | ("1".toBool()).assertEquals(true) 44 | ("nottrue".toBool()).assertEquals(false) 45 | 46 | @Test 47 | function testArrayLength() 48 | let x = [1, 2, 3] 49 | print("length: " + x.length.toString()) 50 | x.length.assertEquals(3) 51 | 52 | @Test 53 | function testParity() 54 | (0).isEven().assertEquals(true) 55 | (5113).isEven().assertEquals(false) 56 | 57 | @Test 58 | function testXor() 59 | xor(true, true).assertEquals(false) 60 | xor(true, false).assertEquals(true) 61 | xor(false, true).assertEquals(true) 62 | xor(false, false).assertEquals(false) 63 | -------------------------------------------------------------------------------- /wurst/_handles/primitives/Real.wurst: -------------------------------------------------------------------------------- 1 | package Real 2 | import NoWurst 3 | 4 | public constant REAL_MAX = 340282366920938000000000000000000000000. 5 | public constant REAL_MIN = -340282366920938000000000000000000000000. 6 | 7 | /** Returns the abolsute value of the given real. 8 | This means, negative values will be return positive. */ 9 | public function real.abs() returns real 10 | return this < 0 ? -this : this 11 | 12 | /** Returns the result of this * this, i.e. the square */ 13 | public function real.squared() returns real 14 | return this * this 15 | 16 | /** Returns the sign of this real */ 17 | public function real.sign() returns int 18 | return (this > 0 ? 1 : (this < 0 ? -1 : 0)) 19 | 20 | /** Rounds the input real to the nearest int */ 21 | public function real.round() returns int 22 | return this > 0 ? (this + .5).toInt() : (this - .5).toInt() 23 | 24 | /** Rounds the input real towards zero to the nearest int */ 25 | public function real.trunc() returns int 26 | return this.toInt() 27 | 28 | /** Rounds the input real downward to the nearest int */ 29 | public function real.floor() returns int 30 | var r = this.toInt() 31 | if this < 0 and this - r != 0. 32 | r -= 1 33 | return r 34 | 35 | /** Rounds the input real upward to the nearest int */ 36 | public function real.ceil() returns int 37 | var r = this.toInt() 38 | if this > 0 and this - r != 0. 39 | r += 1 40 | return r 41 | 42 | /** Limits the input real to the given range */ 43 | public function real.clamp(real lowerBound, real higherBound) returns real 44 | return (this <= lowerBound ? lowerBound : (this >= higherBound ? higherBound : this)) 45 | 46 | /** Converts the input real to an int. This cuts off 47 | the decimal digits. (1.9 -> 1) */ 48 | public function real.toInt() returns int 49 | return R2I(this) 50 | 51 | /** Returns the string representation of this real */ 52 | public function real.toString() returns string 53 | return R2S(this) 54 | 55 | /** Returns the string representation of this real with the given amount if digits precision */ 56 | public function real.toString(int precision) returns string 57 | return R2SW(this, precision, precision) 58 | 59 | /** The inverse trigonometric function of cosine */ 60 | public function real.acos() returns real 61 | return Acos(this) 62 | 63 | /** The inverse trigonometric function of sine */ 64 | public function real.asin() returns real 65 | return Asin(this) 66 | 67 | /** The inverse trigonometric function of tangent */ 68 | public function real.atan() returns real 69 | return Atan(this) 70 | 71 | /** The trigonometric function of cosine */ 72 | public function real.cos() returns real 73 | return Cos(this) 74 | 75 | /** The trigonometric function of sine */ 76 | public function real.sin() returns real 77 | return Sin(this) 78 | 79 | /** The trigonometric function of tangent */ 80 | public function real.tan() returns real 81 | return Tan(this) 82 | 83 | /** The arctangent function with two arguments. 84 | The second argument is needed to determine the appropriate 85 | quadrant of the computed angle. */ 86 | public function real.atan2(real y) returns real 87 | return Atan2(y, this) 88 | 89 | /** Returns this real to the power of the argument real */ 90 | public function real.pow(real x) returns real 91 | return Pow(this, x) 92 | 93 | /** Linear Interpolation with alphafactor(smoothness) */ 94 | public function real.lerp(real target, real alpha) returns real 95 | return (this * (1.0 - alpha)) + (target * alpha) 96 | 97 | /** Checks if this real is between low and high value */ 98 | public function real.isBetween(real low, real high) returns bool 99 | return this >= low and this <= high 100 | -------------------------------------------------------------------------------- /wurst/_handles/primitives/Reference.wurst: -------------------------------------------------------------------------------- 1 | package Reference 2 | /** 3 | A simple wrapper class which contains a reference to a value of the given type. 4 | The main usage is to wrap primitives to modify them inside closures. 5 | 6 | > function limited_sum(LinkedList someList) returns real 7 | > let dist_ref = new Reference(0.) 8 | > someList.for_each() (real elem) -> 9 | > if dist_ref.val < 500 10 | > dist_ref.val += elem 11 | > // Destroy the reference and use the contained value. 12 | > return dist_ref.into() 13 | 14 | Make sure to destroy references if you don't need them anymore. 15 | */ 16 | 17 | public class Reference 18 | T val 19 | 20 | construct(T val) 21 | this.val = val 22 | 23 | /** Consumes the reference, destroying it and returning the contained value. */ 24 | function into() returns T 25 | let val = this.val 26 | destroy this 27 | return val 28 | -------------------------------------------------------------------------------- /wurst/_handles/primitives/_Primitives.wurst: -------------------------------------------------------------------------------- 1 | package _Primitives 2 | import NoWurst 3 | import public Boolean 4 | import public Integer 5 | import public Real 6 | import public String 7 | -------------------------------------------------------------------------------- /wurst/_wurst/Annotations.wurst: -------------------------------------------------------------------------------- 1 | package Annotations 2 | import NoWurst 3 | 4 | // Meta-annotation: 5 | 6 | /** This annotation allows this function to be used as an annotation. */ 7 | @annotation public function annotation() 8 | 9 | // built-in annotations 10 | 11 | /** This annotation means that this function will be executed when the map is compiled. */ 12 | @annotation public function compiletime() 13 | 14 | /** This annotation means that this function should no longer be used. */ 15 | @annotation public function deprecated() 16 | 17 | /** This annotation means that this function should no longer be used. 18 | The corresponding message is a hint for which function to use instead. */ 19 | @annotation public function deprecated(string _message) 20 | 21 | /** Functions annotated with @compiletimenative are natives that are only available at compiletime, but not ingame. */ 22 | @annotation public function compiletimenative() 23 | 24 | /** Elements annotated with @configurable can be configured (i.e. replaced by a different implementation) in configuration packages. */ 25 | @annotation public function configurable() 26 | 27 | /** Functions annotated with @inline will have a higher priority of being inlined by the optimizer. */ 28 | @annotation public function inline() 29 | 30 | /** Functions annotated with @inline will have a lower priority of being inlined by the optimizer. */ 31 | @annotation public function noinline() 32 | 33 | /** Elements annotated with @config configure an element in the correspoding package (i.e. replaced by a different implementation). */ 34 | @annotation public function config() 35 | 36 | /** Functions annotated with @extern are assumed to be defined in external sources and are thus not included in the compilation output. */ 37 | @annotation public function extern() 38 | 39 | /** Functions annotated with @ifNotDefined are only output into the mapscript if they don't already exist. */ 40 | @annotation public function ifnotdefined() 41 | -------------------------------------------------------------------------------- /wurst/_wurst/Basics.wurst: -------------------------------------------------------------------------------- 1 | package Basics 2 | import public _Primitives 3 | import Player 4 | import NoWurst 5 | import Annotations 6 | 7 | /** Standard Period for animation. 8 | Note that 0.03125 is also possible, but doesn't work with order cancel from SetUnitPos */ 9 | @configurable public constant ANIMATION_PERIOD = 0.030 10 | /** Max collision size of any unit */ 11 | @configurable public constant MAX_COLLISION_SIZE = 197.0 12 | 13 | /** Id of Ravenform to allow setting fly height */ 14 | public constant HEIGHT_ENABLER = 'Amrf' 15 | /** Id of Eattree to recognize trees */ 16 | public constant TREE_RECOGNITION = 'Aeat' 17 | /** Id of Locust */ 18 | public constant LOCUST_ID = 'Aloc' 19 | /** Id of Ghost (Visible) */ 20 | public constant GHOST_INVIS_ID = 'Agho' 21 | /** Id of Ghost (Invisible) */ 22 | public constant GHOST_VIS_ID = 'Aeth' 23 | /** Neutral Passive (grey) */ 24 | public constant DUMMY_PLAYER = players[PLAYER_NEUTRAL_PASSIVE] 25 | /** Neutral Hostile (darkgreen) */ 26 | public constant DUMMY_HOSTILE_PLAYER = players[PLAYER_NEUTRAL_AGGRESSIVE] 27 | -------------------------------------------------------------------------------- /wurst/_wurst/ErrorHandling.wurst: -------------------------------------------------------------------------------- 1 | package ErrorHandling 2 | import NoWurst 3 | import public Printing 4 | import public Real 5 | import public Integer 6 | import public String 7 | import public MagicFunctions 8 | import GameTimer 9 | import Hashtable 10 | import Annotations 11 | 12 | @configurable public var MUTE_ERROR_DURATION = 60 13 | 14 | constant PRIMARY_ERROR_KEY = -1 15 | constant HT = compiletime(InitHashtable()) 16 | 17 | /** 18 | Stores the last error thrown by error(). 19 | This error can be inspected when using try() from package Execute. 20 | **/ 21 | public var lastError = "" 22 | 23 | /** 24 | Allows you to suppress error output. 25 | This is primarily useful when you catch 26 | an error with try(). 27 | **/ 28 | public var suppressErrorMessages = false 29 | 30 | /** error handing function. 31 | This function is used by libraries and for internal Wurst errors like 32 | accessing a null-pointer. Overwrite this function to customize error handling. 33 | 34 | Outputs an error message and terminates the current thread. 35 | There is a compiler flag to augment the error messages with stack traces. 36 | Error messages can also be disabled. 37 | 38 | Errors are only displayed once every MUTE_ERROR_DURATION seconds. 39 | To achieve this, the hash of the string is saved in a hashtable together with a timestamp. 40 | 41 | You can also use try() from package Execute to handle an error happening in a callback. 42 | */ 43 | @configurable public function error(string msg) 44 | if compiletime 45 | compileError("ERROR: " + msg) 46 | else 47 | if not suppressErrorMessages 48 | let hash = msg.getHash() 49 | if HT.hasInt(PRIMARY_ERROR_KEY, hash) 50 | // Error has been printed before 51 | if HT.loadInt(PRIMARY_ERROR_KEY, hash) + MUTE_ERROR_DURATION < currentTime 52 | // Time to print the error again 53 | Log.error(msg + getStackTraceString()) 54 | HT.saveInt(PRIMARY_ERROR_KEY, hash, currentTime.toInt()) 55 | HT.saveBoolean(PRIMARY_ERROR_KEY, hash, false) 56 | else if HT.hasBoolean(PRIMARY_ERROR_KEY, hash) 57 | if not HT.loadBoolean(PRIMARY_ERROR_KEY, hash) 58 | Log.error("|cffFF3A29Excessive repeating errors are being omitted") 59 | HT.saveBoolean(PRIMARY_ERROR_KEY, hash, true) 60 | else 61 | Log.error("|cffFF3A29Excessive repeating errors are being omitted") 62 | HT.saveBoolean(PRIMARY_ERROR_KEY, hash, true) 63 | else 64 | HT.saveInt(PRIMARY_ERROR_KEY, hash, currentTime.toInt()) 65 | Log.error("Message: " + msg + getStackTraceString()) 66 | // crash the thread: 67 | lastError = msg 68 | I2S(1 div 0) 69 | 70 | -------------------------------------------------------------------------------- /wurst/_wurst/MagicFunctions.wurst: -------------------------------------------------------------------------------- 1 | package MagicFunctions 2 | import NoWurst 3 | import Annotations 4 | // this package contains functions which are 5 | // implemented in the wurst compiler 6 | 7 | /** Stops compilation with the provided error message */ 8 | @compiletimenative public function compileError(string msg) 9 | 10 | /** Returns the map name from wurst.build file */ 11 | @compiletimenative public function getMapName() returns string 12 | return "" 13 | 14 | /** Returns the build date in uuuu-MM-dd'T'HH:mm format */ 15 | @compiletimenative public function getBuildDate() returns string 16 | return "" 17 | 18 | /** Returns true if the map is being built using the 'buildmap' command, false otherwise. */ 19 | @compiletimenative public function isProductionBuild() returns boolean 20 | return false 21 | 22 | /** builtin function which returns a stack trace. 23 | If stack traces are disabled, this function returns the empty string. 24 | */ 25 | public function getStackTraceString() returns string 26 | return "" 27 | 28 | /** builtin magic function. 29 | Calls all functions with the given annotation, for example 30 | `callFunctionsWithAnnotation("@initializeObject")`. 31 | The called functions must not take any parameters. 32 | */ 33 | public function callFunctionsWithAnnotation(string _annotation) 34 | 35 | /** this is a magic constant. 36 | It is always false at runtime and true during compiletime. 37 | */ 38 | public constant compiletime = false 39 | 40 | /** This is a builtin magic function. 41 | * It evaluates it's argument at compiletime and replaces the call with the result. 42 | */ 43 | public function compiletime(T expr) returns T 44 | return expr 45 | -------------------------------------------------------------------------------- /wurst/_wurst/Reflection.wurst: -------------------------------------------------------------------------------- 1 | /** 2 | This package is designed for debugging. 3 | It can print some details from the runtime presentation 4 | of Wurst objects. 5 | Everything is highly implementation specific, so DO NOT use these functions for anything else than showing debugging text. 6 | */ 7 | package Reflection 8 | 9 | /** returns the maximum type id, can be usd to 10 | iterate over all type-ids from 1 to maxTypeId() */ 11 | public native maxTypeId() returns int 12 | 13 | /** returns the class name for a given type id */ 14 | public native typeIdToTypeName(int typeId) returns string 15 | 16 | /** returns the number of active instances for a typeId */ 17 | public native instanceCount(int typeId) returns int 18 | 19 | /** returns the maximum number of instances reached for the given type id */ 20 | public native maxInstanceCount(int typeId) returns int 21 | -------------------------------------------------------------------------------- /wurst/_wurst/assets/Assets.wurst: -------------------------------------------------------------------------------- 1 | package Assets 2 | 3 | import public Abilities 4 | import public AbilityIds 5 | import public BuffIds 6 | import public Buildings 7 | import public Doodads 8 | import public Environment 9 | import public Icons 10 | import public ItemIds 11 | import public Objects 12 | import public Sounds 13 | import public Soundsets 14 | import public Textures 15 | import public UI 16 | import public Units 17 | import public UnitIds 18 | import public WeatherEffects 19 | import public AttachmentPoints 20 | -------------------------------------------------------------------------------- /wurst/_wurst/assets/OrderIds.wurst: -------------------------------------------------------------------------------- 1 | package OrderIds 2 | import NoWurst 3 | import public Orders 4 | -------------------------------------------------------------------------------- /wurst/_wurst/assets/WeatherEffects.wurst: -------------------------------------------------------------------------------- 1 | package WeatherEffects 2 | import NoWurst 3 | 4 | public class WeatherEffects 5 | static constant ashenvaleRainHeavy = 'RAhr' // Ashenvale Rain Heavy 6 | static constant ashenvaleRainLight = 'RAlr' // Ashenvale Rain Light 7 | static constant dalaranShield = 'MEds' // Dalaran Shield 8 | static constant dungeonBlueFogHeavy = 'FDbh' // Dungeon Blue Fog Heavy 9 | static constant dungeonBlueFogLight = 'FDbl' // Dungeon Blue Fog Light 10 | static constant dungeonGreenFogHeavy = 'FDgh' // Dungeon Green Fog Heavy 11 | static constant dungeonGreenFogLight = 'FDgl' // Dungeon Green Fog Light 12 | static constant dungeonRedFogHeavy = 'FDrh' // Dungeon Red Fog Heavy 13 | static constant dungeonRedFogLight = 'FDrl' // Dungeon Red Fog Light 14 | static constant dungeonWhiteFogHeavy = 'FDwh' // Dungeon White Fog Heavy 15 | static constant dungeonWhiteFogLight = 'FDwl' // Dungeon White Fog Light 16 | static constant lordaeronRainHeavy = 'RLhr' // Lordaeron Rain Heavy 17 | static constant lordaeronRainLight = 'RLlr' // Lordaeron Rain Light 18 | static constant northrendBlizzard = 'SNbs' // Northrend Blizzard 19 | static constant northrendSnowHeavy = 'SNhs' // Northrend Snow Heavy 20 | static constant northrendSnowLight = 'SNls' // Northrend Snow Light 21 | static constant outlandWindHeavy = 'WOcw' // Outland Wind Heavy 22 | static constant outlandWindLight = 'WOlw' // Outland Wind Light 23 | static constant raysOfLight = 'LRaa' // Rays Of Light 24 | static constant raysOfMoonlight = 'LRma' // Rays Of Moonlight 25 | static constant windHeavy = 'WNcw' // Wind Heavy 26 | -------------------------------------------------------------------------------- /wurst/closures/ClosureEventsTests.wurst: -------------------------------------------------------------------------------- 1 | package ClosureEventsTests 2 | 3 | @Test function testEvents() 4 | createUnit(players[0], 'hfoo' , ZERO2, 0 .fromDeg()) 5 | // EventListener.onCast(hero, 'Abld') (caster) -> 6 | // print("OnCast") 7 | -------------------------------------------------------------------------------- /wurst/closures/ClosureFrames.wurst: -------------------------------------------------------------------------------- 1 | package ClosureFrames 2 | import NoWurst 3 | import Trigger 4 | import HashMap 5 | import Unit 6 | 7 | /* 8 | Example usage: 9 | createSimpleFrame("MySimpleButton", GAME_UI, 0) 10 | ..onClick(() -> print("The button was clicked.")) 11 | ..onEnter(() -> print("The mouse is on the button.")) 12 | ..onLeave(() -> print("The mouse left the button.")) 13 | 14 | Important: 15 | * Most frametypes don't support most events (some might not support any) 16 | * Registrating origin frames to event seems to override their default behaviour. 17 | For instance using the origin menu button for a onClick() event will make it not prompt the menu. 18 | */ 19 | 20 | /* CONSTANTS */ 21 | 22 | constant FRAMEHANDLE_TRIGGER = CreateTrigger()..addAction(function onEvent) 23 | constant FRAME_MAP = new HashMap 24 | 25 | 26 | /* CLOSURES */ 27 | 28 | public function framehandle.onCheckboxCheck(FrameHandleListener listener) returns FrameHandleListener 29 | return addListenerForFrame(listener, this, FRAMEEVENT_CHECKBOX_CHECKED) 30 | 31 | public function framehandle.onCheckboxUncheck(FrameHandleListener listener) returns FrameHandleListener 32 | return addListenerForFrame(listener, this, FRAMEEVENT_CHECKBOX_UNCHECKED) 33 | 34 | public function framehandle.onClick(FrameHandleListener listener) returns FrameHandleListener 35 | return addListenerForFrame(listener, this, FRAMEEVENT_CONTROL_CLICK) 36 | 37 | public function framehandle.onDialogAccept(FrameHandleListener listener) returns FrameHandleListener 38 | return addListenerForFrame(listener, this, FRAMEEVENT_DIALOG_ACCEPT) 39 | 40 | public function framehandle.onDialogCancel(FrameHandleListener listener) returns FrameHandleListener 41 | return addListenerForFrame(listener, this, FRAMEEVENT_DIALOG_CANCEL) 42 | 43 | public function framehandle.onEditboxEnter(FrameHandleListener listener) returns FrameHandleListener 44 | return addListenerForFrame(listener, this, FRAMEEVENT_EDITBOX_ENTER) 45 | 46 | public function framehandle.onEditboxChange(FrameHandleListener listener) returns FrameHandleListener 47 | return addListenerForFrame(listener, this, FRAMEEVENT_EDITBOX_TEXT_CHANGED) 48 | 49 | public function framehandle.onMouseDoubleClick(FrameHandleListener listener) returns FrameHandleListener 50 | return addListenerForFrame(listener, this, FRAMEEVENT_MOUSE_DOUBLECLICK) 51 | 52 | public function framehandle.onMouseDown(FrameHandleListener listener) returns FrameHandleListener 53 | return addListenerForFrame(listener, this, FRAMEEVENT_MOUSE_DOWN) 54 | 55 | public function framehandle.onMouseEnter(FrameHandleListener listener) returns FrameHandleListener 56 | return addListenerForFrame(listener, this, FRAMEEVENT_MOUSE_ENTER) 57 | 58 | public function framehandle.onMouseLeave(FrameHandleListener listener) returns FrameHandleListener 59 | return addListenerForFrame(listener, this, FRAMEEVENT_MOUSE_LEAVE) 60 | 61 | public function framehandle.onMouseUp(FrameHandleListener listener) returns FrameHandleListener 62 | return addListenerForFrame(listener, this, FRAMEEVENT_MOUSE_UP) 63 | 64 | public function framehandle.onMouseWheel(FrameHandleListener listener) returns FrameHandleListener 65 | return addListenerForFrame(listener, this, FRAMEEVENT_MOUSE_WHEEL) 66 | 67 | public function framehandle.onPopupMenuItemChange(FrameHandleListener listener) returns FrameHandleListener 68 | return addListenerForFrame(listener, this, FRAMEEVENT_POPUPMENU_ITEM_CHANGED) 69 | 70 | public function framehandle.onSliderValueChange(FrameHandleListener listener) returns FrameHandleListener 71 | return addListenerForFrame(listener, this, FRAMEEVENT_SLIDER_VALUE_CHANGED) 72 | 73 | public function framehandle.onSpriteAnimUpdate(FrameHandleListener listener) returns FrameHandleListener 74 | return addListenerForFrame(listener, this, FRAMEEVENT_SPRITE_ANIM_UPDATE) 75 | 76 | function addListenerForFrame(FrameHandleListener listener, framehandle frame, frameeventtype eventType) returns FrameHandleListener 77 | FRAMEHANDLE_TRIGGER.registerFrameEvent(frame, eventType) 78 | listener.frame = frame 79 | listener.eventType = eventType 80 | let id = frame.getHandleId() 81 | let first = FRAME_MAP.get(id) 82 | if first != null 83 | first.prev = listener 84 | listener.next = first 85 | FRAME_MAP.put(id, listener) 86 | return listener 87 | 88 | 89 | /* ON EVENT */ 90 | 91 | function onEvent() 92 | var listener = FRAME_MAP.get(BlzGetTriggerFrame().getHandleId()) 93 | let eventType = BlzGetTriggerFrameEvent() 94 | while listener != null 95 | let nextListener = listener.next 96 | if listener.eventType == eventType 97 | listener.onEvent() 98 | listener = nextListener 99 | 100 | 101 | /* FRAME HANDLE LISTENER */ 102 | 103 | public abstract class FrameHandleListener 104 | framehandle frame 105 | frameeventtype eventType 106 | thistype next = null 107 | thistype prev = null 108 | 109 | abstract function onEvent() 110 | 111 | ondestroy 112 | let id = frame.getHandleId() 113 | let first = FRAME_MAP.get(id) 114 | if first == this 115 | if next == null 116 | FRAME_MAP.remove(id) 117 | else 118 | FRAME_MAP.put(id, next) 119 | else if prev != null 120 | prev.next = next 121 | next.prev = prev 122 | -------------------------------------------------------------------------------- /wurst/closures/ClosureKeyPresses.wurst: -------------------------------------------------------------------------------- 1 | package ClosureKeyPresses 2 | import HashMap 3 | 4 | /* 5 | This package is a comfort wrapper around wc3 key presses events introduced in 1.31, 6 | for easy event listening via closures. 7 | 8 | Example usage: 9 | onKeyPress(OSKEY_A) -> 10 | print("A player pressed on the A key.") 11 | 12 | onKeyPress(OSKEY_B, OSKEY_META.SHIFT) -> 13 | print("A player pressed on the B key while holding a SHIFT key.") 14 | 15 | onKeyRelease(OSKEY_C) -> 16 | print("A player released the C key.") 17 | 18 | onKeyPress(players[0], OSKEY_D, OSKEY_META.ALT, false) -> 19 | print("Player 0 released the D key while holding an ALT key.") 20 | 21 | Important: 22 | * When the metakey NONE is passed or when no metaKey is given like in `onKeyPress(OSKEY_A, () -> print("A"))`, 23 | pressing or releasing the key will not fire an event while holding a metakey. 24 | */ 25 | 26 | 27 | /* CONSTANTS */ 28 | 29 | constant KEY_TRIGGER = CreateTrigger()..addAction(function onEvent) 30 | constant KEY_MAP = new HashMap 31 | 32 | 33 | /* CLOSURES */ 34 | 35 | public function onKeyPress(oskeytype key, KeyPressListener listener) returns KeyPressListener 36 | return onKeyPress(key, OSKEY_META.NONE, true, listener) 37 | 38 | public function onKeyPress(oskeytype key, OSKEY_META metaKey, KeyPressListener listener) returns KeyPressListener 39 | return onKeyPress(key, metaKey, true, listener) 40 | 41 | public function onKeyRelease(oskeytype key, KeyPressListener listener) returns KeyPressListener 42 | return onKeyPress(key, OSKEY_META.NONE, false, listener) 43 | 44 | public function onKeyPress(oskeytype key, OSKEY_META metaKey, bool onKeyDown, KeyPressListener listener) returns KeyPressListener 45 | for i = 0 to bj_MAX_PLAYERS 46 | if players[i].isIngame() 47 | KEY_TRIGGER.registerPlayerKeyPress(players[i], key, metaKey, onKeyDown) 48 | return registerListener(listener, key, metaKey, onKeyDown) 49 | 50 | public function onKeyPress(player whichPlayer, oskeytype key, KeyPressListener listener) returns KeyPressListener 51 | return onKeyPress(whichPlayer, key, OSKEY_META.NONE, true, listener) 52 | 53 | public function onKeyPress(player whichPlayer, oskeytype key, OSKEY_META metaKey, KeyPressListener listener) returns KeyPressListener 54 | return onKeyPress(whichPlayer, key, metaKey, true, listener) 55 | 56 | public function onKeyRelease(player whichPlayer, oskeytype key, KeyPressListener listener) returns KeyPressListener 57 | return onKeyPress(whichPlayer, key, OSKEY_META.NONE, false, listener) 58 | 59 | public function onKeyPress(player whichPlayer, oskeytype key, OSKEY_META metaKey, bool onKeyDown, KeyPressListener listener) returns KeyPressListener 60 | KEY_TRIGGER.registerPlayerKeyPress(whichPlayer, key, metaKey, onKeyDown) 61 | return registerListener(listener, key, metaKey, onKeyDown) 62 | 63 | function registerListener(KeyPressListener listener, oskeytype key, OSKEY_META metaKey, bool onKeyDown) returns KeyPressListener 64 | listener.key = key 65 | listener.metaKey = metaKey.toInt() 66 | listener.onKeyDown = onKeyDown 67 | let id = key.getHandleId() 68 | let first = KEY_MAP.get(id) 69 | if first != null 70 | first.prev = listener 71 | listener.next = first 72 | KEY_MAP.put(id, listener) 73 | return listener 74 | 75 | 76 | /* ON EVENT */ 77 | 78 | function onEvent() 79 | var listener = KEY_MAP.get(BlzGetTriggerPlayerKey().getHandleId()) 80 | let metaKey = BlzGetTriggerPlayerMetaKey() 81 | let onKeyDown = BlzGetTriggerPlayerIsKeyDown() 82 | while listener != null 83 | if listener.metaKey == metaKey and listener.onKeyDown == onKeyDown 84 | listener.onEvent() 85 | listener = listener.next 86 | 87 | 88 | /* KEY PRESSES LISTENER */ 89 | 90 | abstract class KeyPressListener 91 | oskeytype key 92 | int metaKey 93 | bool onKeyDown 94 | thistype next = null 95 | thistype prev = null 96 | 97 | abstract function onEvent() 98 | 99 | ondestroy 100 | let id = key.getHandleId() 101 | let first = KEY_MAP.get(id) 102 | if first == this 103 | if next == null 104 | KEY_MAP.remove(id) 105 | else 106 | KEY_MAP.put(id, next) 107 | else if prev != null 108 | prev.next = next 109 | next.prev = prev 110 | -------------------------------------------------------------------------------- /wurst/closures/Execute.wurst: -------------------------------------------------------------------------------- 1 | package Execute 2 | import ErrorHandling 3 | 4 | /** 5 | This package exposes a single function execute(), 6 | the primary use of which is to reset the OP limit. 7 | **/ 8 | 9 | public interface ForForceCallback 10 | function run() 11 | 12 | let executeForce = CreateForce() 13 | 14 | init 15 | executeForce.addPlayer(localPlayer) 16 | 17 | function executeCurrentCallback() 18 | lastError = "" 19 | getCurrentCallback().run() 20 | setCurrentCallbackSuccess(true) 21 | 22 | boolean array tempCallbacksSuccess 23 | ForForceCallback array tempCallbacks 24 | int tempCallbacksCount = 0 25 | 26 | function pushCallback(ForForceCallback c) 27 | tempCallbacks[tempCallbacksCount] = c 28 | tempCallbacksSuccess[tempCallbacksCount] = false 29 | tempCallbacksCount++ 30 | 31 | function popCallback() 32 | tempCallbacksCount-- 33 | destroy tempCallbacks[tempCallbacksCount] 34 | 35 | function isLastCallbackSuccessful() returns boolean 36 | return tempCallbacksSuccess[tempCallbacksCount] 37 | 38 | function setCurrentCallbackSuccess(boolean value) 39 | tempCallbacksSuccess[tempCallbacksCount - 1] = value 40 | 41 | function getCurrentCallback() returns ForForceCallback 42 | return tempCallbacks[tempCallbacksCount - 1] 43 | 44 | /** 45 | This function starts a new 'thread' inside the 46 | callback closure, where the op limit is at zero, allowing us to bypass it. 47 | This is extremely useful when you have expensive computations 48 | which would otherwise crash the thread. 49 | 50 | If the callback crashes due to an error or hitting the OP limit, it will 51 | error out as well. 52 | 53 | Note: Unlike timers and doAfter(), this method is synchronous, meaning 54 | that it will wait for the called method to finish before continuing the in the current thread. 55 | 56 | Note: We use ForForce because it does not require a Trigger to be called, only an empty 57 | force which is reusable, unlike TriggerEvaluate. 58 | 59 | Note: You may use this inside a local clause, like this: 60 | if localPlayer == caller 61 | execute(...) 62 | But be warned that this will cause a desync, unless you are 63 | calling this from within another execute(...) clause. 64 | 65 | This will desync: 66 | // code called by a trigger 67 | if localPlayer == caller 68 | execute() 69 | This will not desync: 70 | execute() -> 71 | // do some stuff here 72 | 73 | if localPlayer == caller 74 | execute(...) 75 | 76 | **/ 77 | public function execute(ForForceCallback c) 78 | if not try(c) 79 | error("execute: thread has crashed. caused by:\n| - " + lastError) 80 | 81 | /** 82 | This function works exactly the same as execute(), with 83 | the only difference that it will allow you to inspect 84 | an error if one occured. 85 | 86 | If an error occured, the return value will be false. 87 | 88 | If the error is caused by error(), then you can inspect 89 | the error message using lastError from ErrorHandling. 90 | If the error is caused by another form of thread crash, 91 | such as zero division or OP limit, lastError will be empty. 92 | 93 | The error will also be suppressed from being printed. 94 | **/ 95 | public function try(ForForceCallback c) returns boolean 96 | pushCallback(c) 97 | let suppressErrors = suppressErrorMessages 98 | suppressErrorMessages = true 99 | executeForce.forEach(function executeCurrentCallback) 100 | suppressErrorMessages = suppressErrors 101 | popCallback() 102 | return isLastCallbackSuccessful() 103 | 104 | interface LimitedExecuteCondition 105 | function check() returns boolean 106 | 107 | interface LimitedExecuteAction 108 | function run() 109 | 110 | function executeWhileInternal(int resetCount, LimitedExecuteCondition condition, LimitedExecuteAction action) 111 | execute() -> 112 | var i = 0 113 | while condition.check() and i < resetCount 114 | action.run() 115 | i++ 116 | 117 | // restart the execute if necessary 118 | if condition.check() 119 | executeWhileInternal(resetCount, condition, action) 120 | 121 | /** 122 | This is a utility function which you can use to process arbitrarily large loops. 123 | It functions like a while loop that will run action.run() while condition.check() == true, 124 | however it will reset the OP limit each time after the specified amount of resetCount. 125 | **/ 126 | public function executeWhile(int resetCount, LimitedExecuteCondition condition, LimitedExecuteAction action) 127 | executeWhileInternal(resetCount, condition, action) 128 | destroy condition 129 | destroy action 130 | -------------------------------------------------------------------------------- /wurst/data/BitSet.wurst: -------------------------------------------------------------------------------- 1 | package BitSet 2 | import NoWurst 3 | import Integer 4 | import Wurstunit 5 | import Annotations 6 | 7 | public constant BITSET_SIZE = 32 8 | 9 | int array pows 10 | int array reversePows 11 | 12 | @compiletime function initPows() 13 | pows[0] = 1 14 | var allPows = 1 15 | for i = 1 to BITSET_SIZE - 1 16 | pows[i] = pows[i - 1] * 2 17 | allPows = allPows.bitOr(pows[i]) 18 | for i = 0 to BITSET_SIZE - 1 19 | reversePows[i] = allPows.bitXor(pows[i]) 20 | 21 | init 22 | initPows() 23 | 24 | /** Bitset represents a fixed-size sequence of BITSET_SIZE bits. The bits are contained in a single int. */ 25 | public tuple bitset(int val) 26 | 27 | /** Creates an empty bitset. */ 28 | public function emptyBitset() returns bitset 29 | return bitset(0) 30 | 31 | /** Returns the value of the bit with the specified index. */ 32 | public function bitset.get(int index) returns bool 33 | return this.val.bitAnd(pows[index]) != 0 34 | 35 | /** Sets the bit at the specified index to true. */ 36 | public function bitset.set(int index) returns bitset 37 | return bitset(this.val.bitOr(pows[index])) 38 | 39 | /** Sets the bit at the specified index to false. */ 40 | public function bitset.reset(int index) returns bitset 41 | return bitset(this.val.bitAnd(reversePows[index])) 42 | 43 | /** Sets the bit at the specified index to the specified value. */ 44 | public function bitset.set(int index, bool value) returns bitset 45 | return value ? this.set(index) : this.reset(index) 46 | 47 | /** Sets the bit at the specified index to the complement of its current value. */ 48 | public function bitset.flip(int index) returns bitset 49 | return bitset(this.val.bitXor(pows[index])) 50 | 51 | /** Returns true if this bitset contains no bits that are set to true. */ 52 | public function bitset.isEmpty() returns bool 53 | return this.val == 0 54 | 55 | /** Returns true if the specified bitset has any bits set to true that are also set to true in this bitset. */ 56 | public function bitset.intersects(bitset other) returns bool 57 | return this.val.bitAnd(other.val) != 0 58 | 59 | /** Performs a logical AND of this bit set with the bit set argument. */ 60 | public function bitset.bitAnd(bitset other) returns bitset 61 | return bitset(this.val.bitAnd(other.val)) 62 | 63 | /** Performs a logical OR of this bit set with the bit set argument. */ 64 | public function bitset.bitOr(bitset other) returns bitset 65 | return bitset(this.val.bitOr(other.val)) 66 | 67 | /** Performs a logical XOR of this bit set with the bit set argument. */ 68 | public function bitset.bitXor(bitset other) returns bitset 69 | return bitset(this.val.bitXor(other.val)) 70 | 71 | /** Returns an integer representation of the data. */ 72 | public function bitset.toInt() returns int 73 | return this.val 74 | 75 | // Conversion methods for generics 76 | 77 | public function bitsetToIndex(bitset object) returns int 78 | return object.toInt() 79 | 80 | public function bitsetFromIndex(int index) returns bitset 81 | return bitset(index) 82 | 83 | // Tests 84 | 85 | @test function testGet() 86 | let set = bitset(45) 87 | set.get(0).assertTrue() 88 | set.get(1).assertFalse() 89 | set.get(2).assertTrue() 90 | set.get(3).assertTrue() 91 | set.get(4).assertFalse() 92 | set.get(5).assertTrue() 93 | 94 | @test function testSet() 95 | emptyBitset().set(0).set(2).set(3).set(5).val.assertEquals(45) 96 | 97 | @test function testReset() 98 | bitset(45).reset(3).val.assertEquals(37) 99 | 100 | @test function testFlip() 101 | emptyBitset().set(3).flip(3).get(3).assertFalse() 102 | emptyBitset().flip(3).get(3).assertTrue() 103 | 104 | @test function testHighestBit() 105 | let bit = BITSET_SIZE - 1 106 | emptyBitset().set(bit).get(bit).assertTrue() 107 | emptyBitset().set(bit).reset(bit).get(bit).assertFalse() 108 | emptyBitset().flip(bit).get(bit).assertTrue() 109 | emptyBitset().set(bit).flip(bit).get(bit).assertFalse() 110 | 111 | @test function testIsEmpty() 112 | emptyBitset().isEmpty().assertTrue() 113 | emptyBitset().set(7).isEmpty().assertFalse() 114 | 115 | @test function testIntersects() 116 | emptyBitset().set(2).set(5).set(7).intersects(emptyBitset().set(1).set(4).set(21)).assertFalse() 117 | emptyBitset().set(3).set(11).intersects(emptyBitset().set(5).set(11)).assertTrue() 118 | 119 | @test function testOverall() 120 | var bitset = emptyBitset().set(0).set(3).set(8) 121 | 122 | for i = 0 to 8 123 | if i == 0 or i == 3 or i == 8 124 | bitset.get(i).assertTrue() 125 | else 126 | bitset.get(i).assertFalse() 127 | 128 | bitset = bitset.reset(8) 129 | .set(3) // should not change anything 130 | .reset(2) // should not change anything 131 | .flip(0) 132 | .flip(2) 133 | 134 | for i = 0 to 8 135 | if i == 2 or i == 3 136 | bitset.get(i).assertTrue() 137 | else 138 | bitset.get(i).assertFalse() 139 | -------------------------------------------------------------------------------- /wurst/data/HashListTests.wurst: -------------------------------------------------------------------------------- 1 | package HashListTests 2 | import HashList 3 | 4 | @Test 5 | function testAdd() 6 | let list = new HashList 7 | list.add(5) 8 | list.get(0).assertEquals(5) 9 | 10 | @Test 11 | function testSet() 12 | let list = new HashList 13 | list.add(3) 14 | list.add(5) 15 | list.set(1, 6) 16 | list.get(1).assertEquals(6) 17 | 18 | list.set(3, 42) 19 | list.get(3).assertEquals(42) 20 | // resize when index out of size 21 | list.size().assertEquals(4) 22 | // elem insert use null 23 | list.hasAt(2).assertTrue() 24 | list.get(2).assertEquals(0) 25 | list.has(0).assertTrue() 26 | list.count(0).assertEquals(1) 27 | 28 | list.set(3, 42) 29 | // not resize 30 | list.size().assertEquals(4) 31 | // un-inserted 32 | list.count(0).assertEquals(1) 33 | 34 | @Test 35 | function testAddAll() 36 | let vals = [5, 3, 6, 4, 1, 9] 37 | let list = new HashList 38 | ..add(5, 3, 6) 39 | 40 | let addl = new HashList 41 | ..add(4, 1, 9) 42 | 43 | list.addAll(addl) 44 | var i = 0 45 | for val in list 46 | val.assertEquals(vals[i]) 47 | i++ 48 | 49 | list.get(4).assertEquals(1) 50 | 51 | @Test 52 | function testClear() 53 | let list = new HashList 54 | ..add(5, 3, 4) 55 | 56 | list.clear() 57 | list.size().assertEquals(0) 58 | 59 | @Test 60 | function testRemoveAll() 61 | let list = new HashList 62 | ..add(5, 3, 4) 63 | 64 | list.clear() 65 | list.size().assertEquals(0) 66 | 67 | @Test 68 | function testRemoveAt() 69 | let list = new HashList 70 | ..add(3, 5) 71 | 72 | list.removeAt(1) 73 | list.get(0).assertEquals(3) 74 | list.get(1).assertEquals(0) 75 | 76 | @Test 77 | function testRemove() 78 | let list = new HashList 79 | ..add(1, 3, 5) 80 | 81 | list.remove(3) 82 | list.get(0).assertEquals(1) 83 | list.get(1).assertEquals(5) 84 | 85 | @Test 86 | function testSize() 87 | let list = new HashList 88 | list.size().assertEquals(0) 89 | list 90 | ..add(5, 3, 1) 91 | list.size().assertEquals(3) 92 | 93 | @Test 94 | function testIsEmpty() 95 | let list = new HashList 96 | list.isEmpty().assertEquals(true) 97 | list.add(1) 98 | list.isEmpty().assertEquals(false) 99 | 100 | @Test 101 | function testGet() 102 | let list = new HashList 103 | ..add(5, 1, 3) 104 | 105 | list.get(1).assertEquals(1) 106 | 107 | @Test 108 | function testHas() 109 | let list = new HashList 110 | ..add(1, 3, 5) 111 | 112 | list.has(3).assertEquals(true) 113 | list.add(3) 114 | list.remove(3) 115 | list.has(3).assertEquals(true) 116 | list.remove(3) 117 | list.has(3).assertEquals(false) 118 | list.has(9).assertEquals(false) 119 | 120 | @Test 121 | function testIterator() 122 | let list = new HashList 123 | let vals = [1, 3, 5] 124 | var i = 0 125 | 126 | list 127 | ..add(1, 3, 5) 128 | 129 | for val in list 130 | val.assertEquals(vals[i]) 131 | i++ 132 | 133 | @Test 134 | function testCopy() 135 | let list = new HashList 136 | ..add(1, 3, 5) 137 | 138 | let copy = list.copy() 139 | var i = 0 140 | 141 | for val in list 142 | val.assertEquals(copy.get(i)) 143 | i++ 144 | 145 | @Test 146 | function testCopyConstructor() 147 | let list = new HashList 148 | ..add(1, 3, 5) 149 | 150 | let copy = new HashList(list) 151 | var i = 0 152 | 153 | for val in list 154 | val.assertEquals(copy.get(i)) 155 | i++ 156 | 157 | @Test 158 | function testHasAt() 159 | let list = new HashList 160 | ..add(1, 3, 5, 7, 9) 161 | 162 | list.hasAt(4).assertTrue() 163 | list.hasAt(5).assertFalse() 164 | 165 | list.hasAt(-1).assertFalse() 166 | 167 | list..remove(3)..remove(9) 168 | 169 | list.hasAt(4).assertFalse() 170 | list.hasAt(2).assertTrue() 171 | 172 | list.hasAt(0).assertTrue() 173 | 174 | 175 | var xn_1 = 0 176 | var xn = 1 177 | @Test 178 | function testForEachFibonacci() 179 | let list = new HashList 180 | ..add(1, 1, 2, 3, 5, 8, 13, 21, 34) 181 | 182 | 183 | list.forEach() (entry) -> 184 | xn.assertEquals(entry) 185 | let oldxn = xn 186 | xn = xn + xn_1 187 | xn_1 = oldxn 188 | 189 | 190 | @Test 191 | function testIteratorRemove() 192 | let list = new HashList 193 | ..add(1, 1, 2, 3, 5, 8, 13, 21, 34) 194 | 195 | let itr = list.iterator() 196 | while itr.hasNext() 197 | let next = itr.next() 198 | if next > 2 and next < 25 199 | itr.remove() 200 | 201 | list.size().assertEquals(4) 202 | list.get(0).assertEquals(1) 203 | list.get(1).assertEquals(1) 204 | list.get(2).assertEquals(2) 205 | list.get(3).assertEquals(34) 206 | 207 | -------------------------------------------------------------------------------- /wurst/data/HashMap.wurst: -------------------------------------------------------------------------------- 1 | package HashMap 2 | import NoWurst 3 | import HashList 4 | import public TypeCasting 5 | import public Table 6 | 7 | /** Generic Table Wrapper */ 8 | public class HashMap extends Table 9 | 10 | protected int size = 0 11 | 12 | /** Whether a value exists under the given key or not */ 13 | function has(K key) returns boolean 14 | return hasInt(key castTo int) 15 | 16 | /** Saves the given value under the given key */ 17 | function put(K key, V value) 18 | if not has(key) 19 | size++ 20 | saveInt(key castTo int, value castTo int) 21 | 22 | /** Retrieves the value saved under the given key */ 23 | function get(K key) returns V 24 | return loadInt(key castTo int) castTo V 25 | 26 | /** Removes the value saved under the given key */ 27 | function remove(K key) 28 | if has(key) 29 | size-- 30 | removeInt(key castTo int) 31 | 32 | /** Retrieves the value saved under the given key and removes it */ 33 | function getAndRemove(K key) returns V 34 | let result = get(key) 35 | remove(key) 36 | return result 37 | 38 | function size() returns int 39 | return size 40 | 41 | override function flush() 42 | size = 0 43 | super.flush() 44 | 45 | /** Iterable generic Table Wrapper */ 46 | public class IterableMap extends HashMap 47 | protected let keys = new HashList 48 | private bool _destroyed = false 49 | 50 | construct() 51 | 52 | /** Create a new map by copying all elements from another list into it */ 53 | construct(thistype base) 54 | for key in base 55 | put(key, base.get(key)) 56 | 57 | 58 | /** Saves the given value under the given key */ 59 | override function put(K key, V value) 60 | super.put(key, value) 61 | if not hasKey(key) 62 | keys.add(key) 63 | 64 | /** Removes the key-value pair saved under the given key */ 65 | override function remove(K key) 66 | super.remove(key) 67 | if hasKey(key) 68 | keys.remove(key) 69 | 70 | /** Remove all data from this map */ 71 | override function flush() 72 | if not _destroyed 73 | keys.clear() 74 | super.flush() 75 | 76 | /** Retrieves the value saved under the given key and removes it */ 77 | override function getAndRemove(K key) returns V 78 | let result = super.getAndRemove(key) 79 | keys.remove(key) 80 | return result 81 | 82 | /** Returns an iterator that iterates over the map's keys */ 83 | function iterator() returns HLIterator 84 | return keys.iterator() 85 | 86 | /** Returns whether this map uses the given key */ 87 | function hasKey(K key) returns bool 88 | return keys.has(key) 89 | 90 | 91 | /** Removes either a single occurence or all occurences from of the value from the map */ 92 | function removeValue(V val, bool all) 93 | K array toRemove 94 | int num = 0 95 | for elem in this 96 | if get(elem) == val 97 | if all 98 | toRemove[num] = elem 99 | num++ 100 | if all and num > 0 101 | for i = 0 to num - 1 102 | remove(toRemove[i]) 103 | 104 | /** Copies all elements from another map into this one 105 | Use with caution, since it will replace elements that were under the same key! */ 106 | function addAll(IterableMap map) 107 | for key in map 108 | put(key, map.get(key)) 109 | 110 | /** Returns a shallow copy of this map */ 111 | function copy() returns IterableMap 112 | let map = new IterableMap 113 | for key in this 114 | map.put(key, get(key)) 115 | return map 116 | 117 | /** Returns the length of this IterableMap **/ 118 | override function size() returns int 119 | return keys.size() 120 | 121 | /** Executes the closure for each key-value pair */ 122 | function forEach(IMItrClosure itr) returns IterableMap 123 | for i = 0 to size() - 1 124 | let key = keys.get(i) 125 | let value = this.get(key) 126 | itr.run(key, value) 127 | destroy itr 128 | return this 129 | 130 | ondestroy 131 | destroy keys 132 | // Makes sure that no attempts to .clear() a destroyed group are made 133 | // since .flush() is called after this destructor 134 | _destroyed = true 135 | 136 | public interface IMItrClosure 137 | function run(K key, V value) 138 | -------------------------------------------------------------------------------- /wurst/data/HashMapTests.wurst: -------------------------------------------------------------------------------- 1 | package HashMapTests 2 | import HashMap 3 | 4 | @Test 5 | function testHas() 6 | let map = new HashMap 7 | map.has(5).assertEquals(false) 8 | map.put(5, 1) 9 | map.has(5).assertEquals(true) 10 | 11 | @Test 12 | function testPutGet() 13 | let map = new HashMap 14 | map.put(3, 1) 15 | map.get(3).assertEquals(1) 16 | map.get(5).assertEquals(0) 17 | map.remove(3) 18 | map.get(3).assertEquals(0) 19 | 20 | @Test 21 | function testRemove() 22 | let map = new HashMap 23 | ..put(3, 5) 24 | map.has(3).assertEquals(true) 25 | map.has(5).assertEquals(false) 26 | map.remove(3) 27 | map.has(3).assertEquals(false) 28 | 29 | @Test 30 | function testBooleanHashmap() 31 | let hm = new HashMap 32 | hm.put(1, true) 33 | hm.get(1).assertEquals(true) 34 | 35 | 36 | @Test 37 | function testSize() 38 | let hm = new HashMap 39 | hm.put(1, true) 40 | hm.size().assertEquals(1) 41 | hm.get(1).assertEquals(true) 42 | hm.size().assertEquals(1) 43 | hm.put(1, false) 44 | hm.size().assertEquals(1) 45 | hm.put(2, true) 46 | hm.size().assertEquals(2) 47 | hm.put(3, true) 48 | hm.put(4, true) 49 | hm.size().assertEquals(4) 50 | hm.remove(4) 51 | hm.size().assertEquals(3) 52 | hm.remove(3) 53 | hm.remove(2) 54 | hm.size().assertEquals(1) 55 | hm.getAndRemove(2) 56 | hm.size().assertEquals(1) 57 | hm.getAndRemove(1) 58 | hm.size().assertEquals(0) 59 | 60 | /* IterableMap */ 61 | @Test 62 | function testIterableMap() 63 | let map = new IterableMap 64 | map.put(3, "string1 ") 65 | map.put(9, "removed") 66 | map.put(2, "string2 ") 67 | map.put(8, "removed") 68 | map.put(1, "string3") 69 | 70 | map.remove(2) 71 | map.removeValue("removed", true) 72 | 73 | let otherMap = new IterableMap(map) 74 | 75 | string test = "" 76 | for key in otherMap 77 | test += otherMap.get(key) 78 | 79 | test.assertEquals("string1 string3") 80 | otherMap.hasKey(2).assertFalse() 81 | otherMap.hasKey(3).assertTrue() 82 | 83 | @Test 84 | function testIMIterator() 85 | let map = new IterableMap 86 | ..put(5, 1) 87 | ..put(8, 3) 88 | ..put(9, 5) 89 | 90 | var i = 0 91 | let vals = [1, 3, 5] 92 | for key in map 93 | map.get(key).assertEquals(vals[i]) 94 | i++ 95 | 96 | @Test 97 | function testIMGetPut() 98 | let map = new IterableMap 99 | ..put(5, 1) 100 | ..put(8, 3) 101 | ..put(9, 5) 102 | 103 | map.get(8).assertEquals(3) 104 | map.get(2).assertEquals(0) 105 | map.get(5).assertEquals(1) 106 | map.put(5, 2) 107 | map.get(5).assertEquals(2) 108 | 109 | @Test 110 | function testIMHasKeyRemove() 111 | let map = new IterableMap 112 | ..put(5, 1) 113 | ..put(8, 3) 114 | ..put(9, 5) 115 | 116 | map.hasKey(5).assertEquals(true) 117 | map.hasKey(6).assertEquals(false) 118 | map.remove(5) 119 | map.hasKey(5).assertEquals(false) 120 | 121 | @Test 122 | function testIMRemoveValue() 123 | let map = new IterableMap 124 | ..put(5, 1) 125 | ..put(8, 3) 126 | ..put(6, 1) 127 | ..put(9, 2) 128 | ..put(3, 1) 129 | ..put(4, 5) 130 | 131 | map.removeValue(1, false) 132 | map.get(6).assertEquals(1) 133 | map.removeValue(1, true) 134 | map.hasKey(3).assertEquals(false) 135 | 136 | @Test 137 | function testIMAddAll() 138 | let map = new IterableMap 139 | ..put(5, 1) 140 | ..put(8, 3) 141 | ..put(9, 5) 142 | 143 | let map2 = new IterableMap 144 | ..put(4, 1) 145 | ..put(8, 5) 146 | ..put(3, 5) 147 | 148 | let keys = [5, 8, 9, 4, 3] 149 | let vals = [1, 5, 5, 1, 5] 150 | 151 | map.addAll(map2) 152 | for i = 0 to 4 153 | map.get(keys[i]).assertEquals(vals[i]) 154 | 155 | @Test 156 | function testIMCopy() 157 | let map = new IterableMap 158 | ..put(5, 1) 159 | ..put(8, 3) 160 | ..put(9, 5) 161 | 162 | let map2 = map.copy() 163 | 164 | for key in map 165 | map2.get(key).assertEquals(map.get(key)) 166 | 167 | @Test 168 | function testIMCopyConstructor() 169 | let map = new IterableMap 170 | ..put(5, 1) 171 | ..put(8, 3) 172 | ..put(9, 5) 173 | 174 | let map2 = new IterableMap(map) 175 | 176 | for key in map 177 | map2.get(key).assertEquals(map.get(key)) 178 | 179 | @Test 180 | function testIMFlush() 181 | let map = new IterableMap 182 | ..put(5, 1) 183 | ..put(8, 3) 184 | ..put(9, 5) 185 | 186 | map.flush() 187 | map.hasKey(5).assertEquals(false) 188 | map.get(5).assertEquals(0) 189 | destroy map 190 | 191 | @Test 192 | function testIMForEach() 193 | let map = new IterableMap 194 | ..put(5, 1) 195 | ..put(8, 3) 196 | ..put(9, 5) 197 | 198 | var count = 0 199 | map.forEach() (integer key, integer value) -> 200 | count++ 201 | if count == 1 202 | key.assertEquals(5) 203 | value.assertEquals(1) 204 | else if count == 2 205 | key.assertEquals(8) 206 | value.assertEquals(3) 207 | else if count == 3 208 | key.assertEquals(9) 209 | value.assertEquals(5) 210 | 211 | 212 | destroy map 213 | -------------------------------------------------------------------------------- /wurst/data/HashSet.wurst: -------------------------------------------------------------------------------- 1 | package HashSet 2 | import public HashList 3 | 4 | /** Generic set implementation */ 5 | public class HashSet extends HashList 6 | private static constant position = compiletime(InitHashtable()) 7 | 8 | /** Adds an element to this set */ 9 | override function add(vararg T elems) 10 | for elem in elems 11 | if count(elem) <= 0 12 | position.saveInt(this castTo int, elem castTo int, size()) 13 | super.add(elem) 14 | 15 | /** Removes an element from this set if present */ 16 | override function remove(T elem) returns boolean 17 | var result = false 18 | if count(elem) > 0 19 | removeAt(position.loadInt(this castTo int, elem castTo int)) 20 | result = true 21 | return result 22 | 23 | /** Remove the element at the given index */ 24 | override function removeAt(int index) returns T 25 | position.removeInt(this castTo int, get(index) castTo int) 26 | for i = index + 1 to size() 27 | position.saveInt(this castTo int, get(i) castTo int, position.loadInt(this castTo int, get(i) castTo int) - 1) 28 | return super.removeAt(index) 29 | 30 | override function clear() 31 | super.clear() 32 | position.flushChild(this castTo int) 33 | 34 | ondestroy 35 | position.flushChild(this castTo int) 36 | 37 | -------------------------------------------------------------------------------- /wurst/data/HashSetTests.wurst: -------------------------------------------------------------------------------- 1 | package HashSetTests 2 | import HashSet 3 | 4 | @Test 5 | function testHashSet() 6 | let set = new HashSet 7 | ..add(4, 5, 6, 3, 5) 8 | 9 | // Test count 10 | set.count(5).assertEquals(1) 11 | set.count(6).assertEquals(1) 12 | set.count(8).assertEquals(0) 13 | 14 | // Test has 15 | set.has(5).assertEquals(true) 16 | set.has(9).assertEquals(false) 17 | 18 | // Test removeAt 19 | set.get(0).assertEquals(4) 20 | set.has(4).assertEquals(true) 21 | set.removeAt(0) 22 | set.has(4).assertEquals(false) 23 | 24 | // Test remove 25 | set.get(0).assertEquals(5) 26 | set.remove(3) 27 | set.size().assertEquals(2) 28 | 29 | @Test function testFenixOrder() 30 | let hset = new HashSet 31 | hset..add(1, 2, 3)..remove(2) 32 | hset.get(0).assertEquals(1) 33 | hset.get(1).assertEquals(3) 34 | 35 | @Test function testRemoval() 36 | let hset = new HashSet 37 | ..add(4, 5, 6, 3, 5) 38 | hset.removeAt(0) 39 | hset.get(0).assertEquals(5) 40 | hset.remove(5) 41 | hset.get(0).assertEquals(6) 42 | -------------------------------------------------------------------------------- /wurst/data/LinkedListModule.wurst: -------------------------------------------------------------------------------- 1 | package LinkedListModule 2 | 3 | /** Turns a class into a linked list where each instance knows it's previous 4 | and next member in the list */ 5 | public module LinkedListModule 6 | static thistype first = null 7 | static thistype last = null 8 | static int size = 0 9 | thistype prev 10 | thistype next 11 | 12 | construct() 13 | size++ 14 | if size == 1 15 | first = this 16 | prev = null 17 | else 18 | prev = last 19 | last.next = this 20 | first.prev = this 21 | next = null 22 | last = this 23 | 24 | static function getFirst() returns thistype 25 | return first 26 | 27 | function getNext() returns thistype 28 | if next == null 29 | return first 30 | return next 31 | 32 | function getPrev() returns thistype 33 | if prev == null 34 | return last 35 | return prev 36 | 37 | function remove() 38 | size-- 39 | if this != first 40 | prev.next = next 41 | else 42 | first = next 43 | if this != last 44 | next.prev = prev 45 | else 46 | last = prev 47 | 48 | ondestroy 49 | remove() 50 | 51 | /** An iterator which iterates over all instances of this class */ 52 | static function iterator() returns Iterator 53 | return new Iterator(true) 54 | 55 | private static let staticItr = new Iterator(false) 56 | 57 | /** An iterator which iterates over all instances of this class */ 58 | static function staticItr() returns Iterator 59 | return staticItr..reset() 60 | 61 | /** An iterator which iterates over all instances of this class in reverse */ 62 | static function backIterator() returns BackIterator 63 | return new BackIterator(true) 64 | 65 | private static let staticBackItr = new BackIterator(false) 66 | 67 | /** An iterator which iterates over all instances of this class in reverse */ 68 | static function staticBackItr() returns BackIterator 69 | return staticBackItr..reset() 70 | 71 | static class Iterator 72 | LinkedListModule.thistype current = first 73 | protected bool destroyOnClose 74 | 75 | construct(bool destroyOnClose) 76 | this.destroyOnClose = destroyOnClose 77 | 78 | function hasNext() returns boolean 79 | return current != null 80 | 81 | function next() returns LinkedListModule.thistype 82 | let res = current 83 | current = current.next 84 | return res 85 | 86 | function reset() 87 | current = first 88 | 89 | function close() 90 | if destroyOnClose 91 | destroy this 92 | 93 | static class BackIterator 94 | LinkedListModule.thistype current = last 95 | protected bool destroyOnClose 96 | 97 | construct(bool destroyOnClose) 98 | this.destroyOnClose = destroyOnClose 99 | 100 | function hasNext() returns boolean 101 | return current != null 102 | 103 | function next() returns LinkedListModule.thistype 104 | let res = current 105 | current = current.prev 106 | return res 107 | 108 | function reset() 109 | current = last 110 | 111 | function close() 112 | if destroyOnClose 113 | destroy this 114 | 115 | -------------------------------------------------------------------------------- /wurst/data/LinkedListModuleTests.wurst: -------------------------------------------------------------------------------- 1 | package LinkedListModuleTests 2 | import LinkedListModule 3 | 4 | // -----TESTS------ 5 | class TestClass 6 | use LinkedListModule 7 | string str 8 | construct(string str) 9 | this.str = str 10 | 11 | function allToString() returns string 12 | var res = "" 13 | for x in TestClass 14 | res += x.str 15 | return res 16 | 17 | @Test 18 | function test() 19 | let a = new TestClass("a") 20 | let b = new TestClass("b") 21 | let c = new TestClass("c") 22 | let d = new TestClass("d") 23 | 24 | allToString().assertEquals("abcd") 25 | destroy c 26 | allToString().assertEquals("abd") 27 | destroy d 28 | allToString().assertEquals("ab") 29 | destroy a 30 | allToString().assertEquals("b") 31 | destroy b 32 | allToString().assertEquals("") 33 | 34 | @Test 35 | function testDestroy() 36 | new TestClass("a") 37 | let b = new TestClass("b") 38 | new TestClass("c") 39 | new TestClass("d") 40 | var s = "" 41 | for x in TestClass 42 | s += x.str 43 | if x == b 44 | destroy x 45 | s.assertEquals("abcd") 46 | allToString().assertEquals("acd") 47 | for x in TestClass 48 | destroy x 49 | allToString().assertEquals("") 50 | -------------------------------------------------------------------------------- /wurst/dummy/DummyCaster.wurst: -------------------------------------------------------------------------------- 1 | package DummyCaster 2 | import DummyRecycler 3 | import MapBounds 4 | import ClosureTimers 5 | 6 | /** 7 | * Dummy casters are commonly used in custom spells to cast abilities dynamically onto targets or points. 8 | * For example hexing all units in an AoE, or impaling in multiple directions. 9 | * If your spell's effect is entirely instant, you should use `InstantDummyCaster`. 10 | * For many spells however, InstantDummyCaster is not feasible, because they 11 | * - are of type impale or channel 12 | * - attach effects to the caster, like chain lightning 13 | * - deal delayed damage (fireball, shockwave) 14 | * In all of these scenarios the unit has to stand in a place and the spell's effects are not instant. 15 | * 16 | * DummyCaster uses one dummy per cast, and only removes it after a set delay, to allow effects and damage to process 17 | * Example with long delay for blizzard: 18 | * 19 | * new DummyCaster() 20 | * ..origin(casterPos) 21 | * ..owner(caster) 22 | * ..delay(15) 23 | * ..castPoint('A000', 1, OrderIds.OrderIds.blizzard, target) 24 | * 25 | * The dummy caster will be recycled automatically, after the last spell's `delay` duration has expired. 26 | * You shouldn't destroy the object yourself. Just cast any amount of spells at once or shortly after and leave it. 27 | * Allocate DummyCasters dynamically on the spot and don't save them. 28 | */ 29 | public class DummyCaster 30 | private var castCount = 0 31 | protected var delay = 5.0 32 | protected var owner = DUMMY_PLAYER 33 | protected var origin = ZERO3 34 | 35 | construct() 36 | 37 | /** Sets the origin of the dummy with a z-value of 0 */ 38 | function origin(vec2 pos) 39 | this.origin = pos.toVec3() 40 | 41 | /** Sets the origin of the dummy */ 42 | function origin(vec3 pos) 43 | this.origin = pos 44 | 45 | /** Sets the owner of the dummy */ 46 | function owner(player owner) 47 | this.owner = owner 48 | 49 | /** Delay after which the dummy is recycled. Increase this if your spell is longer. 50 | Default 5 seconds. */ 51 | function delay(real delay) 52 | this.delay = delay 53 | 54 | /** Returns the casting dummy unit if the cast order was successful, or null if cast failed.*/ 55 | function castImmediate(int abilityId, int lvl, int orderId) returns unit 56 | var dummy = prepare(abilityId, lvl) 57 | let success = dummy.issueImmediateOrderById(orderId) 58 | finish(dummy, abilityId) 59 | if not success 60 | dummy = null 61 | return dummy 62 | 63 | /** Returns the casting dummy unit if the cast order was successful, or null if cast failed. */ 64 | function castTarget(int abilityId, int lvl, int orderId, widget target) returns unit 65 | var dummy = prepare(abilityId, lvl) 66 | dummy.setFacing(dummy.getPos().angleTo(target.getPos())) 67 | let success = dummy.issueTargetOrderById(orderId, target) 68 | finish(dummy, abilityId) 69 | if not success 70 | dummy = null 71 | return dummy 72 | 73 | /** Returns the casting dummy unit if the cast order was successful, or null if cast failed. */ 74 | function castPoint(int abilityId, int lvl, int orderId, vec2 targetPos) returns unit 75 | var dummy = prepare(abilityId, lvl) 76 | dummy.setFacing(dummy.getPos().angleTo(targetPos)) 77 | let success = dummy.issuePointOrderById(orderId, targetPos) 78 | finish(dummy, abilityId) 79 | if not success 80 | dummy = null 81 | return dummy 82 | 83 | protected function prepare(int id, int lvl) returns unit 84 | let dummy = DummyRecycler.get(origin.toVec2(), angle(0)) 85 | if origin.inBounds() 86 | dummy.setXYZ(origin) 87 | dummy..addAbility(id)..setMana(1000000) 88 | if lvl > 1 89 | dummy.setAbilityLevel(id, lvl) 90 | dummy.setOwner(owner, false) 91 | return dummy 92 | 93 | protected function finish(unit dummy, int id) 94 | castCount++ 95 | doAfter(delay) -> 96 | recycleDummy(dummy, id) 97 | castCount-- 98 | if castCount == 0 99 | destroy this 100 | 101 | protected function recycleDummy(unit dummy, int id) 102 | dummy..removeAbility(id) 103 | DummyRecycler.recycle(dummy) 104 | -------------------------------------------------------------------------------- /wurst/dummy/DummyDamage.wurst: -------------------------------------------------------------------------------- 1 | package DummyDamage 2 | import DummyRecycler 3 | 4 | /** 5 | DummyDamage is used when you want to inflict damage from a certain player, 6 | without having access to a unit by that player, that the damage should originate from, 7 | e.g. an effect missile, that does damage on impact, but doesn't know the caster unit. 8 | A DummyDamage is constructed analogous to a DummyCaster using the builder pattern. 9 | 10 | > new DummyDamage() 11 | > ..attacker(attackingPlayer) 12 | > ..target(targetWidget) 13 | > ..amount(50) 14 | > ..attackType(ATTACK_TYPE_NORMAL) 15 | > ..apply(true) 16 | 17 | The object is only destroyed if you pass true to the #apply function, 18 | and can therefore be reused to apply the same type of damage to multiple targets. 19 | 20 | */ 21 | 22 | public class DummyDamage 23 | private unit dummy = DummyRecycler.get(origin, angle(0)) 24 | private widget target = null 25 | private var amount = 0. 26 | private var attack = false 27 | private var ranged = false 28 | private var origin = ZERO2 29 | private var attacker = DUMMY_PLAYER 30 | private var attackType = ATTACK_TYPE_CHAOS 31 | private var damageType = DAMAGE_TYPE_UNKNOWN 32 | private var weaponType = WEAPON_TYPE_WHOKNOWS 33 | 34 | function apply() 35 | apply(false) 36 | 37 | function apply(boolean terminate) 38 | dummy 39 | ..setOwner(attacker, false) 40 | ..damageTarget(target, amount, attack, ranged, attackType, damageType, weaponType) 41 | 42 | if terminate 43 | destroy this 44 | 45 | function target(widget target) 46 | this.target = target 47 | 48 | function attacker(player attacker) 49 | this.attacker = attacker 50 | 51 | function amount(real amount) 52 | this.amount = amount 53 | 54 | function isAttack(boolean isAttack) 55 | this.attack = isAttack 56 | 57 | function isRanged(boolean isRanged) 58 | this.ranged = isRanged 59 | 60 | function origin(vec2 origin) 61 | this.origin = origin 62 | 63 | function attackType(attacktype attackType) 64 | this.attackType = attackType 65 | 66 | function damageType(damagetype damageType) 67 | this.damageType = damageType 68 | 69 | function weaponType(weapontype weaponType) 70 | this.weaponType = weaponType 71 | 72 | ondestroy 73 | DummyRecycler.recycle(dummy) 74 | -------------------------------------------------------------------------------- /wurst/dummy/DummyRecycler.wurst: -------------------------------------------------------------------------------- 1 | package DummyRecycler 2 | import NoWurst 3 | import public Unit 4 | import public Timer 5 | import Basics 6 | import ErrorHandling 7 | import MapBounds 8 | import AbilityObjEditing 9 | import ObjectIds 10 | import Colors 11 | import Annotations 12 | import UnitIds 13 | import Icons 14 | import ObjectIdGenerator 15 | import LinkedList 16 | import TypeCasting 17 | import ClosureTimers 18 | 19 | /** Id of the dummy unit */ 20 | public constant DUMMY_UNIT_ID = compiletime(UNIT_ID_GEN.next()) 21 | 22 | /** Id of the root ability 23 | it makes unit: 24 | 1. not need to turn when spell or attack 25 | 2. can't move or turn by order like a building 26 | 3. but you can still set it's postion with code 27 | 4. the effect remains even if this ability is removed 28 | */ 29 | public constant ROOT_ENABLER = compiletime(ABIL_ID_GEN.next()) 30 | 31 | public constant DISABLE_AUTO_ACQUIRE_ATTACK_TARGETS_ABILITY_ID = compiletime(ABIL_ID_GEN.next()) 32 | 33 | constant DIFFERENT_ANGLES = 8 34 | constant ANGLE_DEGREE = 360 / DIFFERENT_ANGLES 35 | constant SAVED_UNITS_PER_ANGLE = 6 36 | 37 | 38 | public class DummyRecycler 39 | static LinkedList array angleQueues 40 | 41 | static function get(vec2 pos, angle a) returns unit 42 | let angleIndex = (a.degrees() % 360 / ANGLE_DEGREE).round() mod DIFFERENT_ANGLES 43 | if angleQueues[angleIndex].size() > 0 44 | return angleQueues[angleIndex].dequeue()..setXY(pos)..unpause() 45 | else 46 | return createDummy(pos, a) 47 | 48 | static function recycle(unit u) 49 | var smallestQueue = 0 50 | for i = 1 to DIFFERENT_ANGLES - 1 51 | if angleQueues[smallestQueue].size() > angleQueues[i].size() 52 | smallestQueue = i 53 | if angleQueues[smallestQueue].size() >= SAVED_UNITS_PER_ANGLE 54 | u.remove() 55 | else 56 | angleQueues[smallestQueue].enqueue(u) 57 | u..setXY(boundMax - vec2(16, 16)) 58 | ..pause() 59 | ..setFacing((smallestQueue * ANGLE_DEGREE).asAngleDegrees()) 60 | ..setScale(1) 61 | ..setVertexColor(COLOR_WHITE) 62 | ..setOwner(DUMMY_PLAYER, true) 63 | 64 | static function recycle(unit u, real delay) 65 | doAfter(delay) -> 66 | DummyRecycler.recycle(u) 67 | 68 | function createDummy(vec2 pos, angle facing) returns unit 69 | let u = createUnit(DUMMY_PLAYER, DUMMY_UNIT_ID, pos, facing) 70 | u..addAbility(HEIGHT_ENABLER)..removeAbility(HEIGHT_ENABLER) 71 | ..addAbility(ROOT_ENABLER)..removeAbility(ROOT_ENABLER) 72 | ..setXY(pos)..setFacing(facing) 73 | return u 74 | 75 | 76 | init 77 | realToIndex(1) 78 | for i = 0 to DIFFERENT_ANGLES - 1 79 | DummyRecycler.angleQueues[i] = new LinkedList() 80 | let facing = angle(i * ANGLE_DEGREE) 81 | for j = 0 to SAVED_UNITS_PER_ANGLE - 1 82 | let dummy = createDummy(boundMax - vec2(16, 16), facing) 83 | DummyRecycler.angleQueues[i].enqueue(dummy) 84 | 85 | @compiletime function generateDummyUnit() 86 | new AbilityDefinitionRootAncients(ROOT_ENABLER) 87 | 88 | new AbilityDefinitionPermanentInvisibility(DISABLE_AUTO_ACQUIRE_ATTACK_TARGETS_ABILITY_ID) 89 | ..presetAutoAcquireAttackTargets(_ -> false) 90 | ..presetDurationNormal(_ -> -1) 91 | ..presetDurationHero(_ -> -1) 92 | 93 | new UnitDefinition(DUMMY_UNIT_ID, UnitIds.wisp) 94 | ..setName("Effect Dummy Unit") 95 | ..setUpgradesUsed("") 96 | ..setStructuresBuilt("") 97 | ..setManaRegeneration(0.1) 98 | ..setManaInitialAmount(1000) 99 | ..setManaMaximum(1000) 100 | ..setCollisionSize(0.0) 101 | ..setRace(Race.Commoner) 102 | ..setFoodCost(0) 103 | ..setArmorType(ArmorType.Divine) 104 | ..setIconGameInterface(Icons.bTNTemp) 105 | ..setSpeedBase(522) 106 | ..setModelFile("dummy.mdl") 107 | ..setAnimationBlendTimeseconds(0.0) 108 | ..setAnimationCastBackswing(0.0) 109 | ..setMaximumPitchAngledegrees(0.0) 110 | ..setMaximumRollAngledegrees(0.0) 111 | ..setArtSpecial("") 112 | ..setProjectileImpactZ(0.0) 113 | ..setProjectileLaunchZ(0.0) 114 | ..setNameEditorSuffix("(Basics)") 115 | ..setSightRadiusDay(1) 116 | ..setSightRadiusNight(1) 117 | ..setUnitClassification("_") 118 | ..setPropulsionWindowdegrees(1.0) 119 | ..setTooltipBasic("") 120 | ..setAttacksEnabled(AttacksEnabled.AttackOneOnly.toObjectInt()) 121 | ..setAttack1AnimationBackswingPoint(0) 122 | ..setAttack1AnimationDamagePoint(0) 123 | ..setAttack1CooldownTime(0) 124 | ..setAttack1AttackType(AttackType.Unknown) 125 | ..setAttack1DamageBase(-1) 126 | ..setAttack1DamageNumberofDice(1) 127 | ..setAttack1DamageSidesperDie(1) 128 | ..setAttack1ProjectileSpeed(INT_MAX) 129 | ..setAttack1Range(INT_MAX) 130 | ..setAttack1TargetsAllowed( 131 | commaList(TargetsAllowed.air, TargetsAllowed.debris, TargetsAllowed.ground, TargetsAllowed.invulnerable, 132 | TargetsAllowed.item_t, TargetsAllowed.structure, TargetsAllowed.vulnerable, TargetsAllowed.ward)) 133 | ..setAttack1WeaponSound(WeaponSound.Nothing) 134 | ..setAttack1WeaponType(WeaponType.Instant) 135 | ..setAcquisitionRange(INT_MAX.toReal()) 136 | ..setNormalAbilities( 137 | commaList(AbilityIds.invulnerable, AbilityIds.locust, DISABLE_AUTO_ACQUIRE_ATTACK_TARGETS_ABILITY_ID)) 138 | 139 | -------------------------------------------------------------------------------- /wurst/dummy/Fx2.wurst: -------------------------------------------------------------------------------- 1 | package Fx2 2 | import public Quaternion 3 | 4 | /* Pendant to Fx.wurst but using the new effect natives instead of a unit 5 | and a quaternion for full 3D orientation support. */ 6 | 7 | public class Fx2 8 | effect eff 9 | 10 | var orientation = IDENTITYQ 11 | var tint = COLOR_WHITE 12 | 13 | construct(vec3 pos, string path) 14 | eff = addEffect(path, pos) 15 | 16 | function setTint(colorA tint) 17 | this.tint = tint 18 | eff.setColor(tint) 19 | 20 | function setTintFromPlayer(player p) 21 | this.tint = p.getColor().toColor().withAlpha(255) 22 | eff.setColor(tint) 23 | 24 | function setOrientation(quat orientation) 25 | this.orientation = orientation 26 | eff.setOrientation(orientation) 27 | 28 | ondestroy 29 | eff.destr() 30 | -------------------------------------------------------------------------------- /wurst/event/DamageDetection.wurst: -------------------------------------------------------------------------------- 1 | package DamageDetection 2 | import OnUnitEnterLeave 3 | 4 | /** 5 | Use `addOnDamageFunc` to add a listener to any damage event that happens on the map. 6 | Example: 7 | 8 | init 9 | addOnDamageFunc() -> 10 | print("Damage Event with dmg: " + GetEventDamage().toString()) 11 | */ 12 | 13 | @configurable constant SWAP_TIMEOUT = 600. 14 | 15 | boolexpr array func 16 | int funcNext = 0 17 | trigger current = null 18 | trigger toDestroy = null 19 | 20 | constant filter = Filter(() -> addUnit(GetFilterUnit())) 21 | 22 | function addUnit(unit u) 23 | current.registerUnitEvent(u, EVENT_UNIT_DAMAGED) 24 | 25 | /** Add a Action to the damage Event. */ 26 | public function addOnDamageFunc(code c) 27 | let cf = Condition(c) 28 | current.addCondition(cf) 29 | func[funcNext] = cf 30 | funcNext++ 31 | 32 | /** Add a Condition(Action) to the damage Event. */ 33 | public function addOnDamageFunc(boolexpr cf) 34 | current.addCondition(cf) 35 | func[funcNext] = cf 36 | funcNext++ 37 | 38 | /** Disables the damage events */ 39 | public function disableDamageDetect() 40 | DisableTrigger(current) 41 | 42 | /** Enables the damage events */ 43 | public function enableDamageDetect() 44 | EnableTrigger(current) 45 | 46 | /** Periodic Cleanup-Function */ 47 | function swap() 48 | let isEnabled = IsTriggerEnabled(current) 49 | 50 | current.disable() 51 | if toDestroy != null 52 | toDestroy.destr() 53 | 54 | toDestroy = current 55 | current = CreateTrigger() 56 | 57 | if not isEnabled 58 | current.disable() 59 | 60 | ENUM_GROUP.enumUnitsAll(filter) 61 | 62 | for i = 0 to funcNext - 1 63 | current.addCondition(func[i]) 64 | 65 | init 66 | current = CreateTrigger() 67 | for i = 0 to funcNext - 1 68 | current.addCondition(func[i]) 69 | 70 | onEnter(() -> addUnit(getEnterLeaveUnit())) 71 | CreateTimer().startPeriodic(SWAP_TIMEOUT, function swap) 72 | -------------------------------------------------------------------------------- /wurst/event/OnUnitEnterLeave.wurst: -------------------------------------------------------------------------------- 1 | package OnUnitEnterLeave 2 | import NoWurst 3 | import Unit 4 | import Trigger 5 | import Group 6 | import Player 7 | import MagicFunctions 8 | import AbilityObjEditing 9 | import RegisterEvents 10 | import ObjectIdGenerator 11 | import MapBounds 12 | import ClosureTimers 13 | import Annotations 14 | import Orders 15 | 16 | /* Provides event API for units entering and leaving the map. 17 | The event will also fire for preplaced units on the map. 18 | 19 | NOTE: Any move applied to a unit as response to an enter event 20 | will fire the enter event again. */ 21 | 22 | let eventTrigger = CreateTrigger() 23 | let preplacedUnits = CreateGroup() 24 | 25 | /* Unit stack for nested usage */ 26 | unit array tempUnits 27 | var tempUnitsCount = 0 28 | 29 | function pushUnit(unit u) 30 | tempUnits[tempUnitsCount] = u 31 | tempUnitsCount++ 32 | 33 | function popUnit() 34 | tempUnitsCount-- 35 | 36 | /* @API */ 37 | 38 | /** Returns the unit that caused the enter/leave event to happen */ 39 | public function getEnterLeaveUnit() returns unit 40 | return tempUnits[tempUnitsCount - 1] 41 | 42 | /** Adds a callback that is run when a unit enters the map */ 43 | public function onEnter(code c) 44 | eventTrigger.addCondition(Filter(c)) 45 | 46 | /** Adds a callback that is run when a unit leaves the map */ 47 | public function onLeave(code c) 48 | eventTrigger.addAction(c) 49 | 50 | /* === Internals === */ 51 | constant ABILITY_ID = compiletime(ABIL_ID_GEN.next()) 52 | 53 | function prepareUnit(unit u) 54 | u..addAbility(ABILITY_ID)..makeAbilityPermanent(ABILITY_ID, true) 55 | pushUnit(u) 56 | eventTrigger.evaluate() 57 | popUnit() 58 | 59 | init 60 | // Make the ability invisible to the player 61 | for i = 0 to bj_MAX_PLAYER_SLOTS - 1 62 | players[i].setAbilityAvailable(ABILITY_ID, false) 63 | 64 | nullTimer() -> 65 | // Create the enter event 66 | CreateTrigger()..registerEnterRegion(boundRegion, Filter(() -> prepareUnit(GetFilterUnit()))) 67 | 68 | // Create the leave event 69 | registerPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER) -> 70 | let leavingUnit = GetTriggerUnit() 71 | if leavingUnit.getAbilityLevel(ABILITY_ID) == 0 and GetIssuedOrderId() == Orders.undefend 72 | pushUnit(leavingUnit) 73 | eventTrigger.execute() 74 | popUnit() 75 | 76 | // Process preplaced units 77 | preplacedUnits.enumUnitsInRect(boundRect) 78 | ForGroup(preplacedUnits, () -> prepareUnit(GetEnumUnit())) 79 | preplacedUnits..clear()..destr() 80 | 81 | @compiletime function generateAbility() 82 | new AbilityDefinitionDefend(ABILITY_ID) 83 | ..setName("Leave Detect") 84 | ..setEditorSuffix("(OnUnitEnterLeave)") 85 | ..setArtCaster("") 86 | ..setIconNormal("") 87 | ..setRace(Race.Unknown) 88 | -------------------------------------------------------------------------------- /wurst/event/RegisterEvents.wurst: -------------------------------------------------------------------------------- 1 | package RegisterEvents 2 | import NoWurst 3 | import _Handles 4 | import HashMap 5 | 6 | /*****PlayerUnitEvent*****/ 7 | 8 | trigger array t 9 | 10 | public function registerPlayerUnitEvent(playerunitevent p, code c) 11 | registerPlayerUnitEvent(p, null, c, null) 12 | 13 | public function registerPlayerUnitEvent(playerunitevent p, code filter, code c) 14 | registerPlayerUnitEvent(p, filter, c, null) 15 | 16 | public function registerPlayerUnitEvent(playerunitevent p, code filter, code condition, code action) 17 | let hid = p.getHandleId() 18 | if t[hid] == null 19 | t[hid] = CreateTrigger() 20 | for k = bj_MAX_PLAYER_SLOTS - 1 downto 0 21 | t[hid].registerPlayerUnitEvent(players[k], p, filter != null ? Filter(filter) : null) 22 | if condition != null 23 | t[hid].addCondition(Filter(condition)) 24 | if action != null 25 | t[hid].addAction(action) 26 | 27 | public function registerPlayerUnitEventForPlayer(playerunitevent p, code c, player pl) 28 | let i = bj_MAX_PLAYER_SLOTS * p.getHandleId() + pl.getId() 29 | if t[i] == null 30 | t[i] = CreateTrigger() 31 | t[i].registerPlayerUnitEvent(pl, p, null) 32 | t[i].addCondition(Filter(c)) 33 | 34 | public function getPlayerUnitEventTrigger(playerunitevent p) returns trigger 35 | return t[GetHandleId(p)] 36 | 37 | /*****PlayerEvent*****/ 38 | 39 | public function registerPlayerEvent(playerevent p, code c) 40 | registerPlayerEvent(p, c, null) 41 | 42 | public function registerPlayerEvent(playerevent p, code condition, code action) 43 | let hid = p.getHandleId() 44 | if t[hid] == null 45 | t[hid] = CreateTrigger() 46 | for k = bj_MAX_PLAYER_SLOTS - 1 downto 0 47 | t[hid].registerPlayerEvent(players[k], p) 48 | if condition != null 49 | t[hid].addCondition(Filter(condition)) 50 | if action != null 51 | t[hid].addAction(action) 52 | 53 | /*****SpellEffectEvent*****/ 54 | 55 | constant onCastMap = new HashMap() 56 | 57 | init 58 | registerPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, () -> (onCastMap.get(GetSpellAbilityId())).evaluate()) 59 | 60 | public function registerSpellEffectEvent(int abil, code onCast) 61 | if not onCastMap.has(abil) 62 | onCastMap.put(abil, CreateTrigger()) 63 | onCastMap.get(abil).addCondition(Filter(onCast)) 64 | -------------------------------------------------------------------------------- /wurst/file/FileIO.wurst: -------------------------------------------------------------------------------- 1 | package FileIO 2 | import public ChunkedString 3 | import ErrorHandling 4 | import SafetyChecks 5 | import AbilityObjEditing 6 | import ObjectIds 7 | 8 | /* FileIO for Patches 1.31 and up with chunked string support for big data. 9 | Reads and writes using preload files by manipulating default ability tooltips. 10 | 11 | Read a file: 12 | > let fileContent = new File("MyFileName.pld").readAsString() 13 | 14 | Write a file: 15 | > new File("MyFileName.pld")..write("MyContent")..close() 16 | */ 17 | 18 | @configurable public constant CHUNKS_PER_FILE = 64 19 | @configurable public constant FILE_IO_ABIL_ID = '$wsl' 20 | 21 | public var CAN_READ_FILES = true 22 | 23 | public constant FILE_IO_ABIL_ID_STR = FILE_IO_ABIL_ID.toRawCode() 24 | 25 | public class File 26 | var filename = "" 27 | 28 | construct(string filename) 29 | this.filename = filename 30 | 31 | function write(player p, string content) 32 | let buffer = new ChunkedString() 33 | buffer.append(content) 34 | write(p, buffer) 35 | destroy buffer 36 | 37 | function write(player p, ChunkedString buffer) 38 | if SAFETY_CHECKS_ENABLED 39 | for i = 0 to buffer.getChunkCount() - 1 40 | let str = buffer.getChunk(i) 41 | if validateInput(str) != null 42 | error("FileIO(" + filename + ") ERROR: Invalid character |cffffcc00" + validateInput(str) + "|r") 43 | 44 | writePreload(p, buffer) 45 | 46 | function close() 47 | destroy this 48 | 49 | function read(player p) returns ChunkedString 50 | let out = new ChunkedString(DEFAULT_CHUNK_SIZE) 51 | if localPlayer == p 52 | readPreload(out) 53 | return out 54 | 55 | function readAsString(player p) returns string 56 | let out = read(p) 57 | let str = out.getUnsafeString() 58 | destroy out 59 | return str 60 | 61 | private function writePreload(player p, ChunkedString buffer) 62 | if localPlayer == p 63 | PreloadGenClear() 64 | PreloadGenStart() 65 | 66 | if buffer.getChunkCount() >= CHUNKS_PER_FILE 67 | error("FileIO(" + filename + ") ERROR: String's chunk count exceeds the limit (" + CHUNKS_PER_FILE.toString() + ").|r") 68 | 69 | for i = 0 to buffer.getChunkCount() - 1 70 | let chunk = buffer.getChunk(i) 71 | Preload("\" )\ncall BlzSetAbilityTooltip('" + FILE_IO_ABIL_ID_STR + "', \"" + chunk + "\", " + i.toString() + ")\n//") 72 | 73 | Preload("\" )\nendfunction\nfunction a takes nothing returns nothing\n //") 74 | PreloadGenEnd(this.filename) 75 | 76 | private function readPreload(ChunkedString output) 77 | Preloader(this.filename) 78 | 79 | // Read the output 80 | for i = 0 to CHUNKS_PER_FILE - 1 81 | let chunk = BlzGetAbilityTooltip(FILE_IO_ABIL_ID, i) 82 | 83 | if chunk == " " 84 | break 85 | 86 | BlzSetAbilityTooltip(FILE_IO_ABIL_ID, " ", i) 87 | output.append(chunk) 88 | 89 | 90 | private static function validateInput(string content) returns string 91 | for char in content 92 | if (char == "\\") 93 | return char 94 | else if (char == "\"") 95 | return char 96 | return null 97 | 98 | init 99 | CAN_READ_FILES = new File("FileTester.pld")..write(localPlayer, "test").readAsString(localPlayer) == "test" 100 | 101 | 102 | @compiletime function generateDummy() 103 | let dummy = new AbilityDefinitionIllidanChannel(FILE_IO_ABIL_ID) 104 | ..setLevels(CHUNKS_PER_FILE) 105 | 106 | for i = 1 to CHUNKS_PER_FILE 107 | dummy.setTooltipNormal(i, " ") 108 | -------------------------------------------------------------------------------- /wurst/file/GameStatus.wurst: -------------------------------------------------------------------------------- 1 | package GameStatus 2 | 3 | public enum GameStatus 4 | UNKNOWN 5 | ONLINE 6 | OFFLINE 7 | REPLAY 8 | 9 | public var gameStatus = GameStatus.UNKNOWN 10 | 11 | init 12 | var firstPlayer = Player(0) 13 | while not firstPlayer.isIngame() 14 | firstPlayer = Player(firstPlayer.getId() + 1) 15 | 16 | // force the player to select a dummy unit 17 | let u = CreateUnit(firstPlayer, 'hfoo', 0, 0, 0) 18 | SelectUnit(u, true) 19 | let selected = IsUnitSelected(u, firstPlayer) 20 | RemoveUnit(u) 21 | 22 | if selected 23 | // detect if replay or offline game 24 | if (ReloadGameCachesFromDisk()) 25 | gameStatus = GameStatus.OFFLINE 26 | else 27 | gameStatus = GameStatus.REPLAY 28 | else 29 | // if the unit wasn't selected instantly, the game is online 30 | gameStatus = GameStatus.ONLINE 31 | -------------------------------------------------------------------------------- /wurst/file/SaveLoadData.wurst: -------------------------------------------------------------------------------- 1 | package SaveLoadData 2 | import public FileIO 3 | import SyncSimple 4 | import ErrorHandling 5 | import GameStatus 6 | 7 | /* Comfort wrapper for player based, synced access to save data. 8 | For small data you can use strings, otherwise use ChunkedString. 9 | 10 | Save data for a player: 11 | > myPlayer.saveData("myFileName", "someDataString") 12 | > myPlayer.saveData("myChunkedFileName", someChunkedString) 13 | 14 | Load data for a player: 15 | > myPlayer.loadData("myFileName") (status, data) -> 16 | > switch status 17 | > case SUCCESS 18 | > Log.info("Load successful. Chunks: " + data.getChunkCount()) 19 | > default 20 | > Log.error("Error occured: " + status.toString()) 21 | */ 22 | 23 | @configurable public constant READ_FILES_IN_REPLAYS = false 24 | 25 | public function LoadStatus.toString() returns string 26 | switch this 27 | case SUCCESS 28 | return "Success" 29 | case FAIL_PLAYER_OFFLINE 30 | return "Failed! Player is offline." 31 | case FAIL_FILE_EMPTY 32 | return "Failed! The file is empty." 33 | case FAIL_CANT_READ 34 | return "Failed! The player can't read files." 35 | 36 | public enum LoadStatus 37 | SUCCESS 38 | FAIL_PLAYER_OFFLINE 39 | FAIL_FILE_EMPTY 40 | FAIL_CANT_READ 41 | 42 | public function LoadStatus.isFail() returns boolean 43 | return this != LoadStatus.SUCCESS 44 | 45 | public interface LoadListener 46 | function onLoad(LoadStatus status, ChunkedString data) 47 | 48 | /** Blockingly saves the given data string for the given player. */ 49 | public function player.saveData(string slotName, string data) 50 | let buffer = new ChunkedString(DEFAULT_CHUNK_SIZE) 51 | buffer.append(data) 52 | this.saveData(slotName, buffer) 53 | destroy buffer 54 | 55 | /** Blockingly saves the given data string for the given player. */ 56 | public function player.saveData(string slotName, ChunkedString data) 57 | if not this.isIngame() 58 | error("Attempting to save data for player who isn't ingame! id: " + this.getId().toString()) 59 | 60 | new File(slotName.endsWith(".pld") ? slotName : slotName + ".pld") 61 | ..write(this, data)..close() 62 | 63 | /** Asynchronously loads the data from the file of the given player 64 | and then synchronizes it with all other players. 65 | The listener will be invoked with the synced data string. */ 66 | public function player.loadData(string slotName, LoadListener listener) 67 | if not this.isIngame() 68 | listener.onLoad(LoadStatus.FAIL_PLAYER_OFFLINE, null) 69 | return 70 | 71 | let file = new File(slotName.endsWith(".pld") ? slotName : slotName + ".pld") 72 | ChunkedString buffer 73 | if shouldReadFile() 74 | buffer = file.read(this) 75 | else 76 | buffer = new ChunkedString() 77 | file.close() 78 | 79 | buffer.sync(this) (syncedBuffer) -> 80 | if syncedBuffer.length() == 0 81 | listener.onLoad(LoadStatus.FAIL_FILE_EMPTY, syncedBuffer) 82 | else if syncedBuffer.readChunk() == "-" 83 | listener.onLoad(LoadStatus.FAIL_CANT_READ, syncedBuffer) 84 | else 85 | syncedBuffer.resetRead() 86 | listener.onLoad(LoadStatus.SUCCESS, syncedBuffer) 87 | 88 | 89 | function shouldReadFile() returns boolean 90 | return READ_FILES_IN_REPLAYS or gameStatus != REPLAY 91 | -------------------------------------------------------------------------------- /wurst/file/SyncSimple.wurst: -------------------------------------------------------------------------------- 1 | package SyncSimple 2 | import LinkedList 3 | import ChunkedString 4 | import GameStatus 5 | 6 | /* Simple String syncing for Patches 1.31 and up with chunked string support for big data. 7 | For improved safety, sync operations are performed sequentially - one string/chunk at a time. 8 | 9 | Sync a string: 10 | > myString.sync(p) (synced) -> 11 | > Log.info("string synced: " + synced.getUnsafeString()) 12 | 13 | Sync a ChunkedString: 14 | > myChunkedString.sync(p) (synced) -> 15 | > while synced.hasChunk() 16 | > Log.info("chunked string synced: " + synced.readChunk()) 17 | 18 | */ 19 | 20 | @configurable public constant DEFAULT_PREFIX = "S" 21 | @configurable public constant LAST_CHUNK_PREFIX = "T" 22 | @configurable public constant SYNC_IN_REPLAYS = false 23 | 24 | 25 | public interface BoolSyncListener 26 | function onDataSynced(bool data) 27 | 28 | public interface IntSyncListener 29 | function onDataSynced(int data) 30 | 31 | public interface RealSyncListener 32 | function onDataSynced(real data) 33 | 34 | public interface StringSyncListener 35 | function onDataSynced(string data) 36 | 37 | public interface BufferSyncListener 38 | function onDataSynced(ChunkedString buffer) 39 | 40 | constant syncQueue = new LinkedList 41 | 42 | /** Syncs a bool from the given player. */ 43 | public function bool.sync(player p, BoolSyncListener listener) 44 | this.toString().sync(p) data -> 45 | listener.onDataSynced(data.toBool()) 46 | destroy listener 47 | 48 | /** Syncs an int from the given player. */ 49 | public function int.sync(player p, IntSyncListener listener) 50 | this.toString().sync(p) data -> 51 | listener.onDataSynced(data.toInt()) 52 | destroy listener 53 | 54 | /** Syncs a real from the given player. */ 55 | public function real.sync(player p, RealSyncListener listener) 56 | this.toString().sync(p) data -> 57 | listener.onDataSynced(data.toReal()) 58 | destroy listener 59 | 60 | /** Syncs a single string from the given player. */ 61 | public function string.sync(player p, StringSyncListener listener) 62 | syncQueue.enqueue(new SyncData(p, listener, this)) 63 | 64 | if syncQueue.size() == 1 65 | checkQueue() 66 | 67 | /** Syncs a chunked string from the given player. */ 68 | public function ChunkedString.sync(player p, BufferSyncListener listener) 69 | syncQueue.enqueue(new SyncData(p, listener, this)) 70 | 71 | if syncQueue.size() == 1 and shouldSync() 72 | checkQueue() 73 | 74 | class SyncData 75 | StringSyncListener slistener = null 76 | BufferSyncListener blistener = null 77 | player syncer = null 78 | string data = null 79 | ChunkedString buffer = null 80 | ChunkedString syncBuffer = null 81 | 82 | construct(player syncer, StringSyncListener slistener, string data) 83 | this.slistener = slistener 84 | this.data = data 85 | this.syncer = syncer 86 | 87 | construct(player syncer, BufferSyncListener blistener, ChunkedString data) 88 | this.blistener = blistener 89 | this.buffer = data 90 | this.syncer = syncer 91 | this.syncBuffer = new ChunkedString() 92 | 93 | ondestroy 94 | if slistener != null 95 | destroy slistener 96 | if blistener != null 97 | destroy blistener 98 | if buffer != null 99 | destroy buffer 100 | 101 | function checkQueue() 102 | if not syncQueue.isEmpty() 103 | let syncData = syncQueue.getFirst() 104 | if syncData.data != null 105 | if localPlayer == syncData.syncer 106 | BlzSendSyncData(DEFAULT_PREFIX, syncData.data) 107 | else 108 | let data = syncData.buffer.readChunk() 109 | let hasChunk = syncData.buffer.hasChunk() 110 | if localPlayer == syncData.syncer 111 | if hasChunk 112 | BlzSendSyncData(DEFAULT_PREFIX, data) 113 | else 114 | BlzSendSyncData(LAST_CHUNK_PREFIX, data) 115 | 116 | 117 | function shouldSync() returns boolean 118 | return SYNC_IN_REPLAYS or gameStatus != REPLAY 119 | 120 | init 121 | let trig = CreateTrigger() 122 | for i = 0 to bj_MAX_PLAYER_SLOTS - 1 123 | BlzTriggerRegisterPlayerSyncEvent(trig, players[i], DEFAULT_PREFIX, false) 124 | BlzTriggerRegisterPlayerSyncEvent(trig, players[i], LAST_CHUNK_PREFIX, false) 125 | 126 | trig.addAction() -> 127 | let eventData = BlzGetTriggerSyncData() 128 | let eventPrefix = BlzGetTriggerSyncPrefix() 129 | let syncData = syncQueue.getFirst() 130 | if syncData.data != null 131 | syncData.slistener.onDataSynced(eventData) 132 | syncQueue.dequeue() 133 | destroy syncData 134 | else 135 | syncData.syncBuffer.append(eventData) 136 | if eventPrefix == LAST_CHUNK_PREFIX 137 | syncData.blistener.onDataSynced(syncData.syncBuffer) 138 | syncQueue.dequeue() 139 | destroy syncData 140 | 141 | if shouldSync() 142 | checkQueue() 143 | -------------------------------------------------------------------------------- /wurst/math/Angle.wurst: -------------------------------------------------------------------------------- 1 | package Angle 2 | import NoWurst 3 | import Wurstunit 4 | 5 | /** Converts Degrees to Radians */ 6 | public constant DEGTORAD = 0.017453293 7 | /** Converts Radians to Degrees */ 8 | public constant RADTODEG = 57.295779513 9 | 10 | /** an angle is just a wrapper around a real which allows for 11 | typesafe APIs without the confusion between radians and degrees*/ 12 | public tuple angle(real radians) 13 | 14 | /** get the real representation of this angle in degrees */ 15 | public function angle.degrees() returns real 16 | return this.radians * RADTODEG 17 | 18 | /** get the real representation of this angle in radians */ 19 | public function angle.radians() returns real 20 | return this.radians 21 | 22 | /** interpret this real as an angle given in degrees */ 23 | public function real.asAngleDegrees() returns angle 24 | return angle(this * DEGTORAD) 25 | 26 | /** interpret this real as an angle given in degrees */ 27 | public function real.fromDeg() returns angle 28 | return angle(this * DEGTORAD) 29 | 30 | /** interpret this real as an angle given in degrees */ 31 | public function real.fromRad() returns angle 32 | return angle(this) 33 | 34 | /** interpret this int as an angle given in degrees */ 35 | public function int.fromRad() returns angle 36 | return angle(this * 1.) 37 | 38 | /** interpret this int as an angle given in radians */ 39 | public function int.asAngleRadians() returns angle 40 | return angle(this * 1.) 41 | 42 | /** interpret this real as an angle given in radians*/ 43 | public function real.asAngleRadians() returns angle 44 | return angle(this) 45 | 46 | /** get the sine of this angle */ 47 | public function angle.sin() returns real 48 | return Sin(this.radians) 49 | 50 | /** get the cosine of this angle */ 51 | public function angle.cos() returns real 52 | return Cos(this.radians) 53 | 54 | /** get the tangent of this angle */ 55 | public function angle.tan() returns real 56 | return Tan(this.radians) 57 | 58 | /** adds two angles */ 59 | public function angle.op_plus(angle other) returns angle 60 | return angle(this.radians + other.radians) 61 | 62 | /** substracts two angles */ 63 | public function angle.op_minus(angle other) returns angle 64 | return angle(this.radians - other.radians) 65 | 66 | /** multiplies the angle with the given scalar */ 67 | public function angle.op_mult(real scalar) returns angle 68 | return angle(this.radians * scalar) 69 | 70 | /** multiplies with the given angle */ 71 | public function real.op_mult(angle ang) returns angle 72 | return angle(ang.radians * this) 73 | 74 | /** divides the angle by the given scalar */ 75 | public function angle.op_divReal(real scalar) returns angle 76 | return angle(this.radians / scalar) 77 | 78 | @Test function testAngle() 79 | (180 .fromDeg().radians).assertEquals(180 .fromDeg().degrees().fromDeg().radians) 80 | 81 | (180 .fromDeg() + 90 .fromDeg()).degrees().assertEquals(270) 82 | 83 | (0 .fromDeg()).sin().assertEquals(0) 84 | (90 .fromDeg()).sin().assertEquals(1) 85 | -------------------------------------------------------------------------------- /wurst/math/BigNum.wurst: -------------------------------------------------------------------------------- 1 | package BigNum 2 | // Credits: Pipedream 3 | //prefer algebraic approach because of real subtraction issues 4 | public function log(real py, real pbase) returns real 5 | real y = py 6 | real base = pbase 7 | real factor = 1.0 8 | real logy = 0.0 9 | real sign = 1.0 10 | if y < 0. 11 | return 0.0 12 | 13 | if y < 1. 14 | y = 1.0 / y 15 | sign = -1.0 16 | 17 | //Chop out powers of the base 18 | while y >= 1.0001 19 | if y > base 20 | y = y / base 21 | logy = logy + factor 22 | else 23 | base = SquareRoot(base) //If you use just one base a lot, precompute its squareroots 24 | factor = factor / 2. 25 | 26 | return sign * logy 27 | 28 | public class BigNum_l 29 | int leaf = 0 30 | BigNum_l next = null 31 | static int count = 0 32 | 33 | construct() 34 | count++ 35 | 36 | ondestroy 37 | count-- 38 | 39 | //true: want destroy 40 | function clean() returns boolean 41 | if next == null and leaf == 0 42 | return true 43 | else if next != null and next.clean() 44 | destroy next 45 | next = null 46 | return leaf == 0 47 | else 48 | return false 49 | 50 | 51 | function divSmall (int base, int denom) returns int 52 | int remainder = 0 53 | if next != null 54 | remainder = next.divSmall(base,denom) 55 | 56 | let num = leaf + remainder * base 57 | let quotient = num div denom 58 | remainder = num - quotient * denom 59 | leaf = quotient 60 | return remainder 61 | 62 | 63 | public class BigNum 64 | BigNum_l list = null 65 | int base 66 | 67 | construct(int base) 68 | this.base = base 69 | 70 | ondestroy 71 | BigNum_l cur = list 72 | BigNum_l next 73 | while cur != null 74 | next = cur.next 75 | destroy cur 76 | cur = next 77 | 78 | function isZero() returns boolean 79 | BigNum_l cur = list 80 | while cur != null 81 | if cur.leaf != 0 82 | return false 83 | cur = cur.next 84 | return true 85 | 86 | 87 | function clean() 88 | list.clean() 89 | 90 | 91 | //fails if bignum is null 92 | //BASE() + carry must be less than MAXINT() 93 | function addSmall(int pcarry) 94 | BigNum_l cur = list 95 | int sum 96 | int carry = pcarry 97 | 98 | if cur == null 99 | cur = new BigNum_l() 100 | list = cur 101 | 102 | 103 | while carry != 0 104 | sum = cur.leaf + carry 105 | carry = sum div base 106 | sum = sum - carry * base 107 | cur.leaf = sum 108 | 109 | if cur.next == null 110 | cur.next = new BigNum_l() 111 | 112 | cur = cur.next 113 | 114 | 115 | // x * BASE() must be less than MAXINT() 116 | function mulSmall(int x) 117 | BigNum_l cur = list 118 | int product 119 | int remainder 120 | int carry = 0 121 | while cur != null or carry != 0 122 | product = x * cur.leaf + carry 123 | carry = product div base 124 | remainder = product - carry * base 125 | cur.leaf = remainder 126 | if cur.next == null and carry != 0 127 | cur.next = new BigNum_l() 128 | 129 | cur = cur.next 130 | 131 | //Returns remainder 132 | function divSmall(int denom) returns int 133 | return list.divSmall(base,denom) 134 | 135 | function lastDigit() returns int 136 | BigNum_l cur = list 137 | BigNum_l next = cur.next 138 | while next != null 139 | cur = next 140 | next = cur.next 141 | 142 | return cur.leaf 143 | 144 | function dump() returns string 145 | BigNum_l cur = this.list 146 | string s = "" 147 | while cur != null 148 | s = I2S(cur.leaf)+" "+s 149 | cur = cur.next 150 | return s 151 | 152 | -------------------------------------------------------------------------------- /wurst/math/BitwiseInit.wurst: -------------------------------------------------------------------------------- 1 | package BitwiseInit 2 | 3 | public int array lbyte 4 | public int array rbyte 5 | 6 | public constant andData = [ 7 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 8 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 9 | 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 10 | 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 11 | 0x0, 0x0, 0x2, 0x2, 0x0, 0x0, 0x2, 0x2, 12 | 0x0, 0x0, 0x2, 0x2, 0x0, 0x0, 0x2, 0x2, 13 | 0x0, 0x1, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 14 | 0x0, 0x1, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 15 | 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 16 | 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 17 | 0x0, 0x1, 0x0, 0x1, 0x4, 0x5, 0x4, 0x5, 18 | 0x0, 0x1, 0x0, 0x1, 0x4, 0x5, 0x4, 0x5, 19 | 0x0, 0x0, 0x2, 0x2, 0x4, 0x4, 0x6, 0x6, 20 | 0x0, 0x0, 0x2, 0x2, 0x4, 0x4, 0x6, 0x6, 21 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 22 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 23 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 24 | 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 25 | 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 26 | 0x8, 0x9, 0x8, 0x9, 0x8, 0x9, 0x8, 0x9, 27 | 0x0, 0x0, 0x2, 0x2, 0x0, 0x0, 0x2, 0x2, 28 | 0x8, 0x8, 0xa, 0xa, 0x8, 0x8, 0xa, 0xa, 29 | 0x0, 0x1, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 30 | 0x8, 0x9, 0xa, 0xb, 0x8, 0x9, 0xa, 0xb, 31 | 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 32 | 0x8, 0x8, 0x8, 0x8, 0xc, 0xc, 0xc, 0xc, 33 | 0x0, 0x1, 0x0, 0x1, 0x4, 0x5, 0x4, 0x5, 34 | 0x8, 0x9, 0x8, 0x9, 0xc, 0xd, 0xc, 0xd, 35 | 0x0, 0x0, 0x2, 0x2, 0x4, 0x4, 0x6, 0x6, 36 | 0x8, 0x8, 0xa, 0xa, 0xc, 0xc, 0xe, 0xe, 37 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 38 | 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf] 39 | 40 | public constant orData = [ 41 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 42 | 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 43 | 0x1, 0x1, 0x3, 0x3, 0x5, 0x5, 0x7, 0x7, 44 | 0x9, 0x9, 0xb, 0xb, 0xd, 0xd, 0xf, 0xf, 45 | 0x2, 0x3, 0x2, 0x3, 0x6, 0x7, 0x6, 0x7, 46 | 0xa, 0xb, 0xa, 0xb, 0xe, 0xf, 0xe, 0xf, 47 | 0x3, 0x3, 0x3, 0x3, 0x7, 0x7, 0x7, 0x7, 48 | 0xb, 0xb, 0xb, 0xb, 0xf, 0xf, 0xf, 0xf, 49 | 0x4, 0x5, 0x6, 0x7, 0x4, 0x5, 0x6, 0x7, 50 | 0xc, 0xd, 0xe, 0xf, 0xc, 0xd, 0xe, 0xf, 51 | 0x5, 0x5, 0x7, 0x7, 0x5, 0x5, 0x7, 0x7, 52 | 0xd, 0xd, 0xf, 0xf, 0xd, 0xd, 0xf, 0xf, 53 | 0x6, 0x7, 0x6, 0x7, 0x6, 0x7, 0x6, 0x7, 54 | 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 55 | 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 56 | 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 57 | 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 58 | 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 59 | 0x9, 0x9, 0xb, 0xb, 0xd, 0xd, 0xf, 0xf, 60 | 0x9, 0x9, 0xb, 0xb, 0xd, 0xd, 0xf, 0xf, 61 | 0xa, 0xb, 0xa, 0xb, 0xe, 0xf, 0xe, 0xf, 62 | 0xa, 0xb, 0xa, 0xb, 0xe, 0xf, 0xe, 0xf, 63 | 0xb, 0xb, 0xb, 0xb, 0xf, 0xf, 0xf, 0xf, 64 | 0xb, 0xb, 0xb, 0xb, 0xf, 0xf, 0xf, 0xf, 65 | 0xc, 0xd, 0xe, 0xf, 0xc, 0xd, 0xe, 0xf, 66 | 0xc, 0xd, 0xe, 0xf, 0xc, 0xd, 0xe, 0xf, 67 | 0xd, 0xd, 0xf, 0xf, 0xd, 0xd, 0xf, 0xf, 68 | 0xd, 0xd, 0xf, 0xf, 0xd, 0xd, 0xf, 0xf, 69 | 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 70 | 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 71 | 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 72 | 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf] 73 | 74 | public constant xorData = [ 75 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 76 | 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 77 | 0x1, 0x0, 0x3, 0x2, 0x5, 0x4, 0x7, 0x6, 78 | 0x9, 0x8, 0xb, 0xa, 0xd, 0xc, 0xf, 0xe, 79 | 0x2, 0x3, 0x0, 0x1, 0x6, 0x7, 0x4, 0x5, 80 | 0xa, 0xb, 0x8, 0x9, 0xe, 0xf, 0xc, 0xd, 81 | 0x3, 0x2, 0x1, 0x0, 0x7, 0x6, 0x5, 0x4, 82 | 0xb, 0xa, 0x9, 0x8, 0xf, 0xe, 0xd, 0xc, 83 | 0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3, 84 | 0xc, 0xd, 0xe, 0xf, 0x8, 0x9, 0xa, 0xb, 85 | 0x5, 0x4, 0x7, 0x6, 0x1, 0x0, 0x3, 0x2, 86 | 0xd, 0xc, 0xf, 0xe, 0x9, 0x8, 0xb, 0xa, 87 | 0x6, 0x7, 0x4, 0x5, 0x2, 0x3, 0x0, 0x1, 88 | 0xe, 0xf, 0xc, 0xd, 0xa, 0xb, 0x8, 0x9, 89 | 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, 90 | 0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 91 | 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 92 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 93 | 0x9, 0x8, 0xb, 0xa, 0xd, 0xc, 0xf, 0xe, 94 | 0x1, 0x0, 0x3, 0x2, 0x5, 0x4, 0x7, 0x6, 95 | 0xa, 0xb, 0x8, 0x9, 0xe, 0xf, 0xc, 0xd, 96 | 0x2, 0x3, 0x0, 0x1, 0x6, 0x7, 0x4, 0x5, 97 | 0xb, 0xa, 0x9, 0x8, 0xf, 0xe, 0xd, 0xc, 98 | 0x3, 0x2, 0x1, 0x0, 0x7, 0x6, 0x5, 0x4, 99 | 0xc, 0xd, 0xe, 0xf, 0x8, 0x9, 0xa, 0xb, 100 | 0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3, 101 | 0xd, 0xc, 0xf, 0xe, 0x9, 0x8, 0xb, 0xa, 102 | 0x5, 0x4, 0x7, 0x6, 0x1, 0x0, 0x3, 0x2, 103 | 0xe, 0xf, 0xc, 0xd, 0xa, 0xb, 0x8, 0x9, 104 | 0x6, 0x7, 0x4, 0x5, 0x2, 0x3, 0x0, 0x1, 105 | 0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 106 | 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0] 107 | 108 | public int array powShift 109 | 110 | public function initLbyte() 111 | var i16 = 0x0 112 | var i256 = 0x0 113 | while i256 < 0x100 114 | var i = 0 115 | while i < 0x10 116 | lbyte[i256] = i16 117 | i256++ 118 | i++ 119 | i16++ 120 | 121 | public function initRbyte() 122 | var i256 = 0x0 123 | while i256 < 0x100 124 | var i16 = 0 125 | while i16 < 0x10 126 | rbyte[i256] = i16 127 | i256++ 128 | i16++ 129 | 130 | public function initShift() 131 | var bit = 0 132 | var pow = 1 133 | while bit <= 0x20 134 | powShift[bit] = pow 135 | pow *= 2 136 | bit++ 137 | 138 | @compiletime 139 | function initBitwise() 140 | initLbyte() 141 | initRbyte() 142 | initShift() 143 | 144 | init 145 | initBitwise() 146 | -------------------------------------------------------------------------------- /wurst/math/BitwiseTests.wurst: -------------------------------------------------------------------------------- 1 | package BitwiseTests 2 | import Bitwise 3 | 4 | @test 5 | function byteTestsNegative() 6 | let bytes = 0xFABCDE10.bytes() 7 | 8 | bytes.b1.assertEquals(0x10) 9 | bytes.b2.assertEquals(0xDE) 10 | bytes.b3.assertEquals(0xBC) 11 | bytes.b4.assertEquals(0xFA) 12 | 13 | @test 14 | function byteTestsPositive() 15 | let bytes = 0x0FBCDE10.bytes() 16 | 17 | bytes.b1.assertEquals(0x10) 18 | bytes.b2.assertEquals(0xDE) 19 | bytes.b3.assertEquals(0xBC) 20 | bytes.b4.assertEquals(0x0F) 21 | 22 | @test 23 | function bitStringTests() 24 | "1".fromBitString().assertEquals(1) 25 | "01".fromBitString().assertEquals(1) 26 | "0001".fromBitString().assertEquals(1) 27 | "1000".fromBitString().assertEquals(8) 28 | "1001".fromBitString().assertEquals(9) 29 | "10000000 00000000 00000000 00000000".fromBitString().assertEquals(INT_MIN) 30 | "11111111 11111111 11111111 11111111".fromBitString().assertEquals(-1) 31 | 32 | @test 33 | function shiftTests() 34 | 0xFF.shiftl(4).assertEquals(0xFF0) 35 | 0xFF.shiftl(8).assertEquals(0xFF00) 36 | 0xFF.shiftl(12).assertEquals(0xFF000) 37 | 0xFF.shiftl(16).assertEquals(0xFF0000) 38 | 0xFF.shiftl(24).assertEquals(0xFF000000) 39 | 0xFF00FF.shiftr(4).assertEquals(0xFF00F) 40 | 0xFF00FF.shiftr(8).assertEquals(0xFF00) 41 | 0xFF00FF.shiftr(12).assertEquals(0xFF0) 42 | 0xFF00FF.shiftr(16).assertEquals(0xFF) 43 | 0xFF0000FF.shiftr(16).assertEquals(0xFF00) 44 | 0xFF0000FF.shiftr(24).assertEquals(0xFF) 45 | 46 | @test 47 | function tests32() 48 | "1001 0001 0100 0010".fromBitString().and32("1000 0100 0010 0110".fromBitString()).assertEquals("1000 0000 0000 0010".fromBitString()) 49 | "1000 0000 0000 0000 1001 0001 0100 0010".fromBitString().and32("1000 0000 0000 0000 1000 0100 0010 0110".fromBitString()).assertEquals("1000 0000 0000 0000 1000 0000 0000 0010".fromBitString()) 50 | "1001 0001 0100 0010".fromBitString().or32("1000 0100 0010 0110".fromBitString()).assertEquals("1001 0101 0110 0110".fromBitString()) 51 | "1000 0000 0000 0000 1001 0001 0100 0010".fromBitString().or32("1000 0000 0000 0000 1000 0100 0010 0110".fromBitString()).assertEquals("1000 0000 0000 0000 1001 0101 0110 0110".fromBitString()) 52 | "1001 0001 0100 0010".fromBitString().xor32("1000 0100 0010 0110".fromBitString()).assertEquals("0001 0101 0110 0100".fromBitString()) 53 | "1000 0000 0000 0000 1001 0001 0100 0010".fromBitString().xor32("1000 0000 0000 0000 1000 0100 0010 0110".fromBitString()).assertEquals("0000 0000 0000 0000 0001 0101 0110 0100".fromBitString()) 54 | "1000 0000 0000 0000 1001 0001 0100 0010".fromBitString().xor32("0000 0000 0000 0000 1000 0100 0010 0110".fromBitString()).assertEquals("1000 0000 0000 0000 0001 0101 0110 0100".fromBitString()) 55 | -------------------------------------------------------------------------------- /wurst/math/Maths.wurst: -------------------------------------------------------------------------------- 1 | package Maths 2 | import NoWurst 3 | import public Angle 4 | import Real 5 | import Integer 6 | import Wurstunit 7 | 8 | /** Euler's constant */ 9 | public constant real EULER = 2.718281828 10 | /** PI constant */ 11 | public constant real PI = 3.141592654 12 | /** PI * 2 constant */ 13 | public constant real PI2 = 6.28318 14 | /** PI / 2 constant */ 15 | public constant real PIHALF = 1.570796326 16 | /** 17 | * Returns the length of the hypotenuse of a right-angle triangle with the 18 | * given side lengths. Does not overflow or underflow as fast as 19 | * SquareRoot(x^2 + y^2) does. 20 | */ 21 | public function hypot(real dx, real dy) returns real 22 | real x = dx.abs() 23 | let y = dy.abs() 24 | real t = 0 25 | if x > y 26 | t = y / x 27 | else if x < y 28 | t = x / y 29 | x = y 30 | return x * SquareRoot(1 + t * t) 31 | 32 | /** Returns the angle of a height-slope */ 33 | public function getSlopeAngle(real z1, real z2, real dist) returns angle 34 | return angle(Atan2(z2 - z1, dist)) 35 | 36 | public function int.moduloInt(int divisor) returns int 37 | return this - (this div divisor) * divisor 38 | 39 | /** Returns the largest value in the set provided as arguments. 40 | Example: max(6, 4, -12) // Returns 6. */ 41 | public function max(vararg int numbers) returns int 42 | var maxNumber = INT_MIN 43 | for number in numbers 44 | maxNumber = number > maxNumber ? number : maxNumber 45 | return maxNumber 46 | 47 | /** Returns the largest value in the set provided as arguments. 48 | Example: max(6.5, 4.5, -12.5) // Returns 6.5. */ 49 | public function max(vararg real numbers) returns real 50 | var maxNumber = REAL_MIN 51 | for number in numbers 52 | maxNumber = number > maxNumber ? number : maxNumber 53 | return maxNumber 54 | 55 | /** Returns the smallest value in the set provided as arguments. 56 | Example: min(6, 4, -12) // Returns -12. */ 57 | public function min(vararg int numbers) returns int 58 | var minNumber = INT_MAX 59 | for number in numbers 60 | minNumber = number < minNumber ? number : minNumber 61 | return minNumber 62 | 63 | /** Returns the smallest value in the set provided as arguments. 64 | Example: min(6.5, 4.5, -12.5) // Returns -12,5. */ 65 | public function min(vararg real numbers) returns real 66 | var minNumber = REAL_MAX 67 | for number in numbers 68 | minNumber = number < minNumber ? number : minNumber 69 | return minNumber 70 | 71 | @test function minmax() 72 | let three = 3 73 | max(three,4,21,43,51,30).assertEquals(51) 74 | max(3.,4.,5.).assertEquals(5) 75 | max(-4,three).assertEquals(3) 76 | min(three,4,0,-2,-1).assertEquals(-2) 77 | min(3.,4.,-20.,-3.).assertEquals(-20.) 78 | 79 | @test function test_hypot() 80 | test_hypot_help(5,6) 81 | test_hypot_help(5000000.,6) 82 | test_hypot_help(3.,123456789.) 83 | test_hypot_help(543532403425324.,123456789.) 84 | test_hypot_help(0.0000001,0.0000000000023) 85 | test_hypot_help(0,0) 86 | 87 | 88 | function test_hypot_help(real x, real y) 89 | hypot(x,y).assertEquals(SquareRoot(x * x + y * y), 0.0001) 90 | 91 | -------------------------------------------------------------------------------- /wurst/objediting/DestructableObjEditing.wurst: -------------------------------------------------------------------------------- 1 | package DestructableObjEditing 2 | import ObjEditingNatives 3 | import NoWurst 4 | 5 | public class W3BDefinition 6 | ObjectDefinition def 7 | 8 | protected int newId 9 | protected int baseId 10 | 11 | function getNewId() returns int 12 | return newId 13 | 14 | function getBaseId() returns int 15 | return baseId 16 | 17 | construct(int newId, int baseId) 18 | this.newId = newId 19 | this.baseId = baseId 20 | def = createObjectDefinition("w3b", newId, baseId) 21 | 22 | public class DestructableDefinition extends W3BDefinition 23 | construct(int newID,int origID) 24 | super(newID,origID) 25 | 26 | function setTexture(string data) 27 | def.setString("btxf",data) 28 | 29 | function setTextureId(int data) 30 | def.setInt("btxi",data) 31 | 32 | function setCategory(string data) 33 | def.setString("bcat",data) 34 | 35 | function setName(string data) 36 | def.setString("bnam",data) 37 | 38 | function setPath(string data) 39 | def.setString("bptx",data) 40 | 41 | function setModel(string data) 42 | def.setString("bfil",data) 43 | 44 | function setSoundOnDestroy(string data) 45 | def.setString("bdsn",data) 46 | -------------------------------------------------------------------------------- /wurst/objediting/ItemObjEditing.wurst: -------------------------------------------------------------------------------- 1 | package ItemObjEditing 2 | import ObjEditingNatives 3 | import UnitObjEditing 4 | import NoWurst 5 | 6 | public class W3TDefinition 7 | ObjectDefinition def 8 | 9 | protected int newId 10 | protected int baseId 11 | 12 | function getNewId() returns int 13 | return newId 14 | 15 | function getBaseId() returns int 16 | return baseId 17 | 18 | construct(int newId, int baseId) 19 | this.newId = newId 20 | this.baseId = baseId 21 | def = createObjectDefinition("w3t", newId, baseId) 22 | 23 | function setTooltipExtended(string data) 24 | def.setString("utub", data) 25 | 26 | function setTooltipBasic(string data) 27 | def.setString("utip", data) 28 | 29 | function setRequirementsLevels(string data) 30 | def.setString("urqa", data) 31 | 32 | function setRequirements(string data) 33 | def.setString("ureq", data) 34 | 35 | function setName(string data) 36 | def.setString("unam", data) 37 | 38 | function setHotkey(string data) 39 | def.setString("uhot", data) 40 | 41 | function setDescription(string data) 42 | def.setString("ides", data) 43 | 44 | function setButtonPositionY(int data) 45 | def.setInt("ubpy", data) 46 | 47 | function setButtonPositionX(int data) 48 | def.setInt("ubpx", data) 49 | 50 | public class ItemDefinition extends W3TDefinition 51 | 52 | construct(int newId, int origId) 53 | super(newId, origId) 54 | 55 | function setAbilities(string data) 56 | def.setString("iabi", data) 57 | 58 | function setArmorType(ArmorType data) 59 | def.setString("iarm", data.toObjectString()) 60 | 61 | function setClassification(string data) 62 | def.setString("icla", data) 63 | 64 | function setTintingColor3Blue(int data) 65 | def.setInt("iclb", data) 66 | 67 | function setTintingColor2Green(int data) 68 | def.setInt("iclg", data) 69 | 70 | function setTintingColor1Red(int data) 71 | def.setInt("iclr", data) 72 | 73 | function setCooldownGroup(string data) 74 | def.setString("icid", data) 75 | 76 | function setDroppedWhenCarrierDies(boolean data) 77 | def.setBoolean("idrp", data) 78 | 79 | function setCanBeDropped(boolean data) 80 | def.setBoolean("idro", data) 81 | 82 | function setModelUsed(string data) 83 | def.setString("ifil", data) 84 | 85 | function setGoldCost(int data) 86 | def.setInt("igol", data) 87 | 88 | function setHitPoints(int data) 89 | def.setInt("ihtp", data) 90 | 91 | function setIgnoreCooldown(boolean data) 92 | def.setBoolean("iicd", data) 93 | 94 | function setLevel(int data) 95 | def.setInt("ilev", data) 96 | 97 | function setLumberCost(int data) 98 | def.setInt("ilum", data) 99 | 100 | function setValidTargetForTransformation(boolean data) 101 | def.setBoolean("imor", data) 102 | 103 | function setLevelUnclassified(int data) 104 | def.setInt("ilvo", data) 105 | 106 | function setPerishable(boolean data) 107 | def.setBoolean("iper", data) 108 | 109 | function setIncludeAsRandomChoice(boolean data) 110 | def.setBoolean("iprn", data) 111 | 112 | function setUseAutomaticallyWhenAcquired(boolean data) 113 | def.setBoolean("ipow", data) 114 | 115 | function setPriority(int data) 116 | def.setInt("ipri", data) 117 | 118 | function setScalingValue(real data) 119 | def.setReal("isca", data) 120 | 121 | function setCanBeSoldByMerchants(boolean data) 122 | def.setBoolean("isel", data) 123 | 124 | function setCanBeSoldToMerchants(boolean data) 125 | def.setBoolean("ipaw", data) 126 | 127 | function setStockMaximum(int data) 128 | def.setInt("isto", data) 129 | 130 | function setStockReplenishInterval(int data) 131 | def.setInt("istr", data) 132 | 133 | function setStockStartDelay(int data) 134 | def.setInt("isst", data) 135 | 136 | function setActivelyUsed(boolean data) 137 | def.setBoolean("iusa", data) 138 | 139 | function setNumberofCharges(int data) 140 | def.setInt("iuse", data) 141 | 142 | function setInterfaceIcon(string data) 143 | def.setString("iico", data) 144 | 145 | function setMaxStack(int data) 146 | def.setInt("ista", data) 147 | -------------------------------------------------------------------------------- /wurst/objediting/ObjEditingNatives.wurst: -------------------------------------------------------------------------------- 1 | package ObjEditingNatives 2 | import NoWurst 3 | import Boolean 4 | import Annotations 5 | 6 | public interface StringLevelClosure 7 | function run(int lvl) returns string 8 | 9 | public interface IntLevelClosure 10 | function run(int lvl) returns int 11 | 12 | public interface RealLevelClosure 13 | function run(int lvl) returns real 14 | 15 | public interface BooleanLevelClosure 16 | function run(int lvl) returns boolean 17 | 18 | 19 | public tuple ObjectDefinition(string key) 20 | 21 | // creates a new unit objectdefinition 22 | // for example to create a new unit with id "h000" which is based on a footman ("hfoo") 23 | // let u = createObjectDefinition("w3u", "h000", "hfoo") 24 | @compiletimenative public function createObjectDefinition(string fileType, int newId, int deriveFrom) returns ObjectDefinition 25 | return ObjectDefinition("") // dummy implementation 26 | 27 | // changes an int value of an objectdefinition 28 | @compiletimenative public function ObjectDefinition.setInt(string modification, int value) 29 | 30 | public function ObjectDefinition.setBoolean(string modification, boolean value) 31 | this.setInt(modification, value.toInt()) 32 | 33 | // changes a string value of an objectdefinition 34 | @compiletimenative public function ObjectDefinition.setString(string modification, string value) 35 | 36 | // changes a real of an objectdefinition 37 | @compiletimenative public function ObjectDefinition.setReal(string modification, real value) 38 | 39 | // changes a unreal (real between 0.0 and 1.0) of an objectdefinition 40 | @compiletimenative public function ObjectDefinition.setUnreal(string modification, real value) 41 | 42 | // changes an int value of an objectdefinition 43 | @compiletimenative public function ObjectDefinition.setLvlInt(string modification, int lvl, int value) 44 | 45 | // changes a string value of an objectdefinition 46 | @compiletimenative public function ObjectDefinition.setLvlString(string modification, int lvl, string value) 47 | 48 | // changes a real of an objectdefinition 49 | @compiletimenative public function ObjectDefinition.setLvlReal(string modification, int lvl, real value) 50 | 51 | // changes a unreal (real between 0.0 and 1.0) of an objectdefinition 52 | @compiletimenative public function ObjectDefinition.setLvlUnreal(string modification, int lvl, real value) 53 | 54 | // changes an int value of an objectdefinition 55 | @compiletimenative public function ObjectDefinition.setLvlDataInt(string modification, int lvl, int dataPointer, int value) 56 | 57 | // changes a string value of an objectdefinition 58 | @compiletimenative public function ObjectDefinition.setLvlDataString(string modification, int lvl, int dataPointer, string value) 59 | 60 | // changes a real of an objectdefinition 61 | @compiletimenative public function ObjectDefinition.setLvlDataReal(string modification, int lvl, int dataPointer, real value) 62 | 63 | // changes a unreal (real between 0.0 and 1.0) of an objectdefinition 64 | @compiletimenative public function ObjectDefinition.setLvlDataUnreal(string modification, int lvl, int dataPointer, real value) 65 | 66 | public function ObjectDefinition.setLvlDataBoolean(string modification, int lvl, int dataPointer, boolean value) 67 | if value 68 | this.setLvlDataInt(modification, lvl, dataPointer, 1) 69 | else 70 | this.setLvlDataInt(modification, lvl, dataPointer, 0) 71 | 72 | public function ObjectDefinition.setLevelsDataString(string modification, int maxLevel, int dataPointer, StringLevelClosure lc) 73 | for i = 1 to maxLevel 74 | this.setLvlDataString(modification, i, dataPointer, lc.run(i)) 75 | 76 | public function ObjectDefinition.setLevelsDataInt(string modification, int maxLevel, int dataPointer, IntLevelClosure lc) 77 | for i = 1 to maxLevel 78 | this.setLvlDataInt(modification, i, dataPointer, lc.run(i)) 79 | 80 | public function ObjectDefinition.setLevelsDataUnreal(string modification, int maxLevel, int dataPointer, RealLevelClosure lc) 81 | for i = 1 to maxLevel 82 | this.setLvlDataUnreal(modification, i, dataPointer, lc.run(i)) 83 | 84 | public function ObjectDefinition.setLevelsDataReal(string modification, int maxLevel, int dataPointer, RealLevelClosure lc) 85 | for i = 1 to maxLevel 86 | this.setLvlDataReal(modification, i, dataPointer, lc.run(i)) 87 | 88 | public function ObjectDefinition.setLevelsDataBoolean(string modification, int maxLevel, int dataPointer, BooleanLevelClosure lc) 89 | for i = 1 to maxLevel 90 | this.setLvlDataBoolean(modification, i, dataPointer, lc.run(i)) 91 | -------------------------------------------------------------------------------- /wurst/objediting/ObjectIds.wurst: -------------------------------------------------------------------------------- 1 | package ObjectIds 2 | import NoWurst 3 | import ErrorHandling 4 | import Wurstunit 5 | import AbilityIds 6 | import LinkedList 7 | import TypeCasting 8 | 9 | constant CHARMAP = ".................................!.#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................." 10 | 11 | /** Convert a integer id value into a 4-letter id code. */ 12 | public function toRawCode(int value) returns string 13 | var result = "" 14 | var remainingValue = value 15 | 16 | for int byteno = 0 to 3 17 | let charValue = remainingValue mod 256 18 | remainingValue = remainingValue div 256 19 | result = CHARMAP.charAt(charValue) + result 20 | 21 | return result 22 | 23 | /** converts a 4-letter id code to an integer */ 24 | public function fromRawCode(string value) returns int 25 | var result = 0 26 | if value.length() != 4 27 | error("Fourchar value must be 4 chars long") 28 | else 29 | var factor = 1 30 | for i = 0 to 3 31 | let pos = CHARMAP.indexOf(value.charAt(3-i)) 32 | result += factor * pos 33 | factor *= 256 34 | 35 | return result 36 | 37 | public function string.fromRawCode() returns int 38 | return fromRawCode(this) 39 | 40 | public function int.toRawCode() returns string 41 | return toRawCode(this) 42 | 43 | /** Consumes the provided list, producing a string joined by ','. */ 44 | public function commaList(LinkedList ids) returns string 45 | let result = ids.joinBy(",") 46 | destroy ids 47 | return result 48 | 49 | /** Consumes the provided list, producing a string joined by ','. */ 50 | public function commaList(LinkedList ids) returns string 51 | let list = ids.map(t -> t.toRawCode()) 52 | destroy ids 53 | return commaList(list) 54 | 55 | public function commaList(vararg int ids) returns string 56 | let list = new LinkedList 57 | for id in ids 58 | list.add(id) 59 | return commaList(list) 60 | 61 | public function commaList(vararg string values) returns string 62 | let list = new LinkedList 63 | for v in values 64 | list.add(v) 65 | return commaList(list) 66 | 67 | @Test function asListIntTest() 68 | realToIndex(0.) 69 | commaList( 70 | AbilityIds.invulnerable, 71 | AbilityIds.reveal 72 | ).assertEquals("Avul,Adta") 73 | 74 | @Test function asListStringTest() 75 | commaList( 76 | "self", 77 | "structure" 78 | ).assertEquals("self,structure") 79 | 80 | @Test function test_idString2IdInteger() 81 | fromRawCode("hfoo") .assertEquals('hfoo') 82 | fromRawCode("abcd") .assertEquals('abcd') 83 | fromRawCode("ABCD") .assertEquals('ABCD') 84 | 85 | @Test function test_idInteger2IdString() 86 | toRawCode('hfoo') .assertEquals("hfoo") 87 | toRawCode('abcd') .assertEquals("abcd") 88 | toRawCode('ABCD') .assertEquals("ABCD") 89 | -------------------------------------------------------------------------------- /wurst/objediting/TargetsAllowed.wurst: -------------------------------------------------------------------------------- 1 | package TargetsAllowed 2 | import NoWurst 3 | 4 | public class TargetsAllowed 5 | /** Can only target air units. */ 6 | static constant air = "air" 7 | /** Can only target alive units (non-skeleton). */ 8 | static constant alive = "alive" 9 | /** Can only target allied units, but not units on the same team as casting unit. */ 10 | static constant allies = "allies" 11 | /** Can only target ancients i.e only the night elf buildings that can uproot. */ 12 | static constant ancient = "ancient" 13 | /** Can only target dead units (corpses, skeletons). */ 14 | static constant dead = "dead" 15 | /** Can only target debris, e.g crates. */ 16 | static constant debris = "debris" 17 | /** TODO: unknown what this target allows. */ 18 | static constant decoration = "decoration" 19 | /** Can only target enemy units. */ 20 | static constant enemies = "enemies" 21 | /** Can only target allied and own units. */ 22 | static constant friend = "friend" 23 | /** Can only target units without flying/hover movement type. */ 24 | static constant ground = "ground" 25 | /** Can only target hero units. */ 26 | static constant hero = "hero" 27 | /** Can only target invulnerable targets. */ 28 | static constant invulnerable = "invulnerable" 29 | /** Can only target items lying on the ground. */ 30 | static constant item_t = "item" // Suffixed with _t to prevent collision with native type item 31 | /** Can only target mechanichal units catapults etc. */ 32 | static constant mechanical = "mechanical" 33 | /** Can only target units which belong to neutral players (Neutral Hostile, Neutral Passive) */ 34 | static constant neutral = "neutral" 35 | /** Can only target non-ancient units. */ 36 | static constant nonancient = "nonancient" 37 | /** No targets allowed. */ 38 | static constant none = "none" 39 | /** Can only target non-heroes. */ 40 | static constant nonhero = "nonhero" 41 | /** Can only target non-suicidal units. */ 42 | static constant nonsapper = "nonsapper" 43 | /** Cannot target casting unit. */ 44 | static constant notself = "notself" 45 | /** Can only target non-mechanical units. */ 46 | static constant organic = "organic" 47 | /** TOOD: this is unknown what it does. */ 48 | static constant player_t = "player" // Suffixed with _t to prevent collision with native type player 49 | /** Only able to target units from the same player as casting unit. */ 50 | static constant playerunits = "playerunits" 51 | /** Can only target suicidal units such as Goblin Sapper. */ 52 | static constant sapper = "sapper" 53 | /** Can only target caster. */ 54 | static constant self = "self" 55 | /** Can target buildings. */ 56 | static constant structure = "structure" 57 | /** Can only target landscape such as grass, water or dirt. */ 58 | static constant terrain = "terrain" 59 | /** Can only target trees. */ 60 | static constant tree = "tree" 61 | /** Can only target units that can take damage. */ 62 | static constant vulnerable = "vulnerable" 63 | /** TODO: uncertain if this limits valid targets to gates. */ 64 | static constant wall = "wall" 65 | /** Limit to units which are wards (Statis Traps, etc). */ 66 | static constant ward = "ward" 67 | -------------------------------------------------------------------------------- /wurst/objediting/presets/ChannelAbilityPreset.wurst: -------------------------------------------------------------------------------- 1 | package ChannelAbilityPreset 2 | import NoWurst 3 | import public AbilityObjEditing 4 | import public ObjectIds 5 | import public ObjectIdGenerator 6 | import public Icons 7 | import OrderStringFactory 8 | import BitSet 9 | import Annotations 10 | 11 | var odf = new OrderStringFactory() 12 | @compiletime function initOdf() 13 | odf = new OrderStringFactory() 14 | 15 | 16 | public enum Option 17 | VISIBLE 18 | TARGETIMAGE 19 | PHYSICALSPELL 20 | UNIVERSALSPELL 21 | UNIQUECAST 22 | 23 | public enum Targettype 24 | NONE 25 | UNIT 26 | POINT 27 | POINTUNIT 28 | PASSIVE 29 | 30 | public function Targettype.toString() returns string 31 | switch this 32 | case NONE 33 | return "Instant Cast" 34 | case Targettype.POINT 35 | return "Point Target" 36 | case Targettype.UNIT 37 | return "Unit Target" 38 | case Targettype.POINTUNIT 39 | return "Point & Unit Target" 40 | case PASSIVE 41 | return "Passive" 42 | 43 | public class ChannelAbilityPreset extends AbilityDefinitionIllidanChannel 44 | private bitset optionSet = bitset(0) 45 | private string orderString 46 | 47 | function getOrderString() returns string 48 | return orderString 49 | 50 | construct(int newId, int lvls, boolean removeChannelProperties, TooltipGenerator tgen) 51 | super(newId) 52 | registerTooltipGenerator(tgen) 53 | setup(lvls, removeChannelProperties) 54 | 55 | construct(int newId, int lvls, boolean removeChannelProperties) 56 | super(newId) 57 | setup(lvls, removeChannelProperties) 58 | 59 | function setup(int lvls, boolean removeChannelProperties) 60 | this.lvls = lvls 61 | tooltipStartListen() 62 | setLevels(lvls) 63 | tooltipStopListen(false) 64 | orderString = odf.getOrderString(true) 65 | presetBaseOrderID(_lvl -> orderString) 66 | if removeChannelProperties 67 | removeChannelProperties(true) 68 | 69 | function makeUnitSpell(int mana, real cooldown) 70 | setHeroAbility(false) 71 | presetManaCost(lvl -> mana) 72 | presetCooldown(lvl -> cooldown) 73 | 74 | function removeChannelProperties(boolean removeVisuals) 75 | removeChannelProperties(removeVisuals, true) 76 | 77 | function removeChannelProperties(boolean removeVisuals, boolean makeVisible) 78 | presetDisableOtherAbilities(lvl -> false) 79 | presetFollowThroughTime(lvl -> 0.) 80 | presetArtDuration(lvl -> 0.) 81 | if makeVisible 82 | presetOption(Option.VISIBLE, true) 83 | if removeVisuals 84 | setArtCaster("") 85 | setArtEffect("") 86 | setArtSpecial("") 87 | setArtTarget("") 88 | 89 | function presetTargetTypes(Targettype ttype) 90 | presetTargetType(lvl -> ttype castTo int) 91 | 92 | function presetOption(Option opt, boolean flag) 93 | optionSet = optionSet.set(opt castTo int, flag) 94 | presetOptions(lvl -> optionSet.val) 95 | 96 | function hasOption(Option option) returns boolean 97 | return optionSet.get(option castTo int) 98 | -------------------------------------------------------------------------------- /wurst/objediting/presets/HeroPreset.wurst: -------------------------------------------------------------------------------- 1 | package HeroPreset 2 | import public UnitObjEditing 3 | import LinkedList 4 | import ObjectIds 5 | import AbilityIds 6 | 7 | public class HeroPreset extends HeroDefinition 8 | constant properNames = new LinkedList() 9 | string heroAbilityString = "" 10 | string normalAbilityString = AbilityIds.inventory.toRawCode() + "," 11 | 12 | construct(int newId, int origId, string name) 13 | super(newId, origId) 14 | setName(name) 15 | 16 | function buildHero() 17 | buildProperNames() 18 | buildHeroAbilities() 19 | buildNormalAbilities() 20 | 21 | 22 | /** Adds a proper Name to the Hero. 23 | Remember to call .buildHero() after you finished your changes */ 24 | function addProperName(string name) 25 | properNames.add(name) 26 | 27 | function buildProperNames() 28 | string s = "" 29 | if properNames.size() > 0 30 | for pn in properNames 31 | s += pn + "," 32 | s = s.substring(0, s.length()-1) 33 | setProperNames(s) 34 | 35 | function addHeroAbility(int id) 36 | heroAbilityString += id.toRawCode() + "," 37 | 38 | function buildHeroAbilities() 39 | if heroAbilityString.length() > 0 40 | setHeroAbilities(heroAbilityString.substring(0,heroAbilityString.length()-1)) 41 | 42 | function addNormalAbility(int id) 43 | normalAbilityString += id.toRawCode() + "," 44 | 45 | function buildNormalAbilities() 46 | if normalAbilityString.length() > 0 47 | setNormalAbilities(normalAbilityString.substring(0,normalAbilityString.length()-1)) 48 | -------------------------------------------------------------------------------- /wurst/objediting/presets/ObjectIdGenerator.wurst: -------------------------------------------------------------------------------- 1 | package ObjectIdGenerator 2 | import NoWurst 3 | import ErrorHandling 4 | import Preloader 5 | 6 | public constant UNIT_ID_GEN = new IdGenerator('x000') 7 | public constant HERO_ID_GEN = new IdGenerator('HM00') 8 | public constant ABIL_ID_GEN = new IdGenerator('AM00') 9 | public constant BUFF_ID_GEN = new IdGenerator('BM00') 10 | public constant ITEM_ID_GEN = new IdGenerator('IM00') 11 | public constant UPGD_ID_GEN = new IdGenerator('RM00') 12 | 13 | public class IdGenerator 14 | private int i1 15 | private int i2 16 | private int i3 17 | private int i4 18 | 19 | construct(int start) 20 | i1 = start mod 256 21 | i2 = (start mod 65536) div 256 22 | i3 = (start mod 16777216) div 65536 23 | i4 = start div 16777216 24 | 25 | function isInvalid(int char) returns boolean 26 | return (char < 48 or (char > 57 and char < 97)) 27 | 28 | function next() returns int 29 | if i1 < '~' 30 | i1++ 31 | while isInvalid(i1) 32 | i1++ 33 | else if i2 < '~' 34 | i1 = '!' 35 | i2++ 36 | while isInvalid(i2) 37 | i2++ 38 | else if i3 < '~' 39 | i1 = '!' 40 | i2 = '!' 41 | i3++ 42 | while isInvalid(i3) 43 | i3++ 44 | else if i4 < '~' 45 | i1 = '!' 46 | i2 = '!' 47 | i3 = '!' 48 | i4++ 49 | while isInvalid(i4) 50 | i4++ 51 | else 52 | error("No vaild id left") 53 | return 0 54 | let id = i1 + (i2 * 256) + (i3 * 65536) + (i4 * 16777216) 55 | if not compiletime 56 | preloadAbility(id) 57 | return id 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /wurst/util/Cinematic.wurst: -------------------------------------------------------------------------------- 1 | package Cinematic 2 | 3 | import ClosureTimers 4 | import LinkedList 5 | import Time 6 | 7 | /** 8 | A cinematic sequence. Cinematic has built-in cinematic sequences: 9 | 10 | - Cinematic.fadeOutSeq(duration) 11 | - Cinematic.fadeInSeq(duration) 12 | - Cinematic.applyCameraSetupSeq(camerasetup) 13 | - Cinematic.snapCameraPosSeq(vec2) 14 | - Cinematic.doNothingSeq() 15 | */ 16 | public interface CineSeq 17 | function apply() returns duration 18 | 19 | /** 20 | Create a cinematic by composing cinematic sequences: 21 | 22 | let cine = new Cinematic( 23 | asList( 24 | Cinematic.fadeOutSeq(0.5.seconds()), 25 | Cinematic.snapCameraPosSeq(ZERO2), 26 | Cinematic.fadeInSeq(1..seconds()), 27 | () -> begin 28 | print("Cinematic!") 29 | return 5..seconds() 30 | end, 31 | Cinematic.fadeOutSeq(0.5.seconds()) 32 | ), 33 | // This final cinematic seq is always run, even if cinematic is skipped. Use this for cleanup. 34 | () -> begin 35 | FogEnable(true) 36 | SelectUnit(u, true) 37 | reurn 0..seconds() 38 | end, 39 | ) 40 | 41 | A cinematic can be skipped, which signals the controller to halt processing sequences and play the closing sequence. 42 | cine.skip_() 43 | */ 44 | public class Cinematic 45 | var running = false 46 | 47 | private function controller(LinkedList sequence, CineSeq ending) 48 | if running and not sequence.isEmpty() 49 | let next = sequence.dequeue() 50 | let duration = next.apply() 51 | doAfter(duration.seconds) -> 52 | controller(sequence, ending) 53 | else 54 | endCinematic() 55 | ending.apply() 56 | 57 | /** 58 | Build a cinematic from a sequence of CineSeqs, and one final CineSeq which should be used for cleanup. The final 59 | CineSeq is run even if the cinematic is skip_()'d. 60 | */ 61 | construct(LinkedList sequence, CineSeq ending) 62 | running = true 63 | controller(sequence, ending) 64 | ShowInterface(false, 0.) 65 | EnableUserControl(false) 66 | 67 | private function endCinematic() 68 | running = false 69 | ShowInterface(true, .5) 70 | EnableUserControl(true) 71 | fadeIn(0..seconds()) 72 | 73 | static function fadeOutSeq(duration duration) returns CineSeq 74 | CineSeq seq = () -> 75 | fadeOut(duration) 76 | return duration 77 | return seq 78 | 79 | static function fadeInSeq(duration duration) returns CineSeq 80 | CineSeq seq = () -> 81 | fadeIn(duration) 82 | return duration 83 | return seq 84 | 85 | static function applyCameraSetupSeq(camerasetup setup) returns CineSeq 86 | CineSeq seq = () -> 87 | CameraSetupApply(setup, true, true) 88 | return 0..seconds() 89 | return seq 90 | 91 | static function snapCameraPosSeq(vec2 pos) returns CineSeq 92 | CineSeq seq = () -> 93 | setCameraPosition(pos) 94 | return 0..seconds() 95 | return seq 96 | 97 | static function doNothingSeq(duration duration) returns CineSeq 98 | CineSeq seq = () -> 99 | return duration 100 | return seq 101 | 102 | function skip_() 103 | running = false 104 | 105 | static function fadeOut(duration duration) 106 | EnableUserUI(false) 107 | SetCineFilterTexture("ReplaceableTextures\\CameraMasks\\White_mask.blp") 108 | SetCineFilterBlendMode(BLEND_MODE_BLEND) 109 | SetCineFilterTexMapFlags(TEXMAP_FLAG_NONE) 110 | SetCineFilterStartUV(0, 0, 1, 1) 111 | SetCineFilterEndUV(0, 0, 1, 1) 112 | SetCineFilterStartColor(0, 0, 0, 0) 113 | SetCineFilterEndColor(0, 0, 0, 255) 114 | SetCineFilterDuration(duration.seconds) 115 | DisplayCineFilter(true) 116 | 117 | static function fadeIn(duration duration) 118 | EnableUserUI(true) 119 | SetCineFilterTexture("ReplaceableTextures\\CameraMasks\\White_mask.blp") 120 | SetCineFilterBlendMode(BLEND_MODE_BLEND) 121 | SetCineFilterTexMapFlags(TEXMAP_FLAG_NONE) 122 | SetCineFilterStartUV(0, 0, 1, 1) 123 | SetCineFilterEndUV(0, 0, 1, 1) 124 | SetCineFilterStartColor(0, 0, 0, 255) 125 | SetCineFilterEndColor(0, 0, 0, 0) 126 | SetCineFilterDuration(duration.seconds) 127 | DisplayCineFilter(true) 128 | -------------------------------------------------------------------------------- /wurst/util/DialogBox.wurst: -------------------------------------------------------------------------------- 1 | package DialogBox 2 | import HashMap 3 | import Dialog 4 | import LinkedList 5 | 6 | public interface DialogBoxButtonClosure 7 | function run() 8 | 9 | /** DialogBox lets you create dialogs more easily and associate its buttons with closures. 10 | WARNING: Dialogs cannot be displayed at map init! 11 | 12 | The DialogBox should be destroyed after it is no longer needed. 13 | Example use: 14 | let dBox = new DialogBox("Extra starting gold?") 15 | dBox.addButton("Yes") -> 16 | players[0].addGold(500) 17 | destroy dBox 18 | 19 | dBox.addButton("No", () -> destroy dBox) 20 | // Make sure to not call display at map initialization 21 | dBox.display(players[0], true) 22 | */ 23 | public class DialogBox 24 | 25 | private static constant buttonClosures = new HashMap 26 | private constant buttons = new LinkedList