dfpyre 0.6.3__py3-none-any.whl → 0.7.1__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/items.py CHANGED
@@ -4,19 +4,28 @@ Contains class definitions for code items.
4
4
 
5
5
  from enum import Enum
6
6
  import re
7
- from typing import Literal, Dict, Any
7
+ from typing import Literal, Any
8
8
  from dfpyre.style import is_ampersand_coded, ampersand_to_minimessage
9
+ from dfpyre.util import PyreException, warn
9
10
  from mcitemlib.itemlib import Item as NbtItem
10
11
 
11
12
 
12
13
  NUMBER_REGEX = r'-?\d*\.?\d+'
14
+ VAR_SHORTHAND_CHAR = '$'
15
+ VAR_SCOPES = {'g': 'unsaved', 's': 'saved', 'l': 'local', 'i': 'line'}
13
16
 
14
17
 
15
- class PyreException(Exception):
16
- pass
18
+ def convert_argument(arg: Any):
19
+ if type(arg) in {int, float}:
20
+ return num(arg)
21
+ elif isinstance(arg, str):
22
+ if len(arg) > 2 and arg[0] == VAR_SHORTHAND_CHAR and arg[1] in VAR_SCOPES:
23
+ return var(arg[2:], VAR_SCOPES[arg[1]])
24
+ return text(arg)
25
+ return arg
17
26
 
18
27
 
19
- def _add_slot(d: Dict, slot: int|None):
28
+ def _add_slot(d: dict, slot: int|None):
20
29
  if slot is not None:
21
30
  d['slot'] = slot
22
31
 
@@ -274,7 +283,7 @@ class parameter:
274
283
  self.optional = optional
275
284
  self.description = description
276
285
  self.note = note
277
- self.default_value = default_value
286
+ self.default_value = convert_argument(default_value)
278
287
 
279
288
 
280
289
  def format(self, slot: int):
@@ -292,8 +301,13 @@ class parameter:
292
301
  formatted_dict['item']['data']['description'] = self.description
293
302
  if self.note:
294
303
  formatted_dict['item']['data']['note'] = self.note
295
- if self.default_value is not None and not self.plural and self.optional:
296
- formatted_dict['item']['data']['default_value'] = self.default_value.format(None)['item']
304
+ if self.default_value is not None:
305
+ if not self.optional:
306
+ warn(f'For parameter "{self.name}": Default value cannot be set if optional is False.')
307
+ elif self.plural:
308
+ warn(f'For parameter "{self.name}": Default value cannot be set while plural is True.')
309
+ else:
310
+ formatted_dict['item']['data']['default_value'] = self.default_value.format(None)['item']
297
311
 
298
312
  return formatted_dict
299
313
 
@@ -311,7 +325,7 @@ def _some_or(value: Any, none_value: Any):
311
325
  return value
312
326
 
313
327
 
314
- def item_from_dict(item_dict: Dict) -> object:
328
+ def item_from_dict(item_dict: dict) -> object:
315
329
  item_id = item_dict['id']
316
330
  item_data = item_dict['data']
317
331
 
dfpyre/pyre.py CHANGED
@@ -4,36 +4,26 @@ A package for externally creating code templates for the DiamondFire Minecraft s
4
4
  By Amp
5
5
  """
6
6
 
7
- import base64
8
- import gzip
9
7
  import json
10
- import os
11
8
  from difflib import get_close_matches
12
9
  import datetime
13
- from typing import Tuple, List, Dict
10
+ from typing import Tuple
14
11
  from enum import Enum
15
12
  import socket
16
13
  from mcitemlib.itemlib import Item as NbtItem
14
+ from dfpyre.util import *
17
15
  from dfpyre.items import *
18
16
  from dfpyre.scriptgen import generate_script, GeneratorFlags
17
+ from dfpyre.actiondump import CODEBLOCK_DATA, get_default_tags
19
18
 
20
- COL_WARN = '\x1b[33m'
21
- COL_RESET = '\x1b[0m'
22
- COL_SUCCESS = '\x1b[32m'
23
- COL_ERROR = '\x1b[31m'
24
-
25
- CODEBLOCK_DATA_PATH = os.path.join(os.path.dirname(__file__), 'data/data.json')
26
19
 
27
20
  VARIABLE_TYPES = {'txt', 'comp', 'num', 'item', 'loc', 'var', 'snd', 'part', 'pot', 'g_val', 'vec', 'pn_el'}
28
21
  TEMPLATE_STARTERS = {'event', 'entity_event', 'func', 'process'}
29
- SINGLE_NAME_CODEBLOCKS = {'func', 'process', 'call_func', 'start_process', 'else'}
22
+ DYNAMIC_CODEBLOCKS = {'func', 'process', 'call_func', 'start_process'}
30
23
 
31
24
  TARGETS = ['Selection', 'Default', 'Killer', 'Damager', 'Shooter', 'Victim', 'AllPlayers', 'Projectile', 'AllEntities', 'AllMobs', 'LastEntity']
32
25
  TARGET_CODEBLOCKS = {'player_action', 'entity_action', 'if_player', 'if_entity'}
33
26
 
34
- VAR_SHORTHAND_CHAR = '$'
35
- VAR_SCOPES = {'g': 'unsaved', 's': 'saved', 'l': 'local', 'i': 'line'}
36
-
37
27
  CODECLIENT_URL = 'ws://localhost:31375'
38
28
 
39
29
 
@@ -57,54 +47,59 @@ DEFAULT_TARGET = Target.SELECTION
57
47
 
58
48
 
59
49
  class CodeBlock:
60
- def __init__(self, name: str, args: Tuple=(), target: Target=DEFAULT_TARGET, data: Dict={}):
50
+ def __init__(self, name: str, args: Tuple=(), target: Target=DEFAULT_TARGET, data: dict={}, tags: dict[str, str]={}):
61
51
  self.name = name
62
52
  self.args = args
63
53
  self.target = target
64
54
  self.data = data
55
+ self.tags = tags
65
56
 
66
57
  def __repr__(self) -> str:
67
- if self.name in SINGLE_NAME_CODEBLOCKS:
58
+ if self.name in DYNAMIC_CODEBLOCKS:
68
59
  if self.name == 'else':
69
60
  return 'CodeBlock(else)'
70
61
  return f'CodeBlock({self.name}, {self.data["data"]})'
71
- elif 'block' in self.data:
62
+ if 'block' in self.data:
72
63
  return f'CodeBlock({self.data["block"]}, {self.name})'
73
64
  return f'CodeBlock(bracket, {self.data["type"]}, {self.data["direct"]})'
74
65
 
75
-
76
- def _warn(message):
77
- print(f'{COL_WARN}! WARNING ! {message}{COL_RESET}')
66
+ def build(self, include_tags: bool=True) -> dict:
67
+ """
68
+ Builds a properly formatted block from a CodeBlock object.
69
+ """
70
+ built_block = self.data.copy()
71
+ codeblock_type = self.data.get('block')
72
+
73
+ # add target if necessary ('Selection' is the default when 'target' is blank)
74
+ if codeblock_type in TARGET_CODEBLOCKS and self.target != DEFAULT_TARGET:
75
+ built_block['target'] = self.target.get_string_value()
76
+
77
+ # add items into args
78
+ final_args = [arg.format(slot) for slot, arg in enumerate(self.args) if arg.type in VARIABLE_TYPES]
79
+
80
+ # check for unrecognized name, add tags
81
+ if codeblock_type is not None: # for brackets
82
+ if self.name not in CODEBLOCK_DATA[codeblock_type]:
83
+ _warn_unrecognized_name(codeblock_type, self.name)
84
+ elif include_tags:
85
+ tags = _get_codeblock_tags(codeblock_type, self.name, self.tags)
86
+ if len(final_args) + len(tags) > 27:
87
+ final_args = final_args[:(27-len(tags))] # trim list if over 27 elements
88
+ final_args.extend(tags) # add tags to end
89
+
90
+ # if final_args:
91
+ built_block['args'] = {'items': final_args}
92
+ return built_block
78
93
 
79
94
 
80
95
  def _warn_unrecognized_name(codeblock_type: str, codeblock_name: str):
81
- close = get_close_matches(codeblock_name, TAGDATA[codeblock_type].keys())
96
+ close = get_close_matches(codeblock_name, CODEBLOCK_DATA[codeblock_type].keys())
82
97
  if close:
83
- _warn(f'Code block name "{codeblock_name}" not recognized. Did you mean "{close[0]}"?')
98
+ warn(f'Code block name "{codeblock_name}" not recognized. Did you mean "{close[0]}"?')
84
99
  else:
85
- _warn(f'Code block name "{codeblock_name}" not recognized. Try spell checking or retyping without spaces.')
100
+ warn(f'Code block name "{codeblock_name}" not recognized. Try spell checking or retyping without spaces.')
86
101
 
87
102
 
88
- def _load_codeblock_data() -> Tuple:
89
- tag_data = {}
90
- if os.path.exists(CODEBLOCK_DATA_PATH):
91
- with open(CODEBLOCK_DATA_PATH, 'r') as f:
92
- tag_data = json.load(f)
93
- else:
94
- _warn('data.json not found -- Item tags and error checking will not work.')
95
- return ({}, set(), set())
96
-
97
- del tag_data['meta']
98
-
99
- all_names = [x for l in [d.keys() for d in tag_data.values()] for x in l] # flatten list
100
- return (
101
- tag_data,
102
- set(tag_data['extras'].keys()),
103
- set(all_names)
104
- )
105
-
106
- TAGDATA, TAGDATA_EXTRAS_KEYS, ALL_CODEBLOCK_NAMES = _load_codeblock_data()
107
-
108
103
  def _add_inverted(data, inverted):
109
104
  """
110
105
  If inverted is true, add 'inverted': 'NOT' to data.
@@ -113,36 +108,48 @@ def _add_inverted(data, inverted):
113
108
  data['inverted'] = 'NOT'
114
109
 
115
110
 
116
- def _convert_data_types(args):
117
- converted_args = []
118
- for value in args:
119
- if type(value) in {int, float}:
120
- converted_args.append(num(value))
121
- elif type(value) is str:
122
- if len(value) > 2 and value[0] == VAR_SHORTHAND_CHAR and value[1] in VAR_SCOPES:
123
- var_object = var(value[2:], VAR_SCOPES[value[1]])
124
- converted_args.append(var_object)
125
- else:
126
- converted_args.append(text(value))
111
+ def _convert_args(args):
112
+ return tuple(map(convert_argument, args))
113
+
114
+
115
+ def _check_applied_tags(tags: list[dict], applied_tags: dict[str, str], codeblock_name: str) -> dict[str, str]:
116
+ if len(applied_tags) > 0 and len(tags) == 0:
117
+ warn(f'Action "{codeblock_name}" does not have any tags, but still received {len(applied_tags)}.')
118
+ return {}
119
+ valid_tags = {}
120
+ tags_formatted = {t['name']: t for t in tags}
121
+ for name, option in applied_tags.items():
122
+ if name not in tags_formatted:
123
+ tag_names_joined = '\n'.join(map(lambda s: ' - '+s, tags_formatted.keys()))
124
+ warn(f'Tag "{name}" does not exist for action "{codeblock_name}". Available tags:\n{tag_names_joined}')
125
+ elif option not in tags_formatted[name]['options']:
126
+ options_joined = '\n'.join(map(lambda s: ' - '+s, tags_formatted[name]['options']))
127
+ warn(f'Tag "{name}" does not have the option "{option}". Available tag options:\n{options_joined}')
127
128
  else:
128
- converted_args.append(value)
129
- return tuple(converted_args)
129
+ valid_tags[name] = option
130
+ return valid_tags
130
131
 
131
132
 
132
- def _reformat_codeblock_tags(tags, codeblock_type: str, codeblock_name: str):
133
+ def _reformat_codeblock_tags(tags: list[dict], codeblock_type: str, codeblock_name: str, applied_tags: dict[str, str]):
133
134
  """
134
- Turns data.json tag items into DiamondFire formatted tag items
135
+ Turns tag objects into DiamondFire formatted tag items
135
136
  """
137
+
138
+ valid_applied_tags = _check_applied_tags(tags, applied_tags, codeblock_name)
136
139
  reformatted_tags = []
137
140
  for tag_item in tags:
138
- action_value = codeblock_name if 'action' not in tag_item else tag_item['action']
141
+ tag_name = tag_item['name']
142
+ tag_option = tag_item['default']
143
+ if tag_name in valid_applied_tags:
144
+ tag_option = valid_applied_tags[tag_name]
145
+
139
146
  new_tag_item = {
140
147
  'item': {
141
148
  'id': 'bl_tag',
142
149
  'data': {
143
- 'option': tag_item['option'],
144
- 'tag': tag_item['tag'],
145
- 'action': action_value,
150
+ 'option': tag_option,
151
+ 'tag': tag_name,
152
+ 'action': codeblock_name,
146
153
  'block': codeblock_type
147
154
  }
148
155
  },
@@ -152,57 +159,15 @@ def _reformat_codeblock_tags(tags, codeblock_type: str, codeblock_name: str):
152
159
  return reformatted_tags
153
160
 
154
161
 
155
- def _get_codeblock_tags(codeblock_type: str, codeblock_name: str):
162
+ def _get_codeblock_tags(codeblock_type: str, codeblock_name: str, applied_tags: dict[str, str]):
156
163
  """
157
164
  Get tags for the specified codeblock type and name
158
165
  """
159
- tags = None
160
- if codeblock_type in TAGDATA_EXTRAS_KEYS:
161
- tags = TAGDATA['extras'][codeblock_type]
162
- else:
163
- tags = TAGDATA[codeblock_type].get(codeblock_name)
164
- return _reformat_codeblock_tags(tags, codeblock_type, codeblock_name)
165
-
166
-
167
- def _build_block(codeblock: CodeBlock, include_tags: bool):
168
- """
169
- Builds a properly formatted block from a CodeBlock object.
170
- """
171
- built_block = codeblock.data.copy()
172
- codeblock_type = codeblock.data.get('block')
173
-
174
- # add target if necessary ('Selection' is the default when 'target' is blank)
175
- if codeblock_type in TARGET_CODEBLOCKS and codeblock.target != DEFAULT_TARGET:
176
- built_block['target'] = codeblock.target.get_string_value()
177
-
178
- # add items into args
179
- final_args = [arg.format(slot) for slot, arg in enumerate(codeblock.args) if arg.type in VARIABLE_TYPES]
180
-
181
- # check for unrecognized name, add tags
182
- if codeblock_type is not None: # for brackets
183
- if codeblock_type not in TAGDATA_EXTRAS_KEYS and codeblock.name not in ALL_CODEBLOCK_NAMES:
184
- _warn_unrecognized_name(codeblock_type, codeblock.name)
185
- elif include_tags:
186
- tags = _get_codeblock_tags(codeblock_type, codeblock.name)
187
- if len(final_args) + len(tags) > 27:
188
- final_args = final_args[:(27-len(tags))] # trim list if over 27 elements
189
- final_args.extend(tags) # add tags to end
190
-
191
- if final_args:
192
- built_block['args'] = {'items': final_args}
193
- return built_block
194
-
195
-
196
- def _df_encode(json_string: str) -> str:
197
- """
198
- Encodes a stringified json.
199
- """
200
- encoded_string = gzip.compress(json_string.encode('utf-8'))
201
- return base64.b64encode(encoded_string).decode('utf-8')
202
-
203
-
204
- def _df_decode(encoded_string: str) -> str:
205
- return gzip.decompress(base64.b64decode(encoded_string)).decode('utf-8')
166
+ action_data = CODEBLOCK_DATA[codeblock_type][codeblock_name]
167
+ if 'deprecatedNote' in action_data:
168
+ warn(f'Action "{codeblock_name}" is deprecated: {action_data["deprecatedNote"]}')
169
+ tags = action_data['tags']
170
+ return _reformat_codeblock_tags(tags, codeblock_type, codeblock_name, applied_tags)
206
171
 
207
172
 
208
173
  def _get_template_item(template_code: str, name: str, author: str) -> NbtItem:
@@ -232,7 +197,7 @@ class DFTemplate:
232
197
  Represents a DiamondFire code template.
233
198
  """
234
199
  def __init__(self, name: str=None, author: str='pyre'):
235
- self.codeblocks: List[CodeBlock] = []
200
+ self.codeblocks: list[CodeBlock] = []
236
201
  self.bracket_stack: list[str] = []
237
202
  self.name = name
238
203
  self.author = author
@@ -247,27 +212,33 @@ class DFTemplate:
247
212
  """
248
213
  Create a template object from an existing template code.
249
214
  """
250
- template_dict = json.loads(_df_decode(template_code))
215
+ template_dict = json.loads(df_decode(template_code))
251
216
  template = DFTemplate()
252
217
  for block_dict in template_dict['blocks']:
218
+ block_tags = get_default_tags(block_dict.get('block'), block_dict.get('action'))
253
219
  if 'args' in block_dict:
254
220
  args = []
255
221
  for item_dict in block_dict['args']['items']:
222
+ if item_dict['item'].get('id') == 'bl_tag':
223
+ tag_data = item_dict['item']['data']
224
+ block_tags[tag_data['tag']] = tag_data['option']
256
225
  parsed_item = item_from_dict(item_dict['item'])
257
226
  if parsed_item is not None:
258
227
  args.append(parsed_item)
259
228
  target = Target(TARGETS.index(block_dict['target'])) if 'target' in block_dict else DEFAULT_TARGET
260
229
 
261
- codeblock_name = 'bracket'
262
- if 'block' in block_dict and block_dict['block'] in SINGLE_NAME_CODEBLOCKS:
263
- codeblock_name = block_dict['block']
230
+ codeblock_action = 'bracket'
231
+ if block_dict.get('block') == 'else':
232
+ codeblock_action = 'else'
233
+ elif block_dict.get('block') in DYNAMIC_CODEBLOCKS:
234
+ codeblock_action = 'dynamic'
264
235
  elif 'action' in block_dict:
265
- codeblock_name = block_dict['action']
236
+ codeblock_action = block_dict['action']
266
237
 
267
- if codeblock_name == 'bracket' or block_dict['block'] == 'else':
268
- codeblock = CodeBlock(codeblock_name, data=block_dict)
238
+ if codeblock_action == 'bracket' or block_dict['block'] == 'else':
239
+ codeblock = CodeBlock(codeblock_action, data=block_dict)
269
240
  else:
270
- codeblock = CodeBlock(codeblock_name, args, target, block_dict)
241
+ codeblock = CodeBlock(codeblock_action, args, target, block_dict, tags=block_tags)
271
242
  template.codeblocks.append(codeblock)
272
243
 
273
244
  return template
@@ -301,16 +272,16 @@ class DFTemplate:
301
272
  :param bool include_tags: If True, include item tags in code blocks. Otherwise omit them.
302
273
  :return: String containing encoded template data.
303
274
  """
304
- template_dict_blocks = [_build_block(codeblock, include_tags) for codeblock in self.codeblocks]
275
+ template_dict_blocks = [codeblock.build(include_tags) for codeblock in self.codeblocks]
305
276
  template_dict = {'blocks': template_dict_blocks}
306
277
  first_block = template_dict_blocks[0]
307
278
  if first_block['block'] not in TEMPLATE_STARTERS:
308
- _warn('Template does not start with an event, function, or process.')
279
+ warn('Template does not start with an event, function, or process.')
309
280
 
310
281
  self._set_template_name(first_block)
311
282
 
312
283
  json_string = json.dumps(template_dict, separators=(',', ':'))
313
- return _df_encode(json_string)
284
+ return df_encode(json_string)
314
285
 
315
286
 
316
287
  def build_and_send(self, method: Literal['recode', 'codeclient'], include_tags: bool=True) -> int:
@@ -355,78 +326,79 @@ class DFTemplate:
355
326
  self._add_codeblock(cmd, index)
356
327
 
357
328
 
358
- def function(self, name: str, *args, index: int|None=None):
359
- args = _convert_data_types(args)
360
- cmd = CodeBlock('func', args, data={'id': 'block', 'block': 'func', 'data': name})
329
+ def function(self, name: str, *args, tags: dict[str, str]={}, index: int|None=None):
330
+ args = _convert_args(args)
331
+ cmd = CodeBlock('dynamic', args, data={'id': 'block', 'block': 'func', 'data': name}, tags=tags)
361
332
  self._add_codeblock(cmd, index)
362
333
 
363
334
 
364
- def process(self, name: str, *args, index: int|None=None):
365
- args = _convert_data_types(args)
366
- cmd = CodeBlock('process', args, data={'id': 'block', 'block': 'process', 'data': name})
335
+ def process(self, name: str, *args, tags: dict[str, str]={}, index: int|None=None):
336
+ args = _convert_args(args)
337
+ cmd = CodeBlock('dynamic', args, data={'id': 'block', 'block': 'process', 'data': name}, tags=tags)
367
338
  self._add_codeblock(cmd, index)
368
339
 
369
340
 
370
341
  def call_function(self, name: str, *args, index: int|None=None):
371
- args = _convert_data_types(args)
372
- cmd = CodeBlock('call_func', args, data={'id': 'block', 'block': 'call_func', 'data': name})
342
+ args = _convert_args(args)
343
+ cmd = CodeBlock('dynamic', args, data={'id': 'block', 'block': 'call_func', 'data': name})
373
344
  self._add_codeblock(cmd, index)
374
345
 
375
- def start_process(self, name: str, index: int|None=None):
376
- cmd = CodeBlock('start_process', data={'id': 'block', 'block': 'start_process', 'data': name})
346
+
347
+ def start_process(self, name: str, tags: dict[str, str]={}, index: int|None=None):
348
+ cmd = CodeBlock('dynamic', data={'id': 'block', 'block': 'start_process', 'data': name}, tags=tags)
377
349
  self._add_codeblock(cmd, index)
378
350
 
379
351
 
380
- def player_action(self, name: str, *args, target: Target=DEFAULT_TARGET, index: int|None=None):
381
- args = _convert_data_types(args)
382
- cmd = CodeBlock(name, args, target=target, data={'id': 'block', 'block': 'player_action', 'action': name})
352
+ def player_action(self, name: str, *args, target: Target=DEFAULT_TARGET, tags: dict[str, str]={}, index: int|None=None):
353
+ args = _convert_args(args)
354
+ cmd = CodeBlock(name, args, target=target, data={'id': 'block', 'block': 'player_action', 'action': name}, tags=tags)
383
355
  self._add_codeblock(cmd, index)
384
356
 
385
357
 
386
- def game_action(self, name: str, *args, index: int|None=None):
387
- args = _convert_data_types(args)
388
- cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'game_action', 'action': name})
358
+ def game_action(self, name: str, *args, tags: dict[str, str]={}, index: int|None=None):
359
+ args = _convert_args(args)
360
+ cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'game_action', 'action': name}, tags=tags)
389
361
  self._add_codeblock(cmd, index)
390
362
 
391
363
 
392
- def entity_action(self, name: str, *args, target: Target=DEFAULT_TARGET, index: int|None=None):
393
- args = _convert_data_types(args)
394
- cmd = CodeBlock(name, args, target=target, data={'id': 'block', 'block': 'entity_action', 'action': name})
364
+ def entity_action(self, name: str, *args, target: Target=DEFAULT_TARGET, tags: dict[str, str]={}, index: int|None=None):
365
+ args = _convert_args(args)
366
+ cmd = CodeBlock(name, args, target=target, data={'id': 'block', 'block': 'entity_action', 'action': name}, tags=tags)
395
367
  self._add_codeblock(cmd, index)
396
368
 
397
369
 
398
- def if_player(self, name: str, *args, target: Target=DEFAULT_TARGET, inverted: bool=False, index: int|None=None):
399
- args = _convert_data_types(args)
370
+ def if_player(self, name: str, *args, target: Target=DEFAULT_TARGET, tags: dict[str, str]={}, inverted: bool=False, index: int|None=None):
371
+ args = _convert_args(args)
400
372
  data = {'id': 'block', 'block': 'if_player', 'action': name}
401
373
  _add_inverted(data, inverted)
402
- cmd = CodeBlock(name, args, target=target, data=data)
374
+ cmd = CodeBlock(name, args, target=target, data=data, tags=tags)
403
375
  self._add_codeblock(cmd, index)
