# 智能施法

补充了原本 ydlua 对物品施法缺失的部分

将物品技能 handle 跟 单位技能 handle 分离使用

下面的代码 需要在内置 1.48 版本以上 才可以跑


local message = require "jass.message"
local jass = require "jass.common"
local slk = require "jass.slk"
local runtime = require "jass.runtime"
local japi = require "jass.japi"
local keyboard = message.keyboard


-- 目标允许的FLAG
local FLAG = {
    ["地面"]        = 1 << 1,
    ["空中"]        = 1 << 2,
    ["建筑"]        = 1 << 3,
    ["守卫"]        = 1 << 4,
    ["物品"]        = 1 << 5,
    ["树木"]        = 1 << 6,
    ["墙"]          = 1 << 7,
    ["残骸"]        = 1 << 8,
    ["装饰物"]      = 1 << 9,
    ["桥"]          = 1 << 10,
    ["位置"]        = 1 << 11,
    ["自己"]        = 1 << 12,
    ["玩家单位"]    = 1 << 13,
    ["联盟"]        = 1 << 14,
    ["中立"]        = 1 << 15,
    ["敌人"]        = 1 << 16,
    ["未知"]        = 1 << 17,
    ["未知"]        = 1 << 18,
    ["未知"]        = 1 << 19,
    ["可攻击的"]    = 1 << 20,
    ["无敌"]        = 1 << 21,
    ["英雄"]        = 1 << 22,
    ["非-英雄"]     = 1 << 23,
    ["存活"]        = 1 << 24,
    ["死亡"]        = 1 << 25,
    ["有机生物"]    = 1 << 26,
    ["机械类"]      = 1 << 27,
    ["非-自爆工兵"] = 1 << 28,
    ["自爆工兵"]    = 1 << 29,
    ["非-古树"]     = 1 << 30,
    ["古树"]        = 1 << 31,
}

FLAG["敌我判断"] = FLAG["自己"] | FLAG["玩家单位"] | FLAG["联盟"] | FLAG["中立"] | FLAG["敌人"]

-- 判断单位是否符合技能的目标允许
local function target_filter(unit, flag, slk)
    local player = jass.GetOwningPlayer(message.selection())
    -- 可见
    if jass.IsUnitInvisible(unit, player) then
        return false
    end
    -- 存活
    if flag & FLAG["死亡"] == 0 and jass.IsUnitType(unit, jass.UNIT_TYPE_DEAD) then
        return false
    end
    -- 无敌
    if flag & FLAG["无敌"] == 0 and jass.GetUnitAbilityLevel(unit, 1098282348 --[['Avul']]) == 1 then
        return false
    end
    -- 魔免
    if (not tonumber(slk.reqLevel) or tonumber(slk.reqLevel) <= 1) and jass.IsUnitType(unit, jass.UNIT_TYPE_MAGIC_IMMUNE) then
        return false
    end
    if flag & FLAG["敌我判断"] ~= 0 then
        -- 敌人
        if flag & FLAG["敌人"] == 0 and jass.IsUnitEnemy(unit, player) then
            return false
        end
        -- 自己
        if flag & FLAG["自己"] == 0 and unit == message.selection() then
            return false
        end
        -- 玩家单位
        if flag & FLAG["玩家单位"] == 0 and jass.GetOwningPlayer(unit) == player then
            return false
        end
        -- 联盟
        if flag & FLAG["联盟"] == 0 and jass.IsUnitAlly(unit, player) then
            return false
        end
    end
    -- 非英雄
    if flag & FLAG["非-英雄"] ~= 0 and jass.IsHeroUnitId(unit) then
        return false
    end
    -- 英雄
    if flag & FLAG["英雄"] ~= 0 and not jass.IsHeroUnitId(unit) then
        return false
    end
    -- 更多筛选请自己定义
    return true
end

-- 从鼠标位置处找出一个单位
local jass_group = jass.CreateGroup()
local function find_target(ability_handle, x, y)

    local ability = japi.EXGetAbilityId(ability_handle) --获取技能id

    -- 读取技能的目标类型
    local data = slk.ability[ability]
    if not data then
        return nil
    end

    local level = japi.EXGetUnitAbilityLevel(ability_handle)

    -- 取出技能的目标类型
    local target_type = japi.EXGetAbilityDataInteger(ability_handle, level, 0x64)

    local group = {}
    -- 选取单位组
    jass.GroupEnumUnitsInRange(jass_group, x, y, 200, nil)
    while true do
        local unit = jass.FirstOfGroup(jass_group)
        if unit == nil or unit == 0 then 
            break
        end
        -- 判断是否符合目标允许
        if target_filter(unit, target_type, data) then
            table.insert(group, unit)
        end
        jass.GroupRemoveUnit(jass_group, unit)
    end
    -- 排序,找出最接近的那个单位
    table.sort(group, function(u1, u2)
        -- 英雄比非英雄优先
        local h1 = jass.IsHeroUnitId(u1)
        local h2 = jass.IsHeroUnitId(u2)
        if h1 and not h2 then
            -- 返回true表示u1排在u2前面
            return true
        end
        if h2 and not h1 then
            -- 返回false表示u2排在u1前面
            return false
        end
        -- 距离越近越优先
        local x1 = jass.GetUnitX(u1)
        local y1 = jass.GetUnitY(u1)
        local x2 = jass.GetUnitX(u2)
        local y2 = jass.GetUnitY(u2)
        return (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y) < (x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)
    end)
    -- 返回单位组里的第一个单位
    return group[1]
