- Joined
- Jun 18, 2011
- Messages
- 168
I am not very knowledgable on all things JASS and vJASS and I do not have the best understanding how a lot of the libraries I use work. I try to, but nonetheless I am still far behind the experience of many. Still, I put my thoughts in the spoilers about possible issues with the libraries. Furthermore, here is my best explanation as to what I think could be happening.
I know the Equipment/Inventory System UI uses dummy units that are called upon when the UI is opened. So, when the hero is teleported by his ally, the dummy units for the UI know how to update to the location of the hero. However the destructibles used to make the icon textures for the items do not. The hero keeps all of the stat bonuses from the items (I did not include the triggers Item Setups which shows how an item gains bonuses and is linked to a destructible that has the texture for it's icon nor did I include the BonusMod Library). So, to sum up the hero appears in a new location via the teleport spell without the textures in his inventory and equipment (the hero keeps the bonus stats, but the equipment/inv slots become empty and new items can be placed in). If new items are placed in the hero can then be teleported with the Mythical Scroll spell without causing the items to dissapear. Therefore, I could possibly workaround this issue by just having the unit get teleported by an invisible dummy version of this spell after his UI is created.
I may find a solution on my own and post it here, but any help would be greatly appreciated. Here is a test version of my map if you are interested...
Test_Map - Warcraft 3 Maps - Epic War.com
(Only King Arthur is fully set up to use the UI and Merlin has the teleport spell. Type "lvl" to quick level all allied heroes to learn the spell. Use Merlin's staff to teleport to Arthur (they start in different locations). Then select where you'd like to teleport with the spell and then recast the spell on Arthur or Arthur/Merlin together to teleport him/them.)
From TriggerHappy's Equipment/Inventory System v1.43
I know the Equipment/Inventory System UI uses dummy units that are called upon when the UI is opened. So, when the hero is teleported by his ally, the dummy units for the UI know how to update to the location of the hero. However the destructibles used to make the icon textures for the items do not. The hero keeps all of the stat bonuses from the items (I did not include the triggers Item Setups which shows how an item gains bonuses and is linked to a destructible that has the texture for it's icon nor did I include the BonusMod Library). So, to sum up the hero appears in a new location via the teleport spell without the textures in his inventory and equipment (the hero keeps the bonus stats, but the equipment/inv slots become empty and new items can be placed in). If new items are placed in the hero can then be teleported with the Mythical Scroll spell without causing the items to dissapear. Therefore, I could possibly workaround this issue by just having the unit get teleported by an invisible dummy version of this spell after his UI is created.
I may find a solution on my own and post it here, but any help would be greatly appreciated. Here is a test version of my map if you are interested...
Test_Map - Warcraft 3 Maps - Epic War.com
(Only King Arthur is fully set up to use the UI and Merlin has the teleport spell. Type "lvl" to quick level all allied heroes to learn the spell. Use Merlin's staff to teleport to Arthur (they start in different locations). Then select where you'd like to teleport with the spell and then recast the spell on Arthur or Arthur/Merlin together to teleport him/them.)
JASS:
scope MythicalScroll initializer Init_MythicalScroll
//////////// START OF CONF PART ////////////
globals
private constant integer SPELL_PORTAL = 'A04O'
private constant integer SPELL_TELEPORT = 'A04N'
private constant real INTERVAL_PORTAL = 0.5
private constant string EFFECT_PORTAL_PATH = "Eldritch Scroll.mdx"
private constant real EFFECT_PORTAL_SIZE = 1
private constant real EFFECT_PORTAL_HEIGHT = 0
private constant real RADIUS = 300
private constant string EFFECT_SCROLL_PATH = "Boomer Scroll SD.mdl"
private constant real EFFECT_SCROLL_SIZE = 1
private constant real EFFECT_SCROLL_HEIGHT = 0
private constant string EFFECT_TELEPORT_START_PATH = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"
private constant string EFFECT_TELEPORT_ARRIVE_PATH = "Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl"
private constant real FLY_DURATION_SCROLL = 1
private constant real INTERVAL_SCROLL = 0.01
private constant string ERROR_MESSAGE_TEXT = "[Error] - The Portal does not exist anymore."
private constant string ERROR_MESSAGE_COLOR = "ff0000"
private group G = CreateGroup()
endglobals
private function getDurationMax takes real lvl returns real
return 7 + (2 * lvl)
endfunction
//////////// END OF CONF PART ////////////
private keyword Portal
private function randomRadian takes nothing returns real
return GetRandomReal(0, 6.283185)
endfunction
private function distancePoints takes real X, real Y, real X1, real Y1 returns real
return SquareRoot(((Y1-Y)*(Y1-Y))+((X1-X)*(X1-X)))
endfunction
private function createSfx takes string Path, real X, real Y, real A, real Scale, real Height returns effect
set bj_lastCreatedEffect = AddSpecialEffect(Path, X, Y)
call BlzSetSpecialEffectZ(bj_lastCreatedEffect, Height)
if Scale != 1 then
call BlzSetSpecialEffectScale(bj_lastCreatedEffect, Scale)
endif
call BlzSetSpecialEffectYaw(bj_lastCreatedEffect, A)
return bj_lastCreatedEffect
endfunction
private function effectPoint takes string Path, real X, real Y returns nothing
local effect sfx = AddSpecialEffect(Path, X, Y)
call DestroyEffect(sfx)
set sfx = null
endfunction
private function moveSfx takes effect Sfx, real X, real Y, real A returns nothing
call BlzSetSpecialEffectX(Sfx, X)
call BlzSetSpecialEffectY(Sfx, Y)
call BlzSetSpecialEffectYaw(Sfx, A)
endfunction
private function addSpell takes unit U, integer Id returns nothing
call UnitAddAbility(U, Id)
call UnitMakeAbilityPermanent(U, true, Id)
endfunction
private function removeSpell takes unit U, integer Id returns nothing
call UnitMakeAbilityPermanent(U, false, Id)
call UnitRemoveAbility(U, Id)
endfunction
private function colorTxt takes string Message, string ColorCode returns string
if ColorCode == "" or ColorCode == null then
return Message
else
return "|cff" + ColorCode + Message + "|r"
endif
endfunction
private function nullFilter takes nothing returns boolean
return true
endfunction
private struct Scroll
static Scroll array S
static integer ST = 0
static timer T = null
unit caster
real x
real y
real dx
real dy
real dist
real angle
real duration
effect sfx
player p
Portal pt
private method onDestroy takes nothing returns nothing
call DestroyEffect(.sfx)
set .caster = null
set .p = null
set .sfx = null
endmethod
private method scrollTeleport takes nothing returns nothing
local unit U2
local real X1
local real Y1
local real Dist
local real A
call GroupEnumUnitsInRange(G, .x, .y, RADIUS, Condition(function nullFilter))
loop
set U2 = FirstOfGroup(G)
exitwhen U2 == null
call GroupRemoveUnit(G, U2)
if IsUnitAlly(U2, .p) then
set X1 = GetUnitX(U2)
set Y1 = GetUnitY(U2)
set Dist = distancePoints(.x, .y, X1, Y1)
set A = Atan2(Y1-.y, X1-.x)
call effectPoint(EFFECT_TELEPORT_START_PATH, X1, Y1)
call SetUnitPosition(U2, (pt.x + (Cos(A) * Dist)), (pt.y + (Sin(A) * Dist)))
call effectPoint(EFFECT_TELEPORT_ARRIVE_PATH, GetUnitX(U2), GetUnitY(U2))
endif
endloop
endmethod
private method moveScroll takes nothing returns nothing
set .duration = .duration + INTERVAL_SCROLL
if .dist > 0 then
set .x = .x + .dx
set .y = .y + .dy
call moveSfx(.sfx, .x, .y, .angle)
endif
endmethod
static method update takes nothing returns nothing
local Scroll s
local integer I = 0
loop
set I = I + 1
set s = .S[I]
call s.moveScroll()
if s.duration >= FLY_DURATION_SCROLL then
call s.scrollTeleport()
call s.destroy()
set .S[I] = .S[.ST]
set .ST = .ST - 1
set I = I - 1
endif
exitwhen I >= .ST
endloop
if .ST <= 0 then
call PauseTimer(.T)
set .ST = 0
endif
endmethod
static method addScroll takes unit U, real X, real Y, real A, real Distance, player P, Portal PT returns nothing
local Scroll s = Scroll.allocate()
local real dist = Distance / FLY_DURATION_SCROLL * INTERVAL_SCROLL
set s.caster = U
set s.x = X
set s.y = Y
set s.dx = Cos(A) * dist
set s.dy = Sin(A) * dist
set s.dist = dist
set s.angle = A
set s.duration = 0
set s.sfx = createSfx(EFFECT_SCROLL_PATH, X, Y, A, EFFECT_SCROLL_SIZE, EFFECT_SCROLL_HEIGHT)
set s.p = P
set s.pt = PT
set .ST = .ST + 1
set .S[.ST] = s
if .ST == 1 then
call TimerStart(.T, INTERVAL_SCROLL, true, function Scroll.update)
endif
endmethod
endstruct
private struct Portal
static Portal array P
static integer PT = 0
static timer T = null
unit caster
real x
real y
player p
real duration
real durationMax
real lvl
effect sfx
boolean done
private method swapSpells takes boolean showTeleportSpell returns nothing
if showTeleportSpell then
call BlzUnitHideAbility(.caster, SPELL_PORTAL, showTeleportSpell)
call addSpell(.caster, SPELL_TELEPORT)
call SetUnitAbilityLevel(.caster, SPELL_TELEPORT, R2I(.lvl))
else
call removeSpell(.caster, SPELL_TELEPORT)
call BlzUnitHideAbility(.caster, SPELL_PORTAL, showTeleportSpell)
endif
endmethod
private method onDestroy takes nothing returns nothing
call .swapSpells(false)
set .done = true
call DestroyEffect(.sfx)
set .caster = null
set .sfx = null
set .p = null
endmethod
static method update takes nothing returns nothing
local Portal p
local integer I = 0
loop
set I = I + 1
set p = .P[I]
set p.duration = p.duration + INTERVAL_PORTAL
if p.duration >= p.durationMax then
call p.destroy()
set .P[I] = .P[.PT]
set .PT = .PT - 1
set I = I - 1
endif
exitwhen I >= .PT
endloop
if .PT <= 0 then
call PauseTimer(.T)
set .PT = 0
endif
endmethod
static method getByCaster takes unit U returns Portal
local Portal p
local Portal portalToReturn = 0
local integer I = 1
loop
exitwhen I > .PT
set p = .P[I]
if p.caster == U then
set I = .PT
set portalToReturn = p
endif
set I = I + 1
endloop
return portalToReturn
endmethod
static method addPortal takes unit U, real X, real Y, real lvl returns nothing
local Portal p
local boolean found = false
local integer I = 1
loop
exitwhen I > .PT
set p = .P[I]
if p.caster == U then
set I = .PT
set found = true
set p.duration = 0
set p.x = X
set p.y = Y
call BlzSetSpecialEffectX(p.sfx, X)
call BlzSetSpecialEffectY(p.sfx, Y)
if p.lvl != lvl then
set p.lvl = lvl
set p.durationMax = getDurationMax(lvl)
endif
endif
set I = I + 1
endloop
if not(found) then
set p = Portal.allocate()
set p.caster = U
set p.x = X
set p.y = Y
set p.p = GetOwningPlayer(U)
set p.duration = 0
set p.durationMax = getDurationMax(lvl)
set p.lvl = lvl
set p.sfx = createSfx(EFFECT_PORTAL_PATH, X, Y, randomRadian(), EFFECT_PORTAL_SIZE, EFFECT_SCROLL_HEIGHT)
call p.swapSpells(true)
set .PT = .PT + 1
set .P[.PT] = p
if .PT == 1 then
call TimerStart(.T, INTERVAL_PORTAL, true, function Portal.update)
endif
endif
endmethod
endstruct
function Trig_MythicalScrollTeleport_Conditions takes nothing returns boolean
return GetSpellAbilityId() == SPELL_TELEPORT
endfunction
function Trig_MythicalScrollTeleport_Actions takes nothing returns nothing
local unit U = GetTriggerUnit()
local real XCaster = GetUnitX(U)
local real YCaster = GetUnitY(U)
local real XTarget = GetSpellTargetX()
local real YTarget = GetSpellTargetY()
local player P = GetOwningPlayer(U)
local Portal PT = Portal.getByCaster(U)
local real A = Atan2(YTarget-YCaster, XTarget-XCaster)
local real distance = distancePoints(XCaster, YCaster, XTarget, YTarget)
if PT != 0 then
call Scroll.addScroll(U, XCaster, YCaster, A, distance, P, PT)
else
call DisplayTextToPlayer(P, 0, 0, colorTxt(ERROR_MESSAGE_TEXT, ERROR_MESSAGE_COLOR))
endif
set U = null
set P = null
endfunction
function Trig_MythicalScrollPortal_Conditions takes nothing returns boolean
return GetSpellAbilityId() == SPELL_PORTAL
endfunction
function Trig_MythicalScrollPortal_Actions takes nothing returns nothing
local unit U = GetTriggerUnit()
local real XTarget = GetSpellTargetX()
local real YTarget = GetSpellTargetY()
local real lvl = GetUnitAbilityLevel(U, GetSpellAbilityId())
call Portal.addPortal(U, XTarget, YTarget, lvl)
set U = null
endfunction
//===========================================================================
function Init_MythicalScroll takes nothing returns nothing
local trigger T = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( T, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( T, Condition( function Trig_MythicalScrollPortal_Conditions ) )
call TriggerAddAction( T, function Trig_MythicalScrollPortal_Actions )
set T = null
set T = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( T, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( T, Condition( function Trig_MythicalScrollTeleport_Conditions ) )
call TriggerAddAction( T, function Trig_MythicalScrollTeleport_Actions )
set T = null
set Scroll.T = CreateTimer()
set Portal.T = CreateTimer()
call Preload(EFFECT_TELEPORT_START_PATH)
call Preload(EFFECT_TELEPORT_ARRIVE_PATH)
call Preload(EFFECT_SCROLL_PATH)
call Preload(EFFECT_PORTAL_PATH)
endfunction
endscope
From TriggerHappy's Equipment/Inventory System v1.43
JASS:
library UserInterface initializer Init requires optional UnitDex/*or any unit indexer*/, Camera
/***************************************************************
*
* v1.0.6, by TriggerHappy
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*
* - Create UI elements based on dummy units.
*
* - Elements are overlay onto the current camera keeping you in gameplay.
*
* - The camera can be modified to any view including third person, however
* it must be locked.
*
* - Dynamic textures are applied to dummies through an ability based off
* off War Clib, using destructables with their replaceable texture field
* set to the desired texture. (more: http://www.wc3c.net/showthread.php?p=1043980)
*
* - Detect left and right clicks.
*
* - Works in multiplayer (and single) without lag.
* _________________________________________________________________________
* 1. Installation
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* 1. Copy the script to your map and save it (requires JassHelper *or* JNGP)
* 2. Copy the object editor data and paste the correct raw codes into the configuration.
* _________________________________________________________________________
* 2. API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* struct UIButton
*
* static method create takes real minx, real maxy, real W, real H, real z, integer texture returns thistype
* static method updateAll takes nothing returns nothing
* static method click takes unit u, boolean isLeft returns boolean
* static method clickEx takes real x, real y, boolean isLeft returns boolean
* static method clickPeriodicSelect takes player p, boolean isLeft returns boolean
*
* method destroy takes nothing returns nothing
* method update takes nothing returns nothing
* method show takes boolean flag, Camera Cam returns nothing
* method showPlayer takes player p, boolean flag, Camera cam returns nothing
* method isClicked takes real x, real y returns boolean
* method clickL takes nothing returns nothing
* method clickR takes nothing returns nothing
* method setPosition takes real minx, real maxy returns nothing
* method setTexture takes integer texture returns nothing
*
* -----------------------
*
* integer customValue
* integer animIndex
* trigger triggerL
* trigger triggerR
* filterfunc onLeftClick
* filterfunc onRightClick
*
*
* struct UIPicture
*
* static method create takes real minx, real maxy, real w, real h, real z, integer texture returns thistype
* static method createEx takes real minx, real maxy, real z, real scale, integer unitId, real modelWidth, real modelHeight, integer texture returns thistype
* static method updateAll takes nothing returns nothing
* method destroy takes nothing returns nothing
* method update takes nothing returns nothing
* method show takes boolean show, Camera Cam returns nothing
* method showPlayer takes player p, boolean flag, Camera cam returns nothing
* method setPosition takes real minx, real maxy returns nothing
* method setTexture takes integer texture returns nothing
*
* -----------------------
*
* integer customValue
* integer animIndex
*
* struct UIText
*
* static method create takes real minx, real maxy, real z returns thistype
* static method updateAll takes nothing returns nothing
*
* method destroy takes nothing returns nothing
* method update takes nothing returns nothing
* method setPosition takes real minx, real maxy returns nothing
* method show takes boolean show, Camera Cam returns nothing
*
* -----------------------
*
* integer customValue
*
* function GetTriggerButton takes nothing returns UIButton
* function GetClickingPlayer takes nothing returns player
* function GetCameraDeltaZ takes nothing returns real
* function CreateWindow takes real x, real y, real size, real width, real height, race playerRace returns UIPicture
*
* public function ApplySkin takes unit u, integer texture returns boolean
*
***************************************************************/
// configuration
private module InterfaceConfig
// dummy unit with pitch angle animations
static constant integer DUMMY_TYPE = 'uidm'
// model used in CreateWindow function
static constant integer WINDOW_DUMMY = 'uiwn'
// destructable ID of the human border texture. the
// rest of the races should have consecutive ids.
static constant integer RACE_BORDERS_START = 'D201'
// run a timer to check for unit selections.
// faster but more cpu intensive.
static constant boolean LEFT_CLICK_TIMER = true
// really low timer intervals remove the flicker
// effect, most of the time.
static constant real LEFT_CLICK_TIMER_RATE = 0.01
// in multiplayer, IsUnitSelected is synchronous
// so it doesn't instantly return false when you use
// SelectUnit(u, false). So we must disable the button
// for the clicking player for a brief period or else
// the event will be spammed.
static constant real LEFT_CLICK_DELAY = 0.6
// ability id's
static constant integer DESTROYER_FORM_ID = 'Aave'
static constant integer LOCUST_ID = 'Aloc'
// ability used to change dummy unit textures
static constant integer SKIN_CHANGE_ABILITY = 'Skin'
// removes pathing and possibly other things from the unit.
// similar to locust, but allows clicking.
static constant integer GHOST_ABILITY = 'Aeth'
// dummy player
static constant player PLAYER = CAMERA_DUMMY_PLAYER // from Camera library
endmodule
// end configuration
globals
public destructable DummyTree = null
private trigger Trig = CreateTrigger()
private UIButton ClickedButton = 0
private player ClickingPlayer = null
private location DummyLoc = Location(0,0)
endglobals
function GetTriggerButton takes nothing returns UIButton
return ClickedButton
endfunction
function GetClickingPlayer takes nothing returns player
return ClickingPlayer
endfunction
public function Eval takes filterfunc func returns boolean
call TriggerClearConditions(Trig)
call TriggerAddCondition(Trig, func)
return TriggerEvaluate(Trig)
endfunction
public function GetTerrainZ takes real x, real y returns real
call MoveLocation(DummyLoc, x, y)
return GetLocationZ(DummyLoc)
endfunction
// adjusts
public function FindModelAnimIndex takes real w, real h, real z returns integer
local real W = w*SCREEN_WIDTH*z
local real H = h*SCREEN_HEIGHT*z
local real anim
if (H<W) then
set anim = 10*(W/H-1)
return UIMath_R2I_N(anim)
else
set anim = 10*(H/W-1)
if anim >= 0.5 then
return 100+UIMath_R2I_N(anim)
endif
endif
return 0
endfunction
public function FindModelSize takes real w, real h, real z returns real
local real W = w*SCREEN_WIDTH*z
local real H = h*SCREEN_HEIGHT*z
if (H<W) then
return 0.5*H
endif
return 0.5*W
endfunction
private function LocalReal takes player p, real forPlayer, real default returns real
if (User.Local == p) then
return forPlayer
endif
return default
endfunction
public function ApplySkin takes unit u, integer texture returns boolean
local boolean out = false
if (texture != 0) then
call UnitAddAbility(u, Interface.SKIN_CHANGE_ABILITY)
set DummyTree = CreateDestructable(texture,GetUnitX(u),GetUnitY(u),0,0,1)
set out = IssueTargetOrder(u, "grabtree", DummyTree)
call RemoveDestructable(DummyTree)
set DummyTree = null
call UnitRemoveAbility(u, Interface.SKIN_CHANGE_ABILITY)
endif
return out
endfunction
struct UIButton
integer customValue
integer animIndex
trigger triggerL
trigger triggerR
filterfunc onLeftClick
filterfunc onRightClick
boolean disabled
unit selectUnit
static boolean useDelay = false
readonly static thistype array AllShow
readonly static integer CountShow = 0
readonly integer index
readonly Camera camera
readonly real scale
readonly real centerx
readonly real centery
readonly real minx
readonly real maxx
readonly real miny
readonly real maxy
readonly real width
readonly real height
readonly real z
readonly unit picture
readonly effect model
readonly boolean displayed
readonly integer texture
private integer unitId
private static thistype array Unit2Button
static method create takes real minx, real maxy, real W, real H, real z, integer texture returns thistype
local thistype this = thistype.allocate()
set .texture = texture
set .customValue = 0
set .camera = 0
set .width = W
set .height = H
set .minx = minx
set .maxx = minx+W
set .maxy = maxy
set .miny = maxy-H
set .z = 100.2+z
set .centerx = minx+W/2.0
set .centery = maxy-H/2.0
set .displayed = false
set .scale = FindModelSize(W, H, .z)
set .triggerL = null
set .triggerR = null
set .onLeftClick = null
set .onRightClick = null
set .selectUnit = null
set .picture = CreateUnit(Interface.PLAYER, Interface.DUMMY_TYPE, 0, 0, 0)
// apply texture
call ApplySkin(.picture, texture)
set .animIndex = FindModelAnimIndex(W, H, .z)
call SetUnitAnimationByIndex(.picture, .animIndex)
call UnitAddAbility(.picture, Interface.DESTROYER_FORM_ID)
call UnitRemoveAbility(.picture, Interface.DESTROYER_FORM_ID)
call UnitAddAbility(.picture, Interface.SKIN_CHANGE_ABILITY)
call UnitAddAbility(.picture, Interface.GHOST_ABILITY)
set this.unitId = GetUnitUserData(.picture)
set Unit2Button[this.unitId] = this
call SetUnitScale(.picture, 0, 0, 0)
return this
endmethod
method destroy takes nothing returns nothing
call RemoveUnit(.picture)
if .displayed then
set .AllShow[.index] = .AllShow[.CountShow]
set .AllShow[.index].index = .index
set .CountShow = .CountShow - 1
endif
endmethod
method update takes nothing returns nothing
local VECTOR3 Pos
if (.displayed) then
set Pos = .camera.win2World(.centerx, .centery, .z)
call SetUnitX(.picture, Pos.x)
call SetUnitY(.picture, Pos.y)
call SetUnitFlyHeight(.picture, Pos.z-GetTerrainZ(Pos.x, Pos.y), 0)
call Pos.destroy()
endif
endmethod
method show takes boolean flag, Camera Cam returns nothing
if Cam != -1 then
set .camera = Cam
endif
if flag != .displayed then
if flag then
set .AllShow[.CountShow] = this
set .index = .CountShow
set .CountShow = .CountShow + 1
call .update()
call SetUnitAnimationByIndex(.picture, .animIndex)
call SetUnitScale(.picture, .scale, 0, 0)
else
set .CountShow = .CountShow - 1
set .AllShow[.index] = .AllShow[.CountShow]
set .AllShow[.index].index = .index
call SetUnitX(.picture, 0)
call SetUnitY(.picture, 0)
call SetUnitScale(.picture, 0, 0, 0)
endif
endif
set .displayed = flag
endmethod
method showPlayer takes player p, boolean flag, Camera cam returns nothing
call .show(flag, cam)
if (flag) then
call SetUnitScale(.picture, LocalReal(p, .scale, 0), 0, 0)
endif
endmethod
method isClicked takes real x, real y returns boolean
return .minx < x and x < .maxx and .miny < y and y < .maxy
endmethod
method clickL takes nothing returns nothing
set ClickedButton = this
if (.triggerL != null and TriggerEvaluate(.triggerL)) then
call TriggerExecute(.triggerL)
endif
if (.onLeftClick != null) then
call Eval(.onLeftClick)
endif
endmethod
method clickR takes nothing returns nothing
set ClickedButton = this
if (.triggerR != null and TriggerEvaluate(.triggerR)) then
call TriggerExecute(.triggerR)
endif
if (.onRightClick != null) then
call Eval(.onRightClick)
endif
endmethod
method setPosition takes real minx, real maxy returns nothing
set .minx = minx
set .maxx = minx+.width
set .maxy = maxy
set .miny = maxy-.height
set .centerx = minx+.width/2.0
set .centery = maxy-.height/2.0
if .displayed then
call .update()
endif
endmethod
method setTexture takes integer texture returns nothing
call SetUnitX(.picture, 0)
call SetUnitY(.picture, 0)
call ApplySkin(.picture, texture)
if .displayed then
call .update()
endif
set .texture = texture
endmethod
static method updateAll takes nothing returns nothing
local integer i = .CountShow
loop
exitwhen i < 0
call .AllShow[i].update()
set i = i - 1
endloop
endmethod
static method click takes unit u, boolean isLeft returns boolean
local thistype b
if GetUnitTypeId(u) == Interface.DUMMY_TYPE then
set b = Unit2Button[GetUnitUserData(u)]
if b > 0 and b.displayed then
call SelectUnit(u, false)
if isLeft then
call b.clickL()
else
call b.clickR()
endif
return true
endif
endif
return false
endmethod
static method clickEx takes real x, real y, boolean isLeft returns boolean
local integer i = .CountShow
loop
exitwhen i < 0
if .AllShow[i].isClicked(x, y) then
if isLeft then
call .AllShow[i].clickL()
else
call .AllShow[i].clickR()
endif
return true
endif
set i = i - 1
endloop
return false
endmethod
private static method enableButtonCallback takes nothing returns nothing
local timer t = GetExpiredTimer()
static if (LIBRARY_TimerUtils) then
local thistype btn = thistype(GetTimerData(t))
else
local thistype btn = thistype(R2I(TimerGetRemaining(t) + 0.5))
endif
set btn.disabled = false
set t = null
endmethod
static method clickPeriodicSelect takes player p, boolean isLeft returns boolean
local integer i = .CountShow
local timer t
loop
exitwhen i < 0
if (IsUnitSelected(.AllShow[i].picture, p)) then
if (not .AllShow[i].disabled) then
if isLeft then
call .AllShow[i].clickL()
else
call .AllShow[i].clickR()
endif
if (thistype.useDelay) then // static if doesn't work..
set .AllShow[i].disabled = true
static if (LIBRARY_TimerUtils) then
call TimerStart(NewTimerEx(.AllShow[i]), Interface.LEFT_CLICK_DELAY, false, function thistype.enableButtonCallback)
else
set t = CreateTimer()
call TimerStart(t, .AllShow[i], false, null)
call PauseTimer(t)
call TimerStart(t, Interface.LEFT_CLICK_DELAY, false, function thistype.enableButtonCallback)
set t = null
endif
endif
return true
endif
call SelectUnit(.AllShow[i].picture, false)
if (.AllShow[i].selectUnit != null and User.Local == p) then
call SelectUnit(.AllShow[i].selectUnit, true)
endif
endif
set i = i - 1
endloop
return false
endmethod
endstruct
struct UIPicture
integer customValue
integer animIndex
readonly static thistype array AllShow
readonly static integer CountShow = 0
readonly integer index
readonly Camera camera
readonly real scale
readonly real centerx
readonly real centery
readonly real width
readonly real height
readonly real z
readonly unit picture
readonly boolean displayed
readonly integer texture
static method create takes real minx, real maxy, real w, real h, real z, integer texture returns thistype
local thistype this = thistype.allocate()
set .texture = texture
set .customValue = 0
set .camera = 0
set .width = w
set .height = h
set .centerx = minx+w/2.0
set .centery = maxy-h/2.0
set .z = 100.2+z
set .displayed = false
set .picture = CreateUnit(Interface.PLAYER, Interface.DUMMY_TYPE, 0, 0, 0)
set .animIndex = FindModelAnimIndex(w, h, .z)
set .scale = FindModelSize(w, h, .z)
call ApplySkin(.picture, texture)
call SetUnitAnimationByIndex(.picture, .animIndex)
call UnitAddAbility(.picture, Interface.DESTROYER_FORM_ID)
call UnitRemoveAbility(.picture, Interface.DESTROYER_FORM_ID)
call UnitAddAbility(.picture, Interface.LOCUST_ID)
call UnitRemoveAbility(.picture, Interface.LOCUST_ID)
// hide unit
call SetUnitScale(.picture, 0, 0, 0)
return this
endmethod
static method createEx takes real minx, real maxy, real z, real scale, integer unitId, real modelWidth, real modelHeight, integer texture returns thistype
local thistype this = thistype.allocate()
set .customValue = 0
set .camera = 0
set .z = 100.2 + z
set .width = (modelWidth*scale) / (SCREEN_WIDTH*.z)
set .height = (modelHeight*scale) / (SCREEN_HEIGHT*.z)
set .scale = scale
set .centerx = minx+.width/2.0
set .centery = maxy-.height/2.0
set .displayed = false
set .picture = CreateUnit(Interface.PLAYER, unitId, 0, 0, 270)
call ApplySkin(.picture, texture)
call UnitAddAbility(.picture, Interface.DESTROYER_FORM_ID)
call UnitRemoveAbility(.picture, Interface.DESTROYER_FORM_ID)
call UnitAddAbility(.picture, Interface.LOCUST_ID)
call UnitRemoveAbility(.picture, Interface.LOCUST_ID)
// hide unit
call SetUnitScale(.picture, 0, 0, 0)
return this
endmethod
method destroy takes nothing returns nothing
call RemoveUnit(.picture)
if .displayed then
set .AllShow[.index] = .AllShow[.CountShow]
set .AllShow[.index].index = .index
set .CountShow = .CountShow - 1
endif
endmethod
method update takes nothing returns nothing
local VECTOR3 Pos
if (this.displayed) then
set Pos = this.camera.win2World(.centerx, .centery, .z)
call SetUnitX(.picture, Pos.x)
call SetUnitY(.picture, Pos.y)
call SetUnitFlyHeight(.picture, Pos.z - GetTerrainZ(Pos.x, Pos.y), 0)
call Pos.destroy()
endif
endmethod
method show takes boolean show, Camera Cam returns nothing
if Cam != -1 then
set this.camera = Cam
endif
if show != .displayed then
if show then
set .AllShow[.CountShow] = this
set .index = .CountShow
set .CountShow = .CountShow + 1
call .update()
call SetUnitAnimationByIndex(.picture, .animIndex)
call SetUnitScale(.picture, .scale, 0, 0)
else
set .CountShow = .CountShow - 1
set .AllShow[.index] = .AllShow[.CountShow]
set .AllShow[.index].index = .index
call SetUnitX(.picture, 0)
call SetUnitY(.picture, 0)
call SetUnitScale(.picture, 0, 0, 0)
endif
endif
set .displayed = show
endmethod
method showPlayer takes player p, boolean flag, Camera cam returns nothing
call .show(flag, cam)
if (flag) then
call SetUnitScale(.picture, LocalReal(p, .scale, 0), 0, 0)
endif
endmethod
method setPosition takes real minx, real maxy returns nothing
set .centerx = minx+.width/2.0
set .centery = maxy-.height/2.0
if .displayed then
call .update()
endif
endmethod
method setTexture takes integer texture returns nothing
call SetUnitX(.picture, 0)
call SetUnitY(.picture, 0)
call ApplySkin(.picture, texture)
if .displayed then
call .update()
endif
set .texture = texture
endmethod
static method updateAll takes nothing returns nothing
local integer i = .CountShow
loop
exitwhen i < 0
call .AllShow[i].update()
set i = i - 1
endloop
endmethod
endstruct
struct UIText
integer customValue
readonly unit dummy
readonly static thistype array AllShow
readonly static integer CountShow = 0
readonly integer index
readonly Camera camera
readonly real minx
readonly real maxy
readonly real z
readonly texttag text
readonly boolean displayed
static method create takes real minx, real maxy, real z returns thistype
local thistype this = thistype.allocate()
set .customValue = 0
set .camera = 0
set .minx = minx
set .maxy = maxy
set .z = 100 + z
set .displayed = false
set .text = CreateTextTag()
set .dummy = CreateUnit(Interface.PLAYER, Interface.DUMMY_TYPE, 0, 0, 0)
call SetUnitScale(.dummy, 0, 0, 0)
call UnitAddAbility(.dummy, Interface.LOCUST_ID)
call UnitRemoveAbility(.dummy, Interface.LOCUST_ID)
call SetTextTagVisibility(.text, false)
call SetTextTagPosUnit(.text, .dummy, 0)
return this
endmethod
static method createEx takes player p, real minx, real maxy, real z returns thistype
local thistype this = thistype.allocate()
set .customValue = 0
set .camera = 0
set .minx = minx
set .maxy = maxy
set .z = 100 + z
set .displayed = false
set .dummy = CreateUnit(Interface.PLAYER, Interface.DUMMY_TYPE, 0, 0, 0)
if (User.Local == p) then
set .text = CreateTextTag()
endif
call SetUnitScale(.dummy, 0, 0, 0)
call UnitAddAbility(.dummy, Interface.LOCUST_ID)
call UnitRemoveAbility(.dummy, Interface.LOCUST_ID)
call SetTextTagVisibility(.text, false)
call SetTextTagPosUnit(.text, .dummy, 0)
return this
endmethod
method destroy takes nothing returns nothing
call DestroyTextTag(.text)
if .displayed then
set .AllShow[.index] = .AllShow[.CountShow]
set .AllShow[.index].index = .index
set .CountShow = .CountShow - 1
endif
endmethod
method update takes nothing returns nothing
local VECTOR3 Pos = .camera.win2World(.minx, .maxy, .z)
call SetUnitX(.dummy, Pos.x)
call SetUnitY(.dummy, Pos.y)
call SetTextTagPosUnit(.text, .dummy, (Pos.z-GetTerrainZ(Pos.x,Pos.y))-14.8)
call Pos.destroy()
endmethod
method setPosition takes real minx, real maxy returns nothing
set .minx = minx
set .maxy = maxy
if .displayed then
call .update()
endif
endmethod
method show takes boolean show, Camera Cam returns nothing
if Cam != -1 then
set .camera = Cam
endif
call SetTextTagVisibility(.text, show)
if show != .displayed then
if show then
set .AllShow[.CountShow] = this
set .index = .CountShow
set .CountShow = .CountShow + 1
call .update()
else
set .CountShow = .CountShow - 1
set .AllShow[.index] = .AllShow[.CountShow]
set .AllShow[.index].index = .index
endif
endif
set .displayed = show
endmethod
static method updateAll takes nothing returns nothing
local integer i = .CountShow
loop
exitwhen i < 0
call .AllShow[i].update()
set i = i - 1
endloop
endmethod
endstruct
struct Interface extends array
implement InterfaceConfig
static method getRaceBorders takes race r returns integer
return RACE_BORDERS_START + (GetHandleId(r)-1)
endmethod
static method updateAll takes boolean but, boolean pic, boolean tex returns nothing
if but then
call UIButton.updateAll()
endif
if pic then
call UIPicture.updateAll()
endif
if tex then
call UIText.updateAll()
endif
endmethod
endstruct
function CreateWindow takes real x, real y, real size, real width, real height, race playerRace returns UIPicture
return UIPicture.createEx(x, y, /*z=*/2, size, Interface.WINDOW_DUMMY, width, height, Interface.getRaceBorders(playerRace))
endfunction
private function InterfaceClickR takes nothing returns nothing
local unit clicker = GetTriggerUnit()
local unit btnUnit = GetOrderTargetUnit()
set ClickingPlayer = GetOwningPlayer(clicker)
if (UIButton.click(btnUnit, false)) then
call PauseUnit(clicker, true)
call IssueImmediateOrderById(clicker, 851973)
call PauseUnit(clicker, false)
endif
set clicker = null
endfunction
private function InterfaceClickL takes nothing returns nothing
set ClickingPlayer = GetTriggerPlayer()
call UIButton.click(GetTriggerUnit(), true)
endfunction
private function InterfaceClickL_Timer takes nothing returns nothing
local integer i = 0
local integer c = UIButton.CountShow
local User user = User.first
loop
exitwhen user == User.NULL
set ClickingPlayer = user.handle
call UIButton.clickPeriodicSelect(user.handle, true)
set user = user.next
endloop
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local trigger t2 = CreateTrigger()
local integer i = 0
local User user = User.first
loop
exitwhen user == User.NULL
call TriggerRegisterPlayerUnitEvent(t, user.handle, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, null)
static if (not Interface.LEFT_CLICK_TIMER) then
call TriggerRegisterPlayerUnitEvent(t2, user.handle, EVENT_PLAYER_UNIT_SELECTED, null)
endif
set user = user.next
endloop
call TriggerAddAction(t, function InterfaceClickR)
static if (Interface.LEFT_CLICK_TIMER) then
call TimerStart(CreateTimer(), Interface.LEFT_CLICK_TIMER_RATE, true, function InterfaceClickL_Timer)
else
call TriggerAddAction(t2, function InterfaceClickL)
endif
set UIButton.useDelay = Interface.LEFT_CLICK_TIMER
endfunction
endlibrary
JASS:
library UnitDex uses optional WorldBounds, optional GroupUtils
/***************************************************************
*
* v1.2.1, by TriggerHappy
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* UnitDex assigns every unit an unique integer. It attempts to make that number within the
* maximum array limit so you can associate it with one.
* _________________________________________________________________________
* 1. Installation
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the script to your map, save it, then restart the editor and comment out the line below.
*/
///! external ObjectMerger w3a Adef uDex anam "Detect Leave" ansf "(UnitDex)" aart "" acat "" arac 0
/* ________________________________________________________________________
* 2. Configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
private module UnitDexConfig
// The raw code of the leave detection ability.
static constant integer DETECT_LEAVE_ABILITY = 'uDex'
// Allow debug messages (debug mode must also be on)
static constant boolean ALLOW_DEBUGGING = true
// Uncomment the lines below to define a filter for units being indexed
/*static method onFilter takes unit u returns boolean
return true
endmethod*/
endmodule
/* _________________________________________________________________________
* 3. Function API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Every function inlines except for UnitDexRemove
*
* function GetUnitId takes unit whichUnit returns integer
* function GetUnitById takes integer index returns unit
*
* function UnitDexEnable takes boolean flag returns nothing
* function UnitDexRemove takes unit u, boolean runEvents returns boolean
*
* function IsUnitIndexed takes unit u returns boolean
* function IsIndexingEnabled takes nothing returns boolean
*
* function GetIndexedUnit takes nothing returns unit
* function GetIndexedUnitId takes nothing returns integer
*
* function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns indexevent
* function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
* function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
*
* function OnUnitIndex takes code func returns triggercondition
* function OnUnitDeidex takes code func returns triggercondition
* _________________________________________________________________________
* 4. Struct API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* UnitDex.Enabled = false // toggle the indexer
* UnitDex.Initialized // returns true if the preload timer has finished
* UnitDex.Count // returns the amount of units indexed
* UnitDex.Unit[0] // access the UnitDex array directly
* UnitDex.Group // a unit group containing every indexed unit (for enumeration)
* UnitDex.LastIndex // returns the last indexed unit's id
* _________________________________________________________________________
* 5. Public Variables
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* These are to be used with the "eventtype" argument of the event API:
*
* constant integer EVENT_UNIT_INDEX = 0
* constant integer EVENT_UNIT_DEINDEX = 1
* _________________________________________________________________________
* 6. Examples
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* 1. Associate a unit with a variable
*
* set UnitFlag[GetUnitId(yourUnit)] = true
*
* 2. Allocate a struct instance using a units assigned ID
*
* local somestruct data = GetUnitId(yourUnit)
*
* 3. Detect when a unit leaves the map
*
* function Exit takes nothing returns nothing
* call BJDebugMsg("The unit " + GetUnitName(GetIndexedUnit()) + " has left the map.")
* endfunction
*
* call OnUnitDeindex(function Exit)
* // or
* call RegisterUnitIndexEvent(Filter(function Exit), EVENT_UNIT_DEINDEX)
*
*
* _________________________________________________________________________
* 7. How it works
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* 1. Enumerate all preplaced units
* 2. TriggerRegisterEnterRegion native to detect when a unit enters the map
* 3. Assign each unit that enters the map a unique integer
* 4. Give every unit an ability based off of defend. There is no native to accurately
* detect when a unit leaves the map but when a unit dies or is removed from the game
* they are issued the "undefend" order
* 5. Catch the "undefend" order and remove unit from the queue
* _________________________________________________________________________
* 8. Notes
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* - This system is compatable with GUI because it utilizes UnitUserData (custom values for units).
* - The object merger line should be commented out after saving and restarting.
* - All public functions are inlined except UnitDexRemove.
*
* -http://www.hiveworkshop.com/forums/submissions-414/unitdex-lightweight-unit-indexer-248209/
*
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
globals
// Event types
constant integer EVENT_UNIT_INDEX = 0
constant integer EVENT_UNIT_DEINDEX = 1
// System variables
private trigger array IndexTrig
private integer Index = 0
private real E=-1
private boolexpr FilterEnter
endglobals
function GetUnitId takes unit whichUnit returns integer
return GetUnitUserData(whichUnit)
endfunction
function GetUnitById takes integer index returns unit
return UnitDex.Unit[index]
endfunction
function GetIndexedUnit takes nothing returns unit
return UnitDex.Unit[UnitDex.LastIndex]
endfunction
function GetIndexedUnitId takes nothing returns integer
return UnitDex.LastIndex
endfunction
function IsUnitIndexed takes unit u returns boolean
return (GetUnitById(GetUnitId(u)) != null)
endfunction
function UnitDexEnable takes boolean flag returns nothing
set UnitDex.Enabled = flag
endfunction
function IsIndexingEnabled takes nothing returns boolean
return UnitDex.Enabled
endfunction
function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns triggercondition
return TriggerAddCondition(IndexTrig[eventtype], func)
endfunction
function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
call TriggerRemoveCondition(IndexTrig[eventtype], c)
endfunction
function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
call TriggerRegisterVariableEvent(t, SCOPE_PRIVATE + "E", EQUAL, eventtype)
endfunction
function OnUnitIndex takes code func returns triggercondition
return TriggerAddCondition(IndexTrig[EVENT_UNIT_INDEX], Filter(func))
endfunction
function OnUnitDeindex takes code func returns triggercondition
return TriggerAddCondition(IndexTrig[EVENT_UNIT_DEINDEX], Filter(func))
endfunction
function UnitDexRemove takes unit u, boolean runEvents returns boolean
return UnitDex.Remove(u, runEvents)
endfunction
/****************************************************************/
private keyword UnitDexCore
struct UnitDex extends array
static boolean Enabled = true
readonly static integer LastIndex
readonly static boolean Initialized=false
readonly static group Group=CreateGroup()
readonly static unit array Unit
readonly static integer Count = 0
readonly static integer array List
implement UnitDexConfig
private static integer Counter = 0
implement UnitDexCore
endstruct
/****************************************************************/
private module UnitDexCore
static method Remove takes unit u, boolean runEvents returns boolean
local integer i
if (IsUnitIndexed(u)) then
set i = GetUnitId(u)
set UnitDex.List[i] = Index
set Index = i
call GroupRemoveUnit(UnitDex.Group, u)
call SetUnitUserData(u, 0)
if (runEvents) then
set UnitDex.LastIndex = i
set E = EVENT_UNIT_DEINDEX
call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
set E = -1
endif
set UnitDex.Unit[i] = null
set UnitDex.Count = UnitDex.Count - 1
return true
endif
return false
endmethod
private static method onGameStart takes nothing returns nothing
local integer i = 0
static if (not LIBRARY_GroupUtils) then // Check if GroupUtils exists so we can use it's enumeration group.
local group ENUM_GROUP = CreateGroup() // If not, create the group.
endif
// Index preplaced units
loop
call GroupEnumUnitsOfPlayer(ENUM_GROUP, Player(i), FilterEnter)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
static if (not LIBRARY_GroupUtils) then
call DestroyGroup(ENUM_GROUP)
set ENUM_GROUP = null
endif
// run init triggers
set i = 1
loop
exitwhen i > Counter
set LastIndex = i
call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
set E = EVENT_UNIT_INDEX
set E = -1
set i = i + 1
endloop
set LastIndex = Counter
set Initialized = true
set FilterEnter = null
call DestroyTimer(GetExpiredTimer())
endmethod
private static method onEnter takes nothing returns boolean
local unit u = GetFilterUnit()
local integer i = GetUnitId(u)
local integer t = Index
if (i == 0 and thistype.Enabled) then
// If a filter was defined pass the unit through it.
static if (thistype.onFilter.exists) then
if (not thistype.onFilter(u)) then
set u = null
return false // check failed
endif
endif
// Handle debugging
static if (thistype.DEBUG_MODE and thistype.ALLOW_DEBUGGING) then
if (t == 0 and Counter+1 >= JASS_MAX_ARRAY_SIZE) then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "UnitDex: Maximum number of units reached!")
set u = null
return false
endif
endif
// Add to group of indexed units
call GroupAddUnit(thistype.Group, u)
// Give unit the leave detection ability
call UnitAddAbility(u, thistype.DETECT_LEAVE_ABILITY)
call UnitMakeAbilityPermanent(u, true, thistype.DETECT_LEAVE_ABILITY)
// Allocate index
if (Index != 0) then
set Index = List[t]
else
set Counter = Counter + 1
set t = Counter
endif
set List[t] = -1
set LastIndex = t
set thistype.Unit[t] = u
set Count = Count + 1
// Store the index
call SetUnitUserData(u, t)
if (thistype.Initialized) then
// Execute custom events registered with RegisterUnitIndexEvent
call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
// Handle TriggerRegisterUnitIndexEvent
set E = EVENT_UNIT_INDEX
// Reset so the event can occur again
set E = -1
endif
endif
set u = null
return false
endmethod
private static method onLeave takes nothing returns boolean
local unit u
local integer i
// Check if order is undefend.
if (thistype.Enabled and GetIssuedOrderId() == 852056) then
set u = GetTriggerUnit()
// If unit was killed (not removed) then don't continue
if (GetUnitAbilityLevel(u, thistype.DETECT_LEAVE_ABILITY) != 0) then
set u = null
return false
endif
set i = GetUnitId(u)
// If unit has been indexed then deindex it
if (i > 0 and i <= Counter and u == GetUnitById(i)) then
// Recycle the index
set List[i] = Index
set Index = i
set LastIndex = i
// Remove to group of indexed units
call GroupRemoveUnit(thistype.Group, u)
// Execute custom events without any associated triggers
call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
// Handle TriggerRegisterUnitIndexEvent
set E = EVENT_UNIT_DEINDEX
// Remove entry
call SetUnitUserData(u, 0)
set thistype.Unit[i] = null
// Decrement unit count
set Count = Count - 1
// Reset so the event can occur again
set E = -1
endif
set u = null
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local player p
local unit u
static if (not LIBRARY_WorldBounds) then // Check if WorldBounts exists, if not then define the necessary vars
local region reg = CreateRegion() // If WorldBounds wasn't found, create the region manually
local rect world = GetWorldBounds()
endif
set FilterEnter = Filter(function thistype.onEnter)
// Begin to index units when they enter the map
static if (LIBRARY_WorldBounds) then
call TriggerRegisterEnterRegion(CreateTrigger(), WorldBounds.worldRegion, FilterEnter)
else
call RegionAddRect(reg, world)
call TriggerRegisterEnterRegion(CreateTrigger(), reg, FilterEnter)
call RemoveRect(world)
set world = null
endif
call TriggerAddCondition(t, Filter(function thistype.onLeave))
set IndexTrig[EVENT_UNIT_INDEX] = CreateTrigger()
set IndexTrig[EVENT_UNIT_DEINDEX] = CreateTrigger()
loop
set p = Player(i)
// Detect "undefend"
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
// Hide the detect ability from players
call SetPlayerAbilityAvailable(p, thistype.DETECT_LEAVE_ABILITY, false)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call TimerStart(CreateTimer(), 0, false, function thistype.onGameStart)
endmethod
endmodule
endlibrary
-
Unit Indexer
-
Events
-
Map initialization
-
-
Conditions
-
Actions
-
Custom script: call ExecuteFunc("InitializeUnitIndexer")
-
Custom script: endfunction
-
-------- --------
-
-------- This is the core function - it provides an index all existing units and for units as they enter the map --------
-
-------- --------
-
Custom script: function IndexUnit takes nothing returns boolean
-
Custom script: local integer pdex = udg_UDex
-
Custom script: local integer ndex
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
IsUnitPreplaced[0] Equal to False
-
-
Then - Actions
-
-------- --------
-
-------- Check for removed units for every (32) new units created --------
-
-------- --------
-
Set VariableSet UDexWasted = (UDexWasted + 1)
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
UDexWasted Equal to 32
-
-
Then - Actions
-
Set VariableSet UDexWasted = 0
-
Set VariableSet UDex = UDexNext[0]
-
Custom script: loop
-
Custom script: exitwhen udg_UDex == 0
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
(Custom value of UDexUnits[UDex]) Equal to 0
-
-
Then - Actions
-
-------- --------
-
-------- Remove index from linked list --------
-
-------- --------
-
Custom script: set ndex = udg_UDexNext[udg_UDex]
-
Custom script: set udg_UDexNext[udg_UDexPrev[udg_UDex]] = ndex
-
Custom script: set udg_UDexPrev[ndex] = udg_UDexPrev[udg_UDex]
-
Set VariableSet UDexPrev[UDex] = 0
-
Set VariableSet IsUnitPreplaced[UDex] = False
-
-------- --------
-
-------- Fire deindex event for UDex --------
-
-------- --------
-
Set VariableSet UnitIndexEvent = 2.00
-
Set VariableSet UnitIndexEvent = 0.00
-
-------- --------
-
-------- Recycle the index for later use --------
-
-------- --------
-
Set VariableSet UDexUnits[UDex] = No unit
-
Set VariableSet UDexNext[UDex] = UDexRecycle
-
Set VariableSet UDexRecycle = UDex
-
Custom script: set udg_UDex = ndex
-
-
Else - Actions
-
Set VariableSet UDex = UDexNext[UDex]
-
-
-
Custom script: endloop
-
-
Else - Actions
-
-
-
Else - Actions
-
-
-------- --------
-
-------- You can use the boolean UnitIndexerEnabled to protect some of your undesirable units from being indexed --------
-
-------- - Example: --------
-
-------- -- Set UnitIndexerEnabled = False --------
-
-------- -- Unit - Create 1 Dummy for (Triggering player) at TempLoc facing 0.00 degrees --------
-
-------- -- Set UnitIndexerEnabled = True --------
-
-------- --------
-
-------- You can also customize the following block - if conditions are false the (Matching unit) won't be indexed. --------
-
-------- --------
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
UnitIndexerEnabled Equal to True
-
(Custom value of (Matching unit)) Equal to 0
-
-
Then - Actions
-
-------- --------
-
-------- Generate a unique integer index for this unit --------
-
-------- --------
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
UDexRecycle Equal to 0
-
-
Then - Actions
-
Set VariableSet UDex = (UDexGen + 1)
-
Set VariableSet UDexGen = UDex
-
-
Else - Actions
-
Set VariableSet UDex = UDexRecycle
-
Set VariableSet UDexRecycle = UDexNext[UDex]
-
-
-
-------- --------
-
-------- Link index to unit, unit to index --------
-
-------- --------
-
Set VariableSet UDexUnits[UDex] = (Matching unit)
-
Unit - Set the custom value of UDexUnits[UDex] to UDex
-
Set VariableSet IsUnitPreplaced[UDex] = IsUnitPreplaced[0]
-
-------- --------
-
-------- Use a doubly-linked list to store all active indexes --------
-
-------- --------
-
Set VariableSet UDexPrev[UDexNext[0]] = UDex
-
Set VariableSet UDexNext[UDex] = UDexNext[0]
-
Set VariableSet UDexNext[0] = UDex
-
-------- --------
-
-------- Fire index event for UDex --------
-
-------- --------
-
Set VariableSet UnitIndexEvent = 0.00
-
Set VariableSet UnitIndexEvent = 1.00
-
Set VariableSet UnitIndexEvent = 0.00
-
-
Else - Actions
-
-
Custom script: set udg_UDex = pdex
-
Custom script: return false
-
Custom script: endfunction
-
-------- --------
-
-------- The next function initializes the core of the system --------
-
-------- --------
-
Custom script: function InitializeUnitIndexer takes nothing returns nothing
-
Custom script: local integer i = 0
-
Custom script: local region re = CreateRegion()
-
Custom script: local rect r = GetWorldBounds()
-
Custom script: local boolexpr b = Filter(function IndexUnit)
-
Set VariableSet UnitIndexEvent = -1.00
-
Set VariableSet UnitIndexerEnabled = True
-
Set VariableSet IsUnitPreplaced[0] = True
-
Custom script: call RegionAddRect(re, r)
-
Custom script: call TriggerRegisterEnterRegion(CreateTrigger(), re, b)
-
Custom script: call RemoveRect(r)
-
Custom script: set re = null
-
Custom script: set r = null
-
Custom script: loop
-
Custom script: call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, Player(i), b)
-
Custom script: set i = i + 1
-
Custom script: exitwhen i == bj_MAX_PLAYER_SLOTS
-
Custom script: endloop
-
Custom script: set b = null
-
-------- --------
-
-------- This is the "Unit Indexer Initialized" event, use it instead of "Map Initialization" for best results --------
-
-------- --------
-
Set VariableSet IsUnitPreplaced[0] = False
-
Set VariableSet UnitIndexEvent = 3.00
-
-
JASS:
//TESH.scrollpos=0
//TESH.alwaysfold=1
library PlayerUtils
/**************************************************************
*
* v1.2.8 by TriggerHappy
*
* This library provides a struct which caches data about players
* as well as provides functionality for manipulating player colors.
*
* Constants
* ------------------
*
* force FORCE_PLAYING - Player group of everyone who is playing.
*
* Struct API
* -------------------
* struct User
*
* static method fromIndex takes integer i returns User
* static method fromLocal takes nothing returns User
* static method fromPlaying takes integer id returns User
*
* static method operator [] takes integer id returns User
* static method operator count takes nothing returns integer
*
* method operator name takes nothing returns string
* method operator name= takes string name returns nothing
* method operator color takes nothing returns playercolor
* method operator color= takes playercolor c returns nothing
* method operator defaultColor takes nothing returns playercolor
* method operator hex takes nothing returns string
* method operator nameColored takes nothing returns string
*
* method toPlayer takes nothing returns player
* method colorUnits takes playercolor c returns nothing
*
* readonly string originalName
* readonly boolean isPlaying
* readonly static player Local
* readonly static integer LocalId
* readonly static integer AmountPlaying
* readonly static playercolor array Color
* readonly static player array PlayingPlayer
*
**************************************************************/
globals
// automatically change unit colors when changing player color
private constant boolean AUTO_COLOR_UNITS = true
// use an array for name / color lookups (instead of function calls)
private constant boolean ARRAY_LOOKUP = false
// this only applies if ARRAY_LOOKUP is true
private constant boolean HOOK_SAFETY = false // disable for speed, but only use the struct to change name/color safely
constant force FORCE_PLAYING = CreateForce()
private string array Name
private string array Hex
private string array OriginalHex
private playercolor array CurrentColor
endglobals
private keyword PlayerUtilsInit
struct User extends array
static constant integer NULL = 15
readonly player handle
readonly integer id
readonly thistype next
readonly thistype prev
readonly string originalName
readonly boolean isPlaying
readonly static thistype first
readonly static thistype last
readonly static player Local
readonly static integer LocalId
readonly static integer AmountPlaying = 0
readonly static playercolor array Color
static if not (LIBRARY_GroupUtils) then
readonly static group ENUM_GROUP = CreateGroup()
endif
private static thistype array PlayingPlayer
private static integer array PlayingPlayerIndex
// similar to Player(#)
static method fromIndex takes integer i returns thistype
return thistype(i)
endmethod
// similar to GetLocalPlayer
static method fromLocal takes nothing returns thistype
return thistype(thistype.LocalId)
endmethod
// access active players array
static method fromPlaying takes integer index returns thistype
return PlayingPlayer[index]
endmethod
static method operator [] takes player p returns thistype
return thistype(GetPlayerId(p))
endmethod
method toPlayer takes nothing returns player
return this.handle
endmethod
method operator name takes nothing returns string
static if (ARRAY_LOOKUP) then
return Name[this]
else
return GetPlayerName(this.handle)
endif
endmethod
method operator name= takes string newName returns nothing
call SetPlayerName(this.handle, newName)
static if (ARRAY_LOOKUP) then
static if not (HOOK_SAFETY) then
set Name[this] = newName
endif
endif
endmethod
method operator color takes nothing returns playercolor
static if (ARRAY_LOOKUP) then
return CurrentColor[this]
else
return GetPlayerColor(this.handle)
endif
endmethod
method operator hex takes nothing returns string
return OriginalHex[GetHandleId(this.color)]
endmethod
method operator color= takes playercolor c returns nothing
call SetPlayerColor(this.handle, c)
static if (ARRAY_LOOKUP) then
set CurrentColor[this] = c
static if not (HOOK_SAFETY) then
static if (AUTO_COLOR_UNITS) then
call this.colorUnits(color)
endif
endif
endif
endmethod
method operator defaultColor takes nothing returns playercolor
return Color[this]
endmethod
method operator nameColored takes nothing returns string
return hex + this.name + "|r"
endmethod
method colorUnits takes playercolor c returns nothing
local unit u
call GroupEnumUnitsOfPlayer(ENUM_GROUP, this.handle, null)
loop
set u = FirstOfGroup(ENUM_GROUP)
exitwhen u == null
call SetUnitColor(u, c)
call GroupRemoveUnit(ENUM_GROUP, u)
endloop
endmethod
static method onLeave takes nothing returns boolean
local thistype p = thistype[GetTriggerPlayer()]
local integer i = .PlayingPlayerIndex[p.id]
// clean up
call ForceRemovePlayer(FORCE_PLAYING, p.toPlayer())
// recycle index
set .AmountPlaying = .AmountPlaying - 1
set .PlayingPlayerIndex[i] = .PlayingPlayerIndex[.AmountPlaying]
set .PlayingPlayer[i] = .PlayingPlayer[.AmountPlaying]
if (.AmountPlaying == 1) then
set p.prev.next = User.NULL
set p.next.prev = User.NULL
else
set p.prev.next = p.next
set p.next.prev = p.prev
endif
set .last = .PlayingPlayer[.AmountPlaying]
set p.isPlaying = false
return false
endmethod
implement PlayerUtilsInit
endstruct
private module PlayerUtilsInit
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local thistype p
set thistype.Local = GetLocalPlayer()
set thistype.LocalId = GetPlayerId(thistype.Local)
set OriginalHex[0] = "|cffff0303"
set OriginalHex[1] = "|cff0042ff"
set OriginalHex[2] = "|cff1ce6b9"
set OriginalHex[3] = "|cff540081"
set OriginalHex[4] = "|cfffffc01"
set OriginalHex[5] = "|cfffe8a0e"
set OriginalHex[6] = "|cff20c000"
set OriginalHex[7] = "|cffe55bb0"
set OriginalHex[8] = "|cff959697"
set OriginalHex[9] = "|cff7ebff1"
set OriginalHex[10] = "|cff106246"
set OriginalHex[11] = "|cff4e2a04"
set thistype.first = User.NULL
loop
exitwhen i == 12
set p = User(i)
set p.handle = Player(i)
set p.id = i
set thistype.Color[i] = GetPlayerColor(p.handle)
set CurrentColor[i] = thistype.Color[i]
if (GetPlayerController(p.handle) == MAP_CONTROL_USER and GetPlayerSlotState(p.handle) == PLAYER_SLOT_STATE_PLAYING) then
set .PlayingPlayer[AmountPlaying] = p
set .PlayingPlayerIndex[i] = .AmountPlaying
set .last = i
if (.first == User.NULL) then
set .first = i
set User(i).next = User.NULL
set User(i).prev = User.NULL
else
set User(i).prev = PlayingPlayer[AmountPlaying-1].id
set PlayingPlayer[AmountPlaying-1].next = User(i)
set User(i).next = User.NULL
endif
set p.isPlaying = true
call TriggerRegisterPlayerEvent(t, p.handle, EVENT_PLAYER_LEAVE)
call ForceAddPlayer(FORCE_PLAYING, p.handle)
set Hex[p] = OriginalHex[GetHandleId(thistype.Color[i])]
set .AmountPlaying = .AmountPlaying + 1
endif
set Name[p] = GetPlayerName(p.handle)
set p.originalName=Name[p]
set i = i + 1
endloop
call TriggerAddCondition(t, Filter(function thistype.onLeave))
endmethod
endmodule
//===========================================================================
endlibrary
JASS:
library Camera requires UIMath, PlayerUtils
globals
// configuration
// location on the map where the terrain is the default level
// UI placement on screen X,Y
constant real CHECK_DELTAZ_X = -300
constant real CHECK_DELTAZ_Y = 500
constant real SCREEN_WIDTH = 0.544
constant real SCREEN_HEIGHT = 0.302
constant real SCREEN_ASPECT_RATIO = SCREEN_WIDTH/SCREEN_HEIGHT
constant integer CAMERA_DUMMY_TYPE = 'e00D'
constant player CAMERA_DUMMY_PLAYER = Player(bj_PLAYER_NEUTRAL_EXTRA)
private real DeltaZ = 0
// endconfig
public unit array DummyUnit
public location DummyLoc = Location(0,0)
endglobals
function GetTerrainZ takes real x, real y returns real
call MoveLocation(DummyLoc, x, y)
return GetLocationZ(DummyLoc)
endfunction
function GetCameraDeltaZ takes nothing returns real
return DeltaZ
endfunction
private function Matrix4Perspective1 takes MATRIX4 Output, real fovy, real Aspect, real zn, real zf returns MATRIX4
return Output.SetValues(2*zn/fovy,0,0,0,0,2*zn/Aspect,0,0,0,0,zf/(zf-zn),1,0,0,zn*zf/(zn-zf),0)
endfunction
private function Matrix4Perspective2 takes MATRIX4 Output, real n, real f, real r, real l, real t, real b returns MATRIX4
return Output.SetValues(2*n/(r-l), 0, (r+l)/(r-l), 0, 0, 2*n/(t-b), (t+b)/(t-b), 0, 0, 0, -(f+n)/(f-n), -2*f*n/(f-n), 0, 0, -1, 0)
endfunction
private function Matrix4Look takes MATRIX4 Output, VECTOR3 PosCamera, VECTOR3 AxisX, VECTOR3 AxisY, VECTOR3 AxisZ returns MATRIX4
return Output.SetValues(AxisX.x,AxisY.x,AxisZ.x,0,AxisX.y,AxisY.y,AxisZ.y,0,AxisX.z,AxisY.z,AxisZ.z,0,-Vec3Dot(AxisX, PosCamera),-Vec3Dot(AxisY, PosCamera),-Vec3Dot(AxisZ, PosCamera),1)
endfunction
struct Camera
VECTOR3 eye
VECTOR3 at
unit target
real distance
real yaw
real pitch
real roll
VECTOR3 axisX
VECTOR3 axisY
VECTOR3 axisZ
private MATRIX4 view
private MATRIX4 projection
private boolean change
integer customValue
method win2World takes real X, real Y, real Range returns VECTOR3
local VECTOR3 Output = VECTOR3.create()
set Output.x = .eye.x+.axisZ.x*Range+X*.axisX.x*SCREEN_WIDTH*Range+Y*.axisY.x*SCREEN_HEIGHT*Range
set Output.y = .eye.y+.axisZ.y*Range+X*.axisX.y*SCREEN_WIDTH*Range+Y*.axisY.y*SCREEN_HEIGHT*Range
set Output.z = .eye.z+.axisZ.z*Range+X*.axisX.z*SCREEN_WIDTH*Range+Y*.axisY.z*SCREEN_HEIGHT*Range
return Output
endmethod
method world2Win takes real X, real Y, real Z returns VECTOR3
local VECTOR3 Pos = VECTOR3.New_1(X, Y, Z)
local boolean b
call Vec3Transform_2(Pos, Pos, .view)
set b = Pos.z < 0
call Vec3Transform_2(Pos, Pos, .projection)
if b then
set Pos.z = -Pos.z
endif
return Pos
endmethod
private method updateDistanceYawPitch takes nothing returns nothing
local real dx = .at.x-.eye.x
local real dy = .at.y-.eye.y
local real dz = .at.z-.eye.z
local real len2d
set .distance = SquareRoot(dx*dx+dy*dy+dz*dz)
set .yaw = Atan2(dy, dx)
set len2d = SquareRoot(dx*dx+dy*dy)
set .pitch = Atan2(dz, len2d)
endmethod
private method updateAxisMatrix takes nothing returns nothing
local MATRIX3 mat
call Vec3Normalize(.axisZ, Vec3Subtract(.axisZ, .at, .eye))
set mat = Matrix3RotationAxis(MATRIX3.create(), .axisZ, -.roll)
call Vec3Normalize(.axisX, Vec3Cross(.axisX, .axisZ, VECTOR3.oneZ))
call Vec3Transform_1(.axisY, Vec3Cross(.axisY, .axisX, .axisZ), mat)
call Vec3Transform_1(.axisX, .axisX, mat)
call Matrix4Look(.view, .eye, .axisX, .axisY, .axisZ)
call mat.destroy()
endmethod
method applyCameraForPlayer takes player p, boolean ignoreChange returns boolean
if GetLocalPlayer() == p then
call SetCameraField(CAMERA_FIELD_ROTATION, .yaw*bj_RADTODEG, 0)
call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, .pitch*bj_RADTODEG, 0)
call SetCameraField(CAMERA_FIELD_ROLL, .roll*bj_RADTODEG, 0)
call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, .distance, 0)
call SetCameraTargetController(DummyUnit[GetPlayerId(p)], .at.x, .at.y, false)
call SetCameraField(CAMERA_FIELD_ZOFFSET, .at.z-DeltaZ, 0)
endif
if .change or ignoreChange then
set .change = false
return true
endif
return false
endmethod
method setPosition takes real x, real y, real z returns nothing
local real dx = x-.at.x
local real dy = y-.at.y
local real dz = z-.at.z
set .eye.x = .eye.x+dx
set .eye.y = .eye.y+dy
set .eye.z = .eye.z+dz
set .at.x = x
set .at.y = y
set .at.z = z
set .change = true
endmethod
method setEyeAndAt takes real ex, real ey, real ez, real tx, real ty, real tz returns nothing
set .eye.x = ex
set .eye.y = ey
set .eye.z = ez
set .at.x = tx
set .at.y = ty
set .at.z = tz
call .updateDistanceYawPitch()
call .updateAxisMatrix()
set .change = true
endmethod
method setYawPitchRoll takes real yaw, real pitch, real roll, boolean EyeLock returns nothing
local real Z = .distance*Sin(pitch)
local real XY = .distance*Cos(pitch)
local real X = XY*Cos(yaw)
local real Y = XY*Sin(yaw)
set .yaw = yaw
set .pitch = pitch
set .roll = roll
if EyeLock then
set .at.x = .eye.x+X
set .at.y = .eye.y+Y
set .at.z = .eye.z+Z
else
set .eye.x = .at.x-X
set .eye.y = .at.y-Y
set .eye.z = .at.z-Z
endif
call .updateAxisMatrix()
set .change = true
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set .customValue = 0
set .change = true
set .eye = VECTOR3.New_1(0.0,-922.668,DeltaZ+1367.912)
set .at = VECTOR3.New_1(0, 0, DeltaZ)
set .distance = 0
set .yaw = 0
set .pitch = 0
set .roll = 0
set .axisX = VECTOR3.create()
set .axisY = VECTOR3.create()
set .axisZ = VECTOR3.create()
set .view = MATRIX4.create()
set .projection = Matrix4Perspective2(MATRIX4.create(), 0.5, 10000, -SCREEN_WIDTH/2, SCREEN_WIDTH/2, -SCREEN_HEIGHT/2, SCREEN_HEIGHT/2)
call .updateDistanceYawPitch()
call .updateAxisMatrix()
return this
endmethod
method destroy takes nothing returns nothing
call .eye.destroy()
call .at.destroy()
call .axisX.destroy()
call .axisY.destroy()
call .axisZ.destroy()
call .view.destroy()
call .projection.destroy()
call this.destroy()
endmethod
endstruct
private module CamInitModule
private static method onInit takes nothing returns nothing
local integer i = 0
local User user
loop
exitwhen i == User.AmountPlaying
set user = User.fromPlaying(i)
set DummyUnit[user.id] = CreateUnit(CAMERA_DUMMY_PLAYER, CAMERA_DUMMY_TYPE, 0, 0, 0)
call ShowUnit(DummyUnit[user.id], false)
call PauseUnit(DummyUnit[user.id], true)
set i = i + 1
endloop
// init delta z
call SetCameraPosition(CHECK_DELTAZ_X, CHECK_DELTAZ_Y)
set DeltaZ = GetCameraTargetPositionZ() - 6
endmethod
endmodule
private struct CamInit
implement CamInitModule
endstruct
endlibrary
JASS:
library UIMath initializer Init
public function R2I_N takes real r returns integer
local integer i = R2I(r)
if (r < I2R(i)+0.5) then
return i
else
return i+1
endif
endfunction
struct VECTOR3
static VECTOR3 Zero
static VECTOR3 oneX
static VECTOR3 oneY
static VECTOR3 oneZ
real x
real y
real z
static method New_0 takes nothing returns VECTOR3
local VECTOR3 this = VECTOR3.create()
set .x = 0
set .y = 0
set .z = 0
return this
endmethod
static method New_1 takes real x, real y, real z returns VECTOR3
local VECTOR3 this = VECTOR3.create()
set .x = x
set .y = y
set .z = z
return this
endmethod
static method New_2 takes VECTOR3 v returns VECTOR3
local VECTOR3 this = VECTOR3.create()
set .x = v.x
set .y = v.y
set .z = v.z
return this
endmethod
method SetValues takes real x, real y, real z returns VECTOR3
set .x = x
set .y = y
set .z = z
return this
endmethod
method Length takes nothing returns real
return SquareRoot(.x*.x+.y*.y+.z*.z)
endmethod
method LengthSq takes nothing returns real
return .x*.x+.y*.y+.z*.z
endmethod
method ToString takes nothing returns string
return "Vector3 id "+I2S(this) + "\nx = "+R2S(.x)+" y = "+R2S(.y)+" z = "+R2S(.z)
endmethod
endstruct
function Vec3Add takes VECTOR3 Output, VECTOR3 v1, VECTOR3 v2 returns VECTOR3
set Output.x = v1.x + v2.x
set Output.y = v1.y + v2.y
set Output.z = v1.z + v2.z
return Output
endfunction
function Vec3Subtract takes VECTOR3 Output, VECTOR3 v1, VECTOR3 v2 returns VECTOR3
set Output.x = v1.x - v2.x
set Output.y = v1.y - v2.y
set Output.z = v1.z - v2.z
return Output
endfunction
function Vec3Scale takes VECTOR3 Output, VECTOR3 v, real r returns VECTOR3
set Output.x = v.x * r
set Output.y = v.y * r
set Output.z = v.z * r
return Output
endfunction
function Vec3Division takes VECTOR3 Output, VECTOR3 v, real r returns VECTOR3
set Output.x = v.x / r
set Output.y = v.y / r
set Output.z = v.z / r
return Output
endfunction
function Vec3Length takes VECTOR3 v returns real
return SquareRoot(v.x*v.x+v.y*v.y+v.z*v.z)
endfunction
function Vec3LengthSq takes VECTOR3 v returns real
return v.x*v.x+v.y*v.y+v.z*v.z
endfunction
function Vec3Normalize takes VECTOR3 Output, VECTOR3 v returns VECTOR3
local real len = SquareRoot(v.x*v.x+v.y*v.y+v.z*v.z)
set Output.x = v.x/len
set Output.y = v.y/len
set Output.z = v.z/len
return Output
endfunction
function Vec3Dot takes VECTOR3 v1, VECTOR3 v2 returns real
return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
endfunction
function Vec3Cross takes VECTOR3 Output, VECTOR3 v1, VECTOR3 v2 returns VECTOR3
local real y = v1.z * v2.x - v1.x * v2.z
local real z = v1.x * v2.y - v1.y * v2.x
set Output.x = v1.y * v2.z - v1.z * v2.y
set Output.y = y
set Output.z = z
return Output
endfunction
function Vec3Transform_1 takes VECTOR3 Output, VECTOR3 v, MATRIX3 m returns VECTOR3
local real y = v.x*m.m12+v.y*m.m22+v.z*m.m32
local real z = v.x*m.m13+v.y*m.m23+v.z*m.m33
set Output.x = v.x*m.m11+v.y*m.m21+v.z*m.m31
set Output.y = y
set Output.z = z
return Output
endfunction
function Vec3Transform_2 takes VECTOR3 Output, VECTOR3 v, MATRIX4 m returns VECTOR3
local real y = v.x*m.m12+v.y*m.m22+v.z*m.m32+m.m42
local real z = v.x*m.m13+v.y*m.m23+v.z*m.m33+m.m43
local real w = v.x*m.m14+v.y*m.m24+v.z*m.m34+m.m44
set Output.x = (v.x*m.m11+v.y*m.m21+v.z*m.m31+m.m41)/w
set Output.y = y/w
set Output.z = z/w
return Output
endfunction
struct MATRIX3
static MATRIX3 Zero
static MATRIX3 E
real m11
real m12
real m13
real m21
real m22
real m23
real m31
real m32
real m33
static method New_0 takes nothing returns MATRIX3
local MATRIX3 this = MATRIX3.create()
set .m11 = 0
set .m12 = 0
set .m13 = 0
set .m21 = 0
set .m22 = 0
set .m23 = 0
set .m31 = 0
set .m32 = 0
set .m33 = 0
return this
endmethod
static method New_1 takes real r11, real r12, real r13, real r21, real r22, real r23, real r31, real r32, real r33 returns MATRIX3
local MATRIX3 this = MATRIX3.create()
set .m11 = r11
set .m12 = r12
set .m13 = r13
set .m21 = r21
set .m22 = r22
set .m23 = r23
set .m31 = r31
set .m32 = r32
set .m33 = r33
return this
endmethod
static method New_2 takes MATRIX3 m returns MATRIX3
local MATRIX3 this = MATRIX3.create()
set .m11 = m.m11
set .m12 = m.m12
set .m13 = m.m13
set .m21 = m.m21
set .m22 = m.m22
set .m23 = m.m23
set .m31 = m.m31
set .m32 = m.m32
set .m33 = m.m33
return this
endmethod
method SetValues takes real r11, real r12, real r13, real r21, real r22, real r23, real r31, real r32, real r33 returns MATRIX3
set .m11 = r11
set .m12 = r12
set .m13 = r13
set .m21 = r21
set .m22 = r22
set .m23 = r23
set .m31 = r31
set .m32 = r32
set .m33 = r33
return this
endmethod
method ToString takes nothing returns string
return "Matrux3 id "+I2S(this)+"\n"+R2S(.m11)+" "+R2S(.m12)+" "+R2S(.m13)+"\n"+R2S(.m21)+" "+R2S(.m22)+" "+R2S(.m23)+"\n"+R2S(.m31)+" "+R2S(.m32)+" "+R2S(.m33)
endmethod
endstruct
function Matrix3Multiply takes MATRIX3 Output, MATRIX3 M1, MATRIX3 M2 returns MATRIX3
return Output.SetValues(M1.m11*M2.m11+M1.m21*M2.m12+M1.m31*M2.m13,M1.m12*M2.m11+M1.m22*M2.m12+M1.m32*M2.m13,M1.m13*M2.m11+M1.m23*M2.m12+M1.m33*M2.m13,M1.m11*M2.m21+M1.m21*M2.m22+M1.m31*M2.m23,M1.m12*M2.m21+M1.m22*M2.m22+M1.m32*M2.m23,M1.m13*M2.m21+M1.m23*M2.m22+M1.m33*M2.m23,M1.m11*M2.m31+M1.m21*M2.m32+M1.m31*M2.m33,M1.m12*M2.m31+M1.m22*M2.m32+M1.m32*M2.m33,M1.m13*M2.m31+M1.m23*M2.m32+M1.m33*M2.m33)
endfunction
function Matrix3Scaling takes MATRIX3 Output, real x, real y, real z returns MATRIX3
return Output.SetValues(x,0,0,0,y,0,0,0,z)
endfunction
function Matrix3RotationX takes MATRIX3 Output, real a returns MATRIX3
return Output.SetValues(1,0,0,0,Cos(a),-Sin(a),0,Sin(a),Cos(a))
endfunction
function Matrix3RotationY takes MATRIX3 Output, real a returns MATRIX3
return Output.SetValues(Cos(a),0,Sin(a),0,1,0,-Sin(a),0,Cos(a))
endfunction
function Matrix3RotationZ takes MATRIX3 Output, real a returns MATRIX3
return Output.SetValues(Cos(a),-Sin(a),0,Sin(a),Cos(a),0,0,0,1)
endfunction
function Matrix3RotationAxis takes MATRIX3 Output, VECTOR3 v, real a returns MATRIX3
local real cosa = Cos(a)
local real sina = Sin(a)
return Output.SetValues(cosa+(1-cosa)*v.x*v.x,(1-cosa)*v.x*v.y-sina*v.z,(1-cosa)*v.x*v.z+sina*v.y,(1-cosa)*v.y*v.x+sina*v.z,cosa+(1-cosa)*v.y*v.y,(1-cosa)*v.y*v.z-sina*v.x,(1-cosa)*v.z*v.x-sina*v.y,(1-cosa)*v.z*v.y+sina*v.x,cosa+(1-cosa)*v.z*v.z)
endfunction
function Matrix3RotationYawPitchRoll takes MATRIX3 Output, real Yaw, real Pitch, real Roll returns MATRIX3
local real cosa = Cos(Yaw)
local real sina = Sin(Yaw)
local real cosb = Cos(Pitch)
local real sinb = Sin(Pitch)
local real cosy = Cos(Roll)
local real siny = Sin(Roll)
return Output.SetValues(cosa*cosb,cosa*sinb*siny-sina*cosy,cosa*sinb*cosy+sina*siny,sina*cosb,sina*sinb*siny+cosa*cosy,sina*sinb*cosy-cosa*siny,-sinb,cosb*siny,cosb*cosy)
endfunction
struct MATRIX4
static MATRIX4 Zero
static MATRIX4 E
real m11
real m12
real m13
real m14
real m21
real m22
real m23
real m24
real m31
real m32
real m33
real m34
real m41
real m42
real m43
real m44
static method New_0 takes nothing returns MATRIX4
local MATRIX4 this = MATRIX4.create()
set .m11 = 0
set .m12 = 0
set .m13 = 0
set .m14 = 0
set .m21 = 0
set .m22 = 0
set .m23 = 0
set .m24 = 0
set .m31 = 0
set .m32 = 0
set .m33 = 0
set .m34 = 0
set .m41 = 0
set .m42 = 0
set .m43 = 0
set .m44 = 0
return this
endmethod
static method New_1 takes real r11, real r12, real r13, real r14, real r21, real r22, real r23, real r24, real r31, real r32, real r33, real r34, real r41, real r42, real r43, real r44 returns MATRIX4
local MATRIX4 this = MATRIX4.create()
set .m11 = r11
set .m12 = r12
set .m13 = r13
set .m14 = r14
set .m21 = r21
set .m22 = r22
set .m23 = r23
set .m24 = r24
set .m31 = r31
set .m32 = r32
set .m33 = r33
set .m34 = r34
set .m41 = r41
set .m42 = r42
set .m43 = r43
set .m44 = r44
return this
endmethod
static method New_2 takes MATRIX4 m returns MATRIX4
local MATRIX4 this = MATRIX4.create()
set .m11 = m.m11
set .m12 = m.m12
set .m13 = m.m13
set .m14 = m.m14
set .m21 = m.m21
set .m22 = m.m22
set .m23 = m.m23
set .m24 = m.m24
set .m31 = m.m31
set .m32 = m.m32
set .m33 = m.m33
set .m34 = m.m34
set .m41 = m.m41
set .m42 = m.m42
set .m43 = m.m43
set .m44 = m.m44
return this
endmethod
static method New_3 takes MATRIX3 m returns MATRIX4
local MATRIX4 this = MATRIX4.create()
set .m11 = m.m11
set .m12 = m.m12
set .m13 = m.m13
set .m14 = 0
set .m21 = m.m21
set .m22 = m.m22
set .m23 = m.m23
set .m24 = 0
set .m31 = m.m31
set .m32 = m.m32
set .m33 = m.m33
set .m34 = 0
set .m41 = 0
set .m42 = 0
set .m43 = 0
set .m44 = 1
return this
endmethod
method SetValues takes real r11, real r12, real r13, real r14, real r21, real r22, real r23, real r24, real r31, real r32, real r33, real r34, real r41, real r42, real r43, real r44 returns MATRIX4
set .m11 = r11
set .m12 = r12
set .m13 = r13
set .m14 = r14
set .m21 = r21
set .m22 = r22
set .m23 = r23
set .m24 = r24
set .m31 = r31
set .m32 = r32
set .m33 = r33
set .m34 = r34
set .m41 = r41
set .m42 = r42
set .m43 = r43
set .m44 = r44
return this
endmethod
method ToString takes nothing returns string
return "Matrux4 id "+I2S(this)+"\n"+R2S(.m11)+" "+R2S(.m12)+" "+R2S(.m13)+" "+R2S(.m14)+"\n"+R2S(.m21)+" "+R2S(.m22)+" "+R2S(.m23)+" "+R2S(.m24)+"\n"+R2S(.m31)+" "+R2S(.m32)+" "+R2S(.m33)+" "+R2S(.m34)+"\n"+R2S(.m41)+" "+R2S(.m42)+" "+R2S(.m43)+" "+R2S(.m44)
endmethod
endstruct
function Matrix4Multiply takes MATRIX4 Output, MATRIX4 M1, MATRIX4 M2 returns MATRIX4
return Output.SetValues(M1.m11*M2.m11+M1.m21*M2.m12+M1.m31*M2.m13+M1.m41*M2.m14,M1.m12*M2.m11+M1.m22*M2.m12+M1.m32*M2.m13+M1.m42*M2.m14,M1.m13*M2.m11+M1.m23*M2.m12+M1.m33*M2.m13+M1.m43*M2.m14,M1.m14*M2.m11+M1.m24*M2.m12+M1.m34*M2.m13+M1.m44*M2.m14,M1.m11*M2.m21+M1.m21*M2.m22+M1.m31*M2.m23+M1.m41*M2.m24,M1.m12*M2.m21+M1.m22*M2.m22+M1.m32*M2.m23+M1.m42*M2.m24,M1.m13*M2.m21+M1.m23*M2.m22+M1.m33*M2.m23+M1.m43*M2.m24,M1.m14*M2.m21+M1.m24*M2.m22+M1.m34*M2.m23+M1.m44*M2.m24,M1.m11*M2.m31+M1.m21*M2.m32+M1.m31*M2.m33+M1.m41*M2.m34,M1.m12*M2.m31+M1.m22*M2.m32+M1.m32*M2.m33+M1.m42*M2.m34,M1.m13*M2.m31+M1.m23*M2.m32+M1.m33*M2.m33+M1.m43*M2.m34,M1.m14*M2.m31+M1.m24*M2.m32+M1.m34*M2.m33+M1.m44*M2.m34,M1.m11*M2.m41+M1.m21*M2.m42+M1.m31*M2.m43+M1.m41*M2.m44,M1.m12*M2.m41+M1.m22*M2.m42+M1.m32*M2.m43+M1.m42*M2.m44,M1.m13*M2.m41+M1.m23*M2.m42+M1.m33*M2.m43+M1.m43*M2.m44,M1.m14*M2.m41+M1.m24*M2.m42+M1.m34*M2.m43+M1.m44*M2.m44)
endfunction
private function Init takes nothing returns nothing
set VECTOR3.Zero = VECTOR3.New_0()
set VECTOR3.oneX = VECTOR3.New_1(1,0,0)
set VECTOR3.oneY = VECTOR3.New_1(0,1,0)
set VECTOR3.oneZ = VECTOR3.New_1(0,0,1)
set MATRIX3.Zero = MATRIX3.New_0()
set MATRIX3.E = MATRIX3.New_1(1,0,0,0,1,0,0,0,1)
set MATRIX4.Zero = MATRIX4.New_0()
set MATRIX4.E = MATRIX4.New_1(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)
endfunction
endlibrary
JASS:
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
JASS:
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
One map, one hashtable. Welcome to NewTable 4.1.0.1
This newest iteration of Table introduces the new HashTable struct.
You can now instantiate HashTables which enables the use of large
parent and large child keys, just like a standard hashtable. Previously,
the user would have to instantiate a Table to do this on their own which -
while doable - is something the user should not have to do if I can add it
to this resource myself (especially if they are inexperienced).
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
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 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")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! 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 integerm
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) //return this.integer[key]
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key) //call this.integer.remove(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
//NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array
//Enables myHash[parentKey][childKey] syntax.
//Basically, it creates a Table in the place of the parent key if
//it didn't already get created earlier.
method operator [] takes integer index returns Table
local Table t = Table(this)[index]
if t == 0 then
set t = Table.create()
set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
endif
return t
endmethod
//You need to call this on each parent key that you used if you
//intend to destroy the HashTable or simply no longer need that key.
method remove takes integer index returns nothing
local Table t = Table(this)[index]
if t != 0 then
call t.destroy()
call Table(this).remove(index)
endif
endmethod
//Added in version 4.1
method has takes integer index returns boolean
return Table(this).has(index)
endmethod
//HashTables are just fancy Table indices.
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
//Like I said above...
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
endlibrary
JASS:
library Table
//***************************************************************
//* Table object 3.1
//* ------------
//*
//* set t=Table.create() - instanceates a new table object
//* call t.destroy() - destroys it
//* t[1234567] - Get value for key 1234567
//* (zero if not assigned previously)
//* set t[12341]=32 - Assigning it.
//* call t.flush(12341) - Flushes the stored value, so it
//* doesn't use any more memory
//* t.exists(32) - Was key 32 assigned? Notice
//* that flush() unassigns values.
//* call t.reset() - Flushes the whole contents of the
//* Table.
//*
//* call t.destroy() - Does reset() and also recycles the id.
//*
//* If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//* You can use Table on structs' onInit if the struct is
//* placed in a library that requires Table or outside a library.
//*
//* You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//* set Table["thisstring"][ 7 ] = 2
//* set Table["thisstring"][ 5 ] = Table["thisstring"][7]
//*
//***************************************************************
//=============================================================
globals
private constant integer MAX_INSTANCES=8100 //400000
//Feel free to change max instances if necessary, it will only affect allocation
//speed which shouldn't matter that much.
//=========================================================
private hashtable ht = InitHashtable()
endglobals
private struct GTable[MAX_INSTANCES]
method reset takes nothing returns nothing
call FlushChildHashtable(ht, integer(this) )
endmethod
private method onDestroy takes nothing returns nothing
call this.reset()
endmethod
endstruct
//Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
//! textmacro Table__make takes name, type, key
struct $name$ extends GTable
method operator [] takes $type$ key returns integer
return LoadInteger(ht, integer(this), $key$)
endmethod
method operator []= takes $type$ key, integer value returns nothing
call SaveInteger(ht, integer(this) ,$key$, value)
endmethod
method flush takes $type$ key returns nothing
call RemoveSavedInteger(ht, integer(this), $key$)
endmethod
method exists takes $type$ key returns boolean
return HaveSavedInteger( ht, integer(this) ,$key$)
endmethod
static method flush2D takes string firstkey returns nothing
call $name$(- StringHash(firstkey)).reset()
endmethod
static method operator [] takes string firstkey returns $name$
return $name$(- StringHash(firstkey) )
endmethod
endstruct
//! endtextmacro
//! runtextmacro Table__make("Table","integer","key" )
//! runtextmacro Table__make("StringTable","string", "StringHash(key)" )
//! runtextmacro Table__make("HandleTable","handle","GetHandleId(key)" )
endlibrary
JASS:
//TESH.scrollpos=84
//TESH.alwaysfold=0
library Table2
//***************************************************************
//* Table2 object 3.0
//* ------------
//*
//* set t=Table2.create() - instanceates a new table object
//* call t.destroy() - destroys it
//* t[1234567] - Get value for key 1234567
//* (zero if not assigned previously)
//* set t[12341]=32 - Assigning it.
//* call t.flush(12341) - Flushes the stored value, so it
//* doesn't use any more memory
//* t.exists(32) - Was key 32 assigned? Notice
//* that flush() unassigns values.
//* call t.reset() - Flushes the whole contents of the
//* Table.
//*
//* call t.destroy() - Does reset() and also recycles the id.
//*
//* If you use HandleTable2 instead of Table2, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//* You can use Table2 on structs' onInit if the struct is
//* placed in a library that requires Table or outside a library.
//*
//* You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//* set Table2["thisstring"][ 7 ] = 2
//* set Table2["thisstring"][ 5 ] = Table2["thisstring"][7]
//*
//***************************************************************
//=============================================================
globals
private constant integer MAX_INSTANCES=8100 //400000
//Feel free to change max instances if necessary, it will only affect allocation
//speed which shouldn't matter that much.
//=========================================================
private hashtable ht2
endglobals
private struct GTable[MAX_INSTANCES]
method reset takes nothing returns nothing
call FlushChildHashtable(ht2, integer(this) )
endmethod
private method onDestroy takes nothing returns nothing
call this.reset()
endmethod
//=============================================================
// initialize it all.
//
private static method onInit takes nothing returns nothing
set ht2 = InitHashtable()
endmethod
endstruct
//Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
//! textmacro Table2__make takes name, type, key
struct $name$ extends GTable
method operator [] takes $type$ key returns integer
return LoadInteger(ht2, integer(this), $key$)
endmethod
method operator []= takes $type$ key, integer value returns nothing
call SaveInteger(ht2, integer(this) ,$key$, value)
endmethod
method flush takes $type$ key returns nothing
call RemoveSavedInteger(ht2, integer(this), $key$)
endmethod
method exists takes $type$ key returns boolean
return HaveSavedInteger( ht2, integer(this) ,$key$)
endmethod
static method flush2D takes string firstkey returns nothing
call $name$(- StringHash(firstkey)).reset()
endmethod
static method operator [] takes string firstkey returns $name$
return $name$(- StringHash(firstkey) )
endmethod
endstruct
//! endtextmacro
//! runtextmacro Table2__make("Table2","integer","key" )
//! runtextmacro Table2__make("StringTable2","string", "StringHash(key)" )
//! runtextmacro Table2__make("HandleTable2","handle","GetHandleId(key)" )
endlibrary
JASS:
//TESH.scrollpos=4
//TESH.alwaysfold=0
library BoundSentinel initializer init
//*************************************************
//* BoundSentinel
//* -------------
//* Don't leave your units unsupervised, naughty
//* them may try to get out of the map bounds and
//* crash your game.
//*
//* To implement, just get a vJass compiler and
//* copy this library/trigger to your map.
//*
//*************************************************
//==================================================
globals
// High enough so the unit is no longer visible, low enough so the
// game doesn't crash...
//
// I think you need 0.0 or soemthing negative prior to patch 1.22
//
private constant real EXTRA = 500.0
endglobals
//=========================================================================================
globals
private real maxx
private real maxy
private real minx
private real miny
endglobals
//=======================================================================
private function dis takes nothing returns nothing
local unit u=GetTriggerUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
if(x>maxx) then
set x=maxx
elseif(x<minx) then
set x=minx
endif
if(y>maxy) then
set y=maxy
elseif(y<miny) then
set y=miny
endif
call SetUnitX(u,x)
call SetUnitY(u,y)
set u=null
endfunction
private function init takes nothing returns nothing
local trigger t=CreateTrigger()
local region r=CreateRegion()
local rect rc
set minx=GetCameraBoundMinX() - EXTRA
set miny=GetCameraBoundMinY() - EXTRA
set maxx=GetCameraBoundMaxX() + EXTRA
set maxy=GetCameraBoundMaxY() + EXTRA
set rc=Rect(minx,miny,maxx,maxy)
call RegionAddRect(r, rc)
call RemoveRect(rc)
call TriggerRegisterLeaveRegion(t,r, null)
call TriggerAddAction(t, function dis)
//this is not necessary but I'll do it anyway:
set t=null
set r=null
set rc=null
endfunction
endlibrary
JASS:
library BoundSentinel initializer init
//*************************************************
//* BoundSentinel
//* -------------
//* Don't leave your units unsupervised, naughty
//* them may try to get out of the map bounds and
//* crash your game.
//*
//* To implement, just get a vJass compiler and
//* copy this library/trigger to your map.
//*
//*************************************************
//==================================================
globals
// High enough so the unit is no longer visible, low enough so the
// game doesn't crash...
//
// I think you need 0.0 or soemthing negative prior to patch 1.22
//
private constant real EXTRA = 700.0
endglobals
//=========================================================================================
globals
private real maxx
private real maxy
private real minx
private real miny
endglobals
//=======================================================================
private function dis takes nothing returns nothing
local unit u=GetTriggerUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
local real x2=x
local real y2=y
if(x>maxx) then
set x=maxx
elseif(x<minx) then
set x=minx
endif
if(y>maxy) then
set y=maxy
elseif(y<miny) then
set y=miny
endif
if (x != x2) then
call SetUnitX(u,x)
endif
if (y != y2) then
call SetUnitY(u,y)
endif
set u=null
endfunction
private function init takes nothing returns nothing
local trigger t=CreateTrigger()
local region r=CreateRegion()
local rect rc
set minx=GetCameraBoundMinX() - EXTRA
set miny=GetCameraBoundMinY() - EXTRA
set maxx=GetCameraBoundMaxX() + EXTRA
set maxy=GetCameraBoundMaxY() + EXTRA
set rc=Rect(minx,miny,maxx,maxy)
call RegionAddRect(r, rc)
call RemoveRect(rc)
call TriggerRegisterLeaveRegion(t,r, null)
call TriggerAddAction(t, function dis)
//this is not necessary but I'll do it anyway:
set t=null
set r=null
set rc=null
endfunction
endlibrary
JASS:
library xebasicQ
//**************************************************************************
//
// xebasic 0.4
// =======
// XE_DUMMY_UNITIDQ : Rawcode of the dummy unit in your map. It should
// use the dummy.mdx model, so remember to import it as
// well, just use copy&paste to copy the dummy from the
// xe map to yours, then change the rawcode.
//
// XE_HEIGHT_ENABLERQ: Medivh's raven form ability, you may need to change
// this rawcode to another spell that morphs into a flier
// in case you modified medivh's spell in your map.
//
// XE_TREE_RECOGNITIONQ: The ancients' Eat tree ability, same as with medivh
// raven form, you might have to change it.
//
// XE_ANIMATION_PERIODQ: The global period of animation used by whatever
// timer that depends on it, if you put a low value
// the movement will look good but it may hurt your
// performance, if instead you use a high value it
// will not lag but will be fast.
//
// XE_MAX_COLLISION_SIZEQ: The maximum unit collision size in your map, if
// you got a unit bigger than 197.0 it would be
// a good idea to update this constant, since some
// enums will not find it. Likewise, if none of
// your units can go bellow X and X is much smaller
// than 197.0, it would be a good idea to update
// as well, since it will improve the performance
// those enums.
//
// Notice you probably don't have to update this library, unless I specify
// there are new constants which would be unlikely.
//
//**************************************************************************
//===========================================================================
globals
constant integer XE_DUMMY_UNITIDQ = 'e00D'
constant integer XE_HEIGHT_ENABLERQ = 'Amrf'
constant integer XE_TREE_RECOGNITIONQ = 'Aeat'
constant real XE_ANIMATION_PERIODQ = 0.025
constant real XE_MAX_COLLISION_SIZEQ = 197.0
endglobals
endlibrary
JASS:
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xepreload requires xebasicQ, optional TimerUtils
//************************************************************************
// xepreload 0.7 // modified by triggerhappy, module initialiazer
// ---------
// Ah, the joy of preloading abilities, it is such a necessary evil...
// Notice you are not supposed to use this system in places outside map init
//
// This one does the preloading and tries to minimize the hit on loading time
// for example, it only needs one single native call per ability preloaded.
//
//************************************************************************
//===========================================================================================================
globals
private unit dum=null
private constant boolean ALLOW_DEBUGGING=true
endglobals
private keyword DebugIdInteger2IdString
//inline friendly (when debug mode is off..)
function XE_PreloadAbility takes integer abilid returns nothing
call UnitAddAbility(dum, abilid)
static if DEBUG_MODE then
static if ALLOW_DEBUGGING then
if GetUnitAbilityLevel(dum, abilid) == 0 then
call BJDebugMsg("XE_PreloadAbility: Ability "+ GetObjectName(abilid) + " " + DebugIdInteger2IdString.evaluate(abilid)+" does not exist.")
endif
endif
endif
endfunction
// ................................................................................
//================================================================================
// Convert a integer id value into a 4-letter id code.
// * Taken from cheats.j so I don't have to code it again.
// * Used only on debug so making a whole library for it seemed silly
// * Private so people don't begin using xepreload just to call this function....
// * It will not work correctly if you paste this code in the custom script section
// due to the infamous % bug. Then again, if you do that then you probably
// deserve it....
//
private function DebugIdInteger2IdString takes integer value returns string
local string charMap = ".................................!.#$%&'()*+,-./0123456789:;<=>.@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................."
local string result = ""
local integer remainingValue = value
local integer charValue
local integer byteno
set byteno = 0
loop
set charValue = ModuloInteger(remainingValue, 256)
set remainingValue = remainingValue / 256
set result = SubString(charMap, charValue, charValue + 1) + result
set byteno = byteno + 1
exitwhen byteno == 4
endloop
return result
endfunction
//--------------------------------
private function kill takes nothing returns nothing
call RemoveUnit(dum)
set dum=null
static if (LIBRARY_TimerUtils ) then
call ReleaseTimer( GetExpiredTimer() )
else
call DestroyTimer(GetExpiredTimer())
endif
endfunction
private module PreloadInit
private static method onInit takes nothing returns nothing
local timer t
set dum = CreateUnit( Player(15), XE_DUMMY_UNITIDQ, 0,0,0)
if( dum == null) then
debug call BJDebugMsg("xePreload : XE_DUMMY_UNITIDQ ("+DebugIdInteger2IdString.evaluate(XE_DUMMY_UNITIDQ)+") not added correctly to the map.")
endif
static if (LIBRARY_TimerUtils) then
set t=NewTimer()
else
set t=CreateTimer()
endif
call TimerStart(t,0.0,false,function kill)
set t=null
endmethod
endmodule
private struct Init extends array
implement PreloadInit
endstruct
endlibrary
JASS:
library InventoryCore initializer Init requires UserInterface, InventoryItem, PlayerUtils, TimerUtils
struct Inventory
//
// configuration
//
// location of inventory
static real X = 0.61
static real Y = -0.3
// size of the background wwindow
static real SCALE = 0.228
// location of the tooltip information
static real TOOLTIP_X = -0.7
static real TOOLTIP_Y = 0.0
// max # of pages the inventory can have
static integer MAX_PAGES = 1
// how many slots are on each row/column
static constant integer BUTTON_ROWS = 6
static constant integer BUTTON_COLS = 5
// how fast the UI updates
static constant real UI_REFRESH_RATE = 0.01
// show errors
static constant boolean SHOW_ERRORS = true
// icon size
static constant real SLOT_WIDTH = 0.065
static constant real SLOT_HEIGHT = 0.065 * SCREEN_ASPECT_RATIO
static constant real SLOT_SPACING = 1.00 // 0%
// icon offsets (from X,Y)
static constant real SLOT_OFFSET_X = -0.0485
static constant real SLOT_OFFSET_Y = 0.072
// raw id's
static constant integer ICON_EMPTY = 'dbnk'
static constant integer ICON_TRANSPARENT = 'Noth'
static constant integer ICON_PAGEUP = 'BuUp'
static constant integer ICON_PAGEDOWN = 'Down'
static constant integer ICON_SELL = 'Sell'
static constant integer ICON_DROP = 'Drop'
static constant integer RACE_BORDERS_START = 'D201'
static constant integer BACKGROUND = 'bwn2'
//
// don't edit below here unless you know what you're doing
//
static constant integer MAX_SLOTS = BUTTON_ROWS * BUTTON_COLS
static constant integer MAX_ITEMS = MAX_SLOTS * MAX_PAGES
readonly static hashtable Hashtable = InitHashtable()
readonly static trigger ExecL = CreateTrigger()
readonly static trigger ExecR = CreateTrigger()
readonly static integer DisplayCount = 0
readonly static timer UpdateTimer = CreateTimer()
readonly static thistype array PlayerCurrent
readonly static UIButton array buttons
readonly integer itemCount
readonly thistype next
readonly thistype prev
readonly integer pid
readonly User user
readonly unit owner
static UIPicture array background
static UIText array pageNum
static UIButton array btnPageUp
static UIButton array btnPageDown
static UIButton array btnSell
static UIButton array btnDrop
static UIPicture array selector
Camera camera
Table countTable
integer currentPage
readonly boolean displayed
readonly integer freeSlot
public static trigger onSell
public static trigger onDrop
public static key KEY_ITEMS
public static key KEY_ITEM_ID
public static key KeyUnit
public static constant integer KEY_UNIT = INVENTORY_KEY_START + KeyUnit
public static key KeyItemCount
public static constant integer KEY_ITEM_COUNT = INVENTORY_KEY_START + KeyItemCount
static method localInt takes integer pid, integer value, integer other returns integer
if (User.Local != User(pid).handle) then
set value = other
endif
return value
endmethod
static method err takes player p, string s returns nothing
static if (Inventory.SHOW_ERRORS) then
static if (LIBRARY_SimError) then
call SimError(p, s)
else
call DisplayTimedTextToPlayer(p, 0, 0, 15, "Error: " + s)
endif
endif
endmethod
static method operator [] takes unit u returns thistype
return thistype(LoadInteger(.Hashtable, KEY_UNIT, GetHandleId(u)))
endmethod
static method operator []= takes unit u, thistype value returns nothing
call SaveInteger(.Hashtable, KEY_UNIT, GetHandleId(u), value)
endmethod
method getButton takes integer index returns UIButton
return this.buttons[(this.pid * MAX_SLOTS) + index]
endmethod
method setButton takes integer index, UIButton value returns nothing
set this.buttons[(this.pid * MAX_SLOTS) + index] = value
endmethod
method getItem takes integer index returns InvItem
return LoadInteger(.Hashtable, this, (MAX_ITEMS * .KEY_ITEMS) + index)
endmethod
method itemsOfType takes integer id returns integer
return this.countTable[id]
endmethod
static method addLeftClickHook takes code func returns nothing
call TriggerAddAction(ExecL, func)
endmethod
static method addRightClickHook takes code func returns nothing
call TriggerAddAction(ExecR, func)
endmethod
method getItemId takes integer index returns integer
return LoadInteger(.Hashtable, this, (MAX_ITEMS * .KEY_ITEM_ID) + index)
endmethod
method setItem takes integer index, InvItem itm returns nothing
local UIButton btn
local integer slot = index
local integer id = itm.id
local integer icon = GetItemIcon(id)
local InvItem oldItem
if (this.currentPage > 0) then
set slot = index - (.MAX_SLOTS*this.currentPage)
endif
set btn = this.getButton(slot)
set oldItem = this.getItem(index)
call SaveInteger(.Hashtable, this, (MAX_ITEMS * .KEY_ITEMS) + index, itm)
if (itm.tempCustomId > 0) then
call SaveInteger(.Hashtable, this, (MAX_ITEMS * .KEY_ITEM_ID) + index, itm.tempCustomId)
endif
set itm.tempCustomId = 0
if (id == 0 and HaveSavedInteger(.Hashtable, this, (MAX_ITEMS * .KEY_ITEMS) + index)) then
set id = oldItem.id
set this.itemCount = this.itemCount - 1
set this.countTable[id] = this.countTable[id] - 1
call RemoveSavedInteger(.Hashtable, this, (MAX_ITEMS * .KEY_ITEMS) + index)
// remove ability
if (oldItem.ability > 0 and itemsOfType(id) <= 0) then
call UnitRemoveAbility(this.owner, oldItem.ability)
endif
elseif (id > 0) then
set this.itemCount = this.itemCount + 1
set this.countTable[id] = this.countTable[id] + 1
// add ability
if (itm.ability > 0 and itemsOfType(id) == 1) then
call UnitAddAbility(this.owner, itm.ability)
endif
endif
call btn.setTexture(.localInt(this.pid, icon, ICON_TRANSPARENT))
call btn.showPlayer(this.user.handle, this.displayed, this.camera)
endmethod
method findFreeSlot takes integer page returns integer //O(n)
local integer i = 0
loop
exitwhen i == MAX_SLOTS
if (this.getItem((MAX_SLOTS*page)+i) == 0) then
return i
endif
set i = i + 1
endloop
return -1
endmethod
method createButtons takes nothing returns nothing
local integer row = 0
local integer col = 0
local UIButton btn = 0
local integer i = 0
if (btnPageUp[this.pid] == 0) then
set btnPageUp[this.pid] = UIButton.create(X - 0.15, (Y + SLOT_OFFSET_Y) - (col * (SLOT_HEIGHT*SLOT_SPACING)), .SLOT_WIDTH, .SLOT_HEIGHT, 0.5, thistype.localInt(this.pid, ICON_PAGEUP, ICON_TRANSPARENT))
set btnPageDown[this.pid] = UIButton.create(X - 0.15, (Y + SLOT_OFFSET_Y) - ((.BUTTON_COLS-1) * (SLOT_HEIGHT*SLOT_SPACING)), .SLOT_WIDTH, .SLOT_HEIGHT, 0.5, thistype.localInt(this.pid, ICON_PAGEDOWN, ICON_TRANSPARENT))
set btnSell[this.pid] = UIButton.create(X - 0.15, (Y + SLOT_OFFSET_Y) - .300, .SLOT_WIDTH, .SLOT_HEIGHT, 0.5, thistype.localInt(this.pid, ICON_SELL, ICON_TRANSPARENT))
set btnDrop[this.pid] = UIButton.create(X - 0.15, (Y + SLOT_OFFSET_Y) - .150, .SLOT_WIDTH, .SLOT_HEIGHT, 0.5, thistype.localInt(this.pid, ICON_DROP, ICON_TRANSPARENT))
set btnPageUp[this.pid].onLeftClick = InvFuncLClickSlot
set btnPageDown[this.pid].onLeftClick = InvFuncLClickSlot
set btnSell[this.pid].onLeftClick = InvFuncLClickSlot
set btnDrop[this.pid].onLeftClick = InvFuncLClickSlot
set btnPageUp[this.pid].selectUnit = this.owner
set btnPageDown[this.pid].selectUnit = this.owner
set btnSell[this.pid].selectUnit = this.owner
set btnDrop[this.pid].selectUnit = this.owner
set selector[this.pid] = UIPicture.createEx(X - 0.15, (Y + SLOT_OFFSET_Y) - .250, 0, .55, 'e000', 1, 1, 0)
set selector[this.pid].animIndex = 56
call selector[this.user.id].show(false, this.camera)
call AddSpecialEffectTarget("UI\\TRSHerolevel.mdx", selector[this.pid].picture, "origin")
endif
loop
exitwhen col == BUTTON_COLS
set row = 0
loop
exitwhen row == BUTTON_ROWS
if (this.getButton(i) == 0) then
set btn = UIButton.create( (X + SLOT_OFFSET_X) + (row * (SLOT_WIDTH*SLOT_SPACING)), (Y + SLOT_OFFSET_Y) - (col * (SLOT_HEIGHT*SLOT_SPACING)), .SLOT_WIDTH, .SLOT_HEIGHT, 0.5, GetItemIcon(this.getItem(i).id))
set btn.customValue = i
set btn.selectUnit = this.owner
set btn.onLeftClick = InvFuncLClickSlot
set btn.onRightClick = InvFuncRClickSlot
call this.setButton(i, btn)
//call SetUnitVertexColor(.background[this.pid].picture, 255, 255, 255, thistype.localInt(this.pid, 255, 0))
endif
set i = i + 1
set row = row + 1
endloop
set col = col + 1
endloop
endmethod
implement InvPlugins
static method create takes unit owner returns thistype
local thistype this = thistype.allocate()
local boolean showLocally
local integer raceId
local integer i = 0
set this.itemCount = 0
set this.owner = owner
set this.freeSlot = 0
set this.pid = GetPlayerId(GetOwningPlayer(this.owner))
set this.user = User(this.pid)
set this.countTable=Table.create()
set raceId = GetHandleId(GetPlayerRace(User(this.pid).handle))-1
set showLocally = (User.Local == User(this.pid).handle)
call this.createButtons()
static if (thistype.createTooltip.exists) then
call thistype.createTooltip(this, raceId)
endif
if (this.pageNum[this.pid] == 0) then
set this.pageNum[this.pid] = UIText.createEx(this.user.toPlayer(), X + 0.300, Y + .10, 1)
endif
set Inventory[this.owner] = this
if (.background[this.pid] == 0) then
set .background[this.pid] = UIPicture.createEx(X, Y, 2, SCALE, BACKGROUND, 70., 60, .localInt(this.pid, RACE_BORDERS_START + raceId, ICON_TRANSPARENT))
endif
set thistype(0).next.prev = this
set this.next = thistype(0).next
set thistype(0).next = this
set this.prev = 0
return this
endmethod
method addItem takes InvItem itm returns boolean
local integer slot
if (itm <= 0 or itm.id <= 0) then
return false
endif
set slot = findFreeSlot(this.currentPage)
if (slot == -1) then
call err(User(this.pid).handle, "There are no free slots on this inventory page.")
return false
endif
if (itm.icon == ICON_EMPTY) then
return false
endif
call this.setItem((.MAX_SLOTS * this.currentPage) + slot, itm)
return true
endmethod
method destroy takes nothing returns nothing
set this.next.prev = this.prev
set this.prev.next = this.next
call this.deallocate()
endmethod
private static method onDisplay takes nothing returns nothing
local thistype inv
local real x
local real y
local real z
local User user = User(User.LocalId)
set inv = Inventory.PlayerCurrent[user.id]
if (inv == 0 or not inv.displayed) then
return
endif
set x = GetUnitX(inv.owner)
set y = GetUnitY(inv.owner)
set z = GetTerrainZ(x, y) + GetUnitDefaultFlyHeight(inv.owner)
call inv.camera.setPosition(x, y, z)
if inv.camera.applyCameraForPlayer(user.handle, false) then
call Interface.updateAll(true, true, true)
endif
endmethod
method show takes boolean flag, Camera cam returns nothing
local integer i
local thistype equip
local real timeout
if (this.currentPage >= .MAX_PAGES) then
set this.currentPage = 0
elseif (this.currentPage < 0) then
set this.currentPage = .MAX_PAGES - 1
endif
if (flag) then
if (not this.displayed) then
set .DisplayCount = .DisplayCount + 1
endif
if (PlayerCurrent[this.pid] > 0 and PlayerCurrent[this.pid] != this) then
call PlayerCurrent[this.pid].show(false, cam)
endif
set PlayerCurrent[this.pid] = this
call PauseTimer(.UpdateTimer)
call TimerStart(.UpdateTimer, UI_REFRESH_RATE, true, function thistype.onDisplay)
else
set .DisplayCount = .DisplayCount - 1
if (DisplayCount == 0) then
call PauseTimer(.UpdateTimer)
else
call TimerStart(.UpdateTimer, UI_REFRESH_RATE, true, function thistype.onDisplay)
endif
if (User.LocalId == this.pid) then
call ResetToGameCamera(0)
set udg_CCSS_CamUpdateInterval[this.pid] = 0.1
endif
set PlayerCurrent[this.pid] = 0
endif
set this.displayed = flag
set this.camera = cam
call this.btnPageUp[this.pid].show(flag, cam)
call this.btnSell[this.pid].show(flag, cam)
call this.btnDrop[this.pid].show(flag, cam)
if (not flag) then
call this.selector[this.pid].show(flag, cam)
endif
call this.btnPageDown[this.pid].show(flag, cam)
call this.background[this.pid].show(flag, cam)
static if thistype.showTooltip.exists then
if (not flag) then
call thistype.showTooltip(this, flag)
endif
endif
call SetTextTagText(this.pageNum[this.pid].text, I2S(this.currentPage + 1) + "/" + I2S(.MAX_PAGES), 10 * 0.0023)
call this.pageNum[this.pid].show(flag, cam)
set i = 0
loop
exitwhen i == .MAX_SLOTS
call this.getButton(i).setTexture(thistype.localInt(this.pid, GetItemIcon(this.getItem((.MAX_SLOTS*this.currentPage) + i).id), ICON_TRANSPARENT))
call this.getButton(i).showPlayer(this.user.handle, flag, this.camera)
call this.getButton(i).showPlayer(this.user.handle, flag, this.camera)
set i = i + 1
endloop
endmethod
/*private static method onInit takes nothing returns nothing
endmethod*/
endstruct
public function GracePeriod takes nothing returns nothing
local timer t = GetExpiredTimer()
local UIButton but = UIButton(GetTimerData(t))
set InvButtonDisabled[but] = false
call ReleaseTimer(t)
endfunction
function DisableButtonIfUsingTimer takes UIButton but, real time returns nothing
static if (Inventory.USE_LEFT_CLICK_TIMER) then
call TimerStart(NewTimerEx(but), time, false, function GracePeriod)
set InvButtonDisabled[but] = true
endif
endfunction
private function LClickItemSlot takes nothing returns boolean
local UIButton but = GetTriggerButton()
local player p = GetClickingPlayer()
local integer pid = GetPlayerId(p)
local Inventory inv = Inventory.PlayerCurrent[pid]
local integer slot = but.customValue
local integer itemId = 0
local integer icon = 0
local integer last = InvPlayerLastSlot[inv.pid] - 1
local integer lastItemId
local InvItem lastItem
local UIButton lastButton = InvPlayerLastButton[inv.pid]
if (User.Local == p) then
call SelectUnit(but.picture, false)
call SelectUnit(inv.owner, true)
endif
if (InvButtonDisabled[but]) then
return false
endif
// sell item
if (but == Inventory.btnSell[inv.pid]) then
set last = InvPlayerLastSlot[inv.pid] - 1
if (last < 0) then
return false
endif
if (p != User(inv.pid).handle) then
return false
endif
// run onSell event
set InvEventPlayer = User(inv.pid).handle
set InvEventItem = inv.getItem((Inventory.MAX_SLOTS*inv.currentPage)+last)
set InvEventSlot = (Inventory.MAX_SLOTS*inv.currentPage)+last
if (Inventory.onSell != null and TriggerEvaluate(Inventory.onSell)) then
call TriggerExecute(Inventory.onSell)
endif
call DisableButtonIfUsingTimer(but, 2)
return false
elseif (but == Inventory.btnDrop[inv.pid]) then
set last = InvPlayerLastSlot[inv.pid] - 1
if (last < 0) then
return false
endif
if (p != User(inv.pid).handle) then
return false
endif
// run onSell event
set InvEventPlayer = User(inv.pid).handle
set InvEventItem = inv.getItem((Inventory.MAX_SLOTS*inv.currentPage)+last)
set InvEventSlot = (Inventory.MAX_SLOTS*inv.currentPage)+last
if (Inventory.onDrop != null and TriggerEvaluate(Inventory.onDrop)) then
call TriggerExecute(Inventory.onDrop)
return false
endif
call DisableButtonIfUsingTimer(but, 2)
elseif (but == Inventory.btnPageDown[inv.pid]) then
if (p != User(inv.pid).handle) then
return false
endif
set inv.currentPage = inv.currentPage + 1
call inv.show(true, inv.camera)
call DisableButtonIfUsingTimer(but, .6)
return false
elseif (but == Inventory.btnPageUp[inv.pid]) then
if (p != User(inv.pid).handle) then
return false
endif
set inv.currentPage = inv.currentPage - 1
call inv.show(true, inv.camera)
call DisableButtonIfUsingTimer(but, .6)
return false
else
call Inventory.selector[inv.pid].show(true, inv.camera)
call Inventory.selector[pid].setPosition(but.centerx - 0.004, but.centery + 0.0262)
call Inventory.selector[pid].showPlayer(Player(pid), true, inv.camera)
endif
if (p != User(inv.pid).handle) then
return false
endif
if (last + 1 != slot) then
call inv.showLines(inv.pid, false, inv.camera)
endif
call TriggerExecute(Inventory.ExecL)
// get item in slot
set itemId = inv.getItem((Inventory.MAX_SLOTS*inv.currentPage)+slot).id
// check for switch
if (last+1 != 0) then
call SetUnitVertexColor(lastButton.picture, 255, 255, 255, 255)
call SetUnitVertexColor(but.picture, 255, 255, 255, 255)
set InvPlayerLastSlot[inv.pid] = 0
set InvPlayerLastButton[inv.pid] = but
if (itemId == 0) then
set last = (Inventory.MAX_SLOTS * inv.currentPage) + last
set lastItem = inv.getItem(last)
set lastItemId = lastItem.id
set lastItem.tempCustomId = inv.getItemId(last)
call inv.setItem((Inventory.MAX_SLOTS*inv.currentPage)+slot, lastItem)
call inv.setItem(last, 0)
call DisableButtonIfUsingTimer(but, 2)
elseif (inv.getItem((Inventory.MAX_SLOTS * inv.currentPage) + last).isSocket) then
set last = (Inventory.MAX_SLOTS * inv.currentPage) + last
set lastItem = inv.getItem(last)
set lastItem.tempCustomId = inv.getItemId(last)
set slot = (Inventory.MAX_SLOTS*inv.currentPage)+slot
if (Equipment.onSocket != null) then
if (lastItem.sockets <= lastItem.maxSockets) then
set SocketInv = inv
set SocketLast = last
set SocketSlot = slot
if (not inv.getItem(slot).isSocket and Equipment.onSocket != null and TriggerEvaluate(Equipment.onSocket)) then
call TriggerExecute(Equipment.onSocket)
return false
endif
endif
else
set inv.getItem(slot).tempCustomId = inv.getItemId(slot)
if (inv.getItem(slot).addSocket(lastItem)) then
call inv.setItem(last, 0)
endif
endif
endif
else//if (itemId != 0) then
set InvPlayerLastSlot[inv.pid] = 0
set InvPlayerLastButton[inv.pid] = but
endif
if (itemId == 0 or InvPlayerLastSlot[inv.pid] != 0) then
else
set InvPlayerLastSlot[inv.pid] = slot + 1
endif
if (but != lastButton) then
call SetUnitVertexColor(lastButton.picture, 255, 255, 255, 255)
call SetUnitVertexColor(but.picture, 175, 175, 175, 255)
endif
if (itemId > 0) then
call inv.setTooltipTitle(GetObjectName(itemId))
static if (InvItem.buildDescription.exists) then
set GetInvItem(itemId).tempCustomId = inv.getItemId((Inventory.MAX_SLOTS*inv.currentPage)+slot)
call GetInvItem(itemId).buildDescription(inv.owner)
else
call inv.setTooltipInfo(GetItemDescription(itemId))
endif
call inv.setTooltipCost("|cffffcc00" + I2S(GetInvItem(itemId).cost) + "|r")
call inv.setTooltipIcon(inv.localInt(inv.pid, GetItemIcon(itemId), Inventory.ICON_TRANSPARENT))
endif
set Inventory.TOOLTIP_X = but.minx - 0.4
set Inventory.TOOLTIP_Y = but.maxy + 0.4
call inv.showTooltip(inv, itemId > 0)
return false
endfunction
private function RClickItemSlot takes nothing returns boolean
local UIButton but = GetTriggerButton()
local player p = GetClickingPlayer()
local integer pid = GetPlayerId(p)
local Inventory inv = Inventory.PlayerCurrent[pid]
local integer slot = but.customValue
local integer itemId
if (p != User(inv.pid).handle) then
return false
endif
call TriggerExecute(Inventory.ExecR)
set slot = (Inventory.MAX_SLOTS * inv.currentPage) + slot
set itemId = inv.getItem(slot).id
if (itemId == 0) then
return false
endif
call inv.showTooltip(inv, false)
/*call SetItemUserData(CreateItem(itemId, GetUnitX(inv.owner), GetUnitY(inv.owner)), inv.getItemId(slot))
call inv.setItem(slot, 0)*/
return false
endfunction
private function Init takes nothing returns nothing
set InvFuncLClickSlot = Filter(function LClickItemSlot)
set InvFuncRClickSlot = Filter(function RClickItemSlot)
endfunction
endlibrary
JASS:
library InventoryItem uses Table, optional InventoryBonus
globals
public constant integer SEED_START = 0
public constant integer SEED_END = 999
endglobals
struct InvItem
readonly static Table Table
integer id
integer icon
integer cost
integer ability
string info
integer tempCustomId
//item ItemTypeId
implement InvItemPlugins
static if not thistype.buildDescriptionLines.exists then
method buildDescriptionLines takes integer pid, integer startLine returns integer
return buildDescriptionLines(GetPlayerId(GetOwningPlayer(forUnit)), 0)
endmethod
endif
static if not thistype.buildDescription.exists then
method buildDescription takes unit forUnit returns string
return buildDescriptionLines(GetPlayerId(GetOwningPlayer(forUnit)), 0)
endmethod
endif
private static method onInit takes nothing returns nothing
set InvItem.Table = Table.create()
endmethod
endstruct
function GetInvItem takes integer id returns InvItem
return InvItem(InvItem.Table[id])
endfunction
function GetItemInfo takes integer id returns string
return InvItem(InvItem.Table[id]).info
endfunction
function GetItemDescription takes integer id returns string
return InvItem(InvItem.Table[id]).info
endfunction
function GetItemIcon takes integer id returns integer
local integer icon = InvItem(InvItem.Table[id]).icon
if (icon == 0) then
return Inventory.ICON_EMPTY
endif
return icon
endfunction
function CreateInvItem takes integer id, integer icon, integer cost, integer abil, string info returns InvItem
local InvItem itm = InvItem.create()
set itm.id = id
set itm.icon = icon
set itm.cost = cost
set itm.info = info
set itm.ability = abil
set InvItem.Table[itm.id] = itm
return itm
endfunction
struct InvCustomItem extends array
static integer Counter = 1
static Table SeedTable
static method create takes nothing returns thistype
local thistype this = Counter
set SeedTable[this] = GetRandomInt(SEED_START, SEED_END)
set Counter = Counter + 1
return this
endmethod
method seed takes nothing returns integer
return SeedTable[this]
endmethod
static method onInit takes nothing returns nothing
set SeedTable = SeedTable.create()
endmethod
endstruct
endlibrary
JASS:
library InventoryPlugins
// add plugins here
module InvPlugins
implement InvTooltip
endmodule
module InvItemPlugins
implement InvItemTooltip
implement InvItemEquipment
implement InvItemSocket
endmodule
endlibrary
JASS:
library EquipmentCore initializer Init requires InventoryCore, EquipmentItem
globals
public filterfunc FuncLClickSlot = null
public filterfunc FuncRClickSlot = null
public integer array PlayerLastSlot
endglobals
public /*constant*/ function HERO_WINDOW_NAME takes unit u returns string
return User[GetOwningPlayer(u)].nameColored
// return GetHeroProperName(u)
endfunction
struct Equipment
//
// configuration
//
static constant real X = 0.425
static constant real Y = .93
static constant real WINDOW_SIZE = 0.28
static constant real SLOT_OFFSET_Y = 0.065
static constant real SLOT_OFFSET_ROWRIGHT_X = 0.460
static constant real SLOT_OFFSET_ROWLEFT_X = 0.105
static constant real HERO_NAME_Y = 0.9
static constant real CHARMODEL_OFFSET_X = X + 0.32
static constant real CHARMODEL_OFFSET_Y = 0.200
static constant integer MAX_SLOTS = 14
static constant real SLOT_WIDTH = 0.090
static constant real SLOT_HEIGHT = 0.090 * SCREEN_ASPECT_RATIO
static constant integer MODEL_DUMMY = 'e00E' // for character model
static constant integer WINDOW_DUMMY = 'ewin'
//
// end config
//
readonly static boolean Initialized = false
readonly static integer DisplayCount = 0
readonly static hashtable Hashtable
readonly static timer UpdateTimer
readonly static unit array PlayerCurrentUnit
readonly static thistype array UnitsIndex
static UIButton array buttons[.MAX_SLOTS]
static UIPicture array slotButton[.MAX_SLOTS]
InvItem array item[.MAX_SLOTS]
integer array itemId[.MAX_SLOTS]
static UIPicture array pictures[.MAX_SLOTS]
static UIPicture array selector
static UIText array title
static trigger onSocket
UIPicture charModel
Camera camera
unit unit
player player
User user
readonly boolean displayed
readonly thistype next
readonly thistype prev
static method operator [] takes unit u returns thistype
return .UnitsIndex[GetUnitUserData(u)]
endmethod
method getButton takes integer index returns UIButton
return this.buttons[(this.user.id * .MAX_SLOTS) + index]
endmethod
method setButton takes integer index, UIButton value returns nothing
set this.buttons[(this.user.id * .MAX_SLOTS) + index] = value
endmethod
static method create takes unit u returns thistype
local thistype this = thistype.allocate()
local real x1 = X + SLOT_OFFSET_ROWLEFT_X
local real x2 = X + SLOT_OFFSET_ROWRIGHT_X
local real y1 = Y - SLOT_OFFSET_Y
local integer i = 0
set this.unit = u
set this.player = GetOwningPlayer(u)
set this.user = User[this.player]
set .UnitsIndex[GetUnitUserData(u)] = this
//
// GEAR SLOTS
//
if (this.getButton(0) == 0) then
set selector[this.user.id] = UIPicture.createEx(X - 0.15, (Y + SLOT_OFFSET_Y) - .250, 0, .70, MODEL_DUMMY, 1, 1, 0)
set selector[this.user.id].animIndex = 56
call selector[this.user.id].show(false, this.camera)
call AddSpecialEffectTarget("UI\\TRSHerolevel.mdx", selector[this.user.id].picture, "origin")
// middle
call this.setButton(12, UIButton.create(x1 + (.SLOT_WIDTH*1.5), y1 - (.SLOT_HEIGHT*5), .SLOT_WIDTH, .SLOT_HEIGHT, 10, 'Weap'))
call this.setButton(13, UIButton.create(x2 - (.SLOT_WIDTH*1.5), y1 - (.SLOT_HEIGHT*5), .SLOT_WIDTH, .SLOT_HEIGHT, 10, 'Shie'))
// left side
call this.setButton(0, UIButton.create(x1, y1, .SLOT_WIDTH, .SLOT_HEIGHT, 10, 'Head'))
call this.setButton(1, UIButton.create(x1, y1 - (.SLOT_HEIGHT*1), .SLOT_WIDTH, .SLOT_HEIGHT, 10, 'Sho1'))
call this.setButton(2, UIButton.create(x1, y1 - (.SLOT_HEIGHT*2), .SLOT_WIDTH, .SLOT_HEIGHT, 10,'Ches'))
call this.setButton(3, UIButton.create(x1, y1 - (.SLOT_HEIGHT*3), .SLOT_WIDTH, .SLOT_HEIGHT, 10,'Belt'))
call this.setButton(4, UIButton.create(x1, y1 - (.SLOT_HEIGHT*4), .SLOT_WIDTH, .SLOT_HEIGHT, 10,'Pant'))
call this.setButton(5, UIButton.create(x1, y1 - (.SLOT_HEIGHT*5), .SLOT_WIDTH, .SLOT_HEIGHT, 10,'Rin1'))
// right side
call this.setButton(6, UIButton.create(x2, y1, .SLOT_WIDTH, .SLOT_HEIGHT, 10, 'Neck'))
call this.setButton(7, UIButton.create(x2, y1 - (.SLOT_HEIGHT*1), .SLOT_WIDTH, .SLOT_HEIGHT, 10,'Sho2'))
call this.setButton(8, UIButton.create(x2, y1 - (.SLOT_HEIGHT*2), .SLOT_WIDTH, .SLOT_HEIGHT, 10,'Glov'))
call this.setButton(9, UIButton.create(x2, y1 - (.SLOT_HEIGHT*3), .SLOT_WIDTH, .SLOT_HEIGHT, 10,'Brac'))
call this.setButton(10, UIButton.create(x2, y1 - (.SLOT_HEIGHT*4), .SLOT_WIDTH, .SLOT_HEIGHT, 10,'Boot'))
call this.setButton(11, UIButton.create(x2, y1 - (.SLOT_HEIGHT*5), .SLOT_WIDTH, .SLOT_HEIGHT, 10,'Rin2'))
loop
exitwhen i == 14
set this.getButton(i).customValue = i
set this.getButton(i).selectUnit = this.unit
set this.getButton(i).onLeftClick = FuncLClickSlot
set this.getButton(i).onRightClick = FuncRClickSlot
set this.slotButton[(this.user.id * MAX_SLOTS) + i] = UIPicture.create(this.getButton(i).minx + (.SLOT_WIDTH/6.3), this.getButton(i).maxy - 0.022, .SLOT_WIDTH * .7, .SLOT_HEIGHT * .7, 9, Inventory.ICON_EMPTY)
set this.slotButton[(this.user.id * MAX_SLOTS) + i].customValue = i
set i = i + 1
endloop
set .pictures[(this.user.id * MAX_SLOTS) + 0] = UIPicture.createEx(X, Y, 11, WINDOW_SIZE, .WINDOW_DUMMY, 140., 130., Inventory.localInt(this.user.id, Inventory.RACE_BORDERS_START+(GetHandleId(GetPlayerRace(this.player))-1), Inventory.ICON_TRANSPARENT))
endif
// unit model
set i = EquipGetHeroModel(GetUnitTypeId(this.unit))
set .charModel = UIPicture.createEx(CHARMODEL_OFFSET_X, .CHARMODEL_OFFSET_Y, 5, HeroModelData(i).scale, MODEL_DUMMY, 1, 1, 0)
set .charModel.animIndex = 140
call SetUnitColor(.charModel.picture, this.user.color)
call AddSpecialEffectTarget(HeroModelData(i).path, .charModel.picture, "origin")
set .title[this.user.id] = UIText.createEx(this.user.toPlayer(), X/1.4, 0.1, 1)
set thistype(0).next.prev = this
set this.next = thistype(0).next
set thistype(0).next = this
set this.prev = 0
return this
endmethod
method equip takes InvItem itm, integer equipSlot returns boolean
local integer slot = itm.slot - 1
if (slot < 0) then
return false
endif
if (this.item[equipSlot] != 0) then
return false
endif
if (itm.slotAlt - 1 > 0) then
if (equipSlot != slot and equipSlot != itm.slotAlt -1) then
call Inventory.err(this.player, "The item doesn't belong in that slot.")
return false
endif
else
if (equipSlot != slot) then
call Inventory.err(this.player, "The item doesn't belong in that slot.")
return false
endif
endif
if (itm.reqUnitType > 0 and GetUnitTypeId(this.unit) != itm.reqUnitType) then
call Inventory.err(this.player, "Your unit type cannot equip this item.")
return false
endif
if (itm.reqAbility > 0 and GetUnitAbilityLevel(this.unit, itm.reqAbility) < 0) then
call Inventory.err(this.player, "Failed requirements.")
return false
endif
if (GetHeroLevel(this.unit) < itm.reqLevel and GetUnitLevel(this.unit) < itm.reqLevel) then
call Inventory.err(this.player, "Your level is too low to equip that item.")
return false
endif
if (itm.equipCondition != null) then
call TriggerClearConditions(InvItem.equipEval)
call TriggerAddCondition(InvItem.equipEval, itm.equipCondition)
set InvItem.eventUnit = this.unit
if (not TriggerEvaluate(InvItem.equipEval)) then
return false
endif
endif
set slot = equipSlot
if (this.item[slot] != 0) then
return false
endif
if (itm.equipAbility > 0) then
call UnitAddAbility(this.unit, itm.equipAbility)
endif
set this.item[slot] = itm
set this.itemId[slot] = itm.tempCustomId
call this.slotButton[(this.user.id * .MAX_SLOTS) + slot].setTexture(Inventory.localInt(this.user.id, itm.icon, Inventory.ICON_TRANSPARENT))
call this.slotButton[(this.user.id * .MAX_SLOTS) + slot].show(true, this.camera)
set InvEventPlayer = this.player
set InvEventItem = itm
set InvEventSlot = equipSlot
if (InvItem.onEquip != null and TriggerEvaluate(InvItem.onEquip)) then
call TriggerExecute(InvItem.onEquip)
endif
// add bonuses
call itm.applyBonuses(this.unit)
set itm.tempCustomId = 0
return true
endmethod
method unequip takes InvItem itm, integer slot returns boolean
local InvItem i2
local integer i = 0
local integer cid = itm.tempCustomId
if (slot != itm.slot -1 and slot != itm.slotAlt - 1) then
return false
endif
if (itm.equipAbility > 0) then
call UnitRemoveAbility(this.unit, itm.equipAbility)
endif
// run unequip event
set InvEventPlayer = this.player
set InvEventItem = this.item[slot]
set InvEventSlot = slot
if (InvItem.onUnequip != null and TriggerEvaluate(InvItem.onUnequip)) then
call TriggerExecute(InvItem.onUnequip)
endif
set this.item[slot] = 0
call this.slotButton[(this.user.id * .MAX_SLOTS) + slot].setTexture(Inventory.ICON_EMPTY)
call this.slotButton[(this.user.id * .MAX_SLOTS) + slot].show(false, this.camera)
// remove bonuses
call itm.removeBonuses(this.unit)
return true
endmethod
method destroy takes nothing returns nothing
set this.next.prev = this.prev
set this.prev.next = this.next
call this.charModel.destroy()
call this.deallocate()
endmethod
private static method onDisplay takes nothing returns nothing
local User user = User(User.LocalId)
local thistype equipment = Equipment[Equipment.PlayerCurrentUnit[user.id]]
local real x
local real y
local real z
if (equipment == 0 or not equipment.displayed or Inventory.PlayerCurrent[equipment.user.id] > 0 or User.Local != user.handle) then
return
endif
set x = GetUnitX(equipment.unit)
set y = GetUnitY(equipment.unit)
set z = GetTerrainZ(x, y) + GetUnitDefaultFlyHeight(equipment.unit)
call equipment.camera.setPosition(x, y, z)
if equipment.camera.applyCameraForPlayer(user.handle, false) then
call Interface.updateAll(true, true, true)
endif
endmethod
method show takes boolean flag, Camera cam returns nothing
local integer i = 0
local thistype equip = 0
local integer len
local real timeout
set this.displayed = flag
set this.camera = cam
if (flag) then
set .DisplayCount = .DisplayCount + 1
if (DisplayCount >= 1) then
call PauseTimer(.UpdateTimer)
call TimerStart(.UpdateTimer, Inventory.UI_REFRESH_RATE, true, function thistype.onDisplay)
endif
if (.PlayerCurrentUnit[this.user.id] != null) then
set equip = Equipment[.PlayerCurrentUnit[this.user.id]]
endif
if (.PlayerCurrentUnit[this.user.id] != null and .PlayerCurrentUnit[this.user.id] != this.unit and equip != this) then
call equip.show(false, this.camera)
endif
set .PlayerCurrentUnit[this.user.id] = this.unit
if (Inventory.PlayerCurrent[this.user.id] > 0 and Inventory.PlayerCurrent[this.user.id].owner != this.unit) then
call Inventory.PlayerCurrent[this.user.id].show(false, this.camera)
endif
else
set .DisplayCount = .DisplayCount - 1
set .PlayerCurrentUnit[this.user.id] = null
if (DisplayCount == 0) then
call PauseTimer(.UpdateTimer)
else
call PauseTimer(.UpdateTimer)
call TimerStart(.UpdateTimer, Inventory.UI_REFRESH_RATE, true, function thistype.onDisplay)
endif
if (User.Local == this.player) then
call ResetToGameCamera(0)
set udg_CCSS_CamUpdateInterval[GetPlayerId(this.player)] = 0.1
endif
endif
call this.charModel.showPlayer(this.user.toPlayer(), flag, this.camera)
if (not flag) then
call this.selector[this.user.id].show(flag, cam)
endif
call SetUnitColor(.charModel.picture, this.user.color)
// todo: make one line / recode
set len = StringLength(this.user.name)
call .title[this.user.id].setPosition(X + 0.10, HERO_NAME_Y)
call SetTextTagText(.title[this.user.id].text, HERO_WINDOW_NAME(this.unit), 8 * 0.0023)
call this.title[this.user.id].show(flag, this.camera)
set i = 0
loop
exitwhen i == thistype.MAX_SLOTS
if (.getButton(i) != 0) then
call .getButton(i).showPlayer(this.user.handle, flag, this.camera)
endif
if (this.pictures[(this.user.id * .MAX_SLOTS) + i] != 0) then
call .pictures[(this.user.id * .MAX_SLOTS) + i].showPlayer(this.user.handle, flag, this.camera)
endif
if (this.item[i] > 0) then
call slotButton[(this.user.id * .MAX_SLOTS) + i].showPlayer(this.user.handle, flag, this.camera)
endif
set i = i + 1
endloop
endmethod
private static method onInit takes nothing returns nothing
set thistype.Hashtable = InitHashtable()
set thistype.UpdateTimer = CreateTimer()
set thistype.Initialized = true
endmethod
endstruct
private function OnInventoryItemClick takes nothing returns nothing
local UIButton but = GetTriggerButton()
local player p = GetClickingPlayer()
local integer pid = GetPlayerId(p)
local Inventory inv = Inventory.PlayerCurrent[pid]
local integer slot = but.customValue
local integer itemId = 0
local integer last = InvPlayerLastSlot[inv.pid] - 1
local integer lastItemId
local UIButton lastButton = InvPlayerLastButton[inv.pid]
local Equipment gear = Equipment[Equipment.PlayerCurrentUnit[inv.pid]]
local integer gearSlot
local InvItem itm
call Equipment.selector[inv.pid].show(false, inv.camera)
if (gear <= 0) then
return
endif
if (PlayerLastSlot[inv.pid] > 0) then
set gearSlot = PlayerLastSlot[inv.pid] - 1
if (inv.getItem(slot) == 0) then
set gear.item[gearSlot].tempCustomId = gear.itemId[gearSlot]
call inv.setItem(slot, gear.item[gearSlot])
set gear.item[gearSlot].tempCustomId = gear.itemId[gearSlot]
call gear.unequip(gear.item[gearSlot], gearSlot)
endif
set PlayerLastSlot[inv.pid] = 0
else
set itm = inv.getItem(slot)
if (itm > 0 and not itm.isSocket and itm.slot > 0) then
set but = gear.getButton(itm.slot - 1)
call Equipment.selector[inv.pid].show(true, inv.camera)
call Equipment.selector[inv.pid].setPosition(but.centerx - 0.007, but.centery + 0.0272)
call Equipment.selector[inv.pid].showPlayer(Player(inv.pid), true, inv.camera)
endif
endif
endfunction
private function OnInventoryItemRightClick takes nothing returns nothing
local UIButton but = GetTriggerButton()
local player p = GetClickingPlayer()
local integer pid = GetPlayerId(p)
local Inventory inv = Inventory.PlayerCurrent[pid]
local integer slot = but.customValue
local Equipment gear = Equipment[Equipment.PlayerCurrentUnit[inv.pid]]
local InvItem itm = inv.getItem(slot)
local integer gearSlot = itm.slot - 1
local integer unequippedSlot = 0
local InvItem unequipItem = 0
local integer unequipId = 0
if (gear <= 0 or itm <= 0) then
return
endif
set itm.tempCustomId = inv.getItemId(slot)
if (gear.item[gearSlot] > 0) then
set unequipId = gear.itemId[gearSlot]
set gear.item[gearSlot].tempCustomId = unequipId
call gear.unequip(gear.item[gearSlot], gearSlot)
set unequippedSlot = InvEventSlot
set unequipItem = InvEventItem
endif
set itm.tempCustomId = inv.getItemId(slot)
if (gear.equip(itm, itm.slot - 1)) then
call inv.setItem(slot, 0)
set unequipItem.tempCustomId = unequipId
call inv.addItem(unequipItem)
elseif (unequipItem > 0) then
call gear.equip(unequipItem, unequippedSlot)
endif
endfunction
private function RClickItemSlot takes nothing returns boolean
local UIButton but = GetTriggerButton()
local unit u = Equipment.PlayerCurrentUnit[GetPlayerId(GetClickingPlayer())]
local Equipment gear = Equipment[u]
local Inventory inv = Inventory[u]
local integer slot = but.customValue
local integer itemId
local InvItem itm
local integer cid
if (GetClickingPlayer() != User(inv.pid).handle or gear.item[slot] <= 0) then
return false
endif
// clear tooltip
call inv.showLines(inv.pid, false, inv.camera)
set itm = gear.item[slot]
set itemId = itm.id
set cid = gear.itemId[slot]
set gear.item[slot].tempCustomId = cid
if (gear.unequip(itm, slot)) then
set itm.tempCustomId = cid
if (not inv.addItem(itm)) then
set gear.item[slot].tempCustomId = cid
call gear.equip(itm, slot)
return false
endif
else
return false
endif
call Inventory.showTooltip(inv, false)
return false
endfunction
private function LClickItemSlot takes nothing returns boolean
local UIButton but = GetTriggerButton()
local player p = GetClickingPlayer()
local Equipment equip = Equipment[Equipment.PlayerCurrentUnit[GetPlayerId(p)]]
local Inventory inv = Inventory.PlayerCurrent[equip.user.id]
local integer slot = but.customValue
local integer last = InvPlayerLastSlot[inv.pid] - 1
local integer itemId = 0
local UIButton lastButton = InvPlayerLastButton[equip.user.id]
local InvItem itm
if (User.Local == p) then
call SelectUnit(but.picture, false)
call SelectUnit(equip.unit, true)
endif
if (p != equip.user.handle) then
return false
endif
if (InvButtonDisabled[but]) then
return false
endif
call inv.showLines(inv.pid, false, inv.camera)
set PlayerLastSlot[equip.user.id] = slot + 1
// equip item
if (inv > 0 and last+1 > 0) then
set last = (Inventory.MAX_SLOTS * inv.currentPage) + last
set itm = inv.getItem(last)
if (itm.slot - 1 == slot or itm.slotAlt - 1 == slot) then
set itm.tempCustomId = inv.getItemId(last)
if (equip.equip(itm, slot)) then
set PlayerLastSlot[inv.pid] = 0
call inv.setItem(last, 0)
call TimerStart(NewTimerEx(but), 2, false, function InventoryCore_GracePeriod)
set InvButtonDisabled[but] = true
endif
endif
endif
set InvPlayerLastButton[equip.user.id] = but
set InvPlayerLastSlot[equip.user.id] = 0
if (but != lastButton) then
call SetUnitVertexColor(lastButton.picture, 255, 255, 255, 255)
call SetUnitVertexColor(but.picture, 175, 175, 175, 255)
endif
set itemId = equip.item[slot].id
call Inventory.selector[inv.pid].show(false, inv.camera)
call Equipment.selector[inv.pid].setPosition(but.centerx - 0.007, but.centery + 0.0272)
call Equipment.selector[inv.pid].showPlayer(Player(inv.pid), true, inv.camera)
if (itemId > 0) then
set equip.item[slot].tempCustomId = equip.itemId[slot]
call inv.setTooltipTitle(GetObjectName(itemId))
call GetInvItem(itemId).buildDescription(inv.owner)
call inv.setTooltipCost("|cffffcc00" + I2S(GetInvItem(itemId).cost) + "|r")
call inv.setTooltipIcon(inv.localInt(inv.pid, GetItemIcon(itemId), Inventory.ICON_TRANSPARENT))
set equip.item[slot].tempCustomId = 0
endif
// display tooltip
set Inventory.TOOLTIP_X = but.minx - 0.4
set Inventory.TOOLTIP_Y = but.miny - 0.08
call Inventory.showTooltip(inv, itemId > 0)
return false
endfunction
private function Init takes nothing returns nothing
set FuncLClickSlot = Filter(function LClickItemSlot)
set FuncRClickSlot = Filter(function RClickItemSlot)
call Inventory.addLeftClickHook(function OnInventoryItemClick)
call Inventory.addRightClickHook(function OnInventoryItemRightClick)
endfunction
endlibrary
Last edited: