Name | Type | is_array | initial_value |
//TESH.scrollpos=168
//TESH.alwaysfold=0
library BonusMod initializer OnInit requires optional AbilityPreload, optional xepreload
private keyword AbilityBonus
////////////////////////////////////////////////////////////////////////////////
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@ BonusMod - v3.3.1
//@=============================================================================
//@ Credits:
//@-----------------------------------------------------------------------------
//@ Written by:
//@ Earth-Fury
//@ Based on the work of:
//@ weaaddar
//@-----------------------------------------------------------------------------
//@ If you use this system, please at least credit weaaddar. Without him, this
//@ system would not exist. I would be happy if you credited me as well.
//@=============================================================================
//@ Requirements:
//@-----------------------------------------------------------------------------
//@ This library is written in vJass and thus requires JASS Helper in order to
//@ function correctly. This library also uses the ObjectMerger created by
//@ PitzerMike. The ObjectMerger must be configured as an external tool for
//@ JASS Helper.
//@
//@ All of these things are present in the NewGen world editor.
//@
//@=============================================================================
//@ Introduction:
//@-----------------------------------------------------------------------------
//@ BonusMod is a system for applying reversible bonuses to certain stats, such
//@ as attack speed or mana regen, for specific units. Most of the bonuses
//@ provided by BonusMod show green or red numbers in the command card, exactly
//@ like the bonuses provided by items.
//@
//@ BonusMod has two kinds of bonuses:
//@ 1. Ability based bonuses
//@ 2. Code based bonuses
//@
//@ All of the bonuses in the configuration section for the basic BonusMod
//@ library are ability-based bonuses. Code-based bonuses are provided by
//@ additional libraries.
//@
//@ Ability based bonuses have a limit to how much of a bonus they can apply.
//@ The actual limit depends on the number of abilities that type of bonus uses.
//@ See the "Default bonuses" section of this readme for the default limits
//@ of the bonuses that come with BonusMod. For changing the limits of the
//@ default bonuses, or for adding new types of bonuses, see the below
//@ configuration section.
//@
//@ Code based bonuses may or may not have a limit to how much of a bonus they
//@ can apply. The limits for code based bonuses depend entirely on how the
//@ bonus is implemented. See their documentation for more information.
//@
//@=============================================================================
//@ Adding BonusMod to your map:
//@-----------------------------------------------------------------------------
//@ First, you must place the BonusMod library in a custom-text trigger in your
//@ map.
//@
//@ You must then save your map with ability generation enabled. After you save
//@ your map with ability generation enabled, you must close your map in the
//@ editor, and reopen it. You can then disable ability generation.
//@ See the configuration section for information on how to enable and disable
//@ ability generation.
//@
//@=============================================================================
//@ Default bonuses:
//@-----------------------------------------------------------------------------
//@
//@ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//@ | Bonus Type constants: | Minimum bonus: | Maximum bonus: |
//@ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//@ | BONUS_SIGHT_RANGE | -2048 | +2047 |
//@ | BONUS_ATTACK_SPEED | -512 | +511 |
//@ | BONUS_ARMOR | -1024 | +1023 |
//@ | BONUS_MANA_REGEN_PERCENT | -512% | +511% |
//@ | BONUS_LIFE_REGEN | -256 | +255 |
//@ | BONUS_DAMAGE | -1024 | +1023 |
//@ | BONUS_STRENGTH | -256 | +255 |
//@ | BONUS_AGILITY | -256 | +255 |
//@ | BONUS_INTELLIGENCE | -256 | +255 |
//@ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//@
//@ Notes:
//@ - The bonuses for stength, agility, and intelligence can only be
//@ applied to heroes. Attempting to add them to normal units will
//@ fail to work completely.
//@ - Using a negative BONUS_STRENGTH bonus can give a unit negative
//@ maximum life. Don't do that. It really messes stuff up.
//@ - Using a negative BONUS_INTELLIGENCE bonus can remove a hero's
//@ mana. This is not a big issue, as mana will return when the
//@ bonus is removed.
//@ - The maximum effective sight range for a unit is 1800.
//@ - There is a maximum attack speed. I have no idea what it is.
//@
//@ See the configuration section for information on how to change the range of
//@ bonuses, as well as how to add new ability-based bonuses, and remove unused
//@ ones.
//@
//@=============================================================================
//@ Public API / Function list:
//@-----------------------------------------------------------------------------
//@ Note that BonusMod will only output error messages if JASS Helper is set to
//@ compile in debug mode.
//@
//@ Bonus constants such as BONUS_DAMAGE have .min and .max properties which
//@ are the minimum and maximum bonus that type of bonus can apply. Note that
//@ for code based bonuses, these constants may not reflect the minimum or
//@ maximum bonus for a specific unit. Use the IsBonusValid() function to check
//@ if the given bonus value is okay for a given unit.
//@
//@ function SetUnitBonus
//@ takes unit u, Bonus bonusType, integer amount
//@ returns integer
//@
//@ This function sets the bonus of the type bonusType for the given unit to
//@ the given amount. The returned integer is the unit's actual current
//@ bonus, after it has been changed. If the given amount is above the
//@ maximum possible bonus for this type, then the maximum possible bonus
//@ is applied to the unit. The same is true if the given value is below
//@ the minimum possible bonus.
//@
//@ function GetUnitBonus
//@ takes unit u, Bonus bonusType
//@ returns integer
//@
//@ Returns the given unit's current bonus of bonusType. A value of 0 means
//@ that the given unit does not have a bonus of the given type.
//@
//@ function AddUnitBonus
//@ takes unit u, Bonus bonusType, integer amount
//@ returns integer
//@
//@ Increases the unit's bonus by the given amount. You can use a negitive
//@ amount to subtract from the unit's current bonus. Note that the same
//@ rules SetUnitBonus has apply for going over/under the maximum bonus.
//@ The returned value is the unit's actual new bonus.
//@
//@ function RemoveUnitBonus
//@ takes unit u, Bonus bonusType
//@ returns nothing
//@
//@ Sets the bonus of the type bonusType to 0 for the given unit. This
//@ function is faster then using SetUnitBonus(u, bonusType, 0).
//@
//@ function IsBonusValid
//@ takes unit u, Bonus abstractBonus, integer value
//@ returns boolean
//@
//@ Returns true if the given value is a valid bonus value for the given
//@ unit. This will also return false if the given bonus type is a hero-
//@ only bonus type, and the given unit is not a hero.
//@
//@=============================================================================
//@ Writing code-based bonuses:
//@-----------------------------------------------------------------------------
//@ This section of the readme tells you how to create your own bonus types
//@ that apply their bonuses using vJass code instead of abilities. You do not
//@ need to read or understand this to use BonusMod as-is.
//@
//@ Creating a new bonus type is simple. Extend the Bonus struct, implement the
//@ methods provided within it, and create a single instance of your struct
//@ within a variable named BONUS_YOUR_BONUS_TYPES_NAME of the type Bonus.
//@
//@ The methods you must implement are:
//@
//@ method setBonus takes unit u, integer amount returns integer
//@ This method sets the given unit's current bonus to amount, returning
//@ the actual bonus that was applied. If the given amount is higher then
//@ the maximum amount your bonus type can apply to a unit, you must apply
//@ the maximum possible bonus, and return that amount. The same holds true
//@ for the minimum bonus.
//@
//@ method getBonus takes unit u returns integer
//@ This method returns the current bonus the given unit has.
//@
//@ method removeBonus takes unit u returns nothing
//@ This method sets the current bonus of the given unit to 0.
//@
//@ method isValueInRange takes integer value returns boolean
//@ This method returns true if the given integer is a valid bonus amount
//@ for this bonus type, and false otherwise.
//@
//@ Note that it is your responsibility to do any clean up in the event a unit
//@ dies or is removed with an active bonus on it. There is no guarantee that
//@ removeBonus() will be called before a unit dies or is removed.
//@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
////////////////////////////////////////////////////////////////////////////////
//==============================================================================
// Configuration:
//==============================================================================
//------------------------------------------------------------------------------
// If the following constant is set to true, the abilities used by this library
// will be preloaded at map initialization. This will slightly increase loading
// time, but will prevent a slight to medium lag spike the first time a bonus
// of a type is applied.
//
// Note that your map must contain either the xepreload library, or the
// AbilityPreload library for preloading to work.
//
// It is highly recommended that you do not set this to false.
//------------------------------------------------------------------------------
globals
private constant boolean PRELOAD_ABILITIES = false
endglobals
//------------------------------------------------------------------------------
// The BonusMod_BeginBonuses macro takes a single boolean type parameter.
// If set to true, bonus abilities will be created (or recreated) on save.
// If set to false, abilities will not be generated.
//
// If you modify any of the bonus declaration macros, or add new ones, you must
// regenerate abilities.
//
// Note that if you remove a bonus, the abilities it had created will not be
// automatically removed. This is also true of reducing the number of abilities
// a bonus uses.
//
// After you generate abilities, you must close your map and reopen it in the
// editor. You can then disable ability generation until the next time you
// modify the bonus types.
//------------------------------------------------------------------------------
//! runtextmacro BonusMod_BeginBonuses("false")
//--------------------------------------------------------------------------
// Below are where bonus types are defined.
//
// The first parameter is the name of the bonus type. A constant will be
// generated for each bonus type, that will take the form: BONUS_NAME
//
// The second parameter is the maximum power of 2 the bonus type can add
// to a unit. For example, 8 abilities gives a range of -256 to +255.
//
// The third parameter is the base ability. The base ability must give the
// desired effect when the given field is changed.
//
// The fourth parameter is the rawcode prefix of the bonuses generated
// abilities. The prefix must be 3 characters long. Your map must not
// already contain bonuses which start with the given prefix.
//
// The fifth parameter is the object field to modify for each generated
// ability.
//
// The sixth parameter must be true of the bonus should only work on hero
// units, and false otherwise.
//
// The final parameter is the icon that will be displayed in the object
// editor. This has no effect on anything ingame.
//--------------------------------------------------------------------------
// | NAME |ABILITY|SOURCE |PREFIX|FIELD | HERO | ICON
// | | COUNT |ABILITY| | | ONLY |
//! runtextmacro BonusMod_DeclareBonus("ARMOR", "10", "AId1", "BMA", "Idef", "false", "BTNHumanArmorUpOne.blp")
//! runtextmacro BonusMod_DeclareBonus("DAMAGE", "10", "AItg", "BMD", "Iatt", "false", "BTNSteelMelee.blp")
//! runtextmacro BonusMod_DeclareBonus("SIGHT_RANGE", "11", "AIsi", "BMN", "Isib", "false", "BTNTelescope.blp")
//! runtextmacro BonusMod_DeclareBonus("LIFE_REGEN", "8", "Arel", "BME", "Ihpr", "false", "BTNRingSkull.blp")
//! runtextmacro BonusMod_DeclareBonus("STRENGTH", "8", "AIa1", "BMF", "Istr", "true" , "BTNGoldRing.blp")
//! runtextmacro BonusMod_DeclareBonus("AGILITY", "8", "AIa1", "BMG", "Iagi", "true" , "BTNGoldRing.blp")
//! runtextmacro BonusMod_DeclareBonus("INTELLIGENCE", "8", "AIa1", "BMI", "Iint", "true" , "BTNGoldRing.blp")
// | NAME |ABILITY|SOURCE |PREFIX|FIELD |HERO | ICON
// | | COUNT |ABILITY| | | ONLY |
//! runtextmacro BonusMod_DeclarePercentBonus("ATTACK_SPEED", "9", "AIsx", "BMS", "Isx1", "false", "BTNGlove.blp")
//! runtextmacro BonusMod_DeclarePercentBonus("MANA_REGEN_PERCENT", "9", "AIrm", "BMM", "Imrp", "false", "BTNSobiMask.blp")
//! runtextmacro BonusMod_EndBonuses()
//==============================================================================
// End of configuration
//==============================================================================
//! textmacro BonusMod_BeginBonuses takes SHOULD_GENERATE_ABILITIES
private function Setup takes nothing returns nothing
// The following is a lua script for the ObjectMerger, used to generate abilities
//*
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i if "$SHOULD_GENERATE_ABILITIES$" == "true" then
//! i function FormatName(name)
//! i name = string.lower(name)
//! i name = string.gsub(name, "_", " ")
//! i s = name
//! i name = ""
//! i for w in string.gmatch(s, "%a%w*") do
//! i name = name .. string.upper(string.sub(w, 1, 1)) .. string.sub(w, 2, -1)
//! i name = name .. " "
//! i end
//! i name = string.sub(name, 1, string.len(name) - 1)
//! i return name
//! i end
//! i function SetupAbility(name, suffix, icon, hero)
//! i makechange(current, "anam", "BonusMod - " .. FormatName(name))
//! i makechange(current, "ansf", "(" .. suffix .. ")")
//! i makechange(current, "aart", "ReplaceableTextures\\CommandButtons\\" .. icon)
//! i makechange(current, "aite", 0)
//! i if hero then
//! i makechange(current, "Iagi", 1, 0)
//! i makechange(current, "Iint", 1, 0)
//! i makechange(current, "Istr", 1, 0)
//! i end
//! i end
//! i function CreateAbility(sourceAbility, prefix, field, abilityCount, name, icon)
//! i powOf2 = abilityCount - 1
//! i lengthOfMax = string.len(tostring(2^abilityCount))
//! i for i = 0, powOf2 do
//! i padding = ""
//! i for k = 0, lengthOfMax - string.len(tostring(2^i)) - 1 do
//! i padding = padding .. "0"
//! i end
//! i createobject(sourceAbility, prefix .. string.sub(chars, i + 1, i + 1))
//! i SetupAbility(name, "+" .. padding .. tostring(2 ^ i), icon, true)
//! i makechange(current, field, 1, tostring(2^i))
//! i end
//! i createobject(sourceAbility, prefix .. "-")
//! i SetupAbility(name, "-" .. tostring(2 ^ abilityCount), icon, true)
//! i makechange(current, field, 1, tostring(-(2^abilityCount)))
//! i end
//! i function CreatePercentageAbility(sourceAbility, prefix, field, abilityCount, name, icon)
//! i powOf2 = abilityCount - 1
//! i lengthOfMax = string.len(tostring(2^abilityCount))
//! i for i = 0, powOf2 do
//! i padding = ""
//! i for k = 0, lengthOfMax - string.len(tostring(2^i)) - 1 do
//! i padding = padding .. "0"
//! i end
//! i createobject(sourceAbility, prefix .. string.sub(chars, i + 1, i + 1))
//! i SetupAbility(name, "+" .. padding .. tostring(2 ^ i) .. "%", icon, false)
//! i makechange(current, field, 1, tostring((2 ^ i) / 100))
//! i end
//! i createobject(sourceAbility, prefix .. "-")
//! i SetupAbility(name, "-" .. tostring(2 ^ abilityCount) .. "%", icon, false)
//! i makechange(current, field, 1, tostring(-((2 ^ abilityCount) / 100)))
//! i end
//! i setobjecttype("abilities")
//! i chars = "abcdefghijklmnopqrstuvwxyz"
//! i
// */
//! endtextmacro
//! textmacro BonusMod_DeclareBonus takes NAME, ABILITY_COUNT, SOURCE_ABILITY, RAWCODE_PREFIX, FIELD, HERO_ONLY, ICON
//! i CreateAbility("$SOURCE_ABILITY$", "$RAWCODE_PREFIX$", "$FIELD$", $ABILITY_COUNT$, "$NAME$", "$ICON$")
globals
Bonus BONUS_$NAME$
endglobals
set BONUS_$NAME$ = AbilityBonus.create('$RAWCODE_PREFIX$a', $ABILITY_COUNT$, '$RAWCODE_PREFIX$-', $HERO_ONLY$)
//! endtextmacro
//! textmacro BonusMod_DeclarePercentBonus takes NAME, ABILITY_COUNT, SOURCE_ABILITY, RAWCODE_PREFIX, FIELD, HERO_ONLY, ICON
//! i CreatePercentageAbility("$SOURCE_ABILITY$", "$RAWCODE_PREFIX$", "$FIELD$", $ABILITY_COUNT$, "$NAME$", "$ICON$")
globals
Bonus BONUS_$NAME$
endglobals
set BONUS_$NAME$ = AbilityBonus.create('$RAWCODE_PREFIX$a', $ABILITY_COUNT$, '$RAWCODE_PREFIX$-', $HERO_ONLY$)
//! endtextmacro
//! textmacro BonusMod_EndBonuses
//*
//! i end
//! endexternalblock
// */
endfunction
//! endtextmacro
// ===
// Precomputed integer powers of 2
// ===
globals
private integer array powersOf2
private integer powersOf2Count = 0
endglobals
// ===
// Utility functions
// ===
private function ErrorMsg takes string func, string s returns nothing
call BJDebugMsg("|cffFF0000BonusMod Error|r|cffFFFF00:|r |cff8080FF" + func + "|r|cffFFFF00:|r " + s)
endfunction
private function LoadAbility takes integer abilityId returns nothing
static if PRELOAD_ABILITIES then
static if LIBRARY_xepreload then
call XE_PreloadAbility(abilityId)
else
static if LIBRARY_AbilityPreload then
call AbilityPreload(abilityId)
endif
endif
endif
endfunction
// ===
// Bonus Types
// ===
private interface BonusInterface
integer minBonus = 0
integer maxBonus = 0
private method destroy takes nothing returns nothing defaults nothing
endinterface
private keyword isBonusObject
struct Bonus extends BonusInterface
boolean isBonusObject = false
public static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set this.isBonusObject = true
return this
endmethod
stub method setBonus takes unit u, integer amount returns integer
debug call ErrorMsg("Bonus.setBonus()", "I have no idea how or why you did this, but don't do it.")
return 0
endmethod
stub method getBonus takes unit u returns integer
debug call ErrorMsg("Bonus.getBonus()", "I have no idea how or why you did this, but don't do it.")
return 0
endmethod
stub method removeBonus takes unit u returns nothing
call this.setBonus(u, 0)
endmethod
stub method isValidBonus takes unit u, integer value returns boolean
return true
endmethod
method operator min takes nothing returns integer
return this.minBonus
endmethod
method operator max takes nothing returns integer
return this.maxBonus
endmethod
endstruct
private struct AbilityBonus extends Bonus
public integer count
public integer rawcode
public integer negativeRawcode
public integer minBonus = 0
public integer maxBonus = 0
public boolean heroesOnly
public static method create takes integer rawcode, integer count, integer negativeRawcode, boolean heroesOnly returns thistype
local thistype bonus = thistype.allocate()
local integer i
debug local boolean error = false
// Error messages
static if DEBUG_MODE then
if rawcode == 0 then
call ErrorMsg("AbilityBonus.create()", "Bonus constructed with a rawcode of 0?!")
call bonus.destroy()
return 0
endif
if count < 0 or count == 0 then
call ErrorMsg("AbilityBonus.create()", "Bonus constructed with an ability count <= 0?!")
call bonus.destroy()
return 0
endif
endif
// Grow powers of 2
if powersOf2Count < count then
set i = powersOf2Count
loop
exitwhen i > count
set powersOf2[i] = 2 * powersOf2[i - 1]
set i = i + 1
endloop
set powersOf2Count = count
endif
// Preload this bonus' abilities
static if PRELOAD_ABILITIES then
set i = 0
loop
exitwhen i == count
call LoadAbility(rawcode + i)
set i = i + 1
endloop
if negativeRawcode != 0 then
call LoadAbility(negativeRawcode)
endif
endif
// Set up this bonus object
set bonus.count = count
set bonus.negativeRawcode = negativeRawcode
set bonus.rawcode = rawcode
set bonus.heroesOnly = heroesOnly
// Calculate the minimum and maximum bonuses
if negativeRawcode != 0 then
set bonus.minBonus = -powersOf2[count]
else
set bonus.minBonus = 0
endif
set bonus.maxBonus = powersOf2[count] - 1
// Return the bonus object
return bonus
endmethod
// Interface methods:
method setBonus takes unit u, integer amount returns integer
return SetUnitBonus.evaluate(u, this, amount)
endmethod
method getBonus takes unit u returns integer
return GetUnitBonus.evaluate(u, this)
endmethod
method removeBonus takes unit u returns nothing
call RemoveUnitBonus.evaluate(u, this)
endmethod
public method isValidBonus takes unit u, integer value returns boolean
return (value >= this.minBonus) and (value <= this.maxBonus)
endmethod
endstruct
// ===
// Public API
// ===
function IsBonusValid takes unit u, Bonus abstractBonus, integer value returns boolean
local AbilityBonus bonus = AbilityBonus(abstractBonus)
static if DEBUG_MODE then
if not abstractBonus.isBonusObject then
call ErrorMsg("IsBonusValid()", "Invalid bonus type given")
endif
endif
if abstractBonus.min > value or abstractBonus.max < value then
return false
endif
if abstractBonus.getType() != AbilityBonus.typeid then
return abstractBonus.isValidBonus(u, value)
endif
if bonus.heroesOnly and not IsUnitType(u, UNIT_TYPE_HERO) then
return false
endif
return (value >= bonus.minBonus) and (value <= bonus.maxBonus)
endfunction
function RemoveUnitBonus takes unit u, Bonus abstractBonus returns nothing
local integer i = 0
local AbilityBonus bonus = AbilityBonus(abstractBonus)
static if DEBUG_MODE then
if not abstractBonus.isBonusObject then
call ErrorMsg("RemoveUnitBonus()", "Invalid bonus type given")
endif
endif
if abstractBonus.getType() != AbilityBonus.typeid then
call abstractBonus.removeBonus(u)
return
endif
if bonus.heroesOnly and not IsUnitType(u, UNIT_TYPE_HERO) then
debug call ErrorMsg("RemoveUnitBonus()", "Trying to remove a hero-only bonus from a non-hero unit")
return
endif
call UnitRemoveAbility(u, bonus.negativeRawcode)
loop
exitwhen i == bonus.count
call UnitRemoveAbility(u, bonus.rawcode + i)
set i = i + 1
endloop
endfunction
function SetUnitBonus takes unit u, Bonus abstractBonus, integer amount returns integer
local integer i
local integer output = 0
local AbilityBonus bonus = AbilityBonus(abstractBonus)
local boolean applyMinBonus = false
static if DEBUG_MODE then
if not abstractBonus.isBonusObject then
call ErrorMsg("SetUnitBonus()", "Invalid bonus type given")
endif
endif
if amount == 0 then
call RemoveUnitBonus(u, bonus)
return 0
endif
if abstractBonus.getType() != AbilityBonus.typeid then
return abstractBonus.setBonus(u, amount)
endif
if bonus.heroesOnly and not IsUnitType(u, UNIT_TYPE_HERO) then
debug call ErrorMsg("SetUnitBonus()", "Trying to set a hero-only bonus on a non-hero unit")
return 0
endif
if amount < bonus.minBonus then
debug call ErrorMsg("SetUnitBonus()", "Attempting to set a bonus to below its min value")
set amount = bonus.minBonus
elseif amount > bonus.maxBonus then
debug call ErrorMsg("SetUnitBonus()", "Attempting to set a bonus to above its max value")
set amount = bonus.maxBonus
endif
if amount < 0 then
set amount = -(bonus.minBonus - amount)
set applyMinBonus = true
endif
call UnitRemoveAbility(u, bonus.negativeRawcode)
set i = bonus.count - 1
loop
exitwhen i < 0
if amount >= powersOf2[i] then
call UnitAddAbility(u, bonus.rawcode + i)
call UnitMakeAbilityPermanent(u, true, bonus.rawcode + i)
static if DEBUG_MODE then
if GetUnitAbilityLevel(u, bonus.rawcode + i) <= 0 then
call ErrorMsg("SetUnitBonus()", "Failed to give the 2^" + I2S(i) + " ability to the unit!")
endif
endif
set amount = amount - powersOf2[i]
set output = output + powersOf2[i]
else
call UnitRemoveAbility(u, bonus.rawcode + i)
static if DEBUG_MODE then
if GetUnitAbilityLevel(u, bonus.rawcode + i) > 0 then
call ErrorMsg("SetUnitBonus()", "Unit still has the 2^" + I2S(i) + " ability after it was removed!")
endif
endif
endif
set i = i - 1
endloop
if applyMinBonus then
call UnitAddAbility(u, bonus.negativeRawcode)
call UnitMakeAbilityPermanent(u, true, bonus.negativeRawcode)
else
call UnitRemoveAbility(u, bonus.negativeRawcode)
endif
return output
endfunction
function GetUnitBonus takes unit u, Bonus abstractBonus returns integer
local integer i = 0
local integer amount = 0
local AbilityBonus bonus = AbilityBonus(abstractBonus)
static if DEBUG_MODE then
if not abstractBonus.isBonusObject then
call ErrorMsg("GetUnitBonus()", "Invalid bonus type given")
endif
endif
if abstractBonus.getType() != AbilityBonus.typeid then
return abstractBonus.getBonus(u)
endif
if bonus.heroesOnly and not IsUnitType(u, UNIT_TYPE_HERO) then
debug call ErrorMsg("GetUnitBonus()", "Trying to get a hero-only bonus from a non-hero unit")
return 0
endif
if GetUnitAbilityLevel(u, bonus.negativeRawcode) > 0 then
set amount = bonus.minBonus
endif
loop
exitwhen i == bonus.count
if GetUnitAbilityLevel(u, bonus.rawcode + i) > 0 then
set amount = amount + powersOf2[i]
endif
set i = i + 1
endloop
return amount
endfunction
function AddUnitBonus takes unit u, Bonus bonus, integer amount returns integer
return SetUnitBonus(u, bonus, GetUnitBonus(u, bonus) + amount)
endfunction
// ===
// Initialization
// ===
private function OnInit takes nothing returns nothing
local integer i
// Set up powers of 2
set powersOf2[0] = 1
set powersOf2Count = 1
static if DEBUG_MODE and PRELOAD_ABILITIES and not LIBRARY_xepreload and not LIBRARY_AbilityPreload then
call ErrorMsg("Initialization", "PRELOAD_ABILITIES is set to true, but neither usable preloading library is detected")
endif
// Setup bonuses
call Setup()
endfunction
endlibrary
//TESH.scrollpos=15
//TESH.alwaysfold=0
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for debug purposes
private hashtable ht = InitHashtable()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
private hashtable H = InitHashtable()
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer id = GetHandleId(g)
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function Filter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(H, 0, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
call RemoveSavedHandle(H, 0, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function Filter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(H, 0, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(H, 0, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function Filter), filter)
call SaveBooleanExprHandle(H, 0, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
//Set variables to new values
set X = x
set Y = y
set R = radius
//Enumerate
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
//TESH.scrollpos=137
//TESH.alwaysfold=0
scope CoverofDarkness
/*_______________________________________________________________
| Author: Kruemel-Monster
| Version: 1.0
| Requirements:
| -BonusMod (Earth-Fury)
| Description:
| -Disables the shared sight for all enemys Players
| -Decrease the sightrange of all enemy Units
| Tooltip:
| Turns the day into such a deep night, that all enemys
| get a defective sight. So every foe can only fight alone.
|
| Some Bugs:
| -It could malfunctions if you cange the Alliance while on Effect
| -Every unit could attack with autoattack over her sightrange ( so they attack you even if they dont see you )
| This is not rightly tested, i only saw this effect on neutral creeps as they having 0 sightrange
|
| How to import:
| 1. Copy the BonusMod trigger and this trigger into your map
| Info: 2 -> 5. is only if you dont have the Bonus Mod abilities
| 2. Search inside the BonusMod trigger for :
| -> runtextmacro BonusMod_BeginBonuses("false") <- line 225
| Replace the false into a true
| -> runtextmacro BonusMod_BeginBonuses("true")
| 3. Save the map, close the map and reopen the map
| 4. Now you should have like 80 Abilities in you Object Editor
| 5. if that is true, you can change the line again into false
| -> runtextmacro BonusMod_BeginBonuses("false")
| 6. Copy the ability Cover of Darkness into your map
| 7. Search inside this trigger for the global TESTMODE and set it to false
|_________________________________________________________________
*/ // Ye ye i know my Descitption sucks ;D
globals
private constant boolean TESTMODE = true
// It would be lame if your units are not under the effect of the Spell
// The TESTMODE allows you to affect only allies
// TESTMODE = true ::: You and your allies are affected
// TESTMODE = false ::: Your enemys are affected
private constant integer ABIL_ID = 'A000'
// The Base Ability Id
private constant integer SIGHT_MALUS = -575
// The Amount of Sightrange loose
// Be carful the Spell makes a night so all units have a night sight range
// And the normal night sight range is 800
private constant string CASTER_SFX = "Abilities\\Spells\\Other\\HowlOfTerror\\HowlCaster.mdl"
// The Effect on the Caster
endglobals
// It is important that the Spell Duration is equal to the Duration of the Ability
private constant function GetSpellDuration takes integer level returns real
return ((level*2.)+5)
endfunction
// CODE STARTS : READ AND CHANGE AT OWN RISK
globals
private constant real TIME_OUT = 0.1
endglobals
private struct Spell
real Time
unit Caster
integer Level
static Spell array Active
static integer Count = 0
static timer Timer = CreateTimer()
static trigger Trigger = CreateTrigger()
static force Force = CreateForce()
static force Force2 = CreateForce()
static group Group = CreateGroup()
static integer array Multi
static player CurPlayer
static boolean flag_add
method release takes integer number returns nothing
set Count = Count - 1
set Active[number] = Active[Count]
call destroy()
endmethod
static method EnumForce_change takes nothing returns boolean
local integer pId = GetPlayerId(CurPlayer)
if CurPlayer != GetFilterPlayer() then // This is some funny Bug, if you Disable the Shared Visison to your self all units wont see anything ;)
if flag_add then
set Multi[pId] = Multi[pId] - 1
if Multi[pId] == 0 then
call SetPlayerAlliance(CurPlayer,GetFilterPlayer(),ALLIANCE_SHARED_VISION,flag_add)
call SetPlayerAlliance(GetFilterPlayer(),CurPlayer,ALLIANCE_SHARED_VISION,flag_add)
endif
else
set Multi[pId] = Multi[pId] + 1
call SetPlayerAlliance(CurPlayer,GetFilterPlayer(),ALLIANCE_SHARED_VISION,flag_add)
call SetPlayerAlliance(GetFilterPlayer(),CurPlayer,ALLIANCE_SHARED_VISION,flag_add)
endif
endif
return false
endmethod
static method EnumGroup_change takes nothing returns boolean
if not(flag_add) then
call SetUnitBonus(GetFilterUnit(),BONUS_SIGHT_RANGE,SIGHT_MALUS)
else
if Multi[GetPlayerId(CurPlayer)] == 0 then
call SetUnitBonus(GetFilterUnit(),BONUS_SIGHT_RANGE,0)
endif
endif
return false
endmethod
static method EnumForce_enemys takes nothing returns boolean
set CurPlayer = GetFilterPlayer()
call ForceEnumAllies(Force2,CurPlayer,function Spell.EnumForce_change)
call ForceClear(Force2)
call GroupEnumUnitsOfPlayer(Group,CurPlayer,function Spell.EnumGroup_change)
call GroupClear(Group)
return false
endmethod
static method onloop takes nothing returns nothing
local Spell this
local integer index = 0
loop
exitwhen index >= Count
set this = Active[index]
set Time = Time + TIME_OUT
if Time >= GetSpellDuration(Level) then
set flag_add = true
static if TESTMODE then
call ForceEnumAllies(Force,GetOwningPlayer(Caster),function Spell.EnumForce_enemys)
set CurPlayer = Player(12)
call GroupEnumUnitsOfPlayer(Group,Player(12),function Spell.EnumGroup_change)
call GroupClear(Group)
else
call ForceEnumEnemies(Force,GetOwningPlayer(Caster),function Spell.EnumForce_enemys)
set CurPlayer = Player(12)
call GroupEnumUnitsOfPlayer(Group,Player(12),function Spell.EnumGroup_change)
call GroupClear(Group)
endif
call ForceClear(Force)
set Caster = null
call release(index)
else
set index = index + 1
endif
endloop
if Count == 0 then
call PauseTimer(Timer)
endif
endmethod
static method create takes nothing returns Spell
local Spell this
if GetSpellAbilityId() == ABIL_ID then
set this = allocate()
set Active[Count] = this
set Count = Count + 1
set Time = 0
set flag_add = false
set Caster = GetTriggerUnit()
set Level = GetUnitAbilityLevel(Caster,ABIL_ID)
call DestroyEffect(AddSpecialEffectTarget(CASTER_SFX,Caster,"origin"))
static if TESTMODE then
call ForceEnumAllies(Force,GetOwningPlayer(Caster),function Spell.EnumForce_enemys)
set CurPlayer = Player(12)
call GroupEnumUnitsOfPlayer(Group,Player(12),function Spell.EnumGroup_change)
call GroupClear(Group)
else
call ForceEnumEnemies(Force,GetOwningPlayer(Caster),function Spell.EnumForce_enemys)
set CurPlayer = Player(12)
call GroupEnumUnitsOfPlayer(Group,Player(12),function Spell.EnumGroup_change)
call GroupClear(Group)
endif
call ForceClear(Force)
if Count == 1 then
call TimerStart(Timer,TIME_OUT,true,function Spell.onloop)
endif
endif
return this
endmethod
static method onInit takes nothing returns nothing
call TriggerRegisterAnyUnitEventBJ(Trigger,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(Trigger,function Spell.create)
endmethod
endstruct
endscope
//TESH.scrollpos=137
//TESH.alwaysfold=0
scope CoverofDarkness
/*_______________________________________________________________
| Author: Kruemel-Monster
| Version: 1.0
| Requirements:
| -BonusMod (Earth-Fury)
| -GroupUtils (Rising_Dusk)
| Description:
| -Disables the shared sight for all enemys Players
| -Decrease the sightrange of all enemy Units
| Tooltip:
| Turns the day into such a deep night, that all enemys
| get a defective sight. So every foe can only fight alone.
|
| Some Bugs:
| -It could malfunctions if you cange the Alliance while on Effect
| -Every unit could attack with autoattack over her sightrange ( so they attack you even if they dont see you )
| This is not rightly tested, i only saw this effect on neutral creeps as they having 0 sightrange
|
| How to import:
| 1. Copy the BonusMod trigger, GroupUtils trigger and this trigger into your map
| Info: 2 -> 5. is only if you dont have the Bonus Mod abilities
| 2. Search inside the BonusMod trigger for :
| -> runtextmacro BonusMod_BeginBonuses("false") <- line 225
| Replace the false into a true
| -> runtextmacro BonusMod_BeginBonuses("true")
| 3. Save the map, close the map and reopen the map
| 4. Now you should have like 80 Abilities in you Object Editor
| 5. if that is true, you can change the line again into false
| -> runtextmacro BonusMod_BeginBonuses("false")
| 6. Copy the ability Cover of Darkness into your map
| 7. Search inside this trigger for the global TESTMODE and set it to false
|_________________________________________________________________
*/ // Ye ye i know my Descitption sucks ;D
globals
private constant boolean TESTMODE = true
// It would be lame if your units are not under the effect of the Spell
// The TESTMODE allows you to affect only allies
// TESTMODE = true ::: You and your allies are affected
// TESTMODE = false ::: Your enemys are affected
private constant integer ABIL_ID = 'A000'
// The Base Ability Id
private constant integer SIGHT_MALUS = -575
// The Amount of Sightrange loose
// Be carful the Spell makes a night so all units have a night sight range
// And the normal night sight range is 800
private constant string CASTER_SFX = "Abilities\\Spells\\Other\\HowlOfTerror\\HowlCaster.mdl"
// The Effect on the Caster
endglobals
// It is important that the Spell Duration is equal to the Duration of the Ability
private constant function GetSpellDuration takes integer level returns real
return ((level*2.)+5)
endfunction
// CODE STARTS : READ AND CHANGE AT OWN RISK
globals
private constant real TIME_OUT = 0.1
endglobals
private struct Spell
real Time
unit Caster
integer Level
static Spell array Active
static integer Count = 0
static timer Timer = CreateTimer()
static trigger Trigger = CreateTrigger()
static force Force = CreateForce()
static force Force2 = CreateForce()
static integer array Multi
static player CurPlayer
static boolean flag_add
method release takes integer number returns nothing
set Count = Count - 1
set Active[number] = Active[Count]
call destroy()
endmethod
static method EnumForce_change takes nothing returns boolean
local integer pId = GetPlayerId(CurPlayer)
if CurPlayer != GetFilterPlayer() then // This is some funny Bug, if you Disable the Shared Visison to your self all units wont see anything ;)
if flag_add then
set Multi[pId] = Multi[pId] - 1
if Multi[pId] == 0 then
call SetPlayerAlliance(CurPlayer,GetFilterPlayer(),ALLIANCE_SHARED_VISION,flag_add)
call SetPlayerAlliance(GetFilterPlayer(),CurPlayer,ALLIANCE_SHARED_VISION,flag_add)
endif
else
set Multi[pId] = Multi[pId] + 1
call SetPlayerAlliance(CurPlayer,GetFilterPlayer(),ALLIANCE_SHARED_VISION,flag_add)
call SetPlayerAlliance(GetFilterPlayer(),CurPlayer,ALLIANCE_SHARED_VISION,flag_add)
endif
endif
return false
endmethod
static method EnumGroup_change takes nothing returns boolean
if not(flag_add) then
call SetUnitBonus(GetFilterUnit(),BONUS_SIGHT_RANGE,SIGHT_MALUS)
else
if Multi[GetPlayerId(CurPlayer)] == 0 then
call SetUnitBonus(GetFilterUnit(),BONUS_SIGHT_RANGE,0)
endif
endif
return false
endmethod
static method EnumForce_enemys takes nothing returns boolean
set CurPlayer = GetFilterPlayer()
call ForceEnumAllies(Force2,CurPlayer,function Spell.EnumForce_change)
call ForceClear(Force2)
call GroupEnumUnitsOfPlayer(ENUM_GROUP,CurPlayer,function Spell.EnumGroup_change)
call GroupClear(ENUM_GROUP)
return false
endmethod
static method onloop takes nothing returns nothing
local Spell this
local integer index = 0
loop
exitwhen index >= Count
set this = Active[index]
set Time = Time + TIME_OUT
if Time >= GetSpellDuration(Level) then
set flag_add = true
static if TESTMODE then
call ForceEnumAllies(Force,GetOwningPlayer(Caster),function Spell.EnumForce_enemys)
set CurPlayer = Player(12)
call GroupEnumUnitsOfPlayer(ENUM_GROUP,Player(12),function Spell.EnumGroup_change)
call GroupClear(ENUM_GROUP)
else
call ForceEnumEnemies(Force,GetOwningPlayer(Caster),function Spell.EnumForce_enemys)
set CurPlayer = Player(12)
call GroupEnumUnitsOfPlayer(ENUM_GROUP,Player(12),function Spell.EnumGroup_change)
call GroupClear(ENUM_GROUP)
endif
call ForceClear(Force)
set Caster = null
call release(index)
else
set index = index + 1
endif
endloop
if Count == 0 then
call PauseTimer(Timer)
endif
endmethod
static method create takes nothing returns Spell
local Spell this
if GetSpellAbilityId() == ABIL_ID then
set this = allocate()
set Active[Count] = this
set Count = Count + 1
set Time = 0
set flag_add = false
set Caster = GetTriggerUnit()
set Level = GetUnitAbilityLevel(Caster,ABIL_ID)
call DestroyEffect(AddSpecialEffectTarget(CASTER_SFX,Caster,"origin"))
static if TESTMODE then
call ForceEnumAllies(Force,GetOwningPlayer(Caster),function Spell.EnumForce_enemys)
set CurPlayer = Player(12)
call GroupEnumUnitsOfPlayer(ENUM_GROUP,Player(12),function Spell.EnumGroup_change)
call GroupClear(ENUM_GROUP)
else
call ForceEnumEnemies(Force,GetOwningPlayer(Caster),function Spell.EnumForce_enemys)
set CurPlayer = Player(12)
call GroupEnumUnitsOfPlayer(ENUM_GROUP,Player(12),function Spell.EnumGroup_change)
call GroupClear(ENUM_GROUP)
endif
call ForceClear(Force)
if Count == 1 then
call TimerStart(Timer,TIME_OUT,true,function Spell.onloop)
endif
endif
return this
endmethod
static method onInit takes nothing returns nothing
call TriggerRegisterAnyUnitEventBJ(Trigger,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(Trigger,function Spell.create)
endmethod
endstruct
endscope
//TESH.scrollpos=125
//TESH.alwaysfold=0
// THIS IS A TOTAL USELESS SYSTEM AND IS NOT NEEDED FOR THE MAP
library Itemcircle
globals
private constant real TIME_OUT = 0.03
endglobals
struct itemcircle
readonly integer Count = 0
readonly real X
readonly real Y
readonly real radius
readonly real speed
readonly real time
private integer Index
private real angle = 0
private static constant hashtable table = InitHashtable()
private static constant timer Timer = CreateTimer()
private static item TempItem
private static constant timer TempTimer = CreateTimer()
private static constant trigger Trigger = CreateTrigger()
private static thistype array Active
private static integer CountIndex = 0
private static integer LoopIndex
method release takes nothing returns nothing
local integer i = Index
loop
exitwhen i == CountIndex
set Active[i] = Active[i+1]
set Active[i].Index = Active[i].Index - 1
endloop
set CountIndex = CountIndex - 1
set LoopIndex = LoopIndex - 1
call destroy()
endmethod
private static method onLoop takes nothing returns nothing
local thistype this
local integer i
local item tempitem
local real r
set LoopIndex = 0
loop
set this = Active[LoopIndex]
exitwhen LoopIndex >= CountIndex
set i = 0
if Count != 0 then
set r = 360/Count
else
set r = 0
endif
loop
exitwhen i >= Count
if LoadBoolean(table,this,i) then
call SaveReal(table,this,i,LoadReal(table,this,i)-TIME_OUT)
if LoadReal(table,this,i) <= 0 then
call SaveBoolean(table,this,i,false)
set tempitem = CreateItem(LoadInteger(table,this,i),X,Y)
call SaveItemHandle(table,this,i,tempitem)
call SetItemUserData(tempitem,this)
endif
else
set tempitem = LoadItemHandle(table,this,i)
call SetItemPosition(tempitem,X+Cos((angle+(i*r))*bj_DEGTORAD)*radius,Y+Sin((angle+(i*r))*bj_DEGTORAD)*radius)
endif
set i = i + 1
endloop
set angle = angle + speed
if angle > 360 then
set angle = angle - 360
endif
set LoopIndex = LoopIndex + 1
endloop
if CountIndex == 0 then
call PauseTimer(Timer)
endif
endmethod
static method create takes real X, real Y, real radius, real respawntime, real speed returns thistype
local thistype this = allocate()
set this.X = X
set this.Y = Y
set this.time = respawntime
set this.radius = radius
set this.speed = speed * TIME_OUT
set Active[CountIndex] = this
set this.Index = CountIndex
set CountIndex = CountIndex + 1
if CountIndex == 1 then
call TimerStart(Timer,TIME_OUT,true,function thistype.onLoop)
endif
return this
endmethod
private method getIndex takes item tempitem returns integer
local integer i = 0
loop
exitwhen i == Count
if tempitem == LoadItemHandle(table,this,i) then
return i
endif
set i = i + 1
endloop
return -1
endmethod
private method isOk takes item tempitem returns boolean
return getIndex(tempitem) != -1
endmethod
private static method onRemove takes nothing returns nothing
call RemoveItem(TempItem)
endmethod
method remove takes item tempitem returns nothing
local integer i
if isOk(tempitem) then
set i = getIndex(tempitem)
loop
exitwhen i == Count
call SaveItemHandle(table,this,i,LoadItemHandle(table,this,i+1))
set i = i + 1
endloop
set Count = Count - 1
set TempItem = tempitem
call TimerStart(TempTimer,0,false,function thistype.onRemove)
endif
endmethod
private static method onPick takes nothing returns nothing
local item tempitem = GetManipulatedItem()
local thistype this = GetItemUserData(tempitem)
local integer index = getIndex(tempitem)
if index >= 0 then
if time < 0 then
call remove(tempitem)
else
set TempItem = tempitem
call TimerStart(TempTimer,0,false,function thistype.onRemove)
call SaveBoolean(table,this,index,true)
call SaveReal(table,this,index,time)
endif
endif
set tempitem = null
endmethod
method add takes integer itemid returns boolean
local item tempitem = CreateItem(itemid,X,Y)
local boolean flag = false
if ITEM_TYPE_TOME == GetItemType(tempitem) then
call SaveItemHandle(table,this,Count,tempitem)
call SaveInteger(table,this,Count,itemid)
call SaveBoolean(table,this,Count,false)
call SaveReal(table,this,Count,0)
call SetItemUserData(tempitem,this)
set Count = Count + 1
set flag = true
else
call RemoveItem(tempitem)
endif
set tempitem = null
return flag
endmethod
static method onInit takes nothing returns nothing
call TriggerAddAction(Trigger,function thistype.onPick)
call TriggerRegisterPlayerUnitEvent(Trigger,GetLocalPlayer(),EVENT_PLAYER_UNIT_PICKUP_ITEM,null)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
function InitTrig_Itemcircleinit takes nothing returns nothing
local itemcircle this = itemcircle.create(0,0,300,5,75)
call CreateItem('I003',0,0)
call this.add('I000')
call this.add('I000')
call this.add('I000')
call this.add('I000')
call this.add('I000')
call this.add('I000')
call this.add('I000')
call this.add('I000')
set this = itemcircle.create(0,0,200,5,-60)
call this.add('I002')
call this.add('I002')
call this.add('I002')
call this.add('I002')
call this.add('I002')
set this = itemcircle.create(0,0,120,5,50)
call this.add('I001')
call this.add('I001')
call this.add('I001')
endfunction