BonusMod

Scripts

14 years
edited 8 years
Version 3.3.1

Introduction
[INDENT]BonusMod is written in vJass and requires the NewGen editor, or Jass Helper with PitzerMike's Object Merger configured for it. BonusMod requires the latest version of Jass Helper.

BonusMod is a system for applying reversible bonuses to stats, such as damage, for specific units. Most of the bonuses provided by this system show green or red numbers in the command card, much like the bonuses provided by items. BonusMod is limited in the range of bonuses it can apply. The more abilities a bonus type uses, the larger the maximum and minimum bonuses.

BonusMod can also grant bonuses through code. For example, five optional bonus types are distributed with the basic BonusMod library. They are maximum life and mana, absolute mana per second regeneration, percentage of life per second regeneration, and movement speed. Even more bonus types can be provided by external libraries. The minimum and maximum bonuses these can apply differ depending on the library providing them.

Further explanation, and a guide to the API, is in the libraries documentation.[/INDENT]

Credits
  • weaaddar for the original BonusMod
  • PitzerMike for the ObjectMerger
  • Earth-Fury for the rewrite of BonusMod
  • Vexorian for vJass / Jass Helper
  • All of the contributors to the NewGen editor

The BonusMod Library

[indent]Requirements:

  • Optionally, either:

    • AbilityPreload
    • xepreload

The BonusMod library comes with the following bonus types with the given minimum and maximum values:

[btable=Bonus Types and Ranges]Bonus Constant:[c] Minimum:[c]Maximum:[r]
BONUS_DAMAGE[c]
-1024
[c]
+1023
[r]
BONUS_ARMOR[c]
-1024
[c]
+1023
[r]
BONUS_SIGHT_RANGE[c]
-2048
[c]
+2047
[r]
BONUS_LIFE_REGEN[c]
-256
[c]
+255
[r]
BONUS_AGILITY[c]
-256
[c]
+255
[r]
BONUS_STRENGTH[c]
-256
[c]
+255
[r]
BONUS_INTELLIGENCE[c]
-256
[c]
+255
[r]
BONUS_ATTACK_SPEED[c]
-512%
[c]
+511%
[r]
BONUS_MANA_REGEN_PERCENT[c]
-512%
[c]
+511%
[/btable]
Note that you can easily remove unused bonuses, and easily increase or decrease the range of bonuses individually. Information on how is within the libraries documentation.

To add BonusMod to your map, copy and paste the below library in to a custom-text trigger in your map.

You will want to close your map after the first time you save it with BonusMod in it, reopen it, and disable ability (re)generation in the configuration section. If you fail to do that, a small delay will be added every time you save your map.[/indent]

BonusMod

