dfpyre 0.4.2__py3-none-any.whl → 0.10.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dfpyre might be problematic. Click here for more details.
- dfpyre/__init__.py +3 -1
- dfpyre/core/actiondump.py +277 -0
- dfpyre/core/codeblock.py +207 -0
- dfpyre/core/items.py +580 -0
- dfpyre/core/template.py +220 -0
- dfpyre/data/actiondump_min.json +1 -0
- dfpyre/data/deprecated_actions.json +172 -0
- dfpyre/data/method_templates/action.txt +5 -0
- dfpyre/data/method_templates/conditional.txt +7 -0
- dfpyre/data/method_templates/event.txt +6 -0
- dfpyre/data/method_templates/notarget_action.txt +5 -0
- dfpyre/data/method_templates/notarget_conditional.txt +6 -0
- dfpyre/data/method_templates/repeat.txt +5 -0
- dfpyre/data/method_templates/repeat_while.txt +9 -0
- dfpyre/data/method_templates/select_obj_subaction.txt +8 -0
- dfpyre/export/action_classes.py +10891 -0
- dfpyre/export/block_functions.py +90 -0
- dfpyre/gen/action_class_data.py +203 -0
- dfpyre/gen/action_literals.py +20 -0
- dfpyre/scripts/action_gen.py +222 -0
- dfpyre/scripts/action_literal_gen.py +43 -0
- dfpyre/tool/scriptgen.py +274 -0
- dfpyre/tool/slice.py +199 -0
- dfpyre/util/style.py +23 -0
- dfpyre/util/util.py +65 -0
- dfpyre-0.10.5.dist-info/METADATA +64 -0
- dfpyre-0.10.5.dist-info/RECORD +29 -0
- {dfpyre-0.4.2.dist-info → dfpyre-0.10.5.dist-info}/WHEEL +1 -2
- {dfpyre-0.4.2.dist-info → dfpyre-0.10.5.dist-info/licenses}/LICENSE +21 -21
- dfpyre/data/data.json +0 -1
- dfpyre/items.py +0 -244
- dfpyre/pyre.py +0 -407
- dfpyre/style.py +0 -21
- dfpyre-0.4.2.dist-info/METADATA +0 -11
- dfpyre-0.4.2.dist-info/RECORD +0 -10
- dfpyre-0.4.2.dist-info/top_level.txt +0 -1
dfpyre/__init__.py
CHANGED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from typing import Literal
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from dfpyre.util.util import warn
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
ACTIONDUMP_PATH = os.path.join(os.path.dirname(__file__), '../data/actiondump_min.json')
|
|
9
|
+
DEPRECATED_ACTIONS_PATH = os.path.join(os.path.dirname(__file__), '../data/deprecated_actions.json')
|
|
10
|
+
|
|
11
|
+
CODEBLOCK_ID_LOOKUP = {
|
|
12
|
+
'PLAYER ACTION': 'player_action',
|
|
13
|
+
'ENTITY ACTION': 'entity_action',
|
|
14
|
+
'GAME ACTION': 'game_action',
|
|
15
|
+
'SET VARIABLE': 'set_var',
|
|
16
|
+
'IF PLAYER': 'if_player',
|
|
17
|
+
'IF ENTITY': 'if_entity',
|
|
18
|
+
'IF GAME': 'if_game',
|
|
19
|
+
'IF VARIABLE': 'if_var',
|
|
20
|
+
'REPEAT': 'repeat',
|
|
21
|
+
'SELECT OBJECT': 'select_obj',
|
|
22
|
+
'CONTROL': 'control',
|
|
23
|
+
'PLAYER EVENT': 'event',
|
|
24
|
+
'ENTITY EVENT': 'entity_event',
|
|
25
|
+
'FUNCTION': 'func',
|
|
26
|
+
'CALL FUNCTION': 'call_func',
|
|
27
|
+
'PROCESS': 'process',
|
|
28
|
+
'START PROCESS': 'start_process',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
VariableType = Literal['VARIABLE', 'NUMBER', 'TEXT', 'COMPONENT', 'ANY_TYPE', 'DICT', 'LIST', 'LOCATION', 'NONE', 'SOUND', 'PARTICLE', 'VECTOR', 'POTION', 'ITEM']
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class CodeblockDataEntry:
|
|
37
|
+
name: str
|
|
38
|
+
id: str
|
|
39
|
+
description: str
|
|
40
|
+
examples: list[str]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class TagOption:
|
|
45
|
+
name: str
|
|
46
|
+
description: str | None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class ActionTag:
|
|
51
|
+
name: str
|
|
52
|
+
options: list[TagOption]
|
|
53
|
+
default: str
|
|
54
|
+
slot: int
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class ActionArgument:
|
|
59
|
+
type: VariableType
|
|
60
|
+
plural: bool
|
|
61
|
+
optional: bool
|
|
62
|
+
description: str | None
|
|
63
|
+
notes: str | None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class ActionDataEntry:
|
|
68
|
+
tags: list[ActionTag]
|
|
69
|
+
required_rank: Literal['None', 'Noble', 'Emperor', 'Mythic', 'Overlord']
|
|
70
|
+
arguments: list[tuple[ActionArgument, ...]]
|
|
71
|
+
return_values: list[VariableType]
|
|
72
|
+
description: str | None
|
|
73
|
+
is_deprecated: bool
|
|
74
|
+
deprecated_note: str | None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass
|
|
78
|
+
class ActiondumpResult:
|
|
79
|
+
codeblock_data: dict[str, CodeblockDataEntry]
|
|
80
|
+
action_data: dict[str, dict[str, ActionDataEntry]]
|
|
81
|
+
game_values: dict[str, VariableType]
|
|
82
|
+
sound_names: list[str]
|
|
83
|
+
potion_names: list[str]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def get_action_tags(action_data: dict) -> list[ActionTag]:
|
|
87
|
+
action_tags = []
|
|
88
|
+
for tag_data in action_data['tags']:
|
|
89
|
+
options: list[TagOption] = []
|
|
90
|
+
for option in tag_data['options']:
|
|
91
|
+
option_desc = option['icon']['description']
|
|
92
|
+
if option_desc:
|
|
93
|
+
option_desc = ' '.join(option_desc)
|
|
94
|
+
else:
|
|
95
|
+
option_desc = None
|
|
96
|
+
options.append(TagOption(name=option['name'], description=option_desc))
|
|
97
|
+
|
|
98
|
+
converted_tag = ActionTag(
|
|
99
|
+
name=tag_data['name'],
|
|
100
|
+
options=options,
|
|
101
|
+
default=tag_data['defaultOption'],
|
|
102
|
+
slot=tag_data['slot']
|
|
103
|
+
)
|
|
104
|
+
action_tags.append(converted_tag)
|
|
105
|
+
return action_tags
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_action_args(action_data: dict) -> list[ActionArgument]:
|
|
109
|
+
icon = action_data['icon']
|
|
110
|
+
if 'arguments' not in icon:
|
|
111
|
+
return []
|
|
112
|
+
|
|
113
|
+
parsed_arguments: list[ActionArgument] = []
|
|
114
|
+
argument_union_list: list[ActionArgument] = []
|
|
115
|
+
add_to_union = False
|
|
116
|
+
arguments = icon['arguments']
|
|
117
|
+
for arg_data in arguments:
|
|
118
|
+
if 'type' not in arg_data:
|
|
119
|
+
if arg_data.get('text') == 'OR':
|
|
120
|
+
add_to_union = True
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
if argument_union_list and not add_to_union:
|
|
124
|
+
parsed_arguments.append(tuple(argument_union_list))
|
|
125
|
+
argument_union_list = []
|
|
126
|
+
|
|
127
|
+
arg_description = arg_data['description']
|
|
128
|
+
if arg_description:
|
|
129
|
+
arg_description = ' '.join(arg_description)
|
|
130
|
+
else:
|
|
131
|
+
arg_description = None
|
|
132
|
+
|
|
133
|
+
arg_notes = arg_data['notes']
|
|
134
|
+
if arg_notes:
|
|
135
|
+
arg_notes = ' '.join(arg_notes[0])
|
|
136
|
+
else:
|
|
137
|
+
arg_notes = None
|
|
138
|
+
|
|
139
|
+
argument_union_list.append(ActionArgument(
|
|
140
|
+
type=arg_data['type'],
|
|
141
|
+
plural=arg_data['plural'],
|
|
142
|
+
optional=arg_data['optional'],
|
|
143
|
+
description=arg_description,
|
|
144
|
+
notes=arg_notes
|
|
145
|
+
))
|
|
146
|
+
add_to_union = False
|
|
147
|
+
|
|
148
|
+
if argument_union_list:
|
|
149
|
+
parsed_arguments.append(tuple(argument_union_list))
|
|
150
|
+
|
|
151
|
+
return parsed_arguments
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def get_action_return_values(action_data: dict) -> list[VariableType]:
|
|
155
|
+
icon = action_data['icon']
|
|
156
|
+
if 'arguments' not in icon:
|
|
157
|
+
return []
|
|
158
|
+
|
|
159
|
+
parsed_return_values: list[VariableType] = []
|
|
160
|
+
return_values = icon['returnValues']
|
|
161
|
+
for value_data in return_values:
|
|
162
|
+
if 'type' not in value_data:
|
|
163
|
+
continue
|
|
164
|
+
parsed_return_values.append(value_data['type'])
|
|
165
|
+
|
|
166
|
+
return parsed_return_values
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def parse_codeblock_data(raw_codeblock_data: list[dict]) -> dict[str, CodeblockDataEntry]:
|
|
170
|
+
parsed_data: dict[str, CodeblockDataEntry] = {}
|
|
171
|
+
for raw_data in raw_codeblock_data:
|
|
172
|
+
identifier = raw_data['identifier']
|
|
173
|
+
description = ' '.join(raw_data['item']['description'])
|
|
174
|
+
parsed_codeblock = CodeblockDataEntry(
|
|
175
|
+
name=raw_data['name'],
|
|
176
|
+
id=identifier,
|
|
177
|
+
description=description,
|
|
178
|
+
examples=raw_data['item']['example']
|
|
179
|
+
)
|
|
180
|
+
parsed_data[identifier] = parsed_codeblock
|
|
181
|
+
|
|
182
|
+
return parsed_data
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def parse_action_data(raw_action_data: list[dict]):
|
|
186
|
+
all_action_data = {n: {} for n in CODEBLOCK_ID_LOOKUP.values()}
|
|
187
|
+
all_action_data['else'] = dict()
|
|
188
|
+
|
|
189
|
+
with open(DEPRECATED_ACTIONS_PATH, 'r', encoding='utf-8') as f:
|
|
190
|
+
all_deprecated_actions: dict = json.loads(f.read())
|
|
191
|
+
|
|
192
|
+
for action_data in raw_action_data:
|
|
193
|
+
action_tags = get_action_tags(action_data)
|
|
194
|
+
|
|
195
|
+
required_rank = action_data['icon']['requiredRank']
|
|
196
|
+
|
|
197
|
+
action_arguments = get_action_args(action_data)
|
|
198
|
+
action_return_values = get_action_return_values(action_data)
|
|
199
|
+
|
|
200
|
+
action_description = action_data['icon']['description']
|
|
201
|
+
if action_description:
|
|
202
|
+
action_description = ' '.join(action_description)
|
|
203
|
+
else:
|
|
204
|
+
action_description = None
|
|
205
|
+
|
|
206
|
+
dep_note = action_data['icon']['deprecatedNote']
|
|
207
|
+
if dep_note:
|
|
208
|
+
dep_note = ' '.join(dep_note)
|
|
209
|
+
else:
|
|
210
|
+
dep_note = None
|
|
211
|
+
|
|
212
|
+
codeblock_type = CODEBLOCK_ID_LOOKUP[action_data['codeblockName']]
|
|
213
|
+
|
|
214
|
+
deprecated_actions = all_deprecated_actions.get(codeblock_type) or {}
|
|
215
|
+
action_name = action_data['name']
|
|
216
|
+
is_deprecated = action_name in deprecated_actions
|
|
217
|
+
|
|
218
|
+
parsed_action_data = ActionDataEntry(
|
|
219
|
+
tags=action_tags,
|
|
220
|
+
required_rank=required_rank,
|
|
221
|
+
arguments=action_arguments,
|
|
222
|
+
return_values=action_return_values,
|
|
223
|
+
description=action_description,
|
|
224
|
+
is_deprecated=is_deprecated,
|
|
225
|
+
deprecated_note=dep_note
|
|
226
|
+
)
|
|
227
|
+
all_action_data[codeblock_type][action_name] = parsed_action_data
|
|
228
|
+
|
|
229
|
+
return all_action_data
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def parse_actiondump() -> ActiondumpResult:
|
|
233
|
+
if not os.path.isfile(ACTIONDUMP_PATH):
|
|
234
|
+
warn('Actiondump not found -- Item tags and error checking will not work.')
|
|
235
|
+
return ActiondumpResult(codeblock_data={}, action_data={}, game_values=[], sound_names=[], potion_names=[])
|
|
236
|
+
|
|
237
|
+
with open(ACTIONDUMP_PATH, 'r', encoding='utf-8') as f:
|
|
238
|
+
actiondump: dict = json.loads(f.read())
|
|
239
|
+
|
|
240
|
+
codeblock_data = parse_codeblock_data(actiondump['codeblocks'])
|
|
241
|
+
all_action_data = parse_action_data(actiondump['actions'])
|
|
242
|
+
|
|
243
|
+
game_values: dict[str, VariableType] = {}
|
|
244
|
+
for game_value in actiondump['gameValues']:
|
|
245
|
+
icon = game_value['icon']
|
|
246
|
+
game_values[icon['name']] = icon['returnType']
|
|
247
|
+
|
|
248
|
+
sound_names: list[str] = []
|
|
249
|
+
for sound in actiondump['sounds']:
|
|
250
|
+
sound_names.append(sound['icon']['name'])
|
|
251
|
+
|
|
252
|
+
potion_names: list[str] = []
|
|
253
|
+
for potion in actiondump['potions']:
|
|
254
|
+
potion_names.append(potion['icon']['name'])
|
|
255
|
+
|
|
256
|
+
return ActiondumpResult(
|
|
257
|
+
codeblock_data=codeblock_data,
|
|
258
|
+
action_data=all_action_data,
|
|
259
|
+
game_values=game_values,
|
|
260
|
+
sound_names=sound_names,
|
|
261
|
+
potion_names=potion_names
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
ACTIONDUMP = parse_actiondump()
|
|
266
|
+
ACTION_DATA = ACTIONDUMP.action_data
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def get_default_tags(codeblock_type: str|None, codeblock_action: str|None) -> dict[str, str]:
|
|
270
|
+
if not codeblock_type or not codeblock_action:
|
|
271
|
+
return {}
|
|
272
|
+
if codeblock_type not in ACTION_DATA:
|
|
273
|
+
return {}
|
|
274
|
+
if codeblock_action not in ACTION_DATA[codeblock_type]:
|
|
275
|
+
return {}
|
|
276
|
+
|
|
277
|
+
return {t.name: t.default for t in ACTION_DATA[codeblock_type][codeblock_action].tags}
|
dfpyre/core/codeblock.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from difflib import get_close_matches
|
|
4
|
+
from dfpyre.util.util import warn, flatten
|
|
5
|
+
from dfpyre.core.items import convert_literals
|
|
6
|
+
from dfpyre.core.actiondump import ACTION_DATA, ActionTag
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
VARIABLE_TYPES = {'txt', 'comp', 'num', 'item', 'loc', 'var', 'snd', 'part', 'pot', 'g_val', 'vec', 'pn_el', 'bl_tag'}
|
|
10
|
+
TEMPLATE_STARTERS = {'event', 'entity_event', 'func', 'process'}
|
|
11
|
+
EVENT_CODEBLOCKS = {'event', 'entity_event'}
|
|
12
|
+
CONDITIONAL_CODEBLOCKS = {'if_player', 'if_var', 'if_game', 'if_entity'}
|
|
13
|
+
TARGET_CODEBLOCKS = {'player_action', 'entity_action', 'if_player', 'if_entity'}
|
|
14
|
+
TARGETS = ['Selection', 'Default', 'Killer', 'Damager', 'Shooter', 'Victim', 'AllPlayers', 'Projectile', 'AllEntities', 'AllMobs', 'LastEntity']
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Target(Enum):
|
|
18
|
+
SELECTION = 0
|
|
19
|
+
DEFAULT = 1
|
|
20
|
+
KILLER = 2
|
|
21
|
+
DAMAGER = 3
|
|
22
|
+
SHOOTER = 4
|
|
23
|
+
VICTIM = 5
|
|
24
|
+
ALL_PLAYERS = 6
|
|
25
|
+
PROJECTILE = 7
|
|
26
|
+
ALL_ENTITIES = 8
|
|
27
|
+
ALL_MOBS = 9
|
|
28
|
+
LAST_ENTITY = 10
|
|
29
|
+
|
|
30
|
+
def get_string_value(self):
|
|
31
|
+
return TARGETS[self.value]
|
|
32
|
+
|
|
33
|
+
DEFAULT_TARGET = Target.SELECTION
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _warn_unrecognized_name(codeblock_type: str, codeblock_name: str):
|
|
37
|
+
close = get_close_matches(codeblock_name, ACTION_DATA[codeblock_type].keys())
|
|
38
|
+
if close:
|
|
39
|
+
warn(f'Code block name "{codeblock_name}" not recognized. Did you mean "{close[0]}"?')
|
|
40
|
+
else:
|
|
41
|
+
warn(f'Code block name "{codeblock_name}" not recognized. Try spell checking or retyping without spaces.')
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _check_applied_tags(tags: list[ActionTag], applied_tags: dict[str, str], codeblock_name: str) -> dict[str, str]:
|
|
45
|
+
if len(applied_tags) > 0 and len(tags) == 0:
|
|
46
|
+
warn(f'Action "{codeblock_name}" does not have any tags, but still received {len(applied_tags)}.')
|
|
47
|
+
return {}
|
|
48
|
+
|
|
49
|
+
valid_tags = {}
|
|
50
|
+
tags_lookup = {t.name: t for t in tags}
|
|
51
|
+
|
|
52
|
+
for name, option in applied_tags.items():
|
|
53
|
+
option_strings = [o.name for o in tags_lookup[name].options]
|
|
54
|
+
|
|
55
|
+
if name not in tags_lookup:
|
|
56
|
+
tag_names_joined = '\n'.join(map(lambda s: ' - '+s, tags_lookup.keys()))
|
|
57
|
+
warn(f'Tag "{name}" does not exist for action "{codeblock_name}". Available tags:\n{tag_names_joined}')
|
|
58
|
+
elif option not in option_strings:
|
|
59
|
+
options_joined = '\n'.join(map(lambda s: ' - '+s, option_strings))
|
|
60
|
+
warn(f'Tag "{name}" does not have the option "{option}". Available tag options:\n{options_joined}')
|
|
61
|
+
else:
|
|
62
|
+
valid_tags[name] = option
|
|
63
|
+
return valid_tags
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _reformat_codeblock_tags(tags: list[ActionTag], codeblock_type: str, codeblock_action: str, applied_tags: dict[str, str]) -> list[dict]:
|
|
67
|
+
"""
|
|
68
|
+
Turns tag objects into DiamondFire formatted tag items.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def format_tag(option: str, name: str):
|
|
72
|
+
return {
|
|
73
|
+
'item': {
|
|
74
|
+
'id': 'bl_tag',
|
|
75
|
+
'data': {'option': option, 'tag': name, 'action': codeblock_action, 'block': codeblock_type}
|
|
76
|
+
},
|
|
77
|
+
'slot': tag_item.slot
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
valid_applied_tags = _check_applied_tags(tags, applied_tags, codeblock_action)
|
|
81
|
+
reformatted_tags = []
|
|
82
|
+
for tag_item in tags:
|
|
83
|
+
tag_name = tag_item.name
|
|
84
|
+
tag_option = tag_item.default
|
|
85
|
+
|
|
86
|
+
if tag_name in valid_applied_tags:
|
|
87
|
+
tag_option = valid_applied_tags[tag_name]
|
|
88
|
+
|
|
89
|
+
new_tag_item = format_tag(tag_option, tag_name)
|
|
90
|
+
reformatted_tags.append(new_tag_item)
|
|
91
|
+
return reformatted_tags
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _get_codeblock_tags(codeblock_type: str, codeblock_name: str, applied_tags: dict[str, str]) -> list[dict]:
|
|
95
|
+
"""
|
|
96
|
+
Get tags for the specified codeblock type and name.
|
|
97
|
+
"""
|
|
98
|
+
action_data = ACTION_DATA[codeblock_type][codeblock_name]
|
|
99
|
+
if action_data.is_deprecated:
|
|
100
|
+
warn(f'Action "{codeblock_name}" is deprecated: {action_data.deprecated_note}')
|
|
101
|
+
tags = action_data.tags
|
|
102
|
+
return _reformat_codeblock_tags(tags, codeblock_type, codeblock_name, applied_tags)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class CodeBlock:
|
|
106
|
+
def __init__(self, codeblock_type: str, action_name: str, args: tuple=(), target: Target=DEFAULT_TARGET, data: dict={}, tags: dict[str, str]={}):
|
|
107
|
+
self.type = codeblock_type
|
|
108
|
+
self.action_name = action_name
|
|
109
|
+
self.args = [convert_literals(a) for a in flatten(args) if a is not None]
|
|
110
|
+
self.target = target
|
|
111
|
+
self.data = data
|
|
112
|
+
self.tags = tags
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@classmethod
|
|
116
|
+
def new_action(cls, codeblock_type: str, action_name: str, args: tuple, tags: dict[str, str], target: Target=DEFAULT_TARGET) -> "CodeBlock":
|
|
117
|
+
return cls(codeblock_type, action_name, args=args, data={'id': 'block', 'block': codeblock_type, 'action': action_name}, tags=tags, target=target)
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def new_data(cls, codeblock_type: str, data_value: str, args: tuple, tags: dict[str, str]) -> "CodeBlock":
|
|
121
|
+
return cls(codeblock_type, 'dynamic', args=args, data={'id': 'block', 'block': codeblock_type, 'data': data_value}, tags=tags)
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def new_event(cls, codeblock_type: str, action_name: str, ls_cancel: bool):
|
|
125
|
+
data = {'id': 'block', 'block': codeblock_type, 'action': action_name}
|
|
126
|
+
if ls_cancel:
|
|
127
|
+
data['attribute'] = 'LS-CANCEL'
|
|
128
|
+
return cls(codeblock_type, action_name, data=data)
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
def new_conditional(cls, codeblock_type: str, action_name: str, args: tuple, tags: dict[str, str], inverted: bool, target: Target=DEFAULT_TARGET) -> "CodeBlock":
|
|
132
|
+
data = {'id': 'block', 'block': codeblock_type, 'action': action_name}
|
|
133
|
+
if inverted:
|
|
134
|
+
data['attribute'] = 'NOT'
|
|
135
|
+
return cls(codeblock_type, action_name, args=args, data=data, tags=tags, target=target)
|
|
136
|
+
|
|
137
|
+
@classmethod
|
|
138
|
+
def new_subaction_block(cls, codeblock_type: str, action_name: str, args: tuple, tags: dict[str, str], sub_action: str|None, inverted: bool) -> "CodeBlock":
|
|
139
|
+
data = {'id': 'block', 'block': codeblock_type, 'action': action_name}
|
|
140
|
+
if sub_action is not None:
|
|
141
|
+
data['subAction'] = sub_action
|
|
142
|
+
if inverted:
|
|
143
|
+
data['attribute'] = 'NOT'
|
|
144
|
+
return cls(codeblock_type, action_name, args=args, data=data, tags=tags)
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
def new_else(cls) -> "CodeBlock":
|
|
148
|
+
return cls('else', 'else', data={'id': 'block', 'block': 'else'})
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def new_bracket(cls, direction: Literal['open', 'close'], bracket_type: Literal['norm', 'repeat']) -> "CodeBlock":
|
|
152
|
+
return cls('bracket', 'bracket', data={'id': 'bracket', 'direct': direction, 'type': bracket_type})
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def __repr__(self) -> str:
|
|
156
|
+
if self.action_name == 'dynamic':
|
|
157
|
+
return f'CodeBlock({self.data["block"]}, {self.data["data"]})'
|
|
158
|
+
if self.action_name == 'else':
|
|
159
|
+
return 'CodeBlock(else)'
|
|
160
|
+
if 'block' in self.data:
|
|
161
|
+
return f'CodeBlock({self.data["block"]}, {self.action_name})'
|
|
162
|
+
return f'CodeBlock(bracket, {self.data["type"]}, {self.data["direct"]})'
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def get_length(self) -> int:
|
|
166
|
+
"""
|
|
167
|
+
Returns the width of this codeblock in Minecraft blocks.
|
|
168
|
+
"""
|
|
169
|
+
if self.type in CONDITIONAL_CODEBLOCKS or self.type in {'repeat', 'else'}:
|
|
170
|
+
return 1
|
|
171
|
+
if self.type == 'bracket' and self.data['direct'] == 'open':
|
|
172
|
+
return 1
|
|
173
|
+
return 2
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def build(self) -> dict:
|
|
177
|
+
"""
|
|
178
|
+
Builds a properly formatted block from a CodeBlock object.
|
|
179
|
+
"""
|
|
180
|
+
built_block = self.data.copy()
|
|
181
|
+
|
|
182
|
+
# Add target if necessary ('Selection' is the default when 'target' is blank)
|
|
183
|
+
if self.type in TARGET_CODEBLOCKS and self.target != DEFAULT_TARGET:
|
|
184
|
+
built_block['target'] = self.target.get_string_value()
|
|
185
|
+
|
|
186
|
+
# Add items into args
|
|
187
|
+
final_args = [arg.format(slot) for slot, arg in enumerate(self.args) if arg.type in VARIABLE_TYPES]
|
|
188
|
+
already_applied_tags: dict[str, dict] = {a['item']['data']['tag']: a for a in final_args if a['item']['id'] == 'bl_tag'}
|
|
189
|
+
|
|
190
|
+
# Check for unrecognized name, add tags
|
|
191
|
+
if self.type not in {'bracket', 'else'}:
|
|
192
|
+
if self.action_name not in ACTION_DATA[self.type]:
|
|
193
|
+
_warn_unrecognized_name(self.type, self.action_name)
|
|
194
|
+
|
|
195
|
+
tags = _get_codeblock_tags(self.type, self.action_name, self.tags)
|
|
196
|
+
for i, tag_data in enumerate(tags):
|
|
197
|
+
already_applied_tag_data = already_applied_tags.get(tag_data['item']['data']['tag'])
|
|
198
|
+
if already_applied_tag_data is not None:
|
|
199
|
+
tags[i] = already_applied_tag_data
|
|
200
|
+
|
|
201
|
+
if len(final_args) + len(tags) > 27:
|
|
202
|
+
final_args = final_args[:(27-len(tags))] # Trim list if over 27 elements
|
|
203
|
+
|
|
204
|
+
final_args.extend(tags) # Add tags to end
|
|
205
|
+
|
|
206
|
+
built_block['args'] = {'items': final_args}
|
|
207
|
+
return built_block
|