Skip to content

Commit

Permalink
added the new prototypes
Browse files Browse the repository at this point in the history
but they're not finished yet
  • Loading branch information
redruin1 committed Dec 4, 2024
1 parent 8ca7e69 commit 04f9f68
Show file tree
Hide file tree
Showing 147 changed files with 6,975 additions and 4,435 deletions.
3 changes: 3 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# TODO

### Add a more proper interface to accessing prototype parameters directly from Draftsman objects
So have something like `Car.prototype["base_speed"]` instead of having to do something like `entities.raw["car"]["car"]["base_speed"]`

### Add `tiles` to Groups (somehow)
We could simply add a `tiles` list to `Group`, but this has some unintended consequences. See issue #118 for more info.

Expand Down
4 changes: 2 additions & 2 deletions draftsman/_factorio_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# _factorio_version.py

__factorio_version__ = "2.0.16.0"
__factorio_version_info__ = (2, 0, 16, 0)
__factorio_version__ = "2.0.24.0"
__factorio_version_info__ = (2, 0, 24, 0)
6 changes: 3 additions & 3 deletions draftsman/blueprintable.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ def get_blueprintable_from_string(blueprintable_string: str) -> Blueprintable:
"Blueprintable object" in this context means either a :py:class:`.Blueprint`,
:py:class:`.DeconstructionPlanner`, :py:class:`.UpgradePlanner`, or a
:py:class:`.BlueprintBook`, depending on the particular string you passed in.
This function allows you generically accept export strings of any of the
This function allows you generically accept export strings of any of the
above types and return the appropriate class instance.
:param blueprintable_string: The blueprint string to interpret.
:returns: A :py:class:`.Blueprint`, :py:class:`.BlueprintBook`,
:py:class:`.DeconstructionPlanner`, or :py:class:`.UpgradePlanner`
:py:class:`.DeconstructionPlanner`, or :py:class:`.UpgradePlanner`
object.
:exception MalformedBlueprintStringError: If the ``blueprint_string`` cannot
Expand Down Expand Up @@ -70,7 +70,7 @@ def get_blueprintable_from_JSON(blueprintable_JSON: dict) -> Blueprintable:
:param blueprintable_JSON: The blueprint JSON dict to interpret.
:returns: A :py:class:`.Blueprint`, :py:class:`.BlueprintBook`,
:py:class:`.DeconstructionPlanner`, or :py:class:`.UpgradePlanner`
:py:class:`.DeconstructionPlanner`, or :py:class:`.UpgradePlanner`
object.
:exception IncorrectBlueprintTypeError: If the root level of the
Expand Down
87 changes: 55 additions & 32 deletions draftsman/classes/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
)


def _normalize_internal_structure(input_root, entities_in, tiles_in, schedules_in):
def _normalize_internal_structure(input_root, entities_in, tiles_in, schedules_in, wires_in):
# TODO make this a member of blueprint?
def _throw_invalid_association(entity):
raise InvalidAssociationError(
Expand Down Expand Up @@ -234,6 +234,17 @@ def _throw_invalid_association(entity):

input_root["schedules"] = schedules_out

wires_out = []
for wire in wires_in:
entity_1 = wire[0]
entity_2 = wire[2]
wires_out.append([
flattened_entities.index(entity_1()) + 1, wire[1],
flattened_entities.index(entity_2()) + 1, wire[3]
])

input_root["wires"] = wires_out


class Blueprint(Transformable, TileCollection, EntityCollection, Blueprintable):
"""
Expand All @@ -251,6 +262,7 @@ class Format(DraftsmanBaseModel):
_entities: EntityList = PrivateAttr()
_tiles: TileList = PrivateAttr()
_schedules: ScheduleList = PrivateAttr()
_wires: list[tuple[Association, int, Association, int]] = PrivateAttr()

class BlueprintObject(DraftsmanBaseModel):
# Private Internals (Not exported)
Expand Down Expand Up @@ -346,6 +358,13 @@ class BlueprintObject(DraftsmanBaseModel):
The list of all schedules contained in the blueprint.
""",
)
wires: Optional[list[list[int]]] = Field(
[],
description="""
(2.0) The definitions of all wires in the blueprint, including
both power and circuit connections.
"""
)