Code (jass) Select
1
library BonusMod initializer OnInit requires optional AbilityPreload, optional xepreload
2
private keyword AbilityBonus
3
////////////////////////////////////////////////////////////////////////////////
4
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
5
//@ BonusMod - v3.3.1
6
//@=============================================================================
7
//@ Credits:
8
//@-----------------------------------------------------------------------------
9
//@    Written by:
10
//@        Earth-Fury
11
//@    Based on the work of:
12
//@        weaaddar
13
//@-----------------------------------------------------------------------------
14
//@ If you use this system, please at least credit weaaddar. Without him, this
15
//@ system would not exist. I would be happy if you credited me as well.
16
//@=============================================================================
17
//@ Requirements:
18
//@-----------------------------------------------------------------------------
19
//@ This library is written in vJass and thus requires JASS Helper in order to
20
//@ function correctly. This library also uses the ObjectMerger created by
21
//@ PitzerMike. The ObjectMerger must be configured as an external tool for
22
//@ JASS Helper.
23
//@ 
24
//@ All of these things are present in the NewGen world editor.
25
//@ 
26
//@=============================================================================
27
//@ Introduction:
28
//@-----------------------------------------------------------------------------
29
//@ BonusMod is a system for applying reversible bonuses to certain stats, such
30
//@ as attack speed or mana regen, for specific units. Most of the bonuses
31
//@ provided by BonusMod show green or red numbers in the command card, exactly
32
//@ like the bonuses provided by items.
33
//@
34
//@ BonusMod has two kinds of bonuses:
35
//@   1. Ability based bonuses
36
//@   2. Code based bonuses
37
//@ 
38
//@ All of the bonuses in the configuration section for the basic BonusMod
39
//@ library are ability-based bonuses. Code-based bonuses are provided by
40
//@ additional libraries.
41
//@ 
42
//@ Ability based bonuses have a limit to how much of a bonus they can apply.
43
//@ The actual limit depends on the number of abilities that type of bonus uses.
44
//@ See the "Default bonuses" section of this readme for the default limits
45
//@ of the bonuses that come with BonusMod. For changing the limits of the
46
//@ default bonuses, or for adding new types of bonuses, see the below
47
//@ configuration section.
48
//@ 
49
//@ Code based bonuses may or may not have a limit to how much of a bonus they
50
//@ can apply. The limits for code based bonuses depend entirely on how the
51
//@ bonus is implemented. See their documentation for more information.
52
//@
53
//@=============================================================================
54
//@ Adding BonusMod to your map:
55
//@-----------------------------------------------------------------------------
56
//@ First, you must place the BonusMod library in a custom-text trigger in your
57
//@ map.
58
//@
59
//@ You must then save your map with ability generation enabled. After you save
60
//@ your map with ability generation enabled, you must close your map in the
61
//@ editor, and reopen it. You can then disable ability generation.
62
//@ See the configuration section for information on how to enable and disable
63
//@ ability generation.
64
//@
65
//@=============================================================================
66
//@ Default bonuses:
67
//@-----------------------------------------------------------------------------
68
//@ 
69
//@      +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
70
//@      | Bonus Type constants:       | Minimum bonus: | Maximum bonus: |
71
//@      +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
72
//@      | BONUS_SIGHT_RANGE           |   -2048        |   +2047        |
73
//@      | BONUS_ATTACK_SPEED          |   -512         |   +511         |
74
//@      | BONUS_ARMOR                 |   -1024        |   +1023        |
75
//@      | BONUS_MANA_REGEN_PERCENT    |   -512%        |   +511%        |
76
//@      | BONUS_LIFE_REGEN            |   -256         |   +255         |
77
//@      | BONUS_DAMAGE                |   -1024        |   +1023        |
78
//@      | BONUS_STRENGTH              |   -256         |   +255         |
79
//@      | BONUS_AGILITY               |   -256         |   +255         |
80
//@      | BONUS_INTELLIGENCE          |   -256         |   +255         |
81
//@      +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
82
//@
83
//@      Notes:
84
//@         - The bonuses for stength, agility, and intelligence can only be
85
//@           applied to heroes. Attempting to add them to normal units will
86
//@           fail to work completely.
87
//@         - Using a negative BONUS_STRENGTH bonus can give a unit negative
88
//@           maximum life. Don't do that. It really messes stuff up.
89
//@         - Using a negative BONUS_INTELLIGENCE bonus can remove a hero's
90
//@           mana. This is not a big issue, as mana will return when the
91
//@           bonus is removed.
92
//@         - The maximum effective sight range for a unit is 1800.
93
//@         - There is a maximum attack speed. I have no idea what it is.
94
//@ 
95
//@ See the configuration section for information on how to change the range of
96
//@ bonuses, as well as how to add new ability-based bonuses, and remove unused
97
//@ ones.
98
//@ 
99
//@=============================================================================
100
//@ Public API / Function list:
101
//@-----------------------------------------------------------------------------
102
//@ Note that BonusMod will only output error messages if JASS Helper is set to
103
//@ compile in debug mode.
104
//@
105
//@ Bonus constants such as BONUS_DAMAGE have .min and .max properties which
106
//@ are the minimum and maximum bonus that type of bonus can apply. Note that
107
//@ for code based bonuses, these constants may not reflect the minimum or
108
//@ maximum bonus for a specific unit. Use the IsBonusValid() function to check
109
//@ if the given bonus value is okay for a given unit.
110
//@
111
//@ function SetUnitBonus
112
//@ takes unit u, Bonus bonusType, integer amount
113
//@ returns integer
114
//@ 
115
//@     This function sets the bonus of the type bonusType for the given unit to
116
//@     the given amount. The returned integer is the unit's actual current
117
//@     bonus, after it has been changed. If the given amount is above the
118
//@     maximum possible bonus for this type, then the maximum possible bonus
119
//@     is applied to the unit. The same is true if the given value is below
120
//@     the minimum possible bonus.
121
//@
122
//@ function GetUnitBonus
123
//@ takes unit u, Bonus bonusType
124
//@ returns integer
125
//@
126
//@     Returns the given unit's current bonus of bonusType. A value of 0 means
127
//@     that the given unit does not have a bonus of the given type.
128
//@ 
129
//@ function AddUnitBonus
130
//@ takes unit u, Bonus bonusType, integer amount
131
//@ returns integer
132
//@ 
133
//@     Increases the unit's bonus by the given amount. You can use a negitive
134
//@     amount to subtract from the unit's current bonus. Note that the same
135
//@     rules SetUnitBonus has apply for going over/under the maximum bonus.
136
//@     The returned value is the unit's actual new bonus.
137
//@ 
138
//@ function RemoveUnitBonus
139
//@ takes unit u, Bonus bonusType
140
//@ returns nothing
141
//@ 
142
//@     Sets the bonus of the type bonusType to 0 for the given unit. This
143
//@     function is faster then using SetUnitBonus(u, bonusType, 0).
144
//@
145
//@ function IsBonusValid
146
//@ takes unit u, Bonus abstractBonus, integer value
147
//@ returns boolean
148
//@
149
//@     Returns true if the given value is a valid bonus value for the given
150
//@     unit. This will also return false if the given bonus type is a hero-
151
//@     only bonus type, and the given unit is not a hero.
152
//@ 
153
//@=============================================================================
154
//@ Writing code-based bonuses:
155
//@-----------------------------------------------------------------------------
156
//@ This section of the readme tells you how to create your own bonus types
157
//@ that apply their bonuses using vJass code instead of abilities. You do not
158
//@ need to read or understand this to use BonusMod as-is.
159
//@
160
//@ Creating a new bonus type is simple. Extend the Bonus struct, implement the
161
//@ methods provided within it, and create a single instance of your struct
162
//@ within a variable named BONUS_YOUR_BONUS_TYPES_NAME of the type Bonus.
163
//@ 
164
//@ The methods you must implement are:
165
//@
166
//@ method setBonus takes unit u, integer amount returns integer
167
//@     This method sets the given unit's current bonus to amount, returning
168
//@     the actual bonus that was applied. If the given amount is higher then
169
//@     the maximum amount your bonus type can apply to a unit, you must apply
170
//@     the maximum possible bonus, and return that amount. The same holds true
171
//@     for the minimum bonus.
172
//@
173
//@ method getBonus takes unit u returns integer
174
//@     This method returns the current bonus the given unit has.
175
//@
176
//@ method removeBonus takes unit u returns nothing
177
//@     This method sets the current bonus of the given unit to 0.
178
//@
179
//@ method isValueInRange takes integer value returns boolean
180
//@     This method returns true if the given integer is a valid bonus amount
181
//@     for this bonus type, and false otherwise.
182
//@
183
//@ Note that it is your responsibility to do any clean up in the event a unit
184
//@ dies or is removed with an active bonus on it. There is no guarantee that
185
//@ removeBonus() will be called before a unit dies or is removed.
186
//@ 
187
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
188
////////////////////////////////////////////////////////////////////////////////
189
190
//==============================================================================
191
// Configuration:
192
//==============================================================================
193
194
//------------------------------------------------------------------------------
195
// If the following constant is set to true, the abilities used by this library
196
// will be preloaded at map initialization. This will slightly increase loading
197
// time, but will prevent a slight to medium lag spike the first time a bonus
198
// of a type is applied.
199
// 
200
// Note that your map must contain either the xepreload library, or the
201
// AbilityPreload library for preloading to work.
202
// 
203
// It is highly recommended that you do not set this to false.
204
//------------------------------------------------------------------------------
205
globals
206
    private constant boolean PRELOAD_ABILITIES = true
207
endglobals
208
209
//------------------------------------------------------------------------------
210
// The BonusMod_BeginBonuses macro takes a single boolean type parameter.
211
// If set to true, bonus abilities will be created (or recreated) on save.
212
// If set to false, abilities will not be generated.
213
// 
214
// If you modify any of the bonus declaration macros, or add new ones, you must
215
// regenerate abilities.
216
// 
217
// Note that if you remove a bonus, the abilities it had created will not be
218
// automatically removed. This is also true of reducing the number of abilities
219
// a bonus uses.
220
// 
221
// After you generate abilities, you must close your map and reopen it in the
222
// editor. You can then disable ability generation until the next time you
223
// modify the bonus types.
224
//------------------------------------------------------------------------------
225
//! runtextmacro BonusMod_BeginBonuses("true")
226
    //--------------------------------------------------------------------------
227
    // Below are where bonus types are defined.
228
    // 
229
    // The first parameter is the name of the bonus type. A constant will be
230
    // generated for each bonus type, that will take the form: BONUS_NAME
231
    // 
232
    // The second parameter is the maximum power of 2 the bonus type can add
233
    // to a unit. For example, 8 abilities gives a range of -256 to +255.
234
    // 
235
    // The third parameter is the base ability. The base ability must give the
236
    // desired effect when the given field is changed.
237
    // 
238
    // The fourth parameter is the rawcode prefix of the bonuses generated
239
    // abilities. The prefix must be 3 characters long. Your map must not
240
    // already contain bonuses which start with the given prefix.
241
    // 
242
    // The fifth parameter is the object field to modify for each generated
243
    // ability.
244
    // 
245
    // The sixth parameter must be true of the bonus should only work on hero
246
    // units, and false otherwise.
247
    // 
248
    // The final parameter is the icon that will be displayed in the object
249
    // editor. This has no effect on anything ingame.
250
    //--------------------------------------------------------------------------
251
    
252
    //                                    |     NAME     |ABILITY|SOURCE |PREFIX|FIELD  | HERO   |  ICON
253
    //                                    |              | COUNT |ABILITY|      |       |  ONLY  |
254
    //! runtextmacro BonusMod_DeclareBonus("ARMOR",        "10",  "AId1", "(A)", "Idef", "false", "BTNHumanArmorUpOne.blp")
255
    //! runtextmacro BonusMod_DeclareBonus("DAMAGE",       "10",  "AItg", "(B)", "Iatt", "false", "BTNSteelMelee.blp")
256
    //! runtextmacro BonusMod_DeclareBonus("SIGHT_RANGE",  "11",  "AIsi", "(C)", "Isib", "false", "BTNTelescope.blp")
