mirror of
https://github.com/OMGeeky/pytiled_parser.git
synced 2025-12-30 08:08:55 +01:00
Merge branch 'master' of github.com:Beefy-Swain/pytiled_parser
# Conflicts: # pytiled_parser/objects.py # pytiled_parser/xml_parser.py
This commit is contained in:
586
.pylintrc
Normal file
586
.pylintrc
Normal file
@@ -0,0 +1,586 @@
|
||||
[MASTER]
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||
# run arbitrary code.
|
||||
extension-pkg-whitelist=
|
||||
|
||||
# Add files or directories to the blacklist. They should be base names, not
|
||||
# paths.
|
||||
ignore=CVS
|
||||
|
||||
# Add files or directories matching the regex patterns to the blacklist. The
|
||||
# regex matches against base names, not paths.
|
||||
ignore-patterns=
|
||||
|
||||
# Python code to execute, usually for sys.path manipulation such as
|
||||
# pygtk.require().
|
||||
#init-hook=
|
||||
|
||||
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
|
||||
# number of processors available to use.
|
||||
jobs=1
|
||||
|
||||
# Control the amount of potential inferred values when inferring a single
|
||||
# object. This can help the performance when dealing with large functions or
|
||||
# complex, nested conditions.
|
||||
limit-inference-results=100
|
||||
|
||||
# List of plugins (as comma separated values of python modules names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=pylint.extensions.bad_builtin,
|
||||
pylint.extensions.check_elif,
|
||||
pylint.extensions.comparetozero,
|
||||
pylint.extensions.docparams,
|
||||
pylint.extensions.docstyle,
|
||||
pylint.extensions.emptystring,
|
||||
pylint.extensions.overlapping_exceptions,
|
||||
pylint.extensions.redefined_variable_type
|
||||
|
||||
accept-no-param-doc=n
|
||||
accept-no-raise-doc=n
|
||||
accept-no-return-doc=n
|
||||
accept-no-yields-doc=n
|
||||
default-docstring-type=google
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
||||
# Specify a configuration file.
|
||||
#rcfile=
|
||||
|
||||
# When enabled, pylint would attempt to guess common misconfiguration and emit
|
||||
# user-friendly hints instead of false-positive error messages.
|
||||
suggestion-mode=yes
|
||||
|
||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||
# active Python interpreter and may run arbitrary code.
|
||||
unsafe-load-any-extension=no
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
|
||||
confidence=
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s). You
|
||||
# can either give multiple identifiers separated by comma (,) or put this
|
||||
# option multiple times (only on the command line, not in the configuration
|
||||
# file where it should appear only once). You can also use "--disable=all" to
|
||||
# disable everything first and then reenable specific checks. For example, if
|
||||
# you want to run only the similarities checker, you can use "--disable=all
|
||||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use "--disable=all --enable=classes
|
||||
# --disable=W".
|
||||
disable=print-statement,
|
||||
parameter-unpacking,
|
||||
unpacking-in-except,
|
||||
old-raise-syntax,
|
||||
backtick,
|
||||
long-suffix,
|
||||
old-ne-operator,
|
||||
old-octal-literal,
|
||||
import-star-module-level,
|
||||
non-ascii-bytes-literal,
|
||||
raw-checker-failed,
|
||||
bad-inline-option,
|
||||
locally-disabled,
|
||||
file-ignored,
|
||||
suppressed-message,
|
||||
useless-suppression,
|
||||
deprecated-pragma,
|
||||
use-symbolic-message-instead,
|
||||
apply-builtin,
|
||||
basestring-builtin,
|
||||
buffer-builtin,
|
||||
cmp-builtin,
|
||||
coerce-builtin,
|
||||
execfile-builtin,
|
||||
file-builtin,
|
||||
long-builtin,
|
||||
raw_input-builtin,
|
||||
reduce-builtin,
|
||||
standarderror-builtin,
|
||||
unicode-builtin,
|
||||
xrange-builtin,
|
||||
coerce-method,
|
||||
delslice-method,
|
||||
getslice-method,
|
||||
setslice-method,
|
||||
no-absolute-import,
|
||||
old-division,
|
||||
dict-iter-method,
|
||||
dict-view-method,
|
||||
next-method-called,
|
||||
metaclass-assignment,
|
||||
indexing-exception,
|
||||
raising-string,
|
||||
reload-builtin,
|
||||
oct-method,
|
||||
hex-method,
|
||||
nonzero-method,
|
||||
cmp-method,
|
||||
input-builtin,
|
||||
round-builtin,
|
||||
intern-builtin,
|
||||
unichr-builtin,
|
||||
map-builtin-not-iterating,
|
||||
zip-builtin-not-iterating,
|
||||
range-builtin-not-iterating,
|
||||
filter-builtin-not-iterating,
|
||||
using-cmp-argument,
|
||||
eq-without-hash,
|
||||
div-method,
|
||||
idiv-method,
|
||||
rdiv-method,
|
||||
exception-message-attribute,
|
||||
invalid-str-codec,
|
||||
sys-max-int,
|
||||
bad-python3-import,
|
||||
deprecated-string-function,
|
||||
deprecated-str-translate-call,
|
||||
deprecated-itertools-function,
|
||||
deprecated-types-field,
|
||||
next-method-defined,
|
||||
dict-items-not-iterating,
|
||||
dict-keys-not-iterating,
|
||||
dict-values-not-iterating,
|
||||
deprecated-operator-function,
|
||||
deprecated-urllib-function,
|
||||
xreadlines-attribute,
|
||||
deprecated-sys-function,
|
||||
exception-escape,
|
||||
comprehension-escape,
|
||||
c0330 #https://github.com/PyCQA/pylint/issues/289
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time (only on the command line, not in the configuration file where
|
||||
# it should appear only once). See also the "--disable" option for examples.
|
||||
enable=c-extension-no-member
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Python expression which should return a note less than 10 (10 is the highest
|
||||
# note). You have access to the variables errors warning, statement which
|
||||
# respectively contain the number of errors / warnings messages and the total
|
||||
# number of statements analyzed. This is used by the global evaluation report
|
||||
# (RP0004).
|
||||
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||
|
||||
# Template used to display messages. This is a python new-style format string
|
||||
# used to format the message information. See doc for all details.
|
||||
#msg-template=
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, json
|
||||
# and msvs (visual studio). You can also give a reporter class, e.g.
|
||||
# mypackage.mymodule.MyReporterClass.
|
||||
output-format=text
|
||||
|
||||
# Tells whether to display a full report or only the messages.
|
||||
reports=no
|
||||
|
||||
# Activate the evaluation score.
|
||||
score=yes
|
||||
|
||||
|
||||
[REFACTORING]
|
||||
|
||||
# Maximum number of nested blocks for function / method body
|
||||
max-nested-blocks=5
|
||||
|
||||
# Complete name of functions that never returns. When checking for
|
||||
# inconsistent-return-statements if a never returning function is called then
|
||||
# it will be considered as an explicit return statement and no message will be
|
||||
# printed.
|
||||
never-returning-functions=sys.exit
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Ignore comments when computing similarities.
|
||||
ignore-comments=yes
|
||||
|
||||
# Ignore docstrings when computing similarities.
|
||||
ignore-docstrings=yes
|
||||
|
||||
# Ignore imports when computing similarities.
|
||||
ignore-imports=no
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=4
|
||||
|
||||
|
||||
[STRING]
|
||||
|
||||
# This flag controls whether the implicit-str-concat-in-sequence should
|
||||
# generate a warning on implicit string concatenation in sequences defined over
|
||||
# several lines.
|
||||
check-str-concat-over-line-jumps=no
|
||||
|
||||
|
||||
[SPELLING]
|
||||
|
||||
# Limits count of emitted suggestions for spelling mistakes.
|
||||
max-spelling-suggestions=4
|
||||
|
||||
# Spelling dictionary name. Available dictionaries: none. To make it working
|
||||
# install python-enchant package..
|
||||
spelling-dict=
|
||||
|
||||
# List of comma separated words that should not be checked.
|
||||
spelling-ignore-words=
|
||||
|
||||
# A path to a file that contains private dictionary; one word per line.
|
||||
spelling-private-dict-file=
|
||||
|
||||
# Tells whether to store unknown words to indicated private dictionary in
|
||||
# --spelling-private-dict-file option instead of raising a message.
|
||||
spelling-store-unknown-words=no
|
||||
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Naming style matching correct argument names.
|
||||
argument-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct argument names. Overrides argument-
|
||||
# naming-style.
|
||||
#argument-rgx=
|
||||
|
||||
# Naming style matching correct attribute names.
|
||||
attr-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct attribute names. Overrides attr-naming-
|
||||
# style.
|
||||
#attr-rgx=
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma.
|
||||
bad-names=foo,
|
||||
bar,
|
||||
baz,
|
||||
toto,
|
||||
tutu,
|
||||
tata
|
||||
|
||||
# Naming style matching correct class attribute names.
|
||||
class-attribute-naming-style=any
|
||||
|
||||
# Regular expression matching correct class attribute names. Overrides class-
|
||||
# attribute-naming-style.
|
||||
#class-attribute-rgx=
|
||||
|
||||
# Naming style matching correct class names.
|
||||
class-naming-style=PascalCase
|
||||
|
||||
# Regular expression matching correct class names. Overrides class-naming-
|
||||
# style.
|
||||
#class-rgx=
|
||||
|
||||
# Naming style matching correct constant names.
|
||||
const-naming-style=UPPER_CASE
|
||||
|
||||
# Regular expression matching correct constant names. Overrides const-naming-
|
||||
# style.
|
||||
#const-rgx=
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=-1
|
||||
|
||||
# Naming style matching correct function names.
|
||||
function-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct function names. Overrides function-
|
||||
# naming-style.
|
||||
#function-rgx=
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma.
|
||||
good-names=i,
|
||||
j,
|
||||
k,
|
||||
x,
|
||||
y,
|
||||
ex,
|
||||
Run,
|
||||
_
|
||||
|
||||
# Include a hint for the correct naming format with invalid-name.
|
||||
include-naming-hint=no
|
||||
|
||||
# Naming style matching correct inline iteration names.
|
||||
inlinevar-naming-style=any
|
||||
|
||||
# Regular expression matching correct inline iteration names. Overrides
|
||||
# inlinevar-naming-style.
|
||||
#inlinevar-rgx=
|
||||
|
||||
# Naming style matching correct method names.
|
||||
method-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct method names. Overrides method-naming-
|
||||
# style.
|
||||
#method-rgx=
|
||||
|
||||
# Naming style matching correct module names.
|
||||
module-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct module names. Overrides module-naming-
|
||||
# style.
|
||||
#module-rgx=
|
||||
|
||||
# Colon-delimited sets of names that determine each other's naming style when
|
||||
# the name regexes allow several styles.
|
||||
name-group=
|
||||
|
||||
# Regular expression which should only match function or class names that do
|
||||
# not require a docstring.
|
||||
no-docstring-rgx=^_
|
||||
|
||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
||||
# to this list to register other decorators that produce valid properties.
|
||||
# These decorators are taken in consideration only for invalid-name.
|
||||
property-classes=abc.abstractproperty
|
||||
|
||||
# Naming style matching correct variable names.
|
||||
variable-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct variable names. Overrides variable-
|
||||
# naming-style.
|
||||
#variable-rgx=
|
||||
|
||||
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=FIXME,
|
||||
XXX,
|
||||
TODO
|
||||
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid defining new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
# Tells whether unused global variables should be treated as a violation.
|
||||
allow-global-unused-variables=yes
|
||||
|
||||
# List of strings which can identify a callback function by name. A callback
|
||||
# name must start or end with one of those strings.
|
||||
callbacks=cb_,
|
||||
_cb
|
||||
|
||||
# A regular expression matching the name of dummy variables (i.e. expected to
|
||||
# not be used).
|
||||
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
|
||||
|
||||
# Argument names that match this expression will be ignored. Default to name
|
||||
# with leading underscore.
|
||||
ignored-argument-names=_.*|^ignored_|^unused_
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# List of qualified module names which can have objects that can redefine
|
||||
# builtins.
|
||||
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
|
||||
|
||||
|
||||
[LOGGING]
|
||||
|
||||
# Format style used to check logging format string. `old` means using %
|
||||
# formatting, while `new` is for `{}` formatting.
|
||||
logging-format-style=old
|
||||
|
||||
# Logging modules to check that the string format arguments are in logging
|
||||
# function parameter format.
|
||||
logging-modules=logging
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
||||
# List of decorators that produce context managers, such as
|
||||
# contextlib.contextmanager. Add to this list to register other decorators that
|
||||
# produce valid context managers.
|
||||
contextmanager-decorators=contextlib.contextmanager
|
||||
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
||||
# expressions are accepted.
|
||||
generated-members=
|
||||
|
||||
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||
ignore-mixin-members=yes
|
||||
|
||||
# Tells whether to warn about missing members when the owner of the attribute
|
||||
# is inferred to be None.
|
||||
ignore-none=yes
|
||||
|
||||
# This flag controls whether pylint should warn about no-member and similar
|
||||
# checks whenever an opaque object is returned when inferring. The inference
|
||||
# can return multiple potential results while evaluating a Python object, but
|
||||
# some branches might not be evaluated, which results in partial inference. In
|
||||
# that case, it might be useful to still emit no-member and other checks for
|
||||
# the rest of the inferred objects.
|
||||
ignore-on-opaque-inference=yes
|
||||
|
||||
# List of class names for which member attributes should not be checked (useful
|
||||
# for classes with dynamically set attributes). This supports the use of
|
||||
# qualified names.
|
||||
ignored-classes=optparse.Values,thread._local,_thread._local
|
||||
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis. It
|
||||
# supports qualified module names, as well as Unix pattern matching.
|
||||
ignored-modules=
|
||||
|
||||
# Show a hint with possible names when a member name was not found. The aspect
|
||||
# of finding the hint is based on edit distance.
|
||||
missing-member-hint=yes
|
||||
|
||||
# The minimum edit distance a name should have in order to be considered a
|
||||
# similar match for a missing member name.
|
||||
missing-member-hint-distance=1
|
||||
|
||||
# The total number of similar names that should be taken in consideration when
|
||||
# showing a hint for a missing member.
|
||||
missing-member-max-choices=1
|
||||
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||
expected-line-ending-format=
|
||||
|
||||
# Regexp for a line that is allowed to be longer than the limit.
|
||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||
|
||||
# Number of spaces of indent required inside a hanging or continued line.
|
||||
indent-after-paren=4
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
indent-string=' '
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=79
|
||||
|
||||
# Maximum number of lines in a module.
|
||||
max-module-lines=1000
|
||||
|
||||
# List of optional constructs for which whitespace checking is disabled. `dict-
|
||||
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
|
||||
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
|
||||
# `empty-line` allows space-only lines.
|
||||
no-space-check=trailing-comma,
|
||||
dict-separator
|
||||
|
||||
# Allow the body of a class to be on the same line as the declaration if body
|
||||
# contains single statement.
|
||||
single-line-class-stmt=no
|
||||
|
||||
# Allow the body of an if to be on the same line as the test if there is no
|
||||
# else.
|
||||
single-line-if-stmt=no
|
||||
|
||||
|
||||
[DESIGN]
|
||||
|
||||
# Maximum number of arguments for function / method.
|
||||
max-args=5
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=7
|
||||
|
||||
# Maximum number of boolean expressions in an if statement.
|
||||
max-bool-expr=5
|
||||
|
||||
# Maximum number of branch for function / method body.
|
||||
max-branches=12
|
||||
|
||||
# Maximum number of locals for function / method body.
|
||||
max-locals=15
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=7
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=20
|
||||
|
||||
# Maximum number of return / yield for function / method body.
|
||||
max-returns=6
|
||||
|
||||
# Maximum number of statements in function / method body.
|
||||
max-statements=50
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=2
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# Allow wildcard imports from modules that define __all__.
|
||||
allow-wildcard-with-all=no
|
||||
|
||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
||||
# 3 compatible code, which means that the block might have code that exists
|
||||
# only in one or another interpreter, leading to false positives when analysed.
|
||||
analyse-fallback-blocks=no
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma.
|
||||
deprecated-modules=optparse,tkinter.tix
|
||||
|
||||
# Create a graph of external dependencies in the given file (report RP0402 must
|
||||
# not be disabled).
|
||||
ext-import-graph=
|
||||
|
||||
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||
# given file (report RP0402 must not be disabled).
|
||||
import-graph=
|
||||
|
||||
# Create a graph of internal dependencies in the given file (report RP0402 must
|
||||
# not be disabled).
|
||||
int-import-graph=
|
||||
|
||||
# Force import order to recognize a module as part of the standard
|
||||
# compatibility libraries.
|
||||
known-standard-library=
|
||||
|
||||
# Force import order to recognize a module as part of a third party library.
|
||||
known-third-party=enchant
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,
|
||||
__new__,
|
||||
setUp
|
||||
|
||||
# List of member names, which should be excluded from the protected access
|
||||
# warning.
|
||||
exclude-protected=_asdict,
|
||||
_fields,
|
||||
_replace,
|
||||
_source,
|
||||
_make
|
||||
|
||||
# List of valid names for the first argument in a class method.
|
||||
valid-classmethod-first-arg=cls
|
||||
|
||||
# List of valid names for the first argument in a metaclass class method.
|
||||
valid-metaclass-classmethod-first-arg=cls
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when being caught. Defaults to
|
||||
# "BaseException, Exception".
|
||||
overgeneral-exceptions=BaseException,
|
||||
Exception
|
||||
@@ -1,3 +0,0 @@
|
||||
[style]
|
||||
based_on_style = google
|
||||
column_limit = 78
|
||||
@@ -1,2 +1,4 @@
|
||||
# pytiled_parser
|
||||
Python Library for parsing Tiled Map Editor maps.
|
||||
|
||||
NOT READY FOR USE
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
-e .
|
||||
black
|
||||
pytest
|
||||
sphinx
|
||||
coverage
|
||||
|
||||
2
make.sh
2
make.sh
@@ -11,5 +11,5 @@ do
|
||||
pip3 install $file
|
||||
done
|
||||
# sphinx-build -b html doc doc/build/html
|
||||
coverage run --source arcade setup.py test
|
||||
coverage run --source pytiled_parser setup.py test
|
||||
coverage report -m
|
||||
|
||||
21
mypy.ini
Normal file
21
mypy.ini
Normal file
@@ -0,0 +1,21 @@
|
||||
# Global options:
|
||||
|
||||
[mypy]
|
||||
python_version = 3.6
|
||||
warn_unused_configs = False
|
||||
disallow_any_unimported = True
|
||||
disallow_any_expr = True
|
||||
disallow_any_decorated = True
|
||||
disallow_any_explicit = True
|
||||
disallow_any_generics = True
|
||||
disallow_subclassing_any = True
|
||||
disallow_untyped_calls = True
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
check_untyped_defs = True
|
||||
disallow_untyped_decorators = True
|
||||
warn_return_any = True
|
||||
|
||||
# Per-module options:
|
||||
[mypy-tests.*]
|
||||
ignore_errors = True
|
||||
@@ -1,4 +1,4 @@
|
||||
from . import utilities
|
||||
from . import objects
|
||||
"""init for pytiled_parser"""
|
||||
|
||||
from . import objects, utilities
|
||||
from .xml_parser import parse_tile_map
|
||||
|
||||
@@ -1,27 +1,22 @@
|
||||
"""
|
||||
pytiled_parser objects for Tiled maps.
|
||||
"""pytiled_parser objects for Tiled maps.
|
||||
"""
|
||||
|
||||
import dataclasses
|
||||
import functools
|
||||
import re
|
||||
# pylint: disable=too-few-public-methods
|
||||
|
||||
from collections import OrderedDict
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, NamedTuple, Optional, Union
|
||||
|
||||
import xml.etree.ElementTree as etree
|
||||
|
||||
from typing import NamedTuple, Union, Optional, List, Dict
|
||||
import attr
|
||||
|
||||
|
||||
class Color(NamedTuple):
|
||||
"""Color object.
|
||||
|
||||
Attributes:
|
||||
:red (int): Red, between 1 and 255.
|
||||
:green (int): Green, between 1 and 255.
|
||||
:blue (int): Blue, between 1 and 255.
|
||||
:alpha (int): Alpha, between 1 and 255.
|
||||
red (int): Red, between 1 and 255.
|
||||
green (int): Green, between 1 and 255.
|
||||
blue (int): Blue, between 1 and 255.
|
||||
alpha (int): Alpha, between 1 and 255.
|
||||
"""
|
||||
|
||||
red: int
|
||||
@@ -66,24 +61,22 @@ class Size(NamedTuple):
|
||||
height: Union[int, float]
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class Template:
|
||||
"""
|
||||
FIXME TODO
|
||||
"""
|
||||
"""FIXME TODO"""
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@attr.s(auto_attribs=True)
|
||||
class Chunk:
|
||||
"""
|
||||
Chunk object for infinite maps.
|
||||
"""Chunk object for infinite maps.
|
||||
|
||||
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#chunk
|
||||
|
||||
Attributes:
|
||||
:location (OrderedPair): Location of chunk in tiles.
|
||||
:width (int): The width of the chunk in tiles.
|
||||
:height (int): The height of the chunk in tiles.
|
||||
:layer_data (List[List(int)]): The global tile IDs in chunky
|
||||
location (OrderedPair): Location of chunk in tiles.
|
||||
width (int): The width of the chunk in tiles.
|
||||
height (int): The height of the chunk in tiles.
|
||||
layer_data (List[List(int)]): The global tile IDs in chunky
|
||||
according to row.
|
||||
"""
|
||||
|
||||
@@ -93,34 +86,32 @@ class Chunk:
|
||||
chunk_data: List[List[int]]
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@attr.s(auto_attribs=True)
|
||||
class Image:
|
||||
"""
|
||||
Image object.
|
||||
"""Image object.
|
||||
|
||||
This module does not support embedded data in image elements.
|
||||
|
||||
Attributes:
|
||||
:source (Optional[str]): The reference to the tileset image file.
|
||||
source (Optional[str]): The reference to the tileset image file.
|
||||
Not that this is a relative path compared to FIXME
|
||||
:trans (Optional[Color]): Defines a specific color that is treated
|
||||
trans (Optional[Color]): Defines a specific color that is treated
|
||||
as transparent.
|
||||
:width (Optional[str]): The image width in pixels
|
||||
width (Optional[str]): The image width in pixels
|
||||
(optional, used for tile index correction when the image changes).
|
||||
:height (Optional[str]): The image height in pixels (optional).
|
||||
height (Optional[str]): The image height in pixels (optional).
|
||||
"""
|
||||
|
||||
source: str
|
||||
size: Optional[Size] = None
|
||||
trans: Optional[Color] = None
|
||||
trans: Optional[str] = None
|
||||
|
||||
|
||||
Properties = Dict[str, Union[int, float, Color, Path, str]]
|
||||
|
||||
|
||||
class Grid(NamedTuple):
|
||||
"""
|
||||
Contains info for isometric maps.
|
||||
"""Contains info for isometric maps.
|
||||
|
||||
This element is only used in case of isometric orientation, and
|
||||
determines how tile overlays for terrain and collision information
|
||||
@@ -133,12 +124,11 @@ class Grid(NamedTuple):
|
||||
|
||||
|
||||
class Terrain(NamedTuple):
|
||||
"""
|
||||
Terrain object.
|
||||
"""Terrain object.
|
||||
|
||||
Args:
|
||||
:name (str): The name of the terrain type.
|
||||
:tile (int): The local tile-id of the tile that represents the
|
||||
name (str): The name of the terrain type.
|
||||
tile (int): The local tile-id of the tile that represents the
|
||||
terrain visually.
|
||||
"""
|
||||
|
||||
@@ -147,15 +137,14 @@ class Terrain(NamedTuple):
|
||||
|
||||
|
||||
class Frame(NamedTuple):
|
||||
"""
|
||||
Animation Frame object.
|
||||
"""Animation Frame object.
|
||||
|
||||
This is only used as a part of an animation for Tile objects.
|
||||
|
||||
Args:
|
||||
:tile_id (int): The local ID of a tile within the parent tile set
|
||||
tile_id (int): The local ID of a tile within the parent tile set
|
||||
object.
|
||||
:duration (int): How long in milliseconds this frame should be
|
||||
duration (int): How long in milliseconds this frame should be
|
||||
displayed before advancing to the next frame.
|
||||
"""
|
||||
|
||||
@@ -163,19 +152,18 @@ class Frame(NamedTuple):
|
||||
duration: int
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@attr.s(auto_attribs=True)
|
||||
class TileTerrain:
|
||||
"""
|
||||
Defines each corner of a tile by Terrain index in
|
||||
"""Defines each corner of a tile by Terrain index in
|
||||
'TileSet.terrain_types'.
|
||||
|
||||
Defaults to 'None'. 'None' means that corner has no terrain.
|
||||
|
||||
Attributes:
|
||||
:top_left (Optional[int]): Top left terrain type.
|
||||
:top_right (Optional[int]): Top right terrain type.
|
||||
:bottom_left (Optional[int]): Bottom left terrain type.
|
||||
:bottom_right (Optional[int]): Bottom right terrain type.
|
||||
top_left (Optional[int]): Top left terrain type.
|
||||
top_right (Optional[int]): Top right terrain type.
|
||||
bottom_left (Optional[int]): Bottom left terrain type.
|
||||
bottom_right (Optional[int]): Bottom right terrain type.
|
||||
"""
|
||||
|
||||
top_left: Optional[int] = None
|
||||
@@ -184,7 +172,7 @@ class TileTerrain:
|
||||
bottom_right: Optional[int] = None
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
class Layer:
|
||||
"""Class that all layers inherret from.
|
||||
|
||||
@@ -207,7 +195,7 @@ class Layer:
|
||||
for more info.
|
||||
"""
|
||||
|
||||
id: int
|
||||
id_: int
|
||||
name: str
|
||||
|
||||
offset: Optional[OrderedPair]
|
||||
@@ -216,15 +204,14 @@ class Layer:
|
||||
|
||||
|
||||
LayerData = Union[List[List[int]], List[Chunk]]
|
||||
"""
|
||||
The tile data for one layer.
|
||||
"""The tile data for one layer.
|
||||
|
||||
Either a 2 dimensional array of integers representing the global tile IDs
|
||||
for a map layer, or a lists of chunks for an infinite map layer.
|
||||
"""
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
class TileLayer(Layer):
|
||||
"""Tile map layer containing tiles.
|
||||
|
||||
@@ -242,15 +229,34 @@ class TileLayer(Layer):
|
||||
data: LayerData
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class _TiledObjectBase:
|
||||
id: int
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
class TiledObject:
|
||||
"""TiledObject object.
|
||||
|
||||
See:
|
||||
https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#object
|
||||
|
||||
Args:
|
||||
id_ (int): Unique ID of the object. Each object that is placed on a
|
||||
map gets a unique id. Even if an object was deleted, no object
|
||||
gets the same ID.
|
||||
gid (Optional[int]): Global tiled object ID
|
||||
location (OrderedPair): The location of the object in pixels.
|
||||
size (Size): The width of the object in pixels
|
||||
(default: (0, 0)).
|
||||
rotation (int): The rotation of the object in degrees clockwise
|
||||
(default: 0).
|
||||
opacity (int): The opacity of the object. (default: 255)
|
||||
name (Optional[str]): The name of the object.
|
||||
type (Optional[str]): The type of the object.
|
||||
properties (Properties): The properties of the TiledObject.
|
||||
template Optional[Template]: A reference to a Template object FIXME
|
||||
"""
|
||||
|
||||
id_: int
|
||||
gid: Optional[int] = None
|
||||
|
||||
location: OrderedPair
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class _TiledObjectDefaults:
|
||||
gid: int = None
|
||||
size: Size = Size(0, 0)
|
||||
rotation: int = 0
|
||||
opacity: float = 1
|
||||
@@ -262,36 +268,9 @@ class _TiledObjectDefaults:
|
||||
template: Optional[Template] = None
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class TiledObject(_TiledObjectDefaults, _TiledObjectBase):
|
||||
"""
|
||||
TiledObject object.
|
||||
|
||||
See:
|
||||
https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#object
|
||||
|
||||
Args:
|
||||
:id (int): Unique ID of the object. Each object that is placed on a
|
||||
map gets a unique id. Even if an object was deleted, no object
|
||||
gets the same ID.
|
||||
:location (OrderedPair): The location of the object in pixels.
|
||||
:size (Size): The width of the object in pixels
|
||||
(default: (0, 0)).
|
||||
:rotation (int): The rotation of the object in degrees clockwise
|
||||
(default: 0).
|
||||
:opacity (int): The opacity of the object. (default: 255)
|
||||
:name (Optional[str]): The name of the object.
|
||||
:type (Optional[str]): The type of the object.
|
||||
:properties (Properties): The properties of the TiledObject.
|
||||
:template Optional[Template]: A reference to a Template object
|
||||
FIXME
|
||||
"""
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@attr.s()
|
||||
class RectangleObject(TiledObject):
|
||||
"""
|
||||
Rectangle shape defined by a point, width, and height.
|
||||
"""Rectangle shape defined by a point, width, and height.
|
||||
|
||||
See: https://doc.mapeditor.org/en/stable/manual/objects/#insert-rectangle
|
||||
(objects in tiled are rectangles by default, so there is no specific
|
||||
@@ -299,79 +278,87 @@ class RectangleObject(TiledObject):
|
||||
"""
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@attr.s()
|
||||
class ElipseObject(TiledObject):
|
||||
"""
|
||||
Elipse shape defined by a point, width, and height.
|
||||
"""Elipse shape defined by a point, width, and height.
|
||||
|
||||
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#ellipse
|
||||
"""
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@attr.s()
|
||||
class PointObject(TiledObject):
|
||||
"""
|
||||
Point defined by a point (x,y).
|
||||
"""Point defined by a point (x,y).
|
||||
|
||||
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#point
|
||||
"""
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class _TileImageObjectBase(_TiledObjectBase):
|
||||
gid: int
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class TileImageObject(TiledObject, _TileImageObjectBase):
|
||||
"""
|
||||
Polygon shape defined by a set of connections between points.
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
class TileImageObject(TiledObject):
|
||||
"""Polygon shape defined by a set of connections between points.
|
||||
|
||||
See: https://doc.mapeditor.org/en/stable/manual/objects/#insert-tile
|
||||
|
||||
Attributes:
|
||||
:gid (int): Refference to a global tile id.
|
||||
gid (int): Refference to a global tile id.
|
||||
"""
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class _PointsObjectBase(_TiledObjectBase):
|
||||
points: List[OrderedPair]
|
||||
gid: int
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class PolygonObject(TiledObject, _PointsObjectBase):
|
||||
"""
|
||||
Polygon shape defined by a set of connections between points.
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
class PolygonObject(TiledObject):
|
||||
"""Polygon shape defined by a set of connections between points.
|
||||
|
||||
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#polygon
|
||||
|
||||
Attributes:
|
||||
:points (List[OrderedPair])
|
||||
points (List[OrderedPair]): FIXME
|
||||
"""
|
||||
|
||||
points: List[OrderedPair]
|
||||
|
||||
@dataclasses.dataclass
|
||||
class PolylineObject(TiledObject, _PointsObjectBase):
|
||||
"""
|
||||
Polyline defined by a set of connections between points.
|
||||
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
class PolylineObject(TiledObject):
|
||||
"""Polyline defined by a set of connections between points.
|
||||
|
||||
See:
|
||||
https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#polyline
|
||||
|
||||
Attributes:
|
||||
:points (List[Tuple[int, int]]): List of coordinates relative to \
|
||||
points (List[Tuple[int, int]]): List of coordinates relative to \
|
||||
the location of the object.
|
||||
"""
|
||||
|
||||
points: List[OrderedPair]
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
class TextObject(TiledObject):
|
||||
"""Text object with associated settings.
|
||||
|
||||
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#text
|
||||
and https://doc.mapeditor.org/en/stable/manual/objects/#insert-text
|
||||
|
||||
Attributes:
|
||||
font_family (str): The font family used (default: "sans-serif")
|
||||
font_size (int): The size of the font in pixels. (default: 16)
|
||||
wrap (bool): Whether word wrapping is enabled. (default: False)
|
||||
color (Color): Color of the text. (default: #000000)
|
||||
bold (bool): Whether the font is bold. (default: False)
|
||||
italic (bool): Whether the font is italic. (default: False)
|
||||
underline (bool): Whether the text is underlined. (default: False)
|
||||
strike_out (bool): Whether the text is striked-out. (default: False)
|
||||
kerning (bool): Whether kerning should be used while rendering the \
|
||||
text. (default: False)
|
||||
horizontal_align (str): Horizontal alignment of the text \
|
||||
(default: "left")
|
||||
vertical_align (str): Vertical alignment of the text (defalt: "top")
|
||||
"""
|
||||
|
||||
@dataclasses.dataclass
|
||||
class _TextObjectBase(_TiledObjectBase):
|
||||
text: str
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class _TextObjectDefaults(_TiledObjectDefaults):
|
||||
font_family: str = "sans-serif"
|
||||
font_size: int = 16
|
||||
wrap: bool = False
|
||||
@@ -385,38 +372,12 @@ class _TextObjectDefaults(_TiledObjectDefaults):
|
||||
vertical_align: str = "top"
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class TextObject(TiledObject, _TextObjectDefaults, _TextObjectBase):
|
||||
"""
|
||||
Text object with associated settings.
|
||||
|
||||
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#text
|
||||
and https://doc.mapeditor.org/en/stable/manual/objects/#insert-text
|
||||
|
||||
Attributes:
|
||||
:font_family (str): The font family used (default: “sans-serif”)
|
||||
:font_size (int): The size of the font in pixels. (default: 16)
|
||||
:wrap (bool): Whether word wrapping is enabled. (default: False)
|
||||
:color (Color): Color of the text. (default: #000000)
|
||||
:bold (bool): Whether the font is bold. (default: False)
|
||||
:italic (bool): Whether the font is italic. (default: False)
|
||||
:underline (bool): Whether the text is underlined. (default: False)
|
||||
:strike_out (bool): Whether the text is striked-out. (default: False)
|
||||
:kerning (bool): Whether kerning should be used while rendering the \
|
||||
text. (default: False)
|
||||
:horizontal_align (str): Horizontal alignment of the text \
|
||||
(default: "left")
|
||||
:vertical_align (str): Vertical alignment of the text (defalt: "top")
|
||||
"""
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
class ObjectLayer(Layer):
|
||||
"""
|
||||
TiledObject Group Object.
|
||||
"""TiledObject Group Object.
|
||||
|
||||
The object group is in fact a map layer, and is hence called \
|
||||
“object layer” in Tiled.
|
||||
"object layer" in Tiled.
|
||||
|
||||
See:
|
||||
https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#objectgroup
|
||||
@@ -439,10 +400,9 @@ class ObjectLayer(Layer):
|
||||
draw_order: Optional[str] = "topdown"
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
class LayerGroup(Layer):
|
||||
"""
|
||||
Layer Group.
|
||||
"""Layer Group.
|
||||
|
||||
A LayerGroup can be thought of as a layer that contains layers
|
||||
(potentially including other LayerGroups).
|
||||
@@ -452,33 +412,64 @@ class LayerGroup(Layer):
|
||||
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#group
|
||||
|
||||
Attributes:
|
||||
|
||||
Layers (Optional[List[Union["LayerGroup", Layer, ObjectLayer]]]):
|
||||
Layers in group.
|
||||
"""
|
||||
|
||||
layers: Optional[List[Union["LayerGroup", Layer, ObjectLayer]]]
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Hitbox:
|
||||
""" Hitbox
|
||||
"""
|
||||
id: int
|
||||
x: int
|
||||
y: int
|
||||
width: int
|
||||
height: int
|
||||
hitbox_type: str
|
||||
points: str
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class TileSet:
|
||||
pass
|
||||
"""Object for storing a TSX with all associated collision data.
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Tile:
|
||||
Args:
|
||||
name (str): The name of this tileset.
|
||||
max_tile_size (Size): The maximum size of a tile in this
|
||||
tile set in pixels.
|
||||
spacing (int): The spacing in pixels between the tiles in this
|
||||
tileset (applies to the tileset image).
|
||||
margin (int): The margin around the tiles in this tileset
|
||||
(applies to the tileset image).
|
||||
tile_count (int): The number of tiles in this tileset.
|
||||
columns (int): The number of tile columns in the tileset.
|
||||
For image collection tilesets it is editable and is used when
|
||||
displaying the tileset.
|
||||
grid (Grid): Only used in case of isometric orientation, and
|
||||
determines how tile overlays for terrain and collision information
|
||||
are rendered.
|
||||
tileoffset (Optional[OrderedPair]): Used to specify an offset in
|
||||
pixels when drawing a tile from the tileset. When not present, no
|
||||
offset is applied.
|
||||
image (Image): Used for spritesheet tile sets.
|
||||
terrain_types (Dict[str, int]): List of of terrain types which
|
||||
can be referenced from the terrain attribute of the tile object.
|
||||
Ordered according to the terrain element's appearance in the TSX
|
||||
file.
|
||||
tiles (Optional[Dict[int, Tile]]): Dict of Tile objects by Tile.id.
|
||||
"""
|
||||
Individual tile object.
|
||||
|
||||
name: str
|
||||
max_tile_size: Size
|
||||
|
||||
spacing: Optional[int] = None
|
||||
margin: Optional[int] = None
|
||||
tile_count: Optional[int] = None
|
||||
columns: Optional[int] = None
|
||||
tile_offset: Optional[OrderedPair] = None
|
||||
grid: Optional[Grid] = None
|
||||
properties: Optional[Properties] = None
|
||||
image: Optional[Image] = None
|
||||
terrain_types: Optional[List[Terrain]] = None
|
||||
tiles: Optional[Dict[int, "Tile"]] = None
|
||||
|
||||
|
||||
TileSetDict = Dict[int, TileSet]
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
class Tile:
|
||||
"""Individual tile object.
|
||||
|
||||
Args:
|
||||
:id (int): The local tile ID within its tileset.
|
||||
@@ -489,68 +480,18 @@ class Tile:
|
||||
associated with it.
|
||||
"""
|
||||
|
||||
id: int
|
||||
type: Optional[str]
|
||||
terrain: Optional[TileTerrain]
|
||||
animation: Optional[List[Frame]]
|
||||
image: Optional[Image]
|
||||
hitboxes: Optional[List[Hitbox]]
|
||||
properties: Optional[List[Property]]
|
||||
tileset: Optional[TileSet]
|
||||
id_: int
|
||||
type_: Optional[str] = None
|
||||
terrain: Optional[TileTerrain] = None
|
||||
animation: Optional[List[Frame]] = None
|
||||
image: Optional[Image] = None
|
||||
properties: Optional[List[Property]] = None
|
||||
tileset: Optional[TileSet] = None
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class TileSet:
|
||||
"""
|
||||
Object for storing a TSX with all associated collision data.
|
||||
|
||||
Args:
|
||||
:name (str): The name of this tileset.
|
||||
:max_tile_size (Size): The maximum size of a tile in this
|
||||
tile set in pixels.
|
||||
:spacing (int): The spacing in pixels between the tiles in this
|
||||
tileset (applies to the tileset image).
|
||||
:margin (int): The margin around the tiles in this tileset
|
||||
(applies to the tileset image).
|
||||
:tile_count (int): The number of tiles in this tileset.
|
||||
:columns (int): The number of tile columns in the tileset.
|
||||
For image collection tilesets it is editable and is used when
|
||||
displaying the tileset.
|
||||
:grid (Grid): Only used in case of isometric orientation, and
|
||||
determines how tile overlays for terrain and collision information
|
||||
are rendered.
|
||||
:tileoffset (Optional[OrderedPair]): Used to specify an offset in
|
||||
pixels when drawing a tile from the tileset. When not present, no
|
||||
offset is applied.
|
||||
:image (Image): Used for spritesheet tile sets.
|
||||
:terrain_types (Dict[str, int]): List of of terrain types which
|
||||
can be referenced from the terrain attribute of the tile object.
|
||||
Ordered according to the terrain element's appearance in the TSX
|
||||
file.
|
||||
:tiles (Optional[Dict[int, Tile]]): Dict of Tile objects by Tile.id.
|
||||
"""
|
||||
|
||||
name: str
|
||||
max_tile_size: Size
|
||||
spacing: Optional[int]
|
||||
margin: Optional[int]
|
||||
tile_count: Optional[int]
|
||||
columns: Optional[int]
|
||||
tile_offset: Optional[OrderedPair]
|
||||
grid: Optional[Grid]
|
||||
properties: Optional[Properties]
|
||||
image: Optional[Image]
|
||||
terrain_types: Optional[List[Terrain]]
|
||||
tiles: Optional[Dict[int, Tile]]
|
||||
|
||||
|
||||
TileSetDict = Dict[int, TileSet]
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@attr.s(auto_attribs=True)
|
||||
class TileMap:
|
||||
"""
|
||||
Object for storing a TMX with all associated layers and properties.
|
||||
"""Object for storing a TMX with all associated layers and properties.
|
||||
|
||||
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#map
|
||||
|
||||
@@ -560,8 +501,8 @@ class TileMap:
|
||||
:version (str): The TMX format version.
|
||||
:tiledversion (str): The Tiled version used to save the file. May
|
||||
be a date (for snapshot builds).
|
||||
:orientation (str): Map orientation. Tiled supports “orthogonal”,
|
||||
“isometric”, “staggered” and “hexagonal”
|
||||
:orientation (str): Map orientation. Tiled supports "orthogonal",
|
||||
"isometric", "staggered" and "hexagonal"
|
||||
:renderorder (str): The order in which tiles on tile layers are
|
||||
rendered. Valid values are right-down, right-up, left-down and
|
||||
left-up. In all cases, the map is drawn row-by-row. (only
|
||||
@@ -570,12 +511,12 @@ class TileMap:
|
||||
:tile_size (Size): The width of a tile.
|
||||
:infinite (bool): If the map is infinite or not.
|
||||
:hexsidelength (int): Only for hexagonal maps. Determines the width or
|
||||
height (depending on the staggered axis) of the tile’s edge, in
|
||||
height (depending on the staggered axis) of the tile's edge, in
|
||||
pixels.
|
||||
:stagger_axis (str): For staggered and hexagonal maps, determines
|
||||
which axis (“x” or “y”) is staggered.
|
||||
which axis ("x" or "y") is staggered.
|
||||
:staggerindex (str): For staggered and hexagonal maps, determines
|
||||
whether the “even” or “odd” indexes along the staggered axis are
|
||||
whether the "even" or "odd" indexes along the staggered axis are
|
||||
shifted.
|
||||
:backgroundcolor (##FIXME##): The background color of the map.
|
||||
:nextlayerid (int): Stores the next available ID for new layers.
|
||||
@@ -595,7 +536,7 @@ class TileMap:
|
||||
map_size: Size
|
||||
tile_size: Size
|
||||
infinite: bool
|
||||
next_layer_id: int
|
||||
next_layer_id: Optional[int]
|
||||
next_object_id: int
|
||||
|
||||
tile_sets: TileSetDict
|
||||
@@ -607,22 +548,3 @@ class TileMap:
|
||||
background_color: Optional[str] = None
|
||||
|
||||
properties: Optional[Properties] = None
|
||||
|
||||
|
||||
"""
|
||||
[22:16] <__m4ch1n3__> i would "[i for i in int_list if i < littler_then_value]"
|
||||
[22:16] <__m4ch1n3__> it returns a list of integers below "littler_then_value"
|
||||
[22:17] <__m4ch1n3__> !py3 [i for i in [1,2,3,4,1,2,3,4] if i < 3]
|
||||
[22:17] <codebot> __m4ch1n3__: [1, 2, 1, 2]
|
||||
[22:17] <__m4ch1n3__> !py3 [i for i in [1,2,3,4,1,2,3,4] if i < 4]
|
||||
[22:17] <codebot> __m4ch1n3__: [1, 2, 3, 1, 2, 3]
|
||||
[22:22] <__m4ch1n3__> !py3 max([i for i in [1,2,3,4,1,2,3,4] if i < 4])
|
||||
[22:22] <codebot> __m4ch1n3__: 3
|
||||
[22:22] <__m4ch1n3__> max(...) would return the maximum of resulting list
|
||||
[22:23] <__m4ch1n3__> !py3 max([i for i in [1, 10, 100] if i < 20])
|
||||
[22:23] <codebot> __m4ch1n3__: 10
|
||||
[22:23] <__m4ch1n3__> !py3 max([i for i in [1, 10, 100] if i < 242])
|
||||
[22:23] <codebot> __m4ch1n3__: 100
|
||||
[22:23] == markb1 [~mbiggers@45.36.35.206] has quit [Ping timeout: 245 seconds]
|
||||
[22:23] <__m4ch1n3__> !py3 max(i for i in [1, 10, 100] if i < 242)
|
||||
"""
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
"""Helper unitilies for pytiled_parser."""
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
import pytiled_parser.objects as objects
|
||||
|
||||
|
||||
def parse_color(color: str) -> objects.Color:
|
||||
"""
|
||||
Converts the color formats that Tiled uses into ones that Arcade accepts.
|
||||
"""Convert Tiled color format into Arcade's.
|
||||
|
||||
Args:
|
||||
color (str): Tiled formatted color string.
|
||||
|
||||
Returns:
|
||||
:Color: Color object in the format that Arcade understands.
|
||||
"""
|
||||
# strip initial '#' character
|
||||
if not len(color) % 2 == 0:
|
||||
# the actual part we care about is always an even number
|
||||
if len(color) % 2:
|
||||
# strip initial '#' character
|
||||
color = color[1:]
|
||||
|
||||
if len(color) == 6:
|
||||
@@ -27,8 +34,25 @@ def parse_color(color: str) -> objects.Color:
|
||||
return objects.Color(red, green, blue, alpha)
|
||||
|
||||
|
||||
def get_tile_by_gid(tile_sets: objects.TileSetDict, gid: int) -> objects.Tile:
|
||||
"""Gets Tile from a global tile ID.
|
||||
def _get_tile_set_key(gid: int, tile_set_keys: List[int]) -> int:
|
||||
"""Gets tile set key given a tile GID.
|
||||
|
||||
Args:
|
||||
gid (int): Global ID of the tile.
|
||||
tile_set_keys (List[int]): List of tile set keys.
|
||||
|
||||
Returns:
|
||||
int: The key of the tile set that contains the tile for the GID.
|
||||
"""
|
||||
|
||||
# credit to __m4ch1n3__ on ##learnpython for this idea
|
||||
return max([key for key in tile_set_keys if key <= gid])
|
||||
|
||||
|
||||
def get_tile_by_gid(
|
||||
gid: int, tile_sets: objects.TileSetDict
|
||||
) -> Optional[objects.Tile]:
|
||||
"""Gets correct Tile for a given global ID.
|
||||
|
||||
Args:
|
||||
tile_sets (objects.TileSetDict): TileSetDict from TileMap.
|
||||
@@ -36,10 +60,13 @@ def get_tile_by_gid(tile_sets: objects.TileSetDict, gid: int) -> objects.Tile:
|
||||
|
||||
Returns:
|
||||
objects.Tile: The Tile object reffered to by the global tile ID.
|
||||
None: If there is no objects.Tile object in the tile_set.tiles dict
|
||||
for the associated gid.
|
||||
"""
|
||||
for tileset_key, tileset in tile_sets.items():
|
||||
for tile_key, tile in tileset.tiles.items():
|
||||
tile_gid = tile.id + tileset_key
|
||||
if tile_gid == gid:
|
||||
return tile
|
||||
tile_set_key = _get_tile_set_key(gid, list(tile_sets.keys()))
|
||||
tile_set = tile_sets[tile_set_key]
|
||||
|
||||
if tile_set.tiles is not None:
|
||||
return tile_set.tiles.get(gid - tile_set_key)
|
||||
|
||||
return None
|
||||
|
||||
@@ -1,21 +1,33 @@
|
||||
import functools
|
||||
"""Handle XML parsing for TMX files"""
|
||||
|
||||
import base64
|
||||
import functools
|
||||
import gzip
|
||||
import re
|
||||
import zlib
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from typing import Callable, Dict, List, Optional, Tuple, Union
|
||||
import xml.etree.ElementTree as etree
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
from typing import Callable, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import pytiled_parser.objects as objects
|
||||
import pytiled_parser.utilities as utilities
|
||||
|
||||
|
||||
def _decode_base64_data(
|
||||
data_text: str, layer_width: int, compression: Optional[str] = None
|
||||
) -> List[List[int]]:
|
||||
"""Decode base64 data.
|
||||
|
||||
Args:
|
||||
data_text (str): Data to be decoded.
|
||||
layer_width (int): Width of each layer in tiles.
|
||||
compression (Optional[str]): The type of compression for the data.
|
||||
|
||||
Raises:
|
||||
ValueError: If compression type is unsupported.
|
||||
|
||||
Returns:
|
||||
:List[List[int]]: Tile grid.
|
||||
"""
|
||||
tile_grid: List[List[int]] = [[]]
|
||||
|
||||
unencoded_data = base64.b64decode(data_text)
|
||||
@@ -36,12 +48,12 @@ def _decode_base64_data(
|
||||
for byte in unzipped_data:
|
||||
int_value += byte << (byte_count * 8)
|
||||
byte_count += 1
|
||||
if byte_count % 4 == 0:
|
||||
if not byte_count % 4:
|
||||
byte_count = 0
|
||||
int_count += 1
|
||||
tile_grid[row_count].append(int_value)
|
||||
int_value = 0
|
||||
if int_count % layer_width == 0:
|
||||
if not int_count % layer_width:
|
||||
row_count += 1
|
||||
tile_grid.append([])
|
||||
|
||||
@@ -52,7 +64,11 @@ def _decode_base64_data(
|
||||
def _decode_csv_data(data_text: str) -> List[List[int]]:
|
||||
"""Decodes csv encoded layer data.
|
||||
|
||||
Credit:
|
||||
Args:
|
||||
data_text (str): Data to be decoded.
|
||||
|
||||
Returns:
|
||||
List[List[int]]: Tile grid.
|
||||
"""
|
||||
tile_grid = []
|
||||
lines: List[str] = data_text.split("\n")
|
||||
@@ -73,29 +89,38 @@ def _decode_data(
|
||||
element: etree.Element,
|
||||
layer_width: int,
|
||||
encoding: str,
|
||||
compression: Optional[str],
|
||||
compression: Optional[str] = None,
|
||||
) -> List[List[int]]:
|
||||
"""Decodes data or chunk data.
|
||||
|
||||
Args:
|
||||
:element (Element): Element to have text decoded.
|
||||
:layer_width (int): Number of tiles per column in this layer. Used
|
||||
element (Element): Element to have text decoded.
|
||||
layer_width (int): Number of tiles per column in this layer. Used
|
||||
for determining when to cut off a row when decoding base64
|
||||
encoding layers.
|
||||
:encoding (str): Encoding format of the layer data.
|
||||
:compression (str): Compression format of the layer data.
|
||||
encoding (str): Encoding format of the layer data.
|
||||
compression (str): Compression format of the layer data.
|
||||
|
||||
Raises:
|
||||
ValueError: Encoding type is not supported.
|
||||
ValueError: Compression is not supported for this encoding type.
|
||||
ValueError: Compression type is not supported
|
||||
AttributeError: No data in element.
|
||||
|
||||
Returns:
|
||||
List[List[int]]: Tile grid.
|
||||
"""
|
||||
# etree.Element.text comes with an appended and a prepended '\n'
|
||||
supported_encodings = ["base64", "csv"]
|
||||
if encoding not in supported_encodings:
|
||||
raise ValueError("{encoding} is not a valid encoding")
|
||||
raise ValueError(f"{encoding} is not a supported encoding")
|
||||
|
||||
supported_compression = [None, "gzip", "zlib"]
|
||||
if compression is not None:
|
||||
if encoding != "base64":
|
||||
raise ValueError("{encoding} does not support compression")
|
||||
raise ValueError(f"{encoding} does not support compression")
|
||||
if compression not in supported_compression:
|
||||
raise ValueError("{compression} is not a valid compression type")
|
||||
raise ValueError(f"{compression} is not a supported compression")
|
||||
|
||||
try:
|
||||
data_text: str = element.text # type: ignore
|
||||
@@ -108,19 +133,17 @@ def _decode_data(
|
||||
return _decode_base64_data(data_text, layer_width, compression)
|
||||
|
||||
|
||||
def _parse_data(
|
||||
element: etree.Element, layer_width: int
|
||||
) -> objects.LayerData:
|
||||
def _parse_data(element: etree.Element, layer_width: int) -> objects.LayerData:
|
||||
"""Parses layer data.
|
||||
|
||||
Will parse CSV, base64, gzip-base64, or zlip-base64 encoded data.
|
||||
|
||||
Args:
|
||||
:element (Element): Data element to parse.
|
||||
:width (int): Layer width. Used for base64 decoding.
|
||||
element (Element): Data element to parse.
|
||||
layer_width (int): Layer width. Used for base64 decoding.
|
||||
|
||||
Returns:
|
||||
:LayerData: Data object containing layer data or chunks of data.
|
||||
LayerData: Data object containing layer data or chunks of data.
|
||||
"""
|
||||
encoding = element.attrib["encoding"]
|
||||
compression = None
|
||||
@@ -210,6 +233,9 @@ def _parse_tile_layer(element: etree.Element,) -> objects.TileLayer:
|
||||
Args:
|
||||
element: The layer element to be parsed.
|
||||
|
||||
Raises:
|
||||
ValueError: Element has no chile data element.
|
||||
|
||||
Returns:
|
||||
TileLayer: The tile layer object.
|
||||
"""
|
||||
@@ -226,7 +252,13 @@ def _parse_tile_layer(element: etree.Element,) -> objects.TileLayer:
|
||||
raise ValueError(f"{element} has no child data element.")
|
||||
|
||||
return objects.TileLayer(
|
||||
id_, name, offset, opacity, properties, size, data
|
||||
id_=id_,
|
||||
name=name,
|
||||
offset=offset,
|
||||
opacity=opacity,
|
||||
properties=properties,
|
||||
size=size,
|
||||
data=data,
|
||||
)
|
||||
|
||||
|
||||
@@ -249,7 +281,7 @@ def _parse_objects(
|
||||
location_y = float(object_element.attrib["y"])
|
||||
location = objects.OrderedPair(location_x, location_y)
|
||||
|
||||
tiled_object = objects.TiledObject(id_, location)
|
||||
tiled_object = objects.TiledObject(id_=id_, location=location)
|
||||
|
||||
try:
|
||||
tiled_object.gid = int(object_element.attrib["gid"])
|
||||
@@ -259,12 +291,12 @@ def _parse_objects(
|
||||
try:
|
||||
width = float(object_element.attrib["width"])
|
||||
except KeyError:
|
||||
width = 0
|
||||
width = 0.0
|
||||
|
||||
try:
|
||||
height = float(object_element.attrib["height"])
|
||||
except KeyError:
|
||||
height = 0
|
||||
height = 0.0
|
||||
|
||||
tiled_object.size = objects.Size(width, height)
|
||||
|
||||
@@ -303,12 +335,7 @@ def _parse_object_layer(element: etree.Element,) -> objects.ObjectLayer:
|
||||
"""Parse the objectgroup element given.
|
||||
|
||||
Args:
|
||||
layer_type (objects.LayerType):
|
||||
id: The id of the layer.
|
||||
name: The name of the layer.
|
||||
offset: The offset of the layer.
|
||||
opacity: The opacity of the layer.
|
||||
properties: The Properties object of the layer.
|
||||
element (etree.Element): Element to be parsed.
|
||||
|
||||
Returns:
|
||||
ObjectLayer: The object layer object.
|
||||
@@ -330,14 +357,14 @@ def _parse_object_layer(element: etree.Element,) -> objects.ObjectLayer:
|
||||
pass
|
||||
|
||||
return objects.ObjectLayer(
|
||||
id_,
|
||||
name,
|
||||
offset,
|
||||
opacity,
|
||||
properties,
|
||||
tiled_objects,
|
||||
color,
|
||||
draw_order,
|
||||
id_=id_,
|
||||
name=name,
|
||||
offset=offset,
|
||||
opacity=opacity,
|
||||
properties=properties,
|
||||
tiled_objects=tiled_objects,
|
||||
color=color,
|
||||
draw_order=draw_order,
|
||||
)
|
||||
|
||||
|
||||
@@ -345,12 +372,7 @@ def _parse_layer_group(element: etree.Element,) -> objects.LayerGroup:
|
||||
"""Parse the objectgroup element given.
|
||||
|
||||
Args:
|
||||
layer_type (objects.LayerType):
|
||||
id: The id of the layer.
|
||||
name: The name of the layer.
|
||||
offset: The offset of the layer.
|
||||
opacity: The opacity of the layer.
|
||||
properties: The Properties object of the layer.
|
||||
element (etree.Element): Element to be parsed.
|
||||
|
||||
Returns:
|
||||
LayerGroup: The layer group object.
|
||||
@@ -359,7 +381,14 @@ def _parse_layer_group(element: etree.Element,) -> objects.LayerGroup:
|
||||
|
||||
layers = _get_layers(element)
|
||||
|
||||
return objects.LayerGroup(id_, name, offset, opacity, properties, layers)
|
||||
return objects.LayerGroup(
|
||||
id_=id_,
|
||||
name=name,
|
||||
offset=offset,
|
||||
opacity=opacity,
|
||||
properties=properties,
|
||||
layers=layers,
|
||||
)
|
||||
|
||||
|
||||
def _get_layer_parser(
|
||||
@@ -381,12 +410,11 @@ def _get_layer_parser(
|
||||
"""
|
||||
if layer_tag == "layer":
|
||||
return _parse_tile_layer
|
||||
elif layer_tag == "objectgroup":
|
||||
if layer_tag == "objectgroup":
|
||||
return _parse_object_layer
|
||||
elif layer_tag == "group":
|
||||
if layer_tag == "group":
|
||||
return _parse_layer_group
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def _get_layers(map_element: etree.Element) -> List[objects.Layer]:
|
||||
@@ -417,6 +445,13 @@ def _parse_external_tile_set(
|
||||
"""Parses an external tile set.
|
||||
|
||||
Caches the results to speed up subsequent maps with identical tilesets.
|
||||
|
||||
Args:
|
||||
parent_dir (Path): Directory that TMX is in.
|
||||
tile_set_element (etree.Element): Tile set element.
|
||||
|
||||
Returns:
|
||||
objects.Tileset: The tileset being parsed.
|
||||
"""
|
||||
source = Path(tile_set_element.attrib["source"])
|
||||
tile_set_tree = etree.parse(str(parent_dir / Path(source))).getroot()
|
||||
@@ -424,87 +459,30 @@ def _parse_external_tile_set(
|
||||
return _parse_tile_set(tile_set_tree)
|
||||
|
||||
|
||||
def _parse_points(point_string: str) -> List[objects.OrderedPair]:
|
||||
str_pairs = point_string.split(" ")
|
||||
|
||||
points = []
|
||||
for str_pair in str_pairs:
|
||||
xys = str_pair.split(",")
|
||||
x = float(xys[0])
|
||||
y = float(xys[1])
|
||||
points.append((x, y))
|
||||
|
||||
return points
|
||||
|
||||
|
||||
def _parse_hitboxes(element: etree.Element) -> List[objects.TiledObject]:
|
||||
"""Parses all hitboxes for a given tile."""
|
||||
hitbox_elements = element.findall("./object")
|
||||
|
||||
hitboxes = []
|
||||
for hitbox_element in hitbox_elements:
|
||||
|
||||
id_ = None
|
||||
if "id" in hitbox_element.attrib:
|
||||
id_ = hitbox_element.attrib["id"]
|
||||
|
||||
x = None
|
||||
if "x" in hitbox_element.attrib:
|
||||
x = float(hitbox_element.attrib["x"])
|
||||
|
||||
y = None
|
||||
if "y" in hitbox_element.attrib:
|
||||
y = float(hitbox_element.attrib["y"])
|
||||
|
||||
width = None
|
||||
if "width" in hitbox_element.attrib:
|
||||
width = float(hitbox_element.attrib["width"])
|
||||
|
||||
height = None
|
||||
if "height" in hitbox_element.attrib:
|
||||
height = float(hitbox_element.attrib["height"])
|
||||
|
||||
# Default to rectangle as the type
|
||||
hitbox_type = "Rectangle"
|
||||
points = None
|
||||
|
||||
child = hitbox_element.findall("polygon")
|
||||
if child:
|
||||
hitbox_type = "Polygon"
|
||||
points = _parse_points(child[0].attrib["points"])
|
||||
child = hitbox_element.findall("ellipse")
|
||||
if child:
|
||||
hitbox_type = "Ellipse"
|
||||
child = hitbox_element.findall("point")
|
||||
if child:
|
||||
hitbox_type = "Point"
|
||||
child = hitbox_element.findall("polyline")
|
||||
if child:
|
||||
hitbox_type = "Polyline"
|
||||
points = _parse_points(child[0].attrib["points"])
|
||||
|
||||
hitbox = objects.Hitbox(id_, x, y, width, height, hitbox_type, points)
|
||||
hitboxes.append(hitbox)
|
||||
|
||||
return hitboxes
|
||||
|
||||
|
||||
def _parse_tiles(
|
||||
tile_element_list: List[etree.Element]
|
||||
) -> Dict[int, objects.Tile]:
|
||||
"""Parse a list of tile elements.
|
||||
|
||||
Args:
|
||||
tile_element_list (List[etree.Element]): List of tile elements.
|
||||
|
||||
Returns:
|
||||
Dict[int, objects.Tile]: Dictionary containing Tile objects by their
|
||||
ID.
|
||||
"""
|
||||
tiles: Dict[int, objects.Tile] = {}
|
||||
for tile_element in tile_element_list:
|
||||
# id is not optional
|
||||
id_ = int(tile_element.attrib["id"])
|
||||
|
||||
# optional attributes
|
||||
tile_type = None
|
||||
_type = None
|
||||
try:
|
||||
tile_type = tile_element.attrib["type"]
|
||||
_type = tile_element.attrib["type"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
tile_terrain = None
|
||||
try:
|
||||
tile_terrain_attrib = tile_element.attrib["terrain"]
|
||||
except KeyError:
|
||||
@@ -520,11 +498,11 @@ def _parse_tiles(
|
||||
terrain_list: List[Optional[int]] = []
|
||||
# each index in terrain_list_attrib refers to a corner
|
||||
for corner in terrain_list_attrib:
|
||||
if corner == "":
|
||||
if not corner:
|
||||
terrain_list.append(None)
|
||||
else:
|
||||
terrain_list.append(int(corner))
|
||||
tile_terrain = objects.TileTerrain(*terrain_list)
|
||||
terrain = objects.TileTerrain(*terrain_list)
|
||||
|
||||
# tile element optional sub-elements
|
||||
properties: Optional[List[objects.Property]] = None
|
||||
@@ -546,25 +524,26 @@ def _parse_tiles(
|
||||
frames = tile_animation_element.findall("./frame")
|
||||
for frame in frames:
|
||||
# tileid refers to the Tile.id of the animation frame
|
||||
tile_id = int(frame.attrib["tileid"])
|
||||
id_ = int(frame.attrib["tileid"])
|
||||
# duration is in MS. Should perhaps be converted to seconds.
|
||||
# FIXME: make decision
|
||||
duration = int(frame.attrib["duration"])
|
||||
animation.append(objects.Frame(tile_id, duration))
|
||||
animation.append(objects.Frame(id_, duration))
|
||||
|
||||
# if this is None, then the Tile is part of a spritesheet
|
||||
tile_image = None
|
||||
tile_image_element = tile_element.find("./image")
|
||||
if tile_image_element is not None:
|
||||
tile_image = _parse_image_element(tile_image_element)
|
||||
|
||||
hitboxes = None
|
||||
tile_hitboxes_element = tile_element.find("./objectgroup")
|
||||
if tile_hitboxes_element is not None:
|
||||
hitboxes = _parse_hitboxes(tile_hitboxes_element)
|
||||
image = None
|
||||
image_element = tile_element.find("./image")
|
||||
if image_element is not None:
|
||||
image = _parse_image_element(image_element)
|
||||
|
||||
tiles[id_] = objects.Tile(
|
||||
id_, tile_type, tile_terrain, animation, tile_image, hitboxes, properties, tileset=None
|
||||
id_=id_,
|
||||
type_=_type,
|
||||
terrain=terrain,
|
||||
animation=animation,
|
||||
image=image,
|
||||
properties=properties,
|
||||
tileset=None,
|
||||
)
|
||||
|
||||
return tiles
|
||||
@@ -573,9 +552,13 @@ def _parse_tiles(
|
||||
def _parse_image_element(image_element: etree.Element) -> objects.Image:
|
||||
"""Parse image element given.
|
||||
|
||||
Args:
|
||||
image_element (etree.Element): Image element to be parsed.
|
||||
|
||||
Returns:
|
||||
: Color in Arcade's preferred format.
|
||||
objects.Image: FIXME what is this?
|
||||
"""
|
||||
# FIXME doc
|
||||
image = objects.Image(image_element.attrib["source"])
|
||||
|
||||
width_attrib = image_element.attrib.get("width")
|
||||
@@ -597,14 +580,19 @@ def _parse_properties_element(
|
||||
) -> objects.Properties:
|
||||
"""Adds Tiled property to Properties dict.
|
||||
|
||||
Args:
|
||||
:name (str): Name of property.
|
||||
:property_type (str): Type of property. Can be string, int, float,
|
||||
Each property element has a number of attributes:
|
||||
name (str): Name of property.
|
||||
property_type (str): Type of property. Can be string, int, float,
|
||||
bool, color or file. Defaults to string.
|
||||
:value (str): The value of the property.
|
||||
value (str): The value of the property.
|
||||
|
||||
Args:
|
||||
properties_element (etree.Element): Element to be parsed.
|
||||
|
||||
Returns:
|
||||
:Properties: Properties Dict object.
|
||||
objects.Properties: Dict of the property values by property name.
|
||||
|
||||
|
||||
"""
|
||||
properties: objects.Properties = {}
|
||||
for property_element in properties_element.findall("./property"):
|
||||
@@ -641,8 +629,13 @@ def _parse_properties_element(
|
||||
|
||||
|
||||
def _parse_tile_set(tile_set_element: etree.Element) -> objects.TileSet:
|
||||
"""
|
||||
Parses a tile set that is embedded into a TMX.
|
||||
"""Parses a tile set that is embedded into a TMX.
|
||||
|
||||
Args:
|
||||
tile_set_element (etree.Element): Element to be parsed.
|
||||
|
||||
Returns:
|
||||
objects.TileSet: Tile Set from element.
|
||||
"""
|
||||
# get all basic attributes
|
||||
name = tile_set_element.attrib["name"]
|
||||
@@ -728,13 +721,61 @@ def _parse_tile_set(tile_set_element: etree.Element) -> objects.TileSet:
|
||||
|
||||
# Go back and create a circular link so tiles know what tileset they are
|
||||
# part of. Needed for animation.
|
||||
for my_id, my_tile in tiles.items():
|
||||
my_tile.tileset = tileset
|
||||
for id_, tile in tiles.items():
|
||||
tile.tileset = tileset
|
||||
|
||||
return tileset
|
||||
|
||||
|
||||
def _get_tile_sets(
|
||||
map_element: etree.Element, parent_dir: Path
|
||||
) -> objects.TileSetDict:
|
||||
"""Get tile sets.
|
||||
|
||||
Args:
|
||||
map_element (etree.Element): Element to be parsed.
|
||||
parent_dir (Path): Directory that TMX is in.
|
||||
|
||||
Returns:
|
||||
objects.TileSetDict: Dict of tile sets in the TMX by first_gid
|
||||
"""
|
||||
# parse all tilesets
|
||||
tile_sets: objects.TileSetDict = {}
|
||||
tile_set_element_list = map_element.findall("./tileset")
|
||||
for tile_set_element in tile_set_element_list:
|
||||
# tiled docs are ambiguous about the 'firstgid' attribute
|
||||
# current understanding is for the purposes of mapping the layer
|
||||
# data to the tile set data, add the 'firstgid' value to each
|
||||
# tile 'id'; this means that the 'firstgid' is specific to each,
|
||||
# tile set as they pertain to the map, not tile set specific as
|
||||
# the tiled docs can make it seem
|
||||
# 'firstgid' the key for each TileMap
|
||||
first_gid = int(tile_set_element.attrib["firstgid"])
|
||||
try:
|
||||
# check if is an external TSX
|
||||
source = tile_set_element.attrib["source"]
|
||||
except KeyError:
|
||||
# the tile set is embedded
|
||||
name = tile_set_element.attrib["name"]
|
||||
tile_sets[first_gid] = _parse_tile_set(tile_set_element)
|
||||
else:
|
||||
# tile set is external
|
||||
tile_sets[first_gid] = _parse_external_tile_set(
|
||||
parent_dir, tile_set_element
|
||||
)
|
||||
|
||||
return tile_sets
|
||||
|
||||
|
||||
def parse_tile_map(tmx_file: Union[str, Path]) -> objects.TileMap:
|
||||
"""Parse tile map.
|
||||
|
||||
Args:
|
||||
tmx_file (Union[str, Path]): TMX file to be parsed.
|
||||
|
||||
Returns:
|
||||
objects.TileMap: TileMap object generated from the TMX file provided.
|
||||
"""
|
||||
# setting up XML parsing
|
||||
map_tree = etree.parse(str(tmx_file))
|
||||
map_element = map_tree.getroot()
|
||||
@@ -746,43 +787,22 @@ def parse_tile_map(tmx_file: Union[str, Path]) -> objects.TileMap:
|
||||
tiled_version = map_element.attrib["tiledversion"]
|
||||
orientation = map_element.attrib["orientation"]
|
||||
render_order = map_element.attrib["renderorder"]
|
||||
|
||||
map_width = int(map_element.attrib["width"])
|
||||
map_height = int(map_element.attrib["height"])
|
||||
map_size = objects.Size(map_width, map_height)
|
||||
|
||||
tile_width = int(map_element.attrib["tilewidth"])
|
||||
tile_height = int(map_element.attrib["tileheight"])
|
||||
tile_size = objects.Size(tile_width, tile_height)
|
||||
|
||||
infinite_attribute = map_element.attrib["infinite"]
|
||||
infinite = True if infinite_attribute == "true" else False
|
||||
infinite = bool(infinite_attribute == "true")
|
||||
|
||||
next_layer_id = int(map_element.attrib["nextlayerid"])
|
||||
next_object_id = int(map_element.attrib["nextobjectid"])
|
||||
|
||||
# parse all tilesets
|
||||
tile_sets: Dict[int, objects.TileSet] = {}
|
||||
tile_set_element_list = map_element.findall("./tileset")
|
||||
for tile_set_element in tile_set_element_list:
|
||||
# tiled docs are ambiguous about the 'firstgid' attribute
|
||||
# current understanding is for the purposes of mapping the layer
|
||||
# data to the tile set data, add the 'firstgid' value to each
|
||||
# tile 'id'; this means that the 'firstgid' is specific to each,
|
||||
# tile set as they pertain to the map, not tile set specific as
|
||||
# the tiled docs can make it seem
|
||||
# 'firstgid' is saved beside each TileMap
|
||||
firstgid = int(tile_set_element.attrib["firstgid"])
|
||||
try:
|
||||
# check if is an external TSX
|
||||
source = tile_set_element.attrib["source"]
|
||||
except KeyError:
|
||||
# the tile set in embedded
|
||||
name = tile_set_element.attrib["name"]
|
||||
tile_sets[firstgid] = _parse_tile_set(tile_set_element)
|
||||
else:
|
||||
# tile set is external
|
||||
tile_sets[firstgid] = _parse_external_tile_set(
|
||||
parent_dir, tile_set_element
|
||||
)
|
||||
tile_sets = _get_tile_sets(map_element, parent_dir)
|
||||
|
||||
layers = _get_layers(map_element)
|
||||
|
||||
|
||||
7
setup.py
7
setup.py
@@ -1,6 +1,7 @@
|
||||
from os import path
|
||||
import sys
|
||||
from setuptools import setup
|
||||
from os import path
|
||||
|
||||
from setuptools import setup # type: ignore
|
||||
|
||||
BUILD = 0
|
||||
VERSION = "0.0.1"
|
||||
@@ -21,7 +22,7 @@ if __name__ == "__main__":
|
||||
license="MIT",
|
||||
url="https://github.com/Beefy-Swain/pytiled_parser",
|
||||
download_url="https://github.com/Beefy-Swain/pytiled_parser",
|
||||
install_requires=["dataclasses"],
|
||||
install_requires=["attrs"],
|
||||
packages=["pytiled_parser"],
|
||||
classifiers=[
|
||||
"Development Status :: 1 - Planning",
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{ 'background_color': None,
|
||||
'hex_side_length': None,
|
||||
'infinite': False,
|
||||
'layers': [ TileLayer(id=1, name='Tile Layer 1', offset=None, opacity=None, properties=None, size=Size(width=10, height=10), data=[[1, 2, 3, 4, 5, 6, 7, 8, 30, 30], [9, 10, 11, 12, 13, 14, 15, 16, 30, 30], [17, 18, 19, 20, 21, 22, 23, 24, 30, 30], [25, 26, 27, 28, 29, 30, 31, 32, 30, 30], [33, 34, 35, 36, 37, 38, 39, 40, 30, 30], [41, 42, 43, 44, 45, 46, 47, 48, 30, 30], [30, 30, 30, 30, 30, 30, 30, 30, 30, 30], [30, 30, 30, 30, 30, 30, 30, 30, 30, 30], [30, 30, 30, 30, 30, 30, 30, 30, 30, 30], [30, 30, 30, 30, 30, 30, 30, 30, 30, 30]]),
|
||||
TileLayer(id=2, name='Tile Layer 2', offset=None, opacity=0.5, properties=None, size=Size(width=10, height=10), data=[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 46, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 6, 7, 7, 7, 7, 7, 8, 0], [0, 0, 14, 15, 15, 15, 15, 15, 16, 0], [0, 0, 22, 23, 23, 23, 23, 23, 24, 0]]),
|
||||
LayerGroup(id=3, name='Group 1', offset=None, opacity=None, properties={'bool property': True}, layers=[TileLayer(id=5, name='Tile Layer 4', offset=OrderedPair(x=49.0, y=-50.0), opacity=None, properties=None, size=Size(width=10, height=10), data=[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 31, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), TileLayer(id=4, name='Tile Layer 3', offset=None, opacity=None, properties=None, size=Size(width=10, height=10), data=[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 2, 3, 0, 0, 0, 0, 0, 0, 0], [9, 10, 11, 0, 0, 0, 0, 0, 0, 0], [17, 18, 19, 0, 0, 0, 0, 0, 0, 0]])]),
|
||||
ObjectLayer(id=6, name='Object Layer 1', offset=OrderedPair(x=4.66667, y=-4.33333), opacity=0.9, properties=None, tiled_objects=[TiledObject(id=1, location=OrderedPair(x=200.25, y=210.75), size=Size(width=47.25, height=25.0), rotation=15, opacity=1, name='rectangle 1', type='rectangle type', properties=None, template=None), TiledObject(id=2, location=OrderedPair(x=252.5, y=87.75), size=Size(width=0, height=0), rotation=-21, opacity=1, name='polygon 1', type='polygon type', properties=None, template=None), TiledObject(id=3, location=OrderedPair(x=198.75, y=102.5), size=Size(width=17.75, height=14.25), rotation=0, opacity=1, name='elipse 1', type='elipse type', properties=None, template=None), TiledObject(id=4, location=OrderedPair(x=174.25, y=186.0), size=Size(width=0, height=0), rotation=0, opacity=1, name='point 1', type='point type', properties=None, template=None), TiledObject(id=7, location=OrderedPair(x=11.3958, y=48.5833), size=Size(width=107.625, height=27.25), rotation=0, opacity=1, name='insert text 1', type='insert text type', properties=None, template=None), TiledObject(id=6, location=OrderedPair(x=47.25, y=72.5), size=Size(width=47.0, height=53.0), rotation=31, opacity=1, name='inserted tile 1', type='inserted tile type', properties={'tile property bool': True}, template=None), TiledObject(id=8, location=OrderedPair(x=144.667, y=112.0), size=Size(width=0, height=0), rotation=0, opacity=1, name='polyline 1', type='polyline type', properties=None, template=None), TiledObject(id=9, location=OrderedPair(x=69.8333, y=168.333), size=Size(width=0, height=0), rotation=0, opacity=1, name='polygon 2', type='polygon type', properties=None, template=None)], color=Color(red=0, green=0, blue=0, alpha=255), draw_order='index')],
|
||||
'map_size': Size(width=10, height=10),
|
||||
'next_layer_id': 16,
|
||||
'next_object_id': 10,
|
||||
'orientation': 'orthogonal',
|
||||
'parent_dir': PosixPath('/home/benk/Projects/pytiled_parser/venv/pytiled_parser/tests/test_data'),
|
||||
'properties': None,
|
||||
'render_order': 'right-down',
|
||||
'stagger_axis': None,
|
||||
'stagger_index': None,
|
||||
'tile_sets': { 1: TileSet(name='tile_set_image', max_tile_size=Size(width=32, height=32), spacing=1, margin=1, tile_count=48, columns=8, tile_offset=None, grid=None, properties=None, image=Image(source='images/tmw_desert_spacing.png', size=Size(width=265, height=199), trans=None), terrain_types=None, tiles={})},
|
||||
'tile_size': Size(width=32, height=32),
|
||||
'tiled_version': '1.2.3',
|
||||
'version': '1.2'}
|
||||
22
test/test_attr.py
Normal file
22
test/test_attr.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import attr
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
class Foo:
|
||||
x: int
|
||||
y: int
|
||||
|
||||
a: int = 5
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
class Bar(Foo):
|
||||
z: int
|
||||
|
||||
|
||||
foo = Foo(x=1, y=2)
|
||||
|
||||
bar = Bar(x=1, y=2, z=3)
|
||||
|
||||
print(foo)
|
||||
print(bar)
|
||||
178
test/test_output.py
Normal file
178
test/test_output.py
Normal file
@@ -0,0 +1,178 @@
|
||||
{
|
||||
"parent_dir": PosixPath(
|
||||
"/home/ben/Projects/pytiled_parser/pytiled_parser-venv/pytiled_parser/tests/test_data"
|
||||
),
|
||||
"version": "1.2",
|
||||
"tiled_version": "1.2.3",
|
||||
"orientation": "orthogonal",
|
||||
"render_order": "right-down",
|
||||
"map_size": Size(width=8, height=6),
|
||||
"tile_size": Size(width=32, height=32),
|
||||
"infinite": False,
|
||||
"next_layer_id": 2,
|
||||
"next_object_id": 1,
|
||||
"tile_sets": {
|
||||
1: TileSet(
|
||||
name="tile_set_image",
|
||||
max_tile_size=Size(width=32, height=32),
|
||||
spacing=1,
|
||||
margin=1,
|
||||
tile_count=48,
|
||||
columns=8,
|
||||
tile_offset=None,
|
||||
grid=None,
|
||||
properties=None,
|
||||
image=Image(
|
||||
source="images/tmw_desert_spacing.png",
|
||||
size=Size(width=265, height=199),
|
||||
trans=None,
|
||||
),
|
||||
terrain_types=None,
|
||||
tiles={
|
||||
9: Tile(
|
||||
id=9,
|
||||
type=None,
|
||||
terrain=None,
|
||||
animation=None,
|
||||
image=None,
|
||||
hitboxes=[
|
||||
TiledObject(
|
||||
id=2,
|
||||
location=OrderedPair(x=1.0, y=1.0),
|
||||
size=Size(width=32.0, height=32.0),
|
||||
rotation=1,
|
||||
opacity=1,
|
||||
name="wall",
|
||||
type="rectangle type",
|
||||
properties=None,
|
||||
template=None,
|
||||
)
|
||||
],
|
||||
),
|
||||
19: Tile(
|
||||
id=19,
|
||||
type=None,
|
||||
terrain=None,
|
||||
animation=None,
|
||||
image=None,
|
||||
hitboxes=[
|
||||
TiledObject(
|
||||
id=1,
|
||||
location=OrderedPair(x=32.0, y=1.0),
|
||||
size=Size(width=0, height=0),
|
||||
rotation=1,
|
||||
opacity=1,
|
||||
name="wall corner",
|
||||
type="polygon type",
|
||||
properties=None,
|
||||
template=None,
|
||||
)
|
||||
],
|
||||
),
|
||||
20: Tile(
|
||||
id=20,
|
||||
type=None,
|
||||
terrain=None,
|
||||
animation=None,
|
||||
image=None,
|
||||
hitboxes=[
|
||||
TiledObject(
|
||||
id=1,
|
||||
location=OrderedPair(x=1.45455, y=1.45455),
|
||||
size=Size(width=0, height=0),
|
||||
rotation=1,
|
||||
opacity=1,
|
||||
name="polyline",
|
||||
type="polyline type",
|
||||
properties=None,
|
||||
template=None,
|
||||
)
|
||||
],
|
||||
),
|
||||
31: Tile(
|
||||
id=31,
|
||||
type=None,
|
||||
terrain=None,
|
||||
animation=None,
|
||||
image=None,
|
||||
hitboxes=[
|
||||
TiledObject(
|
||||
id=1,
|
||||
location=OrderedPair(x=5.09091, y=2.54545),
|
||||
size=Size(width=19.6364, height=19.2727),
|
||||
rotation=1,
|
||||
opacity=1,
|
||||
name="rock 1",
|
||||
type="elipse type",
|
||||
properties=None,
|
||||
template=None,
|
||||
),
|
||||
TiledObject(
|
||||
id=2,
|
||||
location=OrderedPair(x=16.1818, y=22.0),
|
||||
size=Size(width=8.54545, height=8.36364),
|
||||
rotation=-1,
|
||||
opacity=1,
|
||||
name="rock 2",
|
||||
type="elipse type",
|
||||
properties=None,
|
||||
template=None,
|
||||
),
|
||||
],
|
||||
),
|
||||
45: Tile(
|
||||
id=45,
|
||||
type=None,
|
||||
terrain=None,
|
||||
animation=None,
|
||||
image=None,
|
||||
hitboxes=[
|
||||
TiledObject(
|
||||
id=1,
|
||||
location=OrderedPair(x=14.7273, y=26.3636),
|
||||
size=Size(width=0, height=0),
|
||||
rotation=0,
|
||||
opacity=1,
|
||||
name="sign",
|
||||
type="point type",
|
||||
properties=None,
|
||||
template=None,
|
||||
)
|
||||
],
|
||||
),
|
||||
},
|
||||
)
|
||||
},
|
||||
"layers": [
|
||||
TileLayer(
|
||||
id=1,
|
||||
name="Tile Layer 1",
|
||||
offset=None,
|
||||
opacity=None,
|
||||
properties=None,
|
||||
size=Size(width=8, height=6),
|
||||
data=[
|
||||
[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],
|
||||
],
|
||||
)
|
||||
],
|
||||
"hex_side_length": None,
|
||||
"stagger_axis": None,
|
||||
"stagger_index": None,
|
||||
"background_color": None,
|
||||
"properties": {
|
||||
"bool property - false": False,
|
||||
"bool property - true": True,
|
||||
"color property": "#ff49fcff",
|
||||
"file property": PosixPath("../../../../../../../../var/log/syslog"),
|
||||
"float property": 1.23456789,
|
||||
"int property": 13,
|
||||
"string property": "Hello, World!!",
|
||||
},
|
||||
}
|
||||
e
|
||||
@@ -1,17 +1,11 @@
|
||||
import pprint
|
||||
import pickle
|
||||
|
||||
import pprint
|
||||
from io import StringIO
|
||||
|
||||
import pytiled_parser
|
||||
|
||||
|
||||
pp = pprint.PrettyPrinter(indent=4, compact=True, width=100)
|
||||
|
||||
pp = pp.pprint
|
||||
|
||||
MAP_NAME = "/home/benk/Projects/pytiled_parser/venv/pytiled_parser/tests/test_data/test_map_image_tile_set.tmx"
|
||||
MAP_NAME = "/home/ben/Projects/pytiled_parser/pytiled_parser-venv/pytiled_parser/tests/test_data/test_map_simple_hitboxes.tmx"
|
||||
|
||||
map = pytiled_parser.parse_tile_map(MAP_NAME)
|
||||
|
||||
pp(map.__dict__)
|
||||
print(map.__dict__)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import pytest
|
||||
|
||||
pytest.main(["-x", "tests/unit2"])
|
||||
pytest.main(["--tb=native", "-s", "tests"])
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import pytest
|
||||
"""Unit tests for pytiled_parser"""
|
||||
|
||||
import xml.etree.ElementTree as etree
|
||||
|
||||
from contextlib import contextmanager
|
||||
from typing import Callable, List, Optional, Tuple
|
||||
|
||||
from pytiled_parser import objects, xml_parser, utilities
|
||||
import pytest
|
||||
from pytiled_parser import objects, utilities, xml_parser
|
||||
|
||||
|
||||
@contextmanager
|
||||
@@ -14,25 +13,33 @@ def does_not_raise():
|
||||
|
||||
|
||||
def _get_root_element(xml: str) -> etree.Element:
|
||||
"""Get root element of string of XML.
|
||||
|
||||
Args:
|
||||
xml (str): String of XML to be parsed into etree.
|
||||
|
||||
Returns:
|
||||
etree.Element: Root element of XML given.
|
||||
"""
|
||||
return etree.fromstring(xml)
|
||||
|
||||
|
||||
layer_data = [
|
||||
LAYER_DATA = [
|
||||
(
|
||||
'<layer id="1" name="Tile Layer 1" width="10" height="10">'
|
||||
"</layer>",
|
||||
+ "</layer>",
|
||||
(int(1), "Tile Layer 1", None, None, None),
|
||||
),
|
||||
(
|
||||
'<layer id="2" name="Tile Layer 2" width="10" height="10" opacity="0.5">'
|
||||
"</layer>",
|
||||
+ "</layer>",
|
||||
(int(2), "Tile Layer 2", None, float(0.5), None),
|
||||
),
|
||||
(
|
||||
'<layer id="5" name="Tile Layer 4" width="10" height="10" offsetx="49" offsety="-50">'
|
||||
"<properties>"
|
||||
"</properties>"
|
||||
"</layer>",
|
||||
+ "<properties>"
|
||||
+ "</properties>"
|
||||
+ "</layer>",
|
||||
(
|
||||
int(5),
|
||||
"Tile Layer 4",
|
||||
@@ -44,9 +51,9 @@ layer_data = [
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("xml,expected", layer_data)
|
||||
@pytest.mark.parametrize("xml,expected", LAYER_DATA)
|
||||
def test_parse_layer(xml, expected, monkeypatch):
|
||||
def mockreturn(properties):
|
||||
def mockreturn(*args):
|
||||
return "properties"
|
||||
|
||||
monkeypatch.setattr(xml_parser, "_parse_properties_element", mockreturn)
|
||||
@@ -65,9 +72,6 @@ def test_parse_layer(xml, expected, monkeypatch):
|
||||
],
|
||||
)
|
||||
def test_color_parsing(test_input, expected):
|
||||
"""
|
||||
Tiled has a few different types of color representations.
|
||||
"""
|
||||
assert utilities.parse_color(test_input) == expected
|
||||
|
||||
|
||||
@@ -97,7 +101,7 @@ def test_decode_csv_data(data_csv, expected):
|
||||
assert xml_parser._decode_csv_data(data_csv) == expected
|
||||
|
||||
|
||||
data_base64 = [
|
||||
DATA_BASE64 = [
|
||||
(
|
||||
"AQAAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAFgAAABcAAAAYAAAAGQAAABoAAAAbAAAAHAAAAB0AAAAeAAAAHwAAACAAAAAhAAAAIgAAACMAAAAkAAAAJQAAACYAAAAnAAAAKAAAACkAAAAqAAAAKwAAACwAAAAtAAAALgAAAC8AAAAwAAAA",
|
||||
8,
|
||||
@@ -158,13 +162,55 @@ data_base64 = [
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_base64,width,compression,expected,raises", data_base64
|
||||
"data_base64,width,compression,expected,raises", DATA_BASE64
|
||||
)
|
||||
def test_decode_base64_data(
|
||||
data_base64, width, compression, expected, raises
|
||||
):
|
||||
def test_decode_base64_data(data_base64, width, compression, expected, raises):
|
||||
with raises:
|
||||
assert (
|
||||
xml_parser._decode_base64_data(data_base64, width, compression)
|
||||
== expected
|
||||
)
|
||||
|
||||
|
||||
# FIXME: use hypothesis for this
|
||||
def create_tile_set(qty_of_tiles):
|
||||
tile_set = objects.TileSet(None, None)
|
||||
|
||||
if qty_of_tiles == 0:
|
||||
return tile_set
|
||||
|
||||
tiles = {}
|
||||
|
||||
for tile_id in range(qty_of_tiles):
|
||||
tiles[tile_id] = objects.Tile(id_=tile_id)
|
||||
|
||||
tile_set.tiles = tiles
|
||||
|
||||
return tile_set
|
||||
|
||||
|
||||
tile_by_gid = [
|
||||
(1, {1: create_tile_set(0)}, None),
|
||||
(1, {1: create_tile_set(1)}, objects.Tile(id_=0)),
|
||||
(1, {1: create_tile_set(2)}, objects.Tile(id_=0)),
|
||||
(2, {1: create_tile_set(1)}, None),
|
||||
(10, {1: create_tile_set(10)}, objects.Tile(id_=9)),
|
||||
(1, {1: create_tile_set(1), 2: create_tile_set(1)}, objects.Tile(id_=0)),
|
||||
(2, {1: create_tile_set(1), 2: create_tile_set(1)}, objects.Tile(id_=0)),
|
||||
(3, {1: create_tile_set(1), 2: create_tile_set(1)}, None),
|
||||
(15, {1: create_tile_set(5), 6: create_tile_set(10)}, objects.Tile(id_=9)),
|
||||
(
|
||||
20,
|
||||
{
|
||||
1: create_tile_set(5),
|
||||
6: create_tile_set(10),
|
||||
16: create_tile_set(10),
|
||||
},
|
||||
objects.Tile(id_=4),
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("gid,tile_sets,expected", tile_by_gid)
|
||||
def test_get_tile_by_gid(gid, tile_sets, expected):
|
||||
assert utilities.get_tile_by_gid(gid, tile_sets) == expected
|
||||
@@ -1,9 +1,7 @@
|
||||
import os
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import pytiled_parser
|
||||
|
||||
print(os.path.dirname(os.path.abspath(__file__)))
|
||||
@@ -15,13 +13,11 @@ def test_map_simple():
|
||||
"""
|
||||
TMX with a very simple spritesheet tile set and some properties.
|
||||
"""
|
||||
map = pytiled_parser.parse_tile_map(
|
||||
Path("../test_data/test_map_simple.tmx")
|
||||
)
|
||||
map = pytiled_parser.parse_tile_map(Path("test_data/test_map_simple.tmx"))
|
||||
|
||||
# map
|
||||
# unsure how to get paths to compare propperly
|
||||
assert str(map.parent_dir) == "../test_data"
|
||||
assert str(map.parent_dir) == "test_data"
|
||||
assert map.version == "1.2"
|
||||
assert map.tiled_version == "1.2.3"
|
||||
assert map.orientation == "orthogonal"
|
||||
@@ -41,7 +37,7 @@ def test_map_simple():
|
||||
assert map.properties == {
|
||||
"bool property - false": False,
|
||||
"bool property - true": True,
|
||||
"color property": (0x49, 0xFC, 0xFF, 0xFF),
|
||||
"color property": "#ff49fcff",
|
||||
"file property": Path("/var/log/syslog"),
|
||||
"float property": 1.23456789,
|
||||
"int property": 13,
|
||||
@@ -78,7 +74,7 @@ def test_map_simple():
|
||||
[33, 34, 35, 36, 37, 38, 39, 40],
|
||||
[41, 42, 43, 44, 45, 46, 47, 48],
|
||||
]
|
||||
assert map.layers[0].id == 1
|
||||
assert map.layers[0].id_ == 1
|
||||
assert map.layers[0].name == "Tile Layer 1"
|
||||
assert map.layers[0].offset == None
|
||||
assert map.layers[0].opacity == None
|
||||
Reference in New Issue
Block a user