@field_serializer("snap_to_grid", when_used="unless-none")
def serialize_snapping_grid(self, _):
Expand Down Expand Up @@ -442,6 +461,7 @@ def setup(
entities: Union[EntityList, list[EntityLike]] = [],
tiles: Union[TileList, list[Tile]] = [],
schedules: Union[ScheduleList, list[Schedule]] = [],
wires: list[list[int]] = [],
index: Optional[uint16] = None,
validate: Union[
ValidationMode, Literal["none", "minimum", "strict", "pedantic"]
Expand Down Expand Up @@ -509,41 +529,44 @@ def setup(
# )
self._root._schedules = ScheduleList(schedules)

self._root._wires = wires

self.index = index

# A bit scuffed, but
for kwarg, value in kwargs.items():
self._root[kwarg] = value

# 1.0 code
# Convert circuit and power connections to Associations
for entity in self.entities:
if hasattr(entity, "connections"): # Wire connections
connections: Connections = entity.connections
for side in connections.true_model_fields():
if connections[side] is None:
continue

if side in {"1", "2"}:
for color, _ in connections[side]: # TODO fix
connection_points = connections[side][color]
if connection_points is None:
continue
for point in connection_points:
old = point["entity_id"] - 1
point["entity_id"] = Association(self.entities[old])

elif side in {"Cu0", "Cu1"}: # pragma: no branch
connection_points = connections[side]
if connection_points is None:
continue # pragma: no coverage
for point in connection_points:
old = point["entity_id"] - 1
point["entity_id"] = Association(self.entities[old])

if hasattr(entity, "neighbours"): # Power pole connections
neighbours = entity.neighbours
for i, neighbour in enumerate(neighbours):
neighbours[i] = Association(self.entities[neighbour - 1])
# for entity in self.entities:
# if hasattr(entity, "connections"): # Wire connections
# connections: Connections = entity.connections
# for side in connections.true_model_fields():
# if connections[side] is None:
# continue

# if side in {"1", "2"}:
# for color, _ in connections[side]: # TODO fix
# connection_points = connections[side][color]
# if connection_points is None:
# continue
# for point in connection_points:
# old = point["entity_id"] - 1
# point["entity_id"] = Association(self.entities[old])

# elif side in {"Cu0", "Cu1"}: # pragma: no branch
# connection_points = connections[side]
# if connection_points is None:
# continue # pragma: no coverage
# for point in connection_points:
# old = point["entity_id"] - 1
# point["entity_id"] = Association(self.entities[old])

# if hasattr(entity, "neighbours"): # Power pole connections
# neighbours = entity.neighbours
# for i, neighbour in enumerate(neighbours):
# neighbours[i] = Association(self.entities[neighbour - 1])

# Change all locomotive numbers to use Associations
for schedule in self.schedules:
Expand Down Expand Up @@ -717,7 +740,7 @@ def position_relative_to_grid(self) -> Optional[Vector]:
``absolute_snapping`` is set to ``True`` or ``None``.
:getter: Gets the absolute grid-position offset.
:setter: Sets the absolute grid-position offset. Is given a value of
:setter: Sets the absolute grid-position offset. Is given a value of
``(0, 0)`` if set to ``None``
"""
# return self._root[self._root_item]["position-relative-to-grid"]
Expand Down Expand Up @@ -897,7 +920,7 @@ def get_dimensions(self) -> tuple[int, int]:
in tiles. Returns ``(0, 0)`` if the blueprint's world bounding box is
``None``.
:returns: A 2-length tuple with the maximum tile width as the first
:returns: A 2-length tuple with the maximum tile width as the first
entry and the maximum tile height as the second entry.
"""
return aabb_to_dimensions(self.get_world_bounding_box())
Expand Down Expand Up @@ -943,7 +966,7 @@ def to_dict(self, exclude_none: bool = True, exclude_defaults: bool = True) -> d
# 1-dimensional lists, flattening any Groups that this blueprint
# contains, and swapping their Associations into integer indexes
_normalize_internal_structure(
result[self._root_item], self.entities, self.tiles, self.schedules
result[self._root_item], self.entities, self.tiles, self.schedules, self._root._wires
)

# # Construct a model with the flattened data, not running any validation
Expand Down
4 changes: 2 additions & 2 deletions draftsman/classes/blueprint_book.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ def label_color(self, value: Optional[Color]):
else:
self._root[self._root_item]["label_color"] = value

def set_label_color(self, r, g, b, a=None): # TODO: remove
def set_label_color(self, r, g, b, a=None): # TODO: remove
"""
TODO
"""
Expand Down Expand Up @@ -474,7 +474,7 @@ def validate(

output = ValidationResult([], [])

if mode is ValidationMode.NONE and not force: #(self.is_valid and not force):
if mode is ValidationMode.NONE and not force: # (self.is_valid and not force):
return output

context: dict[str, Any] = {
Expand Down
46 changes: 34 additions & 12 deletions draftsman/classes/blueprintable.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
# blueprintable.py

from draftsman import __factorio_version_info__
from draftsman.classes.exportable import (
Exportable,
ValidationResult,
attempt_and_reissue,
apply_assignment,
test_replace_me
test_replace_me,
)
from draftsman.constants import ValidationMode
from draftsman.error import DataFormatError, IncorrectBlueprintTypeError
from draftsman.signatures import DraftsmanBaseModel, Icon, normalize_version, uint16, uint64
from draftsman.signatures import (
DraftsmanBaseModel,
Icon,
normalize_version,
uint16,
uint64,
)
from draftsman.data.signals import signal_dict
from draftsman.utils import (
decode_version,
Expand Down Expand Up @@ -97,7 +104,13 @@ def __init__(
)

@reissue_warnings
def load_from_string(self, string: str, validate: Union[ValidationMode, Literal["none", "minimum", "strict", "pedantic"]] = ValidationMode.STRICT):
def load_from_string(
self,
string: str,
validate: Union[
ValidationMode, Literal["none", "minimum", "strict", "pedantic"]
] = ValidationMode.STRICT,
):
"""
Load the :py:class:`.Blueprintable` with the contents of ``string``.
Expand Down Expand Up @@ -212,7 +225,12 @@ def label(self) -> Optional[str]:
@label.setter
def label(self, value: Optional[str]):
test_replace_me(
self, self._root_format, self._root[self._root_item], "label", value, self.validate_assignment
self,
self._root_format,
self._root[self._root_item],
"label",
value,
self.validate_assignment,
)
# if self.validate_assignment:
# result = attempt_and_reissue(
Expand Down Expand Up @@ -258,7 +276,7 @@ def description(self, value: Optional[str]):
@property
def icons(self) -> Optional[list[Icon]]:
"""
The visible icons of the blueprintable, as shown in the icon in
The visible icons of the blueprintable, as shown in the icon in
Factorio's GUI.
Stored as a list of ``Icon`` objects, which are dicts that contain a
Expand Down Expand Up @@ -292,7 +310,12 @@ def icons(self) -> Optional[list[Icon]]:
@icons.setter
def icons(self, value: Union[list[str], list[Icon], None]):
test_replace_me(
self, self._root_format, self._root[self._root_item], "icons", value, self.validate_assignment
self,
self._root_format,
self._root[self._root_item],
"icons",
value,
self.validate_assignment,
)
# if self.validate_assignment:
# print("validated")
Expand Down Expand Up @@ -393,9 +416,9 @@ def index(self) -> Optional[uint16]:
:getter: Gets the index of this blueprintable, or ``None`` if not set.
A blueprintable's index is only generated when exporting with
:py:meth:`.Blueprintable.to_dict`, so ``index`` will still be ``None``
:py:meth:`.Blueprintable.to_dict`, so ``index`` will still be ``None``
until specified otherwise.
:setter: Sets the index of the :py:class:`.Blueprintable`, or removes it
:setter: Sets the index of the :py:class:`.Blueprintable`, or removes it
if set to ``None``.
"""
return self._root.get("index", None)
Expand Down Expand Up @@ -478,20 +501,19 @@ def validate(

output = ValidationResult([], [])

if mode is ValidationMode.NONE and not force: #(self.is_valid and not force):
if mode is ValidationMode.NONE and not force: # (self.is_valid and not force):
return output

context = {
"mode": mode,
"object": self,
"warning_list": [],
"assignment": False,
"environment_version": __factorio_version_info__,
}

try:
self.Format.model_validate(
self._root, strict=True, context=context
)
self.Format.model_validate(self._root, strict=True, context=context)
except ValidationError as e:
output.error_list.append(DataFormatError(e))

Expand Down
Loading

0 comments on commit 04f9f68

Please sign in to comment.