257
    //! runtextmacro BonusMod_DeclareBonus("LIFE_REGEN",   "8",   "Arel", "(E)", "Ihpr", "false", "BTNRingSkull.blp")
258
    //! runtextmacro BonusMod_DeclareBonus("STRENGTH",     "8",   "AIa1", "(F)", "Istr", "true" , "BTNGoldRing.blp")
259
    //! runtextmacro BonusMod_DeclareBonus("AGILITY",      "8",   "AIa1", "(G)", "Iagi", "true" , "BTNGoldRing.blp")
260
    //! runtextmacro BonusMod_DeclareBonus("INTELLIGENCE", "8",   "AIa1", "(H)", "Iint", "true" , "BTNGoldRing.blp")
261
    
262
    //                                           |     NAME          |ABILITY|SOURCE |PREFIX|FIELD  |HERO   |  ICON
263
    //                                           |                   | COUNT |ABILITY|      |       | ONLY  |
264
    //! runtextmacro BonusMod_DeclarePercentBonus("ATTACK_SPEED",       "9", "AIsx",  "(I)", "Isx1", "false", "BTNGlove.blp")
265
    //! runtextmacro BonusMod_DeclarePercentBonus("MANA_REGEN_PERCENT", "9", "AIrm",  "(D)", "Imrp", "false", "BTNSobiMask.blp")
266
    
267
//! runtextmacro BonusMod_EndBonuses()
268
269
//==============================================================================
270
// End of configuration
271
//==============================================================================
272
273
//! textmacro BonusMod_BeginBonuses takes SHOULD_GENERATE_ABILITIES
274
    private function Setup takes nothing returns nothing
275
    
276
    // The following is a lua script for the ObjectMerger, used to generate abilities
277
    //*
278
    //! externalblock extension=lua ObjectMerger $FILENAME$
279
    //! i if "$SHOULD_GENERATE_ABILITIES$" == "true" then
280
    //! i function FormatName(name)
281
    //! i     name = string.lower(name)
282
    //! i     name = string.gsub(name, "_", " ")
283
    //! i     s = name
284
    //! i     name = ""
285
    //! i     for w in string.gmatch(s, "%a%w*") do
286
    //! i         name = name .. string.upper(string.sub(w, 1, 1)) .. string.sub(w, 2, -1)
287
    //! i         name = name .. " "
288
    //! i     end
289
    //! i     name = string.sub(name, 1, string.len(name) - 1)
290
    //! i     return name
291
    //! i end
292
    //! i function SetupAbility(name, suffix, icon, hero)
293
    //! i     makechange(current, "anam", "BonusMod - " .. FormatName(name))
294
    //! i     makechange(current, "ansf", "(" .. suffix .. ")")
295
    //! i     makechange(current, "aart", "ReplaceableTextures\\CommandButtons\\" .. icon)
296
    //! i     makechange(current, "aite", 0)
297
    //! i     if hero then
298
    //! i         makechange(current, "Iagi", 1, 0)
299
    //! i         makechange(current, "Iint", 1, 0)
300
    //! i         makechange(current, "Istr", 1, 0)
301
    //! i     end
302
    //! i end
303
    //! i function CreateAbility(sourceAbility, prefix, field, abilityCount, name, icon)
304
    //! i     powOf2 = abilityCount - 1
305
    //! i     lengthOfMax = string.len(tostring(2^abilityCount))
306
    //! i     for i = 0, powOf2 do
307
    //! i         padding = ""
308
    //! i         for k = 0, lengthOfMax - string.len(tostring(2^i)) - 1 do
309
    //! i             padding = padding .. "0"
310
    //! i         end
311
    //! i         createobject(sourceAbility, prefix .. string.sub(chars, i + 1, i + 1))
312
    //! i         SetupAbility(name, "+" .. padding .. tostring(2 ^ i), icon, true)
313
    //! i         makechange(current, field, 1, tostring(2^i))
314
    //! i     end
315
    //! i     createobject(sourceAbility, prefix .. "-")
316
    //! i     SetupAbility(name, "-" .. tostring(2 ^ abilityCount), icon, true)
317
    //! i     makechange(current, field, 1, tostring(-(2^abilityCount)))
318
    //! i end
319
    //! i function CreatePercentageAbility(sourceAbility, prefix, field, abilityCount, name, icon)
320
    //! i     powOf2 = abilityCount - 1
321
    //! i     lengthOfMax = string.len(tostring(2^abilityCount))
322
    //! i     for i = 0, powOf2 do
323
    //! i         padding = ""
324
    //! i         for k = 0, lengthOfMax - string.len(tostring(2^i)) - 1 do
325
    //! i             padding = padding .. "0"
326
    //! i         end
327
    //! i         createobject(sourceAbility, prefix .. string.sub(chars, i + 1, i + 1))
328
    //! i         SetupAbility(name, "+" .. padding .. tostring(2 ^ i) .. "%", icon, false)
329
    //! i         makechange(current, field, 1, tostring((2 ^ i) / 100))
330
    //! i     end
331
    //! i     createobject(sourceAbility, prefix .. "-")
332
    //! i     SetupAbility(name, "-" .. tostring(2 ^ abilityCount) .. "%", icon, false)
333
    //! i     makechange(current, field, 1, tostring(-((2 ^ abilityCount) / 100)))
334
    //! i end
335
    //! i     setobjecttype("abilities")
336
    //! i     chars = "abcdefghijklmnopqrstuvwxyz"
337
    //! i 
338
    // */
339
//! endtextmacro
340
341
//! textmacro BonusMod_DeclareBonus takes NAME, ABILITY_COUNT, SOURCE_ABILITY, RAWCODE_PREFIX, FIELD, HERO_ONLY, ICON
342
    //! i CreateAbility("$SOURCE_ABILITY$", "$RAWCODE_PREFIX$", "$FIELD$", $ABILITY_COUNT$, "$NAME$", "$ICON$")
343
    globals
344
        Bonus BONUS_$NAME$
345
    endglobals
346
    set BONUS_$NAME$ = AbilityBonus.create('$RAWCODE_PREFIX$a', $ABILITY_COUNT$, '$RAWCODE_PREFIX$-', $HERO_ONLY$)
347
//! endtextmacro
348
349
//! textmacro BonusMod_DeclarePercentBonus takes NAME, ABILITY_COUNT, SOURCE_ABILITY, RAWCODE_PREFIX, FIELD, HERO_ONLY, ICON
350
    //! i CreatePercentageAbility("$SOURCE_ABILITY$", "$RAWCODE_PREFIX$", "$FIELD$", $ABILITY_COUNT$, "$NAME$", "$ICON$")
351
    globals
352
        Bonus BONUS_$NAME$
353
    endglobals
354
    set BONUS_$NAME$ = AbilityBonus.create('$RAWCODE_PREFIX$a', $ABILITY_COUNT$, '$RAWCODE_PREFIX$-', $HERO_ONLY$)
355
//! endtextmacro
356
357
//! textmacro BonusMod_EndBonuses
358
    //*
359
    //! i end
360
    //! endexternalblock
361
    // */
362
    
363
    endfunction
364
//! endtextmacro
365
366
// ===
367
//  Precomputed integer powers of 2
368
// ===
369
370
globals
371
    private integer array powersOf2
372
    private integer powersOf2Count = 0