end


-- 本地消息的FLAG
local FLAG = {
    ["队列"] = 1 << 0,    -- 指令进入队列,相当于按住shift发布指令
    ["瞬发"] = 1 << 1,    -- 瞬发指令,该指令会立即触发发布命令事件(即使单位处于晕眩状态)
    ["独立"] = 1 << 2,    -- 单独施放,当选中多个单位时只有一个单位会响应该指令
    ['物品'] = 1 << 3,    -- 发布物品命令时需要的标志
    ["恢复"] = 1 << 5,    -- 恢复指令,该指令完成后会恢复之前的指令
}

local function is_cooling(ability_handle)
    local cool = japi.EXGetAbilityState(ability_handle, 1)
    if cool > 0 then 
        return true 
    end 
    return false
end 

-- 立即发动技能
local function cast_ability(unit, ability_handle, order, target_type)

    -- 鼠标当前指向的位置
    local x, y = message.mouse()
    if target_type == 2 then
        -- 点目标命令
        message.order_point(order, x, y, FLAG["独立"] | FLAG["恢复"])
        -- 返回false表示,这个消息会被扔掉(war3不会收到)
        return false
    elseif target_type == 4 then
        -- 单位目标命令
        -- 寻找技能目标
        local target = find_target(ability_handle, x, y)
        if target then
            message.order_target(order, x, y, target, FLAG["独立"] | FLAG["恢复"])
        end
        return false
    elseif target_type == 1 then
        -- 无目标命令
        message.order_immediate(order, FLAG["独立"] | FLAG["恢复"])
        return false
    end
    return true
end

-- 立即使用物品
local function cast_item_ability(item, ability_handle, order, target_type)

    -- 鼠标当前指向的位置
    local x, y = message.mouse()
    if target_type == 2 then
        -- 点目标命令
        message.order_point(order, item, x, y, FLAG["独立"] | FLAG["恢复"])
        -- 返回false表示,这个消息会被扔掉(war3不会收到)
        return false
    elseif target_type == 4 then
        -- 单位目标命令
        -- 寻找技能目标
        local target = find_target(ability_handle, x, y)
        if target then
            message.order_target(order, item, x, y, target, FLAG["独立"] | FLAG["恢复"])
        end
        return false
    elseif target_type == 1 then
        -- 无目标命令
        message.order_immediate(order, item, FLAG["独立"] | FLAG["恢复"])
        return false
    end
    return true
end


local NUM_KEY = {
	[0x108] = 1,    --小键盘数字键 7 = 物品槽位1
	[0x109] = 2,    --小键盘数字键 8 = 物品槽位2
	[0x105] = 3,    --小键盘数字键 4 = 物品槽位3
	[0x106] = 4,    --小键盘数字键 5 = 物品槽位4
	[0x102] = 5,    --小键盘数字键 1 = 物品槽位5
	[0x103] = 6,    --小键盘数字键 2 = 物品槽位6

    [('1'):byte()] = 1, --大键盘数字1 = 物品槽位1   
    [('2'):byte()] = 2, --大键盘数字2 = 物品槽位1
    [('3'):byte()] = 3, --大键盘数字3 = 物品槽位1
    [('4'):byte()] = 4, --大键盘数字4 = 物品槽位1
    [('5'):byte()] = 5, --大键盘数字5 = 物品槽位1
    [('6'):byte()] = 6, --大键盘数字6 = 物品槽位1

}


--
-- 键盘消息回调函数
-- 大多数的jass函数不能在这个函数里使用
-- 如果需要使用jass函数,可以把键盘消息保存下来,在计时器里处理这个消息
--
function message.hook(msg)
    local unit = japi.GetRealSelectUnit()
    if unit == nil or unit == 0 then
        return true
    end
    if msg.type == 'key_down' then
        -- 遍历技能栏,找到想按的技能
        for x = 0, 3 do
            for y = 0, 2 do
                -- message.button,根据格子找到技能id,命令id,目标类型
                local ability, order, target_type = message.button(x, y)
                local ability_data = slk.ability[ability]
                if ability_data and keyboard[ability_data.Hotkey] == msg.code then
                    local ability_handle = japi.GetUnitSpellAbility(unit, ability)

                    if ability_handle > 0 and not is_cooling(ability_handle) then 
                        return cast_ability(unit, ability_handle, order, target_type)
                    end 

                end
            end
        end

        -- 如果按的是数字键盘
        local slot_id = NUM_KEY[msg.code]
        if slot_id then 
            local item = jass.UnitItemInSlot(unit,  slot_id - 1)
            
            if item ~= nil and item ~= 0 then 
                local order, target_type = message.item_button(slot_id - 1)
                local ability_handle = japi.GetItemAbility(item, 0) --获取该0号技能句柄
  
                if ability_handle > 0 and not is_cooling(ability_handle) then 
                    return cast_item_ability(item, ability_handle, order, target_type)
                end 

            end 
        end 
    end
    -- 返回true表示,war3会继续处理这个消息
    return true
end


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
283
284
285
286
287
288