/
worldpanels.lua
282 lines (230 loc) · 9.82 KB
/
worldpanels.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
WORLDPANELS_VERSION = "0.81"
--[[
Lua-controlled Frankenstein WorldPanels Library by BMD
Installation
-"require" this file inside your code in order to make the WorldPanels API availble
-Ensure that this file is placed in the vscripts/libraries path along with timers.lua and playertables.lua
-Ensure that you have the barebones_worldpanels.xml in your panorama content layout folder.
-Ensure that you have the barebones_worldpanels.js in your panorama content scripts folder.
-Ensure that barebones_worldpanels.xml is included in your custom_ui_manifest.xml with
<CustomUIElement type="Hud" layoutfile="file://{resources}/layout/custom_game/barebones_worldpanels.xml" />
Library Usage
-WorldPanels are a means of displaying panorama layout files to individuals which act as though they are positioned at a specific point in the world
or on a specific entity.
-WorldPanels can be shared with an individual player, a whole team, or all clients at once via
WorldPanels:CreateWorldPanel(playerID, configTable) -- 1 player or array of playerIDs like {3,5,8}
WorldPanels:CreateWorldPanelForTeam(teamID, configTable) -- 1 team
WorldPanels:CreateWorldPanelForAll(configTable) -- All players
-WorldPanels are specified by a configuration table which has several potential parameters:
-layout: The panorama layout file to display in this worldpanel.
-position: Mutually exclusive with "entity". Position is the world vector position to display the worldpanel at.
-entity: Mutually exclusive with "position". Entity is the entity (hscript or index) which the worldpanel will track for its position.
-offsetX: An optional (default is 0) screen pixel offset to apply to the worldpanel (in the x direction)
-offsetY: An optional (default is 0) screen pixel offset to apply to the worldpanel (in the y direction)
-horizontalAlign: An optional (default is "center") alignment for the worldpanel to use when adjusting the panel size. "center", "left", "right" are valid options
-verticalAlign: An optional (default is "bottom") alignment for the worldpanel to use when adjusting the panel size. "bottom", "center", "top" are valid options
-entityHeight: An optional (default is 0) height offset to use for the entity world panel (see: "HealthBarOffset" in unit KV definition)
-edgePadding: An optional (default is to not lock to screen edge) padding percentage of the screen to limit the worldpanel to.
-duration: An optional (default is infinite) duration in GameTime seconds that the panel will exist for and then be automatically destroyed.
-data: An optional table of data which will be attached to the worldpanel so that valeus can be used in javascript through $.GetContextPanel().Data
This table should only contain numeric, string, or table values (no entities/hscripts)
-WorldPanels returned by the Create methods have the following methods:
wp:SetPosition(position)
wp:SetEntity(entity)
wp:SetHorizontalAlign(hAlign)
wp:SetVerticalAlign(vAlign)
wp:SetOffsetX(offsetX)
wp:SetOffsetY(offsetY)
wp:SetEdgePadding(edge)
wp:SetEntityHeight(entityHeight)
wp:SetData(data)
wp:Delete()
-See examples/worldpanelsExample.lua for usage examples.
Notes
-WorldPanel panorama performance can still be an issue (depending on layout). Use sparingly.
-A WorldPanel attached to an entity will only show when the player has vision of that entity
-A WorldPanel is automatically deleted if the entity it is attached to dies (and is not a hero type unit)
-Edge tracking is currently inaccurate in certain sitautions due to a valve bug with Game.WorldToScreenX/Y
-The WorldPanel library provides a few helpful properties for use in your layout file's javascript.
$.GetContextPanel().WorldPanel contains the WorldPanel configuration in the following table format:
{layout, offsetX, offsetY, position, entity, entityHeight, hAlign, vAlign, edge}
$.GetContextPanel().OnEdge true if this worldpanel has edgelocking/padding and is touching the edge/padded edge of the screen. false otherwise. Updates every frame.
$.GetContextPanel().OffScreen true if this worldpanel has no edgelocking/padding and is completely off screen. false otherwise. Updates every frame.
$.GetContextPanel().Data the "data" object passed in to CreateWorldPanel.
Examples
-Create a special worldpanel for the hero entity of player 0, only visible to player 0. Adds 210 to the unit position for the height of the panel.
WorldPanels:CreateWorldPanel(0,
{layout = "file://{resources}/layout/custom_game/worldpanels/healthbar.xml",
entity = PlayerResource:GetSelectedHeroEntity(0),
entityHeight = 210,
})
-Create a worldpanel for all players that displays at 200 height above the ground at Vector(0,0,0) and locks to 5% of the dge of the screen
WorldPanels:CreateWorldPanelForAll(
{layout = "file://{resources}/layout/custom_game/worldpanels/arrow.xml",
position = GetGroundPosition(Vector(0,0,0), nil) + Vector(0,0,200),
edgePadding = 5,
})
]]
require('libraries/timers')
require('libraries/playertables')
local haStoI = {[0]="center", [1]="left", [2]="right"}
local haItoS = {center=0, left=1, right=2}
local vaStoI = {bottom=0, center=1, top=2}
local vaItoS = {[0]="bottom", [1]="center", [2]="top"}
if not WorldPanels then
WorldPanels = class({})
end
local UpdateTable = function(wp)
local idString = wp.idString
local pt = wp.pt
local pids = wp.pids
for i=1,#pids do
local pid = pids[i]
local ptName = "worldpanels_" .. pid
if not PlayerTables:TableExists(ptName) then
PlayerTables:CreateTable(ptName, {[idString]=pt}, {pid})
else
PlayerTables:SetTableValue(ptName, idString, pt)
end
end
end
function WorldPanels:start()
self.initialized = true
self.entToPanels = {}
self.worldPanels = {}
self.nextID = 0
--CustomGameEventManager:RegisterListener("Attachment_DoSphere", Dynamic_Wrap(WorldPanels, "Attachment_DoSphere"))
ListenToGameEvent('entity_killed', Dynamic_Wrap(WorldPanels, 'OnEntityKilled'), self)
end
function WorldPanels:OnEntityKilled( keys )
--print( '[WorldPanels] OnEntityKilled Called' )
--PrintTable( keys )
-- The Ent that was Killed
local killedEnt = EntIndexToHScript( keys.entindex_killed )
local panels = WorldPanels.entToPanels[killedEnt]
if not killedEnt.IsRealHero or not killedEnt:IsRealHero() then
if panels then
for i=1,#panels do
local panel = panels[i]
for j=1,#panel.pids do
local pid = panel.pids[j]
PlayerTables:DeleteTableKey("worldpanels_" .. pid, panel.idString)
end
end
end
end
end
function WorldPanels:CreateWorldPanelForAll(conf)
local pids = {}
for i=0,DOTA_MAX_TEAM_PLAYERS do
if PlayerResource:IsValidPlayer(i) then
pids[#pids+1] = i;
end
end
return WorldPanels:CreateWorldPanel(pids, conf)
end
function WorldPanels:CreateWorldPanelForTeam(team, conf)
local count = PlayerResource:GetPlayerCountForTeam(team)
local pids = {}
for i=1,count do
pids[#pids+1] = PlayerResource:GetNthPlayerIDOnTeam(team, i)
end
return WorldPanels:CreateWorldPanel(pids, conf)
end
function WorldPanels:CreateWorldPanel(pids, conf)
--{position, entity, offsetX, offsetY, hAlign, vAlign, entityHeight, edge, duration, data}
-- duration?
if type(pids) == "number" then
pids = {pids}
end
local ent = conf.entity
local ei = conf.entity
if ent and type(ent) == "number" then
ei = ent
ent = EntIndexToHScript(ent)
elseif ent and ent.GetEntityIndex then
ei = ent:GetEntityIndex()
end
local pt = {
layout = conf.layout,
position = conf.position,
entity = ei,
offsetX = conf.offsetX,
offsetX = conf.offsetY,
entityHeight = conf.entityHeight,
edge = conf.edgePadding,
data = conf.data,
}
if conf.horizontalAlign then pt.hAlign = haStoI[conf.horizontalAlign] end
if conf.verticalAlign then pt.vAlign = vaStoI[conf.verticalAlign] end
local idString = tostring(self.nextID)
local wp = {
id = self.nextID,
idString = idString,
pids = pids,
pt = pt,
}
function wp:SetPosition(pos)
self.pt.entity = nil
self.pt.position = pos
UpdateTable(self)
end
function wp:SetEntity(entity)
local ei = entity
if entity and not type(entity) == "number" and entity.GetEntityIndex then
ei = entity:GetEntityIndex()
end
self.pt.entity = ei
self.pt.position = nil
UpdateTable(self)
end
function wp:SetHorizontalAlign(hAlign)
self.pt.hAlign = haStoI[hAlign]
UpdateTable(self)
end
function wp:SetVerticalAlign(vAlign)
self.pt.vAlign = vaStoI[vAlign]
UpdateTable(self)
end
function wp:SetOffsetX(offX)
self.pt.offsetX = offX
UpdateTable(self)
end
function wp:SetOffsetY(offY)
self.pt.offsetY = offY
UpdateTable(self)
end
function wp:SetEntityHeight(height)
self.pt.entityHeight = height
UpdateTable(self)
end
function wp:SetEdgePadding(edge)
self.pt.edge = edge
UpdateTable(self)
end
function wp:SetData(data)
self.pt.data = data
UpdateTable(self)
end
function wp:Delete()
for j=1,#self.pids do
local pid = self.pids[j]
PlayerTables:DeleteTableKey("worldpanels_" .. pid, self.idString)
end
end
if conf.duration then
pt.endTime = GameRules:GetGameTime() + conf.duration
Timers:CreateTimer(conf.duration,function()
wp:Delete()
end)
end
UpdateTable(wp)
if ei then
self.entToPanels[ent] = self.entToPanels[ent] or {}
table.insert(self.entToPanels[ent], wp)
end
self.worldPanels[self.nextID] = wp
self.nextID = self.nextID + 1
return wp
end
if not WorldPanels.initialized then WorldPanels:start() end