373
endglobals
374
375
// ===
376
//  Utility functions
377
// ===
378
379
private function ErrorMsg takes string func, string s returns nothing
380
    call BJDebugMsg("|cffFF0000BonusMod Error|r|cffFFFF00:|r |cff8080FF" + func + "|r|cffFFFF00:|r " + s)
381
endfunction
382
383
private function LoadAbility takes integer abilityId returns nothing
384
    static if PRELOAD_ABILITIES then
385
        static if LIBRARY_xepreload then
386
            call XE_PreloadAbility(abilityId)
387
        else
388
            static if LIBRARY_AbilityPreload then
389
                call AbilityPreload(abilityId)
390
            endif
391
        endif
392
    endif
393
endfunction
394
395
// ===
396
//  Bonus Types
397
// ===
398
399
private interface BonusInterface
400
    integer minBonus = 0
401
    integer maxBonus = 0
402
    private method destroy takes nothing returns nothing defaults nothing
403
endinterface
404
405
private keyword isBonusObject
406
407
struct Bonus extends BonusInterface
408
    boolean isBonusObject = false
409
    
410
    public static method create takes nothing returns thistype
411
        local thistype this = thistype.allocate()
412
        
413
        set this.isBonusObject = true
414
        
415
        return this
416
    endmethod
417
    
418
    stub method setBonus takes unit u, integer amount returns integer
419
        debug call ErrorMsg("Bonus.setBonus()", "I have no idea how or why you did this, but don't do it.")
420
        return 0
421
    endmethod
422
    
423
    stub method getBonus takes unit u returns integer
424
        debug call ErrorMsg("Bonus.getBonus()", "I have no idea how or why you did this, but don't do it.")
425
        return 0
426
    endmethod
427
    
428
    stub method removeBonus takes unit u returns nothing
429
        call this.setBonus(u, 0)
430
    endmethod
431
    
432
    stub method isValidBonus takes unit u, integer value returns boolean
433
        return true
434
    endmethod
435
    
436
    method operator min takes nothing returns integer
437
        return this.minBonus
438
    endmethod
439
    
440
    method operator max takes nothing returns integer
441
        return this.maxBonus
442
    endmethod
443
endstruct
444
445
private struct AbilityBonus extends Bonus
446
    public integer count
447
    
448
    public integer rawcode
449
    public integer negativeRawcode
450
    
451
    public integer minBonus = 0
452
    public integer maxBonus = 0
453
    
454
    public boolean heroesOnly
455
    
456
    public static method create takes integer rawcode, integer count, integer negativeRawcode, boolean heroesOnly returns thistype
457
        local thistype bonus = thistype.allocate()
458
        local integer i
459
        debug local boolean error = false
460
        
461
        // Error messages
462
        static if DEBUG_MODE then
463
            if rawcode == 0 then
464
                call ErrorMsg("AbilityBonus.create()", "Bonus constructed with a rawcode of 0?!")
465
                call bonus.destroy()
466
                return 0
467
            endif
468
            
469
            if count < 0 or count == 0 then
470
                call ErrorMsg("AbilityBonus.create()", "Bonus constructed with an ability count <= 0?!")
471
                call bonus.destroy()
472
                return 0
473
            endif
474
        endif
475
        
476
        // Grow powers of 2
477
        if powersOf2Count < count then
478
            set i = powersOf2Count
479
            loop
480
                exitwhen i > count
481
                
482
                set powersOf2[i] = 2 * powersOf2[i - 1]
483
484
                set i = i + 1
485
            endloop
486
            set powersOf2Count = count
487
        endif
488
        
489
        // Preload this bonus' abilities
490
        static if PRELOAD_ABILITIES then
491
            set i = 0
492
            loop
493
                exitwhen i == count
494
                
495
                call LoadAbility(rawcode + i)
496
                
497
                set i = i + 1
498
            endloop
499
            
500
            if negativeRawcode != 0 then
501
                call LoadAbility(negativeRawcode)
502
            endif
503
        endif
504
        
505
        // Set up this bonus object
506
        set bonus.count = count
507
        set bonus.negativeRawcode = negativeRawcode
508
        set bonus.rawcode = rawcode
509
        set bonus.heroesOnly = heroesOnly
510
        
511
        // Calculate the minimum and maximum bonuses
512
        if negativeRawcode != 0 then
513
            set bonus.minBonus = -powersOf2[count]
514
        else
515
            set bonus.minBonus = 0
516
        endif
517
        set bonus.maxBonus = powersOf2[count] - 1
518
        
519
        // Return the bonus object
520
        return bonus
521
    endmethod
522
    
523
    // Interface methods:
524
    
525
    method setBonus takes unit u, integer amount returns integer
526
        return SetUnitBonus.evaluate(u, this, amount)
527
    endmethod
528
    
529
    method getBonus takes unit u returns integer
530
        return GetUnitBonus.evaluate(u, this)
531
    endmethod
532
    
533
    method removeBonus takes unit u returns nothing
534
        call RemoveUnitBonus.evaluate(u, this)
535
    endmethod
536
    
537
    public method isValidBonus takes unit u, integer value returns boolean
538
        return (value >= this.minBonus) and (value <= this.maxBonus)
539
    endmethod
540
endstruct
541
542
// ===
543
//  Public API
544
// ===
545
546
function IsBonusValid takes unit u, Bonus abstractBonus, integer value returns boolean
547
    local AbilityBonus bonus = AbilityBonus(abstractBonus)
548
    
549
    static if DEBUG_MODE then
550
        if not abstractBonus.isBonusObject then
551
            call ErrorMsg("IsBonusValid()", "Invalid bonus type given")
552
        endif
553
    endif
554
    
555
    if abstractBonus.min > value or abstractBonus.max < value then
556
        return false
557
    endif
558
    
559
    if abstractBonus.getType() != AbilityBonus.typeid then
560
        return abstractBonus.isValidBonus(u, value)
561
    endif
562
    
563
    if bonus.heroesOnly and not IsUnitType(u, UNIT_TYPE_HERO) then
564
        return false
565
    endif
566
    
567
    return (value >= bonus.minBonus) and (value <= bonus.maxBonus)
568
endfunction
569
570
function RemoveUnitBonus takes unit u, Bonus abstractBonus returns nothing
571
    local integer i = 0
572
    local AbilityBonus bonus = AbilityBonus(abstractBonus)
573
574
    static if DEBUG_MODE then
575
        if not abstractBonus.isBonusObject then
576
            call ErrorMsg("RemoveUnitBonus()", "Invalid bonus type given")
577
        endif
578
    endif
579
    
580
    if abstractBonus.getType() != AbilityBonus.typeid then
581
        call abstractBonus.removeBonus(u)
582
        return
583
    endif
584
    
585
    if bonus.heroesOnly and not IsUnitType(u, UNIT_TYPE_HERO) then
586
        debug call ErrorMsg("RemoveUnitBonus()", "Trying to remove a hero-only bonus from a non-hero unit")
587
        return
588
    endif
589
    
590
    call UnitRemoveAbility(u, bonus.negativeRawcode)
591
    
592
    loop
593
        exitwhen i == bonus.count
594
        
595
        call UnitRemoveAbility(u, bonus.rawcode + i)
596
597
        set i = i + 1
598
    endloop
599
endfunction
600
601
function SetUnitBonus takes unit u, Bonus abstractBonus, integer amount returns integer
602
    local integer i
603
    local integer output = 0
604
    local AbilityBonus bonus = AbilityBonus(abstractBonus)
605
    local boolean applyMinBonus = false
606
    
607
    static if DEBUG_MODE then
608
        if not abstractBonus.isBonusObject then
609
            call ErrorMsg("SetUnitBonus()", "Invalid bonus type given")
610
        endif
611
    endif
612
    
613
    if amount == 0 then
614
        call RemoveUnitBonus(u, bonus)
615
        return 0
616
    endif
617
    
618
    if abstractBonus.getType() != AbilityBonus.typeid then
619
        return abstractBonus.setBonus(u, amount)
620
    endif
621
    
622
    if bonus.heroesOnly and not IsUnitType(u, UNIT_TYPE_HERO) then
623
        debug call ErrorMsg("SetUnitBonus()", "Trying to set a hero-only bonus on a non-hero unit")
624
        return 0
625
    endif
626
    
627
    if amount < bonus.minBonus then
628
        debug call ErrorMsg("SetUnitBonus()", "Attempting to set a bonus to below its min value")
629
        set amount = bonus.minBonus
630
    elseif amount > bonus.maxBonus then
631
        debug call ErrorMsg("SetUnitBonus()", "Attempting to set a bonus to above its max value")
632
        set amount = bonus.maxBonus
633
    endif
634
    
635
    if amount < 0 then
636
        set amount = -(bonus.minBonus - amount)
637
        set applyMinBonus = true
638
    endif
639
    
640
    call UnitRemoveAbility(u, bonus.negativeRawcode)
641
    
642
    set i = bonus.count - 1
643
    loop
644
        exitwhen i < 0
645
        if amount >= powersOf2[i] then
646
            
647
            call UnitAddAbility(u, bonus.rawcode + i)
648
            call UnitMakeAbilityPermanent(u, true, bonus.rawcode + i)
649
            
650
            static if DEBUG_MODE then
651
                if GetUnitAbilityLevel(u, bonus.rawcode + i) <= 0 then
652
                    call ErrorMsg("SetUnitBonus()", "Failed to give the 2^" + I2S(i) + " ability to the unit!")
653
                endif
654
            endif
655
            
656
            set amount = amount - powersOf2[i]
657
            set output = output + powersOf2[i]
658
        else
659
            
660
            call UnitRemoveAbility(u, bonus.rawcode + i)
661
            static if DEBUG_MODE then
662
                if GetUnitAbilityLevel(u, bonus.rawcode + i) > 0 then
663
                    call ErrorMsg("SetUnitBonus()", "Unit still has the 2^" + I2S(i) + " ability after it was removed!")
664
                endif
665
            endif
666
        endif
667
668
        set i = i - 1
669
    endloop
670
    
671
    if applyMinBonus then
672
        call UnitAddAbility(u, bonus.negativeRawcode)
673
        call UnitMakeAbilityPermanent(u, true, bonus.negativeRawcode)
674
    else
675
        call UnitRemoveAbility(u, bonus.negativeRawcode)
676
    endif
677
    
678
    return output
679
endfunction
680
681
function GetUnitBonus takes unit u, Bonus abstractBonus returns integer
682
    local integer i = 0
683
    local integer amount = 0
684
    local AbilityBonus bonus = AbilityBonus(abstractBonus)
685
686
    static if DEBUG_MODE then
687
        if not abstractBonus.isBonusObject then
688
            call ErrorMsg("GetUnitBonus()", "Invalid bonus type given")
689
        endif
690
    endif
691
    
692
    if abstractBonus.getType() != AbilityBonus.typeid then
693
        return abstractBonus.getBonus(u)
694
    endif
695
    
696
    if bonus.heroesOnly and not IsUnitType(u, UNIT_TYPE_HERO) then
697
        debug call ErrorMsg("GetUnitBonus()", "Trying to get a hero-only bonus from a non-hero unit")
698
        return 0
699
    endif
700
    
701
    if GetUnitAbilityLevel(u, bonus.negativeRawcode) > 0 then
702
        set amount = bonus.minBonus
703
    endif
704
705
    loop
706
        exitwhen i == bonus.count
707
        
708
        if GetUnitAbilityLevel(u, bonus.rawcode + i) > 0 then
709
            set amount = amount + powersOf2[i]
710
        endif
711
712
        set i = i + 1
713
    endloop
714
715
    return amount
716
endfunction
717
718
function AddUnitBonus takes unit u, Bonus bonus, integer amount returns integer
719
    return SetUnitBonus(u, bonus, GetUnitBonus(u, bonus) + amount)
720
endfunction
721
722
// ===
723
//  Initialization
724
// ===
725
726
private function OnInit takes nothing returns nothing
727
    local integer i
728
    
729
    // Set up powers of 2
730
    set powersOf2[0] = 1
731
    set powersOf2Count = 1
732
    
733
    static if DEBUG_MODE and PRELOAD_ABILITIES and not LIBRARY_xepreload and not LIBRARY_AbilityPreload then
734
        call ErrorMsg("Initialization", "PRELOAD_ABILITIES is set to true, but neither usable preloading library is detected")
735
    endif
736
    
737
    // Setup bonuses
738
    call Setup()
739
endfunction
740
endlibrary


Additional Bonus Types

[indent]Bonus Maximum Life / Mana

[indent]Requirements:

  • BonusMod
  • AutoIndex

This library provides two new bonus types:
[btable=Bonus Types and Ranges]Bonus Constant:[r]
BONUS_LIFE[r]
BONUS_MANA[/btable]

The minimum bonus these bonus types can apply depends on the unit. You can apply any positive or negative bonus that does not result in a unit's maximum life or maximum mana going below 1.

To add these bonuses to BonusMod, simply copy and paste the following library in to a custom-text trigger in your map.[/indent]
Bonus Max Life / Mana
Code (jass) Select
1
library UnitMaxStateBonuses initializer OnInit requires BonusMod, UnitMaxState, AutoIndex
2
////////////////////////////////////////////////////////////////////////////////
3
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
4
//@ UnitMaxStateBonuses for BonusMod
5
//@=============================================================================
6
//@ Credits:
7
//@-----------------------------------------------------------------------------
8
//@    Written by:
9
//@        Earth-Fury
10
//@=============================================================================
11
//@ Requirements:
12
//@-----------------------------------------------------------------------------
13
//@ This library requires the BonusMod library, the UnitMaxState library by
14
//@ Earth-Fury, and the AutoIndex library by grim001.
15
//@
16
//@=============================================================================
17
//@ Readme:
18
//@-----------------------------------------------------------------------------
19
//@ This library provides two new bonus types:
20
//@
21
//@   - BONUS_LIFE
22
//@   - BONUS_MANA
23
//@ 
24
//@ These bonuses, of course, raise a unit's maximum life and mana.
25
//@ 
26
//@ Note that if you simply wish to permanently increase or decrease the maximum
27
//@ life or mana of a unit, you are likely best off using UnitMaxState directly.
28
//@
29
//@ The minimum life and mana bonuses are 1 more than -(unit's max state).
30
//@ That is to say, these bonuses can not fully remove a unit's life or mana.
31
//@
32
//@ There is no maximum bonus.
33
//@
34
//@ There is nothing to configure.
35
//@ 
36
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
37
////////////////////////////////////////////////////////////////////////////////
38
39
globals
40
    Bonus BONUS_LIFE
41
    Bonus BONUS_MANA
42
endglobals
43
44
private function ErrorMsg takes string func, string s returns nothing
45
    call BJDebugMsg("|cffFF0000UnitMaxStateBonus Error|r|cffFFFF00:|r |cff8080FF" + func + "|r|cffFFFF00:|r " + s)