404
376
  self._openbracket(index)
405
377
 
406
378
 
407
- def if_variable(self, name: str, *args, inverted: bool=False, index: int|None=None):
408
- args = _convert_data_types(args)
379
+ def if_variable(self, name: str, *args, tags: dict[str, str]={}, inverted: bool=False, index: int|None=None):
380
+ args = _convert_args(args)
409
381
  data = {'id': 'block', 'block': 'if_var', 'action': name}
410
382
  _add_inverted(data, inverted)
411
- cmd = CodeBlock(name, args, data=data)
383
+ cmd = CodeBlock(name, args, data=data, tags=tags)
412
384
  self._add_codeblock(cmd, index)
413
385
  self._openbracket(index)
414
386
 
415
387
 
416
- def if_game(self, name: str, *args, inverted: bool=False, index: int|None=None):
417
- args = _convert_data_types(args)
388
+ def if_game(self, name: str, *args, tags: dict[str, str]={}, inverted: bool=False, index: int|None=None):
389
+ args = _convert_args(args)
418
390
  data = {'id': 'block', 'block': 'if_game', 'action': name}
419
391
  _add_inverted(data, inverted)
420
- cmd = CodeBlock(name, args, data=data)
392
+ cmd = CodeBlock(name, args, data=data, tags=tags)
421
393
  self._add_codeblock(cmd, index)
