-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpriorityManagement.py
More file actions
241 lines (232 loc) · 9.84 KB
/
Copy pathpriorityManagement.py
File metadata and controls
241 lines (232 loc) · 9.84 KB
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
# -*- coding: utf-8 -*-
"""
Created on Tue May 31 16:58:16 2016
@author: rmondoncancel
"""
import copy
from skills import s
from buffs import b
def operatorToFunction(operator) :
"""Transform a priority list operator as a string to the matching
comparison function for conditions
"""
if operator == 'is':
return lambda x, y: x == y
elif operator == '>=':
return lambda x, y: x >= y
elif operator == '>':
return lambda x, y: x > y
elif operator == '<=':
return lambda x, y: x <= y
elif operator == '<':
return lambda x, y: x < y
elif operator == 'isnt':
return lambda x, y: x != y
elif operator == 'and':
return lambda x, y: x and y
elif operator == 'or':
return lambda x, y: x or y
def formatPriorityList(priorityList, pClass, useTp) :
"""Format a priority list to add hidden conditions for each skill
See addHiddenConditions for more details
"""
return [ addHiddenConditions(pe, pClass, useTp) for pe in priorityList ]
def addHiddenConditions(priorityElement, pClass, useTp) :
"""Add hidden conditions for a priority element
* Add a condition on GCD type (global vs instant) to use the correct skills
for a given action
* Check for required buffs if skill require a given buff to be castable
* Check if skill is on cooldown if skill has a cooldown
* Prevent instant skills from delaying the GCD
* Add additional conditions present in skill description
"""
# Get the skill matching the priority element
skill = s(pClass)[priorityElement['group']][priorityElement['name']]
newPriorityElement = copy.deepcopy(priorityElement)
# Add GCD type check to condition
if 'condition' not in newPriorityElement :
# Add a condition key if absent
newPriorityElement['condition'] = {
'type': 'gcdType',
'comparison': 'is',
'value': skill['gcdType']
}
else :
# Add the GCD type check to the existing condition if it already exists
newPriorityElement['condition'] = {
'logic': 'and',
'list': [
newPriorityElement['condition'],
{
'type': 'gcdType',
'comparison': 'is',
'value': skill['gcdType']
},
],
}
# Add required buff condition; required buffs are on 'or', so it will match
# if any of the requiredBuff property of skill is present
# Also, if the buff has stacks, it will by default check if the buff is
# at max stacks rather than just present
if 'requiredBuff' in skill :
reqBufList = []
# Loop on required buffs
for bufName in skill['requiredBuff'] :
# Check if buffs has stacks
if 'maxStacks' in b(pClass)[bufName] :
reqBufList = reqBufList + [ {
'type': 'buffAtMaxStacks',
'name': bufName,
'comparison': 'is',
'value': True,
} ]
else :
reqBufList = reqBufList + [ {
'type': 'buffPresent',
'name': bufName,
'comparison': 'is',
'value': True,
} ]
newPriorityElement['condition'] = {
'logic': 'and',
'list': [
newPriorityElement['condition'],
{
'logic': 'or',
'list': reqBufList,
},
],
}
# Add a condition to check if skill is on cooldown if it has one
if skill['cooldown'] > 0:
newPriorityElement['condition'] = {
'logic': 'and',
'list': [
newPriorityElement['condition'],
{
'type': 'cooldownPresent',
'name': skill['name'],
'comparison': 'is',
'value': False,
},
],
}
# Prevent instant skills from delaying GCD by default
if skill['gcdType'] == 'instant' and 'prepull' not in newPriorityElement:
newPriorityElement['condition'] = {
'logic': 'and',
'list': [
newPriorityElement['condition'],
{
'type': 'gcdDelay',
'delay': skill['animationLock'],
'comparison': '<=',
'value': 0,
},
],
}
# Add skill specific skills if present
if 'condition' in skill :
newPriorityElement['condition'] = {
'logic': 'and',
'list': [
newPriorityElement['condition'],
skill['condition'],
]
}
if useTp and 'tpCost' in skill :
newPriorityElement['condition'] = {
'logic': 'and',
'list': [
newPriorityElement['condition'],
{
'type': 'state',
'name': 'tp',
'comparison': '>=',
'value': skill['tpCost']
},
]
}
return newPriorityElement
def actionToGcdType(actionType) :
"""Returns the gcdType matching a given actionType
"""
if actionType == 'gcdSkill':
return 'global'
elif actionType == 'instantSkill':
return 'instant'
def getConditionValue(state, condition) :
"""Returns the value to check for a given single condition
switch on the condition type to return the matching value in the state
Current types are:
* buffPresent: True if buff is present in state else False
* buffAtMaxStacks: True if buff is at max stacks in state else False
* buffTimeLeft: time left before the buff drops in state; 0 if absent
* debuffPresent: True if debuff is present in state else False
* debuffTimeLeft: time left before the debuff drops in state; 0 if absent
* cooldownPresent: True if skill is on cooldown in state else False
* cooldownTimeLeft: time left before the skill is available again in state;
0 if absent
* gcdType: gcdType of current skill (global/instant)
* gcdDelay: how much would the skill delay the next GCD if cast
* enemy: enemy specific values
* lifePercent: percent of life left on the enemy; 100% if time based
simulation
"""
if condition['type'] == 'buffPresent':
return condition['name'] in [ bf[0]['name'] for bf in state['player']['buff'] ]
elif condition['type'] == 'buffStacks':
bufArray = [ bf[1] for bf in state['player']['buff'] if bf[0]['name'] == condition['name'] ]
return bufArray[0] if len(bufArray) >= 1 else 0
elif condition['type'] == 'buffAtMaxStacks':
return condition['name'] in [ bf[0]['name'] for bf in state['player']['buff'] if 'maxStacks' in b(state['player']['class'])[bf[0]['name']] and bf[1] == b(state['player']['class'])[bf[0]['name']]['maxStacks'] ]
elif condition['type'] == 'buffTimeLeft':
timers = [ na[0] - state['timeline']['timestamp'] for na in state['timeline']['nextActions'] if na[1] == { 'type': 'removeBuff', 'name': condition['name'] } ]
if len(timers) == 0:
return 0
return min(timers)
elif condition['type'] == 'debuffPresent':
return condition['name'] in [ d['name'] for d in state['enemy']['debuff'] ]
elif condition['type'] == 'debuffTimeLeft':
timers = [ na[0] - state['timeline']['timestamp'] for na in state['timeline']['nextActions'] if na[1] == { 'type': 'removeDebuff', 'name': condition['name'] } ]
if len(timers) == 0:
return 0
return min(timers)
elif condition['type'] == 'cooldownPresent':
return condition['name'] in state['player']['cooldown']
elif condition['type'] == 'cooldownTimeLeft':
timers = [ na[0] - state['timeline']['timestamp'] for na in state['timeline']['nextActions'] if na[1] == { 'type': 'removeCooldown', 'name': condition['name'] } ]
if len(timers) == 0:
return 0
return min(timers)
elif condition['type'] == 'gcdType':
return actionToGcdType(state['timeline']['currentAction']['type'])
elif condition['type'] == 'gcdDelay':
if state['timeline']['currentAction']['type'] == 'gcdSkill':
return condition['delay']
nextGcdTimestamp = min( na[0] for na in state['timeline']['nextActions'] if na[1]['type'] == 'gcdSkill' )
return max(0, state['timeline']['timestamp'] + condition['delay'] - nextGcdTimestamp)
elif condition['type'] == 'state':
if condition['name'] == 'enemyLifePercent' :
if 'hp' not in state['enemy'] or 'maxHp' not in state['enemy'] :
return 100
return 100 * state['enemy']['hp'] / state['enemy']['maxHp']
elif condition['name'] == 'lastGCD' :
return state['timeline']['lastGCD'] if 'lastGCD' in state['timeline'] else None
elif condition['name'] == 'tp' :
return state['player']['tp'] if 'tp' in state['player'] else 1000
def testCondition(state, condition) :
"""Test a single condition
Compares the value returned by getConditionalValue to the value of the
condition with the comparison operation chosen
"""
val = getConditionValue(state, condition)
return operatorToFunction(condition['comparison'])(val, condition['value'])
def reduceConditions(state, conditions) :
"""Reduce the condition of a complex priority element condition
applies testCondition if condition is single, else reduce the condition
list with the logic if the condition is complex (with and/or)
"""
if 'logic' in conditions:
return reduce(operatorToFunction(conditions['logic']), [ reduceConditions(state, cond) for cond in conditions['list'] ])
return testCondition(state, conditions)