46
endfunction
47
48
private struct BonusValues
49
    public integer mana = 0
50
    public integer life = 0
51
    
52
    public unit owner
53
    
54
    private static thistype array owners
55
    
56
    private static method create takes unit u returns thistype
57
        local thistype this = thistype.allocate()
58
        
59
        set this.owner = u
60
        
61
        set thistype.owners[GetUnitId(u)] = this
62
        
63
        return this
64
    endmethod
65
    
66
    private method onDestroy takes nothing returns nothing
67
        set thistype.owners[GetUnitId(this.owner)] = thistype(0)
68
        set this.owner = null
69
    endmethod
70
    
71
    public static method getInstance takes unit u returns thistype
72
        if thistype.owners[GetUnitId(u)] == 0 then
73
            return thistype.create(u)
74
        else
75
            return thistype.owners[GetUnitId(u)]
76
        endif
77
    endmethod
78
    
79
    public static method operator[] takes unit u returns thistype
80
        return thistype.owners[GetUnitId(u)]
81
    endmethod
82
    
83
    public static method operator[]= takes unit u, thistype i returns nothing
84
        set thistype.owners[GetUnitId(u)] = i
85
    endmethod
86
endstruct
87
88
//! textmacro UnitMaxStateBonus_DefineBonus takes NAME, STATE, MIN
89
private struct MaxStateBonus_$NAME$ extends Bonus
90
    integer minBonus = -2147483648
91
    integer maxBonus =  2147483647
92
    
93
    method setBonus takes unit u, integer amount returns integer
94
        local BonusValues values
95
        local integer actual
96
        local integer new
97
        local real factor
98
        
99
        if not IsUnitIndexed(u) then
100
            debug call ErrorMsg("MaxStateBonus_$NAME$.setBonus()", "Unit that is not indexed by AutoIndex given")
101
            return 0
102
        endif
103
        
104
        set values = BonusValues.getInstance(u)
105
        
106
        set actual = R2I(GetUnitState(u, UNIT_STATE_MAX_$STATE$)) - values.$NAME$
107
        set new = actual + amount
108
        
109
        set factor = GetUnitState(u, UNIT_STATE_$STATE$) / GetUnitState(u, UNIT_STATE_MAX_$STATE$)
110
        
111
        if new < $MIN$ then
112
            if actual < $MIN$ then
113
                set values.$NAME$ = 0
114
            else
115
                set values.$NAME$ = -actual + $MIN$
116
            endif
117
            
118
            call SetUnitState(u, UNIT_STATE_$STATE$, $MIN$)
119
            call SetUnitMaxState(u, UNIT_STATE_MAX_$STATE$, $MIN$)
120
            return values.$NAME$
121
        else
122
            set values.$NAME$ = amount
123
            
124
            call SetUnitMaxState(u, UNIT_STATE_MAX_$STATE$, new)
125
            call SetUnitState(u, UNIT_STATE_$STATE$, new * factor)
126
            
127
            return values.$NAME$
128
        endif
129
    endmethod
130
    
131
    method getBonus takes unit u returns integer
132
        local BonusValues values
133
        local integer actual
134
        local integer new
135
        local real factor
136
        
137
        if not IsUnitIndexed(u) then
138
            debug call ErrorMsg("MaxStateBonus_$NAME$.getBonus()", "Unit that is not indexed by AutoIndex given")
139
            return 0
140
        endif
141
        
142
        set values = BonusValues[u]
143
        
144
        if values == 0 then
145
            return 0
146
        endif
147
        
148
        set values.$NAME$ = R2I(GetUnitState(u, UNIT_STATE_MAX_$STATE$)) - (R2I(GetUnitState(u, UNIT_STATE_MAX_$STATE$)) - values.$NAME$)
149
        
150
        return values.$NAME$
151
    endmethod
152
    
153
    method removeBonus takes unit u returns nothing
154
        call this.setBonus(u, 0)
155
    endmethod
156
    
157
    method isValidBonus takes unit u, integer value returns boolean
158
        local integer currentBonus
159
        
160
        if not IsUnitIndexed(u) then
161
            return false
162
        endif
163
        
164
        set currentBonus = this.getBonus(u)
165
        
166
        return R2I(GetUnitState(u, UNIT_STATE_MAX_$STATE$)) - currentBonus + value >= $MIN$
167
    endmethod
168
endstruct
169
//! endtextmacro
170
171
//! runtextmacro UnitMaxStateBonus_DefineBonus("life", "LIFE", "1")
172
//! runtextmacro UnitMaxStateBonus_DefineBonus("mana", "MANA", "1")
173
174
private function OnLeaveMap takes unit u returns nothing
175
    if BonusValues[u] != 0 then
176
        call BonusValues[u].destroy()
177
    endif
178
endfunction
179
180
private function OnInit takes nothing returns nothing
181
    call OnUnitDeindexed(OnLeaveMap)
182
    
183
    set BONUS_LIFE = MaxStateBonus_life.create()
184
    set BONUS_MANA = MaxStateBonus_mana.create()
185
endfunction
186
endlibrary


Bonus Life Regen (%) and Mana Regen (Absolute)

[indent]Requirements:
  • BonusMod
  • AutoIndex

This library provides two new bonus types:
[btable=Bonus Types and Ranges]Bonus Constant:[r]
BONUS_MANA_REGEN[r]
BONUS_LIFE_REGEN_PERCENT[/btable]
These bonuses do not have minimum or maximum values. Any bonus amount is valid.

To add these bonuses to BonusMod, simply copy and paste the following library in to a custom-text trigger in your map. You may wish to change the refresh delay of the system, to either increase or decrease the time between updates.[/indent]
Bonus Life Regen (%) and Mana Regen (Absolute)
Code (jass) Select
1
library RegenBonuses initializer OnInit requires BonusMod, AutoIndex
2
////////////////////////////////////////////////////////////////////////////////
3
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
4
//@ RegenBonuses for BonusMod
5
//@=============================================================================
6
//@ Credits:
7
//@-----------------------------------------------------------------------------
8
//@    Written by:
9
//@        Earth-Fury
10
//@=============================================================================
11
//@ Requirements:
12
//@-----------------------------------------------------------------------------
13
//@ This library requires the BonusMod library, and the AutoIndex library.
14
//@
15
//@=============================================================================
16
//@ Readme:
17
//@-----------------------------------------------------------------------------
18
//@ This library provides two new bonus types:
19
//@
20
//@   - BONUS_MANA_REGEN
21
//@         Regenerates an absolute amount of mana every second
22
//@
23
//@   - BONUS_LIFE_REGEN_PERCENT
24
//@         Regenerates a percent of a unit's maximum life every second
25
//@ 
26
//@ Any value is a valid bonus. There are no limits.
27
//@ 
28
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
29
////////////////////////////////////////////////////////////////////////////////
30
31
//==============================================================================
32
// Configuration:
33
//==============================================================================
34
35
//------------------------------------------------------------------------------
36
// The following constant determines how often the life and mana bonuses are
37
// applied to units. Note that this will not effect the values of the bonuses.
38
// The values of the bonuses will always be mana per second/percentage of life
39
// per second. This constant simply determines how often the values are updated.
40
//------------------------------------------------------------------------------
41
globals
42
    private real REGEN_PERIOD = 0.1
43
endglobals
44
45
//==============================================================================
46
// End of configuration
47
//==============================================================================
48
49
globals
50
    Bonus BONUS_MANA_REGEN