422
394
  self._openbracket(index)
423
395
 
424
396
 
425
- def if_entity(self, name: str, *args, target: Target=DEFAULT_TARGET, inverted: bool=False, index: int|None=None):
426
- args = _convert_data_types(args)
397
+ def if_entity(self, name: str, *args, target: Target=DEFAULT_TARGET, tags: dict[str, str]={}, inverted: bool=False, index: int|None=None):
398
+ args = _convert_args(args)
427
399
  data = {'id': 'block', 'block': 'if_entity', 'action': name}
428
400
  _add_inverted(data, inverted)
429
- cmd = CodeBlock(name, args, target=target, data=data)
401
+ cmd = CodeBlock(name, args, target=target, data=data, tags=tags)
430
402
  self._add_codeblock(cmd, index)
431
403
  self._openbracket(index)
432
404
 
@@ -437,37 +409,37 @@ class DFTemplate:
437
409
  self._openbracket(index)
438
410
 
439
411
 
440
- def repeat(self, name: str, *args, sub_action: str=None, index: int|None=None):
441
- args = _convert_data_types(args)
412
+ def repeat(self, name: str, *args, tags: dict[str, str]={}, sub_action: str=None, index: int|None=None):
413
+ args = _convert_args(args)
442
414
  data = {'id': 'block', 'block': 'repeat', 'action': name}
443
415
  if sub_action is not None:
444
416
  data['subAction'] = sub_action
445
- cmd = CodeBlock(name, args, data=data)
417
+ cmd = CodeBlock(name, args, data=data, tags=tags)
446
418
  self._add_codeblock(cmd, index)
447
419
  self._openbracket(index, 'repeat')
448
420
 
449
421
 
450
422
  def bracket(self, *args, index: int|None=None):
451
- args = _convert_data_types(args)
423
+ args = _convert_args(args)
452
424
  cmd = CodeBlock('bracket', data={'id': 'bracket', 'direct': 'close', 'type': self.bracket_stack.pop()})
453
425
  self._add_codeblock(cmd, index)
454
426
 
455
427
 
456
- def control(self, name: str, *args, index: int|None=None):
457
- args = _convert_data_types(args)
458
- cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'control', 'action': name})
428
+ def control(self, name: str, *args, tags: dict[str, str]={}, index: int|None=None):
429
+ args = _convert_args(args)
430
+ cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'control', 'action': name}, tags=tags)
459
431
  self._add_codeblock(cmd, index)
460
432
 
461
433
 
462
- def select_object(self, name: str, *args, index: int|None=None):
463
- args = _convert_data_types(args)
464
- cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'select_obj', 'action': name})
434
+ def select_object(self, name: str, *args, tags: dict[str, str]={}, index: int|None=None):
435
+ args = _convert_args(args)
436
+ cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'select_obj', 'action': name}, tags=tags)
465
437
  self._add_codeblock(cmd, index)
466
438
 
467
439
 
468
- def set_variable(self, name: str, *args, index: int|None=None):
469
- args = _convert_data_types(args)
470
- cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'set_var', 'action': name})
440
+ def set_variable(self, name: str, *args, tags: dict[str, str]={}, index: int|None=None):
441
+ args = _convert_args(args)
442
+ cmd = CodeBlock(name, args, data={'id': 'block', 'block': 'set_var', 'action': name}, tags=tags)
471
443
  self._add_codeblock(cmd, index)
472
444
 
473
445
 
@@ -481,6 +453,6 @@ class DFTemplate:
481
453
  :param bool var_shorthand: If True, all variables will be written using variable shorthand.
