Name | Type | is_array | initial_value |
u | unit | No |
//TESH.scrollpos=451
//TESH.alwaysfold=0
[COLOR="Yellow"][SIZE="3"]SmartAI v1.1[/SIZE][/COLOR]
[tabs]
[tab=Introduction]
Since the AI editor has a limited functionality for searching to pick and buy items. This system allows you to fully customize everything.
Featured:
- Allows AI to pick up nearby items
- Optionally searches allied shops to buy items
- Customize shopping time
- Customize maximum item that can be picked/bought
- Configurable picks only 1 item type
- Many more inside the code
[/tab]
[tab=Code]
[jass]
library SmartAI /* v1.1 by mckill2009
************************************************************************************
- Allows AI to pick up nearby items
- Optionally searches allied shops to buy items
************************************************************************************
*/ uses /*
*/ Table /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*/ GetClosestWidget /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-getclosestwidget-204217/
*/ IsUnitChanneling /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-isunitchanneling-211254/
************************************************************************************
Installation:
- Just copy this script and the required libraries to your map
************************************************************************************
API:
static method add takes unit picker, boolean buyItems returns nothing
- Picker should be a hero
- Optionally buys items
static method remove takes unit picker returns nothing
- Removes the hero from the system OFC XD
static method addUnpickableItem takes integer itemID returns nothing
- If the hero reaches it's maximum level it will ignore any registered item types
static method registerShopItem takes unit shop, integer itemID returns nothing
- Shops and itemID must be registered before the hero can buy any item
*/
globals
/**************************************************************
* Chance that the AI will search for allied shops to buy items
* based on percentage, 30 means 30%
***************************************************************/
private constant integer CHANCE_TO_SEARCH_FOR_SHOPS = 30
/**************************************************************
* AI will pick or buy items based on this setting
* default is 5 maximum is 6
***************************************************************/
private constant integer MAX_ITEM_PICK = 5
/**************************************************************
* This should be matched with the Hero Maximum Level in
* Gameplay Constants, coz the hero may pick an unpickable
* item such as an experience powerup when he's already at max level
***************************************************************/
private constant integer HERO_MAX_LEVEL_IN_MAP = 10
/**************************************************************
* Checks how many gold the player has, if it's below this number
* the AI will not buy items
***************************************************************/
private constant integer MINIMUM_GOLD_AMOUNT_TO_GO_SHOPPING = 1000
/**************************************************************
* Checks if the hero has already an item type, else it will ignore it
***************************************************************/
private constant boolean PICK_UP_ONLY_ONE_ITEM_TYPE = true
/**************************************************************
* Checks the surrounding if there's an item to pick
***************************************************************/
private constant real ITEM_RANGE_PICK = 500
/**************************************************************
* This is the delay in which the hero will search for a shop
* to buy items but depends on the CHANCE_TO_SEARCH_FOR_SHOPS
* and MINIMUM_GOLD_AMOUNT_TO_GO_SHOPPING
***************************************************************/
private constant real SEARCH_FOR_SHOP_DELAY = 10 //300
/**************************************************************
* This is the time duration to buy items
***************************************************************/
private constant real SHOPPING_TIME = 5
/**************************************************************
* SHOPPING_TIME starts when hero is NEAR_SHOP
***************************************************************/
private constant real NEAR_SHOP = 40000 //SquareRoot 200
/**************************************************************
* Searches closest shop in range of hero
***************************************************************/
private constant real SHOP_IN_RANGE = 5000
/********************************************************************************************************
* Non- configurable globals, functions and codes, this means DO NOT TOUCH ALL CODES BELOW THIS LINE!
*********************************************************************************************************/
private constant integer ATTACK = 851983
private constant integer MOVE = 851986
private constant integer SMART = 851971 //pick item command
private constant integer SHOP_SELECT_ORDER = 852566
private constant integer SHOP_ABILITY_ID = 'Apit'
private constant real INTERVAL = 1.0
private boolean array IsBuying
private item itm
private rect Rct
private integer array itemIds
private integer itemIdCount = 2
private Table dl
private Table thisInd
private Table sInd
private TableArray si
endglobals
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD)
endfunction
private function GetDistance takes real x1, real y1, real x2, real y2 returns real
return (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)
endfunction
private function FilterShop takes unit shop returns boolean
return UnitAlive(shop) and GetUnitAbilityLevel(shop, SHOP_ABILITY_ID) > 0
endfunction
private function IsItemPickable takes item it returns boolean
local integer i = 0
loop
if (GetItemTypeId(it)==itemIds[i]) then
return true
endif
set i = i+1
exitwhen i==itemIdCount
endloop
return false
endfunction
private function CountItemsInSlot takes unit u returns integer
local integer count = 0
local integer slot = 0
loop
if (UnitItemInSlot(u, slot) != null) then
set count = count + 1
endif
set slot = slot + 1
exitwhen slot==6
endloop
return count
endfunction
private function UnitHasItemType takes unit u, integer itemId returns boolean
local integer i = 0
loop
set itm = UnitItemInSlot(u, i)
if GetItemTypeId(itm)==itemId then
return true
endif
set i = i+1
exitwhen i==6
endloop
return false
endfunction
private module init
private static method onInit takes nothing returns nothing
set Rct = Rect(-ITEM_RANGE_PICK, -ITEM_RANGE_PICK, ITEM_RANGE_PICK, ITEM_RANGE_PICK)
set itemIds[0] = 'texp' //tome of experience
set itemIds[1] = 'tkno' //tome of power
set thisInd = Table.create()
set sInd = Table.create()
set dl = Table.create()
set si = TableArray[0x3000]
endmethod
endmodule
//! textmacro DEALLOCATE
call .deallocate()
set instanceAR[i] = instanceAR[instance]
set instanceAR[instance] = this
set instance = instance - 1
set i = i-1
if instance==0 then
call PauseTimer(t)
call DestroyTimer(t)
endif
//! endtextmacro
private struct Buy
private unit buyer
private unit shop
private real dur
private integer hID
private boolean buying
private static integer instance = 0
private static integer array instanceAR
private static timer t
private static method heroIsBuying takes nothing returns nothing
local thistype this
//local SmartAI sm
local integer i = 0
local integer id
loop
set i = i + 1
set this = instanceAR [i]
if UnitAlive(.buyer) and UnitAlive(.shop) and .buying then
if .dur > 0 then
if (GetDistance(GetUnitX(.buyer), GetUnitY(.buyer), GetUnitX(.shop), GetUnitY(.shop)) < NEAR_SHOP) then
set .dur = .dur - INTERVAL
endif
else
set .buying = false
endif
else
set dl.real[.hID] = 0
set IsBuying[thisInd[.hID]] = false
set .buyer = null
set .shop = null
//! runtextmacro DEALLOCATE()
endif
exitwhen i==instance
endloop
endmethod
static method run takes unit buyer, unit shop returns nothing
local thistype this = allocate()
set .buyer = buyer
set .shop = shop
set .dur = SHOPPING_TIME
set .buying = true
set .hID = GetHandleId(buyer)
if instance==0 then
set t = CreateTimer()
call TimerStart(t, INTERVAL, true, function thistype.heroIsBuying)
endif
set instance = instance + 1
set instanceAR[instance] = this
endmethod
endstruct
struct SmartAI
private unit hero
private unit shop
private boolean pick
private boolean buyItems
private player pl
private integer hID
//boolean isBuying
private static thistype DATA
private static integer instance = 0
private static integer array instanceAR
private static timer t
implement init
private static method closestAllyShop takes nothing returns boolean
local thistype this = DATA
return FilterShop(GetFilterUnit()) and not IsUnitEnemy(GetFilterUnit(), .pl)
endmethod
private static method onPickItem takes nothing returns nothing
local thistype this = DATA
if not IsUnitChanneling(.hero) then
if PICK_UP_ONLY_ONE_ITEM_TYPE then
if not UnitHasItemType(.hero, GetItemTypeId(GetEnumItem())) then
call IssueTargetOrderById(.hero, SMART, GetEnumItem())
endif
else
call IssueTargetOrderById(.hero, SMART, GetEnumItem())
endif
endif
endmethod
//This has a condition MAX_ITEM_PICK
private static method filterItems takes nothing returns boolean
local thistype this = DATA
if GetHeroLevel(.hero)==HERO_MAX_LEVEL_IN_MAP then
return not IsItemPickable(GetFilterItem())
endif
return GetItemType(GetFilterItem())!=ITEM_TYPE_POWERUP
endmethod
//Ensures to pick all powerup items exept the blocked ones
private static method filterPowerupItems takes nothing returns boolean
local thistype this = DATA
if GetHeroLevel(.hero)==HERO_MAX_LEVEL_IN_MAP then
return not IsItemPickable(GetFilterItem())
endif
return GetItemType(GetFilterItem())==ITEM_TYPE_POWERUP or GetItemTypeId(GetFilterItem())=='tkno'
endmethod
private static method lookForItems takes nothing returns nothing
local thistype this
local integer i = 0
local integer sID
local real distance
local real x
local real y
loop
set i = i + 1
set this = instanceAR [i]
if .pick then
if UnitAlive(.hero) then
set x = GetUnitX(.hero)
set y = GetUnitY(.hero)
set DATA = this
if CountItemsInSlot(.hero) < MAX_ITEM_PICK then
/***************************************************
* Unit will search for allied shops to buy items
****************************************************/
if .buyItems then
if GetPlayerState(.pl, PLAYER_STATE_RESOURCE_GOLD) > MINIMUM_GOLD_AMOUNT_TO_GO_SHOPPING then
set dl.real[.hID] = dl.real[.hID] + INTERVAL
if (dl.real[.hID] > SEARCH_FOR_SHOP_DELAY) then
if IsBuying[this] then
if UnitAlive(.shop) and not IsUnitChanneling(.hero) then
if GetDistance(x, y, GetUnitX(.shop), GetUnitY(.shop)) > NEAR_SHOP then
call IssuePointOrderById(.hero, MOVE, GetUnitX(.shop), GetUnitY(.shop))
else
call IssueTargetOrderById(.shop, SHOP_SELECT_ORDER, .hero)
set sID = GetHandleId(.shop)
call IssueImmediateOrderById(.shop, si[GetRandomInt(1, sInd[sID])][sID])
endif
endif
else
if GetRandomInt(0, 100) < CHANCE_TO_SEARCH_FOR_SHOPS then
set .shop = GetClosestUnitInRange(x, y, SHOP_IN_RANGE, Filter(function thistype.closestAllyShop))
if .shop!=null then
set IsBuying[this] = true
call Buy.run(.hero, .shop)
endif
endif
endif
endif
endif
endif
/***************************************************
* Unit will search for nearby items
****************************************************/
call MoveRectTo(Rct, x, y)
if GetRandomInt(0,2)==2 then
call EnumItemsInRect(Rct, Filter(function thistype.filterItems), function thistype.onPickItem)
else
call EnumItemsInRect(Rct, Filter(function thistype.filterPowerupItems), function thistype.onPickItem)
endif
endif
endif
else
set .hero = null
set .shop = null
set .pl = null
//! runtextmacro DEALLOCATE()
endif
exitwhen i==instance
endloop
endmethod
/*************************************************************
* API
**************************************************************/
static method add takes unit picker, boolean buyItems returns nothing
local thistype this
if thisInd.has(GetHandleId(picker)) then
call BJDebugMsg("[thistype][add] ERROR: "+GetUnitName(picker)+" cannot be added twice!")
else
set this = thistype.allocate()
set .hero = picker
set .pl = GetOwningPlayer(.hero)
set .pick = true
set .shop = null
set IsBuying[this] = false
set .buyItems = buyItems
set .hID = GetHandleId(.hero)
set thisInd[.hID] = this
set dl.real[.hID] = 0
if instance==0 then
set t = CreateTimer()
call TimerStart(t, INTERVAL, true, function thistype.lookForItems)
endif
set instance = instance + 1
set instanceAR[instance] = this
endif
endmethod
static method remove takes unit picker returns nothing
local thistype this = thisInd[GetHandleId(picker)]
set .pick = false
endmethod
static method addUnpickableItem takes integer itemID returns nothing
set itemIds[itemIdCount] = itemID
set itemIdCount = itemIdCount + 1
endmethod
static method registerShopItem takes unit shop, integer itemID returns nothing
local integer id = GetHandleId(shop)
set sInd[id] = sInd[id] + 1
set si[sInd[id]][id] = itemID
endmethod
endstruct
endlibrary
[/jass]
[/tab]
[tab=Tutorials and Instructions]
[box=First]
Import the following libraries to your map;
[url=http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/]Table[/url]
[url=http://www.hiveworkshop.com/forums/jass-resources-412/snippet-getclosestwidget-204217/]GetClosestWidget[/url]
[url=http://www.hiveworkshop.com/forums/jass-resources-412/snippet-isunitchanneling-211254/]IsUnitChanneling[/url]
[/box]
[box=Second]
[trigger]
DEMO
Events
Time - Elapsed game time is 0.00 seconds
Conditions
Actions
-------- Add unpickable items when hero reaches it's maximum level, the item will be ignored --------
-------- This is optional but recommended --------
Custom script: call SmartAI.addUnpickableItem('ckng') //Crown of Kings
Custom script: call SmartAI.addUnpickableItem('modt') //Mask of Death
Unit Group - Pick every unit in (Units in (Playable map area) matching (((Matching unit) is A Hero) Equal to True)) and do (Actions)
Loop - Actions
-------- Registers all Heros in the map, with an option to buy items or not --------
Custom script: call SmartAI.add(GetEnumUnit(), true)
-------- Every item in every shop must be registered --------
Unit Group - Pick every unit in (Units of type Arcane Vault) and do (Actions)
Loop - Actions
Custom script: call SmartAI.registerShopItem(GetEnumUnit(), 'sreg') //scroll of regeneration
Custom script: call SmartAI.registerShopItem(GetEnumUnit(), 'plcl') //lesser clarity potion
Custom script: call SmartAI.registerShopItem(GetEnumUnit(), 'mcri') //mechanical critter
[/trigger]
[/box]
[/tab]
[tab=Credits]
[url=http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/]Table[/url] by Bribe
[url=http://www.hiveworkshop.com/forums/jass-resources-412/snippet-getclosestwidget-204217/]GetClosestWidget[/url] by Spinnaker
[url=http://www.hiveworkshop.com/forums/jass-resources-412/snippet-isunitchanneling-211254/]IsUnitChanneling[/url] by Magtheridon96
[/tab]
[tab=Not Supported]
- Selling of items
- Giving items to other heroes
- Use items
- Drop useless items
[/tab]
[tab=Changelogs]
v1.0
- IsUnitChanneling imported and added global item variable for filtering purposes
[/tab]
[tab=Bugs]
[/tab]
[/tabs]
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 3.1.0.1
One map, one hashtable. Welcome to NewTable 3.1
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key)
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb)
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=211
//TESH.alwaysfold=0
/*****************************************************************************
*
* Get Closest Widget
* by Spinnaker v2.0.0.0
*
* Special thanks to Troll-Brain
*
******************************************************************************
*
* This snippet contains several functions which returns closest
* widget to given coordinates and passed filter.
*
******************************************************************************
*
* Requirements:
* Snippet optionaly requires IsDestructableTree,
* enabling user to choose if enumerated should be all destructables
* or only tree-type ones, therefore gives option to get closest tree.
*
******************************************************************************
*
* Important:
* Performance drop depends on amount of objects currently on the map
* Snippet functions are designed to reduce the search time as much as
* posible, although it's recommended to use non specific functions
* wisely, especialy if your map contains large numbers of widgets.
*
******************************************************************************
*
* Functions:
* function GetClosestItem takes real x, real y, boolexpr filter returns item
* - Gets single item, nearest passed coordinates
* function GetClosestItemInRange takes real x, real y, real radius, boolexpr filter returns item
* - Returns single item, closest to coordinates in given range
*
* function GetClosestDestructable takes real x, real y, boolean treeOnly, boolexpr filter returns destructable
* - Gets single destructable, nearest passed coordinates
* function GetClosestDestructableInRange takes real x, real y, real radius, boolean treeOnly, boolexpr filter returns destructable
* - Retrieves single destructable, closest to coordinates in given range
*
* function GetClosestUnit takes real x, real y, boolexpr filter returns unit
* - Returns closest unit matching specific filter
* function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
* - Gets nearest unit in range matching specific filter
* function GetClosestUnitInGroup takes real x, real y, group g returns unit
* - Retrieves closest unit in given group
*
* function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group g, boolexpr filter returns nothing
* - Returns up to N nearest units in range of passed coordinates
* function GetClosestNUnitsInGroup takes real x, real y, integer n, group sourceGroup, group destGroup returns nothing
* - Retrieves up to N closest units in passed group
*
*****************************************************************************/
library GetClosestWidget uses optional IsDestructableTree
private keyword Init
globals
private unit array Q
private real array V
private integer C=0
endglobals
struct ClosestWidget extends array
readonly static destructable cDest=null
readonly static item cItem=null
readonly static real distance=0
readonly static real cX=0
readonly static real cY=0
readonly static unit cUnit=null
static boolean cTree=false
static rect initRect=null
static method resetData takes real x, real y returns nothing
set cDest=null
set distance=100000
set cItem=null
set cUnit=null
set cX=x
set cY=y
endmethod
static method enumItems takes nothing returns nothing
local item i=GetEnumItem()
local real dx=GetWidgetX(i)-cX
local real dy=GetWidgetY(i)-cY
set dx = (dx*dx+dy*dy)/10000.
if dx<distance then
set cItem=i
set distance=dx
endif
set i =null
endmethod
static method enumDestructables takes nothing returns nothing
local destructable d=GetEnumDestructable()
local real dx
local real dy
static if LIBRARY_IsDestructableTree then
if cTree and not IsDestructableTree(d) then
return
endif
endif
set dx=GetWidgetX(d)-cX
set dy=GetWidgetY(d)-cY
set dx=(dx*dx+dy*dy)/10000.
if dx<distance then
set cDest=d
set distance=dx
endif
set d=null
endmethod
static method enumUnits takes nothing returns nothing
local unit u=GetEnumUnit()
local real dx=GetUnitX(u)-cX
local real dy=GetUnitY(u)-cY
set dx=(dx*dx+dy*dy)/10000.
if dx<distance then
set cUnit=u
set distance=dx
endif
set u=null
endmethod
static method sortUnits takes integer l, integer r returns nothing
local integer i=l
local integer j=r
local real v=V[(l+r)/2]
loop
loop
exitwhen V[i]>=v
set i=i+1
endloop
loop
exitwhen V[j]<=v
set j=j-1
endloop
if i<=j then
set V[0]=V[i]
set V[i]=V[j]
set V[j]=V[0]
set Q[0]=Q[i]
set Q[i]=Q[j]
set Q[j]=Q[0]
set i=i+1
set j=j-1
endif
exitwhen i>j
endloop
if l<j then
call sortUnits(l,j)
endif
if r>i then
call sortUnits(i,r)
endif
endmethod
static method saveGroup takes nothing returns nothing
local real dx
local real dy
set C=C+1
set Q[C]=GetEnumUnit()
set dx=GetUnitX(Q[C])-cX
set dy=GetUnitY(Q[C])-cY
set V[C]=(dx*dx+dy*dy)/10000.
endmethod
implement Init
endstruct
private module Init
private static method onInit takes nothing returns nothing
set thistype.initRect=Rect(0,0,0,0)
endmethod
endmodule
function GetClosestItem takes real x, real y, boolexpr filter returns item
local real r=800.
call ClosestWidget.resetData(x,y)
loop
if r>3200. then
call EnumItemsInRect(bj_mapInitialPlayableArea, filter, function ClosestWidget.enumItems)
exitwhen true
else
call SetRect(ClosestWidget.initRect,x-r,y-r,x+r,y+r)
call EnumItemsInRect(ClosestWidget.initRect, filter, function ClosestWidget.enumItems)
exitwhen ClosestWidget.cItem!=null
endif
set r=2*r
endloop
return ClosestWidget.cItem
endfunction
function GetClosestItemInRange takes real x, real y, real radius, boolexpr filter returns item
call ClosestWidget.resetData(x,y)
if radius>=0 then
call SetRect(ClosestWidget.initRect,x-radius,y-radius,x+radius,y+radius)
call EnumItemsInRect(ClosestWidget.initRect, filter, function ClosestWidget.enumItems)
endif
return ClosestWidget.cItem
endfunction
function GetClosestDestructable takes real x, real y, boolean treeOnly, boolexpr filter returns destructable
local real r=800.
call ClosestWidget.resetData(x,y)
set ClosestWidget.cTree=treeOnly
loop
if r>3200. then
call EnumDestructablesInRect(bj_mapInitialPlayableArea, filter, function ClosestWidget.enumDestructables)
exitwhen true
else
call SetRect(ClosestWidget.initRect,x-r,y-r,x+r,y+r)
call EnumDestructablesInRect(ClosestWidget.initRect, filter, function ClosestWidget.enumDestructables)
exitwhen ClosestWidget.cDest!=null
endif
set r=2*r
endloop
return ClosestWidget.cDest
endfunction
function GetClosestDestructableInRange takes real x, real y, real radius, boolean treeOnly, boolexpr filter returns destructable
call ClosestWidget.resetData(x,y)
if radius>=0 then
set ClosestWidget.cTree=treeOnly
call SetRect(ClosestWidget.initRect,x-radius,y-radius,x+radius,y+radius)
call EnumDestructablesInRect(ClosestWidget.initRect, filter, function ClosestWidget.enumDestructables)
endif
return ClosestWidget.cDest
endfunction
function GetClosestUnit takes real x, real y, boolexpr filter returns unit
local real r=800.
call ClosestWidget.resetData(x,y)
loop
if r>3200. then
call GroupEnumUnitsInRect(bj_lastCreatedGroup, bj_mapInitialPlayableArea, filter)
exitwhen true
else
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, r, filter)
exitwhen FirstOfGroup(bj_lastCreatedGroup)!=null
endif
set r=2*r
endloop
call ForGroup(bj_lastCreatedGroup, function ClosestWidget.enumUnits)
return ClosestWidget.cUnit
endfunction
function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
call ClosestWidget.resetData(x,y)
if radius>=0 then
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, radius, filter)
call ForGroup(bj_lastCreatedGroup, function ClosestWidget.enumUnits)
endif
return ClosestWidget.cUnit
endfunction
function GetClosestUnitInGroup takes real x, real y, group g returns unit
call ClosestWidget.resetData(x,y)
call ForGroup(g, function ClosestWidget.enumUnits)
return ClosestWidget.cUnit
endfunction
function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group g, boolexpr filter returns nothing
local integer q=n+1
call ClosestWidget.resetData(x,y)
if radius>=0 then
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, radius, filter)
call ForGroup(bj_lastCreatedGroup, function ClosestWidget.saveGroup)
call ClosestWidget.sortUnits(1,C)
loop
exitwhen n==0 or Q[-n+q]==null
call GroupAddUnit(g, Q[-n+q])
set n =n-1
endloop
endif
set C=0
endfunction
function GetClosestNUnitsInGroup takes real x, real y, integer n, group sourceGroup, group destGroup returns nothing
local integer q=n+1
call ClosestWidget.resetData(x,y)
call ForGroup(sourceGroup, function ClosestWidget.saveGroup)
call ClosestWidget.sortUnits(1,C)
loop
exitwhen n==0 or Q[-n+q]==null
call GroupAddUnit(destGroup, Q[-n+q])
set n=n-1
endloop
set C=0
endfunction
endlibrary
//TESH.scrollpos=10
//TESH.alwaysfold=0
/**************************************
*
* IsUnitChanneling
* v2.1.0.0
* By Magtheridon96
*
* - Tells whether a unit is channeling or not.
*
* Requirements:
* -------------
*
* - RegisterPlayerUnitEvent by Magtheridon96
* - hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/
*
* Optional:
* ---------
*
* - UnitIndexer by Nestharus
* - hiveworkshop.com/forums/jass-resources-412/system-unit-indexer-172090/
* - Table by Bribe
* - hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
* API:
* ----
*
* - function IsUnitChanneling takes unit whichUnit returns boolean
* - Tells whether a unit is channeling or not.
* (This function is only available if you have UnitIndexer)
*
* - function IsUnitChannelingById takes integer unitIndex returns boolean
* - Tells whether a unti is channeling or not given the unit index.
*
**************************************/
library IsUnitChanneling requires optional UnitIndexer, optional Table, RegisterPlayerUnitEvent
private struct OnChannel extends array
static if LIBRARY_UnitIndexer then
static boolean array channeling
else
static if LIBRARY_Table then
static key k
static Table channeling = k
else
static hashtable hash = InitHashtable()
endif
endif
private static method onEvent takes nothing returns nothing
static if LIBRARY_UnitIndexer then
local integer id = GetUnitUserData(GetTriggerUnit())
set channeling[id] = not channeling[id]
else
static if LIBRARY_Table then
local integer id = GetHandleId(GetTriggerUnit())
set channeling.boolean[id] = not channeling.boolean[id]
else
local integer id = GetHandleId(GetTriggerUnit())
call SaveBoolean(hash, 0, id, not LoadBoolean(hash, 0, id))
endif
endif
endmethod
private static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function thistype.onEvent)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function thistype.onEvent)
endmethod
endstruct
static if LIBRARY_UnitIndexer then
function IsUnitChannelingById takes integer id returns boolean
return OnChannel.channeling[id]
endfunction
endif
function IsUnitChanneling takes unit u returns boolean
static if LIBRARY_UnitIndexer then
return OnChannel.channeling[GetUnitUserData(u)]
else
static if LIBRARY_Table then
return OnChannel.channeling.boolean[GetHandleId(u)]
else
return LoadBoolean(OnChannel.hash, 0, GetHandleId(u))
endif
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=141
//TESH.alwaysfold=0
library SmartAI /* v1.1 by mckill2009
************************************************************************************
- Allows AI to pick up nearby items
- Optionally searches allied shops to buy items
************************************************************************************
*/ uses /*
*/ Table /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*/ GetClosestWidget /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-getclosestwidget-204217/
*/ IsUnitChanneling /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-isunitchanneling-211254/
************************************************************************************
Installation:
- Just copy this script and the required libraries to your map
************************************************************************************
API:
static method add takes unit picker, boolean buyItems returns nothing
- Picker should be a hero
- Optionally buys items
static method remove takes unit picker returns nothing
- Removes the hero from the system OFC XD
static method addUnpickableItem takes integer itemID returns nothing
- If the hero reaches it's maximum level it will ignore any registered item types
static method registerShopItem takes unit shop, integer itemID returns nothing
- Shops and itemID must be registered before the hero can buy any item
*/
globals
/**************************************************************
* Chance that the AI will search for allied shops to buy items
* based on percentage, 30 means 30%
***************************************************************/
private constant integer CHANCE_TO_SEARCH_FOR_SHOPS = 30
/**************************************************************
* AI will pick or buy items based on this setting
* default is 5 maximum is 6
***************************************************************/
private constant integer MAX_ITEM_PICK = 5
/**************************************************************
* This should be matched with the Hero Maximum Level in
* Gameplay Constants, coz the hero may pick an unpickable
* item such as an experience powerup when he's already at max level
***************************************************************/
private constant integer HERO_MAX_LEVEL_IN_MAP = 10
/**************************************************************
* Checks how many gold the player has, if it's below this number
* the AI will not buy items
***************************************************************/
private constant integer MINIMUM_GOLD_AMOUNT_TO_GO_SHOPPING = 1000
/**************************************************************
* Checks if the hero has already an item type, else it will ignore it
***************************************************************/
private constant boolean PICK_UP_ONLY_ONE_ITEM_TYPE = true
/**************************************************************
* Checks the surrounding if there's an item to pick
***************************************************************/
private constant real ITEM_RANGE_PICK = 500
/**************************************************************
* This is the delay in which the hero will search for a shop
* to buy items but depends on the CHANCE_TO_SEARCH_FOR_SHOPS
* and MINIMUM_GOLD_AMOUNT_TO_GO_SHOPPING
***************************************************************/
private constant real SEARCH_FOR_SHOP_DELAY = 10 //300
/**************************************************************
* This is the time duration to buy items
***************************************************************/
private constant real SHOPPING_TIME = 5
/**************************************************************
* SHOPPING_TIME starts when hero is NEAR_SHOP
***************************************************************/
private constant real NEAR_SHOP = 40000 //SquareRoot 200
/**************************************************************
* Searches closest shop in range of hero
***************************************************************/
private constant real SHOP_IN_RANGE = 5000
/********************************************************************************************************
* Non- configurable globals, functions and codes, this means DO NOT TOUCH ALL CODES BELOW THIS LINE!
*********************************************************************************************************/
private constant integer ATTACK = 851983
private constant integer MOVE = 851986
private constant integer SMART = 851971 //pick item command
private constant integer SHOP_SELECT_ORDER = 852566
private constant integer SHOP_ABILITY_ID = 'Apit'
private constant real INTERVAL = 1.0
private boolean array IsBuying
private item itm
private rect Rct
private integer array itemIds
private integer itemIdCount = 2
private Table dl
private Table thisInd
private Table sInd
private TableArray si
endglobals
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD)
endfunction
private function GetDistance takes real x1, real y1, real x2, real y2 returns real
return (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)
endfunction
private function FilterShop takes unit shop returns boolean
return UnitAlive(shop) and GetUnitAbilityLevel(shop, SHOP_ABILITY_ID) > 0
endfunction
private function IsItemPickable takes item it returns boolean
local integer i = 0
loop
if (GetItemTypeId(it)==itemIds[i]) then
return true
endif
set i = i+1
exitwhen i==itemIdCount
endloop
return false
endfunction
private function CountItemsInSlot takes unit u returns integer
local integer count = 0
local integer slot = 0
loop
if (UnitItemInSlot(u, slot) != null) then
set count = count + 1
endif
set slot = slot + 1
exitwhen slot==6
endloop
return count
endfunction
private function UnitHasItemType takes unit u, integer itemId returns boolean
local integer i = 0
loop
set itm = UnitItemInSlot(u, i)
if GetItemTypeId(itm)==itemId then
return true
endif
set i = i+1
exitwhen i==6
endloop
return false
endfunction
private module init
private static method onInit takes nothing returns nothing
set Rct = Rect(-ITEM_RANGE_PICK, -ITEM_RANGE_PICK, ITEM_RANGE_PICK, ITEM_RANGE_PICK)
set itemIds[0] = 'texp' //tome of experience
set itemIds[1] = 'tkno' //tome of power
set thisInd = Table.create()
set sInd = Table.create()
set dl = Table.create()
set si = TableArray[0x3000]
endmethod
endmodule
//! textmacro DEALLOCATE
call .deallocate()
set instanceAR[i] = instanceAR[instance]
set instanceAR[instance] = this
set instance = instance - 1
set i = i-1
if instance==0 then
call PauseTimer(t)
call DestroyTimer(t)
endif
//! endtextmacro
private struct Buy
private unit buyer
private unit shop
private real dur
private integer hID
private boolean buying
private static integer instance = 0
private static integer array instanceAR
private static timer t
private static method heroIsBuying takes nothing returns nothing
local thistype this
//local SmartAI sm
local integer i = 0
local integer id
loop
set i = i + 1
set this = instanceAR [i]
if UnitAlive(.buyer) and UnitAlive(.shop) and .buying then
if .dur > 0 then
if (GetDistance(GetUnitX(.buyer), GetUnitY(.buyer), GetUnitX(.shop), GetUnitY(.shop)) < NEAR_SHOP) then
set .dur = .dur - INTERVAL
endif
else
set .buying = false
endif
else
set dl.real[.hID] = 0
set IsBuying[thisInd[.hID]] = false
set .buyer = null
set .shop = null
//! runtextmacro DEALLOCATE()
endif
exitwhen i==instance
endloop
endmethod
static method run takes unit buyer, unit shop returns nothing
local thistype this = allocate()
set .buyer = buyer
set .shop = shop
set .dur = SHOPPING_TIME
set .buying = true
set .hID = GetHandleId(buyer)
if instance==0 then
set t = CreateTimer()
call TimerStart(t, INTERVAL, true, function thistype.heroIsBuying)
endif
set instance = instance + 1
set instanceAR[instance] = this
endmethod
endstruct
struct SmartAI
private unit hero
private unit shop
private boolean pick
private boolean buyItems
private player pl
private integer hID
//boolean isBuying
private static thistype DATA
private static integer instance = 0
private static integer array instanceAR
private static timer t
implement init
private static method closestAllyShop takes nothing returns boolean
local thistype this = DATA
return FilterShop(GetFilterUnit()) and not IsUnitEnemy(GetFilterUnit(), .pl)
endmethod
private static method onPickItem takes nothing returns nothing
local thistype this = DATA
if not IsUnitChanneling(.hero) then
if PICK_UP_ONLY_ONE_ITEM_TYPE then
if not UnitHasItemType(.hero, GetItemTypeId(GetEnumItem())) then
call IssueTargetOrderById(.hero, SMART, GetEnumItem())
endif
else
call IssueTargetOrderById(.hero, SMART, GetEnumItem())
endif
endif
endmethod
//This has a condition MAX_ITEM_PICK
private static method filterItems takes nothing returns boolean
local thistype this = DATA
if GetHeroLevel(.hero)==HERO_MAX_LEVEL_IN_MAP then
return not IsItemPickable(GetFilterItem())
endif
return GetItemType(GetFilterItem())!=ITEM_TYPE_POWERUP
endmethod
//Ensures to pick all powerup items exept the blocked ones
private static method filterPowerupItems takes nothing returns boolean
local thistype this = DATA
if GetHeroLevel(.hero)==HERO_MAX_LEVEL_IN_MAP then
return not IsItemPickable(GetFilterItem())
endif
return GetItemType(GetFilterItem())==ITEM_TYPE_POWERUP or GetItemTypeId(GetFilterItem())=='tkno'
endmethod
private static method lookForItems takes nothing returns nothing
local thistype this
local integer i = 0
local integer sID
local real distance
local real x
local real y
loop
set i = i + 1
set this = instanceAR [i]
if .pick then
if UnitAlive(.hero) then
set x = GetUnitX(.hero)
set y = GetUnitY(.hero)
set DATA = this
if CountItemsInSlot(.hero) < MAX_ITEM_PICK then
/***************************************************
* Unit will search for allied shops to buy items
****************************************************/
if .buyItems then
if GetPlayerState(.pl, PLAYER_STATE_RESOURCE_GOLD) > MINIMUM_GOLD_AMOUNT_TO_GO_SHOPPING then
set dl.real[.hID] = dl.real[.hID] + INTERVAL
if (dl.real[.hID] > SEARCH_FOR_SHOP_DELAY) then
if IsBuying[this] then
if UnitAlive(.shop) and not IsUnitChanneling(.hero) then
if GetDistance(x, y, GetUnitX(.shop), GetUnitY(.shop)) > NEAR_SHOP then
call IssuePointOrderById(.hero, MOVE, GetUnitX(.shop), GetUnitY(.shop))
else
call IssueTargetOrderById(.shop, SHOP_SELECT_ORDER, .hero)
set sID = GetHandleId(.shop)
call IssueImmediateOrderById(.shop, si[GetRandomInt(1, sInd[sID])][sID])
endif
endif
else
if GetRandomInt(0, 100) < CHANCE_TO_SEARCH_FOR_SHOPS then
set .shop = GetClosestUnitInRange(x, y, SHOP_IN_RANGE, Filter(function thistype.closestAllyShop))
if .shop!=null then
set IsBuying[this] = true
call Buy.run(.hero, .shop)
endif
endif
endif
endif
endif
endif
/***************************************************
* Unit will search for nearby items
****************************************************/
call MoveRectTo(Rct, x, y)
if GetRandomInt(0,2)==2 then
call EnumItemsInRect(Rct, Filter(function thistype.filterItems), function thistype.onPickItem)
else
call EnumItemsInRect(Rct, Filter(function thistype.filterPowerupItems), function thistype.onPickItem)
endif
endif
endif
else
set .hero = null
set .shop = null
set .pl = null
//! runtextmacro DEALLOCATE()
endif
exitwhen i==instance
endloop
endmethod
/*************************************************************
* API
**************************************************************/
static method add takes unit picker, boolean buyItems returns nothing
local thistype this
if thisInd.has(GetHandleId(picker)) then
call BJDebugMsg("[thistype][add] ERROR: "+GetUnitName(picker)+" cannot be added twice!")
else
set this = thistype.allocate()
set .hero = picker
set .pl = GetOwningPlayer(.hero)
set .pick = true
set .shop = null
set IsBuying[this] = false
set .buyItems = buyItems
set .hID = GetHandleId(.hero)
set thisInd[.hID] = this
set dl.real[.hID] = 0
if instance==0 then
set t = CreateTimer()
call TimerStart(t, INTERVAL, true, function thistype.lookForItems)
endif
set instance = instance + 1
set instanceAR[instance] = this
endif
endmethod
static method remove takes unit picker returns nothing
local thistype this = thisInd[GetHandleId(picker)]
set .pick = false
endmethod
static method addUnpickableItem takes integer itemID returns nothing
set itemIds[itemIdCount] = itemID
set itemIdCount = itemIdCount + 1
endmethod
static method registerShopItem takes unit shop, integer itemID returns nothing
local integer id = GetHandleId(shop)
set sInd[id] = sInd[id] + 1
set si[sInd[id]][id] = itemID
endmethod
endstruct
endlibrary