51
    Bonus BONUS_LIFE_REGEN_PERCENT
52
endglobals
53
54
private function ErrorMsg takes string func, string s returns nothing
55
    call BJDebugMsg("|cffFF0000RegenBonuses Error|r|cffFFFF00:|r |cff8080FF" + func + "|r|cffFFFF00:|r " + s)
56
endfunction
57
58
globals
59
    private timer regenTimer = CreateTimer()
60
endglobals
61
62
private keyword BonusValues
63
64
private function DoRegen takes nothing returns nothing
65
    local integer i
66
    local BonusValues values
67
    
68
    // Loop over all active bonuses
69
    set i = 0
70
    loop
71
        exitwhen i == BonusValues.count
72
        
73
        set values = BonusValues.all[i]
74
        
75
        if GetWidgetLife(values.owner) > 0.0 then
76
            if values.manaRegen > 0 then
77
                // Do mana regen:
78
                call SetUnitState(values.owner, UNIT_STATE_MANA, GetUnitState(values.owner, UNIT_STATE_MANA) + (values.manaRegen * REGEN_PERIOD))
79
            endif
80
            
81
            if values.lifeRegen > 0 then
82
                // Do life regen:
83
                call SetUnitState(values.owner, UNIT_STATE_LIFE, GetUnitState(values.owner, UNIT_STATE_LIFE) + (GetUnitState(values.owner, UNIT_STATE_MAX_LIFE) * (values.lifeRegen / 100.0)) * REGEN_PERIOD)
84
            endif
85
        endif
86
        
87
        set i = i + 1
88
    endloop
89
endfunction
90
91
private struct BonusValues
92
    public integer manaRegen = 0
93
    public integer lifeRegen = 0
94
    
95
    public unit owner
96
    
97
    private static thistype array owners
98
    
99
    public static thistype array all
100
    public static integer count = 0
101
    public integer index
102
    
103
    private static method create takes unit u returns thistype
104
        local thistype this = thistype.allocate()
105
        
106
        set this.owner = u
107
        
108
        set thistype.owners[GetUnitId(u)] = this
109
        
110
        if thistype.count == 0 then
111
            call TimerStart(regenTimer, REGEN_PERIOD, true, function DoRegen)
112
        endif
113
        
114
        set thistype.all[thistype.count] = this
115
        set this.index = thistype.count
116
        set thistype.count = thistype.count + 1
117
        
118
        return this
119
    endmethod
120
    
121
    private method onDestroy takes nothing returns nothing
122
        set thistype.owners[GetUnitId(this.owner)] = thistype(0)
123
        
124
        set thistype.all[this.index] = thistype.all[thistype.count]
125
        set thistype.count = thistype.count - 1
126
        
127
        if thistype.count == 0 then
128
            call PauseTimer(regenTimer)
129
        endif
130
        
131
        set this.owner = null
132
    endmethod
133
    
134
    public static method getInstance takes unit u returns thistype
135
        if thistype.owners[GetUnitId(u)] == 0 then
136
            return thistype.create(u)
137
        else
138
            return thistype.owners[GetUnitId(u)]
139
        endif
140
    endmethod
141
    
142
    public static method operator[] takes unit u returns thistype
143
        return thistype.owners[GetUnitId(u)]
144
    endmethod
145
    
146
    public static method operator[]= takes unit u, thistype i returns nothing
147
        set thistype.owners[GetUnitId(u)] = i
148
    endmethod
149
endstruct
150
151
//! textmacro RegenBonuses_RegenBonusDefine takes NAME
152
private struct RegenBonus_$NAME$ extends Bonus
153
    integer minBonus = -2147483648
154
    integer maxBonus =  2147483647
155
    
156
    method setBonus takes unit u, integer amount returns integer
157
        local BonusValues values
158
        
159
        if not IsUnitIndexed(u) then
160
            debug call ErrorMsg("RegenBonus_$NAME$.setBonus()", "Unit that is not indexed by AutoIndex given")
161
            return 0
162
        endif
163
        
164
        set values = BonusValues.getInstance(u)
165
        set values.$NAME$Regen = amount
166
        
167
        return amount
168
    endmethod
169
    
170
    method getBonus takes unit u returns integer
171
        local BonusValues values
172
        
173
        if not IsUnitIndexed(u) then
174
            debug call ErrorMsg("RegenBonus_$NAME$.getBonus()", "Unit that is not indexed by AutoIndex given")
175
            return 0
176
        endif
177
        
178
        set values = BonusValues[u]
179
        
180
        if values == 0 then
181
            return 0
182
        endif
183
        
184
        return values.$NAME$Regen
185
    endmethod
186
    
187
    method removeBonus takes unit u returns nothing
188
        call this.setBonus(u, 0)
189
    endmethod
190
    
191
    method isValidBonus takes unit u, integer value returns boolean
192
        return IsUnitIndexed(u)
193
    endmethod
194
endstruct
195
//! endtextmacro
196
197
//! runtextmacro RegenBonuses_RegenBonusDefine("life")
198
//! runtextmacro RegenBonuses_RegenBonusDefine("mana")
199
200
private function OnLeaveMap takes unit u returns nothing
201
    if BonusValues[u] != 0 then
202
        call BonusValues[u].destroy()
203
    endif
204
endfunction
205
206
private function OnInit takes nothing returns nothing
207
    call OnUnitDeindexed(OnLeaveMap)
208
    
209
    set BONUS_MANA_REGEN         = RegenBonus_mana.create()
210
    set BONUS_LIFE_REGEN_PERCENT = RegenBonus_life.create()
211
endfunction
212
endlibrary

Bonus Movement Speed

[indent]Requirements:
  • BonusMod
  • AutoIndex

This library provides one new bonus type:
[btable=Bonus Types and Ranges]Bonus Constant:[r]
BONUS_MOVEMENT_SPEED[/btable]

The maximum and minimum bonus depends on a unit's base movement speed, and the maximum and minimum movement speed set in the gameplay constants. A unit's movement speed can not go below or above the minimum or maximum movement speed as defined in the gameplay constants.

To add this bonus to BonusMod, simply copy and paste the following library in to a custom-text trigger in your map. You must change the constants for maximum/minimum movement speed if you change the gameplay constants from their defaults.[/indent]
Bonus Movement Speed
Code (jass) Select
1
library MovementBonus initializer OnInit requires BonusMod, AutoIndex
2
////////////////////////////////////////////////////////////////////////////////
3
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
4
//@ MovementBonus for BonusMod
5
//@=============================================================================
6
//@ Credits:
7
//@-----------------------------------------------------------------------------
8
//@    Written by:
9
//@        Earth-Fury
10
//@=============================================================================
11
//@ Requirements:
12
//@-----------------------------------------------------------------------------
13
//@ This library requires the BonusMod library, and the AutoIndex library.
14
//@
15
//@=============================================================================
16
//@ Readme:
17
//@-----------------------------------------------------------------------------
18
//@ This library provides one new bonus type:
19
//@
20
//@   - BONUS_MOVEMENT_SPEED
21
//@         Modifies a unit's movement speed. 
22
//@ 
23
//@ A unit's movement speed can never go below or above the values configured
24
//@ in the gameplay constants for a map.
25
//@ 
26
//@ You should not modify a unit's movement speed any way other than via
27
//@ BonusMod. Doing so will cause issues.
28
//@ 
29
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
30
////////////////////////////////////////////////////////////////////////////////
31
32
//==============================================================================
33
// Configuration:
34
//==============================================================================
35
36
//------------------------------------------------------------------------------
37
// The following constants must be equal to the equivalent gameplay constants.
38
// By default, these are set to the default vlues for the gameplay constants.
39
//------------------------------------------------------------------------------
40
globals
41
    constant integer MAX_UNIT_MOVEMENT = 400