482
454
  """
483
455
  flags = GeneratorFlags(indent_size, literal_shorthand, var_shorthand)
484
- with open(output_path, 'w') as f:
456
+ with open(output_path, 'w', encoding='utf-8') as f:
485
457
  f.write(generate_script(self, flags))
486
458
 
dfpyre/scriptgen.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import dataclasses
2
2
  import re
3
3
  from dfpyre.items import *
4
+ from dfpyre.actiondump import get_default_tags
4
5
 
5
6
  SCRIPT_START = '''from dfpyre import *
6
7
  t = DFTemplate()
@@ -28,7 +29,6 @@ TEMPLATE_METHOD_LOOKUP = {
28
29
  }
29
30
 
30
31
  TARGET_CODEBLOCKS = {'player_action', 'entity_action', 'if_player', 'if_entity'}
31
- SINGLE_NAME_CODEBLOCKS = {'func', 'process', 'call_func', 'start_process'}
32
32
  VAR_SCOPES = {'unsaved': 'g', 'saved': 's', 'local': 'l', 'line': 'i'}
33
33
 
34
34
 
@@ -96,7 +96,7 @@ def argument_item_to_string(flags: GeneratorFlags, arg_item: object) -> str:
96
96
  if arg_item.optional:
97
97
  param_args.append('optional=True')
98
98
  if arg_item.default_value is not None:
99
- param_args.append(f'default_value={argument_item_to_string(arg_item.default_value)}')
99
+ param_args.append(f'default_value={argument_item_to_string(flags, arg_item.default_value)}')
100
100
  if arg_item.description:
101
101
  param_args.append(f'description="{arg_item.description}"')
102
102
  if arg_item.note:
@@ -114,6 +114,7 @@ def add_script_line(flags: GeneratorFlags, script_lines: list[str], indent_level
114
114
  script_lines.append(added_line)
115
115
 
116
116
 
117
+ # TODO: add tag values if not default
117
118
  def generate_script(template, flags: GeneratorFlags) -> str:
118
119
  indent_level = 0
119
120
  script_lines = []
@@ -132,7 +133,7 @@ def generate_script(template, flags: GeneratorFlags) -> str:
132
133
 
133
134
  method_name = TEMPLATE_METHOD_LOOKUP[codeblock.data['block']]
134
135
  method_args = [f'"{codeblock.name}"']
135
- if codeblock.name in SINGLE_NAME_CODEBLOCKS:
136
+ if codeblock.name == 'dynamic':
136
137
  method_args[0] = f'"{codeblock.data["data"]}"'
137
138
 
138
139
  codeblock_args = [argument_item_to_string(flags, i) for i in codeblock.args]
@@ -140,7 +141,12 @@ def generate_script(template, flags: GeneratorFlags) -> str:
140
141
  method_args.extend(codeblock_args)
141
142
  if method_name in TARGET_CODEBLOCKS and codeblock.target.name != 'SELECTION':
142
143
  method_args.append(f'target=Target.{codeblock.target.name}')
144
+ if codeblock.tags:
145
+ default_tags = get_default_tags(codeblock.data.get('block'), codeblock.name)
146
+ written_tags = {t: o for t, o in codeblock.tags.items() if default_tags[t] != o}
147
+ if written_tags:
148
+ method_args.append(f'tags={str(written_tags)}')
143
149
 
144
150
  line = f't.{method_name}({", ".join(method_args)})'
145
151
  add_script_line(flags, script_lines, indent_level, line)
146
- return SCRIPT_START + '\n'.join(script_lines)
152
+ return SCRIPT_START + '\n'.join(script_lines)
dfpyre/util.py ADDED
@@ -0,0 +1,28 @@
1
+ import base64
2
+ import gzip
3
+
4
+
5
+ COL_WARN = '\x1b[33m'
6
+ COL_RESET = '\x1b[0m'
7
+ COL_SUCCESS = '\x1b[32m'
8
+ COL_ERROR = '\x1b[31m'
9
+
10
+
11
+ class PyreException(Exception):
12
+ pass
13
+
14
+
15
+ def warn(message: str):
16
+ print(f'{COL_WARN}! WARNING ! {message}{COL_RESET}')
17
+
18
+
19
+ def df_encode(json_string: str) -> str:
20
+ """
21
+ Encodes a stringified json.
22
+ """
23
+ encoded_string = gzip.compress(json_string.encode('utf-8'))
24
+ return base64.b64encode(encoded_string).decode('utf-8')
25
+
26
+
27
+ def df_decode(encoded_string: str) -> str:
28
+ return gzip.decompress(base64.b64decode(encoded_string)).decode('utf-8')