42
    constant integer MIN_UNIT_MOVEMENT = 150
43
    constant integer MAX_BUILDING_MOVEMENT = 400
44
    constant integer MIN_BUILDING_MOVEMENT = 25
45
endglobals
46
47
//==============================================================================
48
// End of configuration
49
//==============================================================================
50
globals
51
    Bonus BONUS_MOVEMENT_SPEED
52
endglobals
53
54
private function ErrorMsg takes string func, string s returns nothing
55
    call BJDebugMsg("|cffFF0000MovementBonus Error|r|cffFFFF00:|r |cff8080FF" + func + "|r|cffFFFF00:|r " + s)
56
endfunction
57
58
private struct BonusValue
59
    public integer speed = 0
60
    
61
    public unit owner
62
    
63
    private static thistype array owners
64
    
65
    public static thistype array all
66
    public static integer count = 0
67
    public integer index
68
    
69
    private static method create takes unit u returns thistype
70
        local thistype this = thistype.allocate()
71
        
72
        set this.owner = u
73
        
74
        set thistype.owners[GetUnitId(u)] = this
75
        
76
        set thistype.all[thistype.count] = this
77
        set this.index = thistype.count
78
        set thistype.count = thistype.count + 1
79
        
80
        return this
81
    endmethod
82
    
83
    private method onDestroy takes nothing returns nothing
84
        set thistype.owners[GetUnitId(this.owner)] = thistype(0)
85
        
86
        set thistype.all[this.index] = thistype.all[thistype.count]
87
        set thistype.count = thistype.count - 1
88
        
89
        set this.owner = null
90
    endmethod
91
    
92
    public static method getInstance takes unit u returns thistype
93
        if thistype.owners[GetUnitId(u)] == 0 then
94
            return thistype.create(u)
95
        else
96
            return thistype.owners[GetUnitId(u)]
97
        endif
98
    endmethod
99
    
100
    public static method operator[] takes unit u returns thistype
101
        return thistype.owners[GetUnitId(u)]
102
    endmethod
103
    
104
    public static method operator[]= takes unit u, thistype i returns nothing
105
        set thistype.owners[GetUnitId(u)] = i
106
    endmethod
107
endstruct
108
109
private struct MovementBonus extends Bonus
110
    integer minBonus
111
    integer maxBonus
112
    
113
    static method create takes integer min, integer max returns thistype
114
        local thistype this = allocate()
115
        
116
        set this.minBonus = min
117
        set this.maxBonus = max
118
        
119
        return this
120
    endmethod
121
    
122
    method setBonus takes unit u, integer amount returns integer
123
        local BonusValue value
124
        local integer min
125
        local integer max
126
        
127
        local integer actualSpeed
128
        
129
        if not IsUnitIndexed(u) then
130
            debug call ErrorMsg("MovementBonus.setBonus()", "Unit that is not indexed by AutoIndex given")
131
            return 0
132
        endif
133
        
134
        set value = BonusValue.getInstance(u)
135
        
136
        if IsUnitType(u, UNIT_TYPE_STRUCTURE) then
137
            set min = MIN_BUILDING_MOVEMENT
138
            set max = MAX_BUILDING_MOVEMENT
139
        else
140
            set min = MIN_UNIT_MOVEMENT
141
            set max = MAX_UNIT_MOVEMENT
142
        endif
143
        
144
        set actualSpeed = R2I(GetUnitMoveSpeed(u)) - value.speed
145
        
146
        if actualSpeed + amount < min then
147
            debug call ErrorMsg("MovementBonus.setBonus()", "Attempting to set a unit's speed below it's minimum")
148
            set amount = -actualSpeed + min
149
        elseif actualSpeed + amount > max then
150
            debug call ErrorMsg("MovementBonus.setBonus()", "Ateempting to set a unit's speed above it's maximum")
151
            set amount = max - actualSpeed
152
        endif
153
        
154
        call SetUnitMoveSpeed(u, actualSpeed + amount)
155
        set value.speed = amount
156
        
157
        return amount
158
    endmethod
159
    
160
    method getBonus takes unit u returns integer
161
        local BonusValue value
162
        
163
        if not IsUnitIndexed(u) then
164
            debug call ErrorMsg("MovementBonus.getBonus()", "Unit that is not indexed by AutoIndex given")
165
            return 0
166
        endif
167
        
168
        set value = BonusValue[u]
169
        
170
        if value == 0 then
171
            return 0
172
        endif
173
        
174
        return value.speed
175
    endmethod
176
    
177
    method removeBonus takes unit u returns nothing
178
        call this.setBonus(u, 0)
179
    endmethod
180
    
181
    method isValidBonus takes unit u, integer value returns boolean
182
        local integer min
183
        local integer max
184
        
185
        local integer currentBonus
186
        local integer actualSpeed
187
        
188
        if not IsUnitIndexed(u) then
189
            return false
190
        endif
191
        
192
        if IsUnitType(u, UNIT_TYPE_STRUCTURE) then
193
            set min = MIN_BUILDING_MOVEMENT
194
            set max = MAX_BUILDING_MOVEMENT
195
        else
196
            set min = MIN_UNIT_MOVEMENT
197
            set max = MAX_UNIT_MOVEMENT
198
        endif
199
        
200
        set actualSpeed = R2I(GetUnitMoveSpeed(u)) - this.getBonus(u)
201
        
202
        if actualSpeed + value < min then
203
            return false
204
        elseif actualSpeed + value > max then
205
            return false
206
        else
207
            return true
208
        endif
209
    endmethod
210
endstruct
211
212
private function OnLeaveMap takes unit u returns nothing
213
    if BonusValue[u] != 0 then
214
        call BonusValue[u].destroy()
215
    endif
216
endfunction
217
218
private function OnInit takes nothing returns nothing
219
    local integer min
220
    local integer max
221
    
222
    call OnUnitDeindexed(OnLeaveMap)
223
    
224
    if MAX_UNIT_MOVEMENT > MAX_BUILDING_MOVEMENT then
225
        set max = MAX_UNIT_MOVEMENT
226
    else
227
        set max = MAX_BUILDING_MOVEMENT
228
    endif
229
    
230
    if MIN_UNIT_MOVEMENT < MIN_BUILDING_MOVEMENT then
231
        set min = MIN_UNIT_MOVEMENT
232
    else
233
        set min = MIN_BUILDING_MOVEMENT
234
    endif
235
    
236
    set BONUS_MOVEMENT_SPEED = MovementBonus.create(min, max)
237
endfunction
238
endlibrary

[/indent]
Demo Map
[indent]Below is the demo map for BonusMod. You can download it to test out the functionality.

[/indent]
Change Log

BonusMod & Additional Bonuses Change Log

  • 3.3.1:
    • Movement Speed bonus added

  • 3.3.0:
    • Switched ability creation to lua
    • Minor optimizations

  • 3.0 - 3.2:
    • Complete rewrite
    • Bug fixes

  • 1.0 - 2.0:
    • Ancient versions
  • BonusMod 3.3.1.w3x (100.54 KB)