Skip to content

Commit

Permalink
Change: Use extended Action1s
Browse files Browse the repository at this point in the history
Each spriteset gets its own unique ID.
Removes the "all spritesets in a single sprite group/layout must have the same size" limitation.
  • Loading branch information
glx22 committed Oct 7, 2023
1 parent bbe945e commit 98b5eff
Show file tree
Hide file tree
Showing 40 changed files with 142 additions and 159 deletions.
152 changes: 68 additions & 84 deletions nml/actions/action1.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,7 @@
with NML; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""

from nml import generic
from nml.actions import base_action, real_sprite

"""
Maximum number of sprites per block.
This can be increased by switching to extended Action1.
"""
max_sprite_block_size = 0xFF
from nml.actions import base_action, real_sprite, action2


class Action1(base_action.BaseAction):
Expand All @@ -30,24 +23,30 @@ class Action1(base_action.BaseAction):
@ivar feature: Feature of this action1
@type feature: C{int}
@ivar first_set: Number of the first sprite set in this action 1.
@type first_set: C{int}
@ivar num_sets: Number of (sprite) sets that follow this action 1.
@type num_sets: C{int}
@ivar num_ent: Number of sprites per set (e.g. (usually) 8 for vehicles)
@type num_ent: C{int}
"""

def __init__(self, feature, num_sets, num_ent):
def __init__(self, feature, first_set, num_sets, num_ent):
self.feature = feature
self.first_set = first_set
self.num_sets = num_sets
self.num_ent = num_ent

def write(self, file):
# <Sprite-number> * <Length> 01 <feature> <num-sets> <num-ent>
file.start_sprite(6)
# <Sprite-number> * <Length> 01 <feature> 00 <first_set> <num-sets> <num-ent>
file.start_sprite(12)
file.print_bytex(1)
file.print_bytex(self.feature)
file.print_byte(self.num_sets)
file.print_bytex(0)
file.print_varx(self.first_set, 3)
file.print_varx(self.num_sets, 3)
file.print_varx(self.num_ent, 3)
file.newline()
file.end_sprite()
Expand All @@ -61,6 +60,9 @@ class SpritesetCollection(base_action.BaseAction):
@ivar feature: The feature number the action1 will get.
@type feature: C{int}
@ivar first_set: Number of the first sprite set in this action 1.
@type first_set: C{int}
@ivar num_sprites_per_spriteset: The number of sprites in each spriteset.
@type num_sprites_per_spriteset: C{int}
Expand All @@ -71,10 +73,12 @@ class SpritesetCollection(base_action.BaseAction):
@type spritesets: C{dict} mapping L{SpriteSet} to C{int}.
"""

def __init__(self, feature, num_sprites_per_spriteset):
def __init__(self, feature, first_set, num_sprites_per_spriteset):
self.feature = feature
self.first_set = first_set
self.num_sprites_per_spriteset = num_sprites_per_spriteset
self.spritesets = {}
self.max_id = 0x3FFF if feature in action2.features_sprite_layout else 0xFFFF

def skip_action7(self):
return False
Expand All @@ -85,41 +89,33 @@ def skip_action9(self):
def skip_needed(self):
return False

def can_add(self, spritesets, feature):
def can_add(self, spriteset):
"""
Test whether the given list of spritesets can be added to this collection.
@param spritesets: The list of spritesets to test for addition.
@type spritesets: C{list} of L{SpriteSet}
@param feature: The feature of the given spritesets.
@type feature: C{int}
@param spriteset: The spriteset to test for addition.
@type spriteset: L{SpriteSet}
@return: True iff the given spritesets can be added to this collection.
@return: True iff the given spriteset can be added to this collection.
@rtype: C{bool}
"""
assert len(spritesets) <= max_sprite_block_size
if feature != self.feature:
assert self.first_set + 1 <= self.max_id
if len(real_sprite.parse_sprite_data(spriteset)) != self.num_sprites_per_spriteset:
return False
for spriteset in spritesets:
if len(real_sprite.parse_sprite_data(spriteset)) != self.num_sprites_per_spriteset:
return False
num_new_sets = sum(1 for x in spritesets if x not in self.spritesets)
return len(self.spritesets) + num_new_sets <= max_sprite_block_size
return self.first_set + len(self.spritesets) + (1 if spriteset not in self.spritesets else 0) <= self.max_id

def add(self, spritesets):
def add(self, spriteset):
"""
Add a list of spritesets to this collection.
Add a spriteset to this collection.
@param spritesets: The list of spritesets to add.
@type spritesets: C{list} of L{SpriteSet}
@param spriteset: The spriteset to add.
@type spriteset: L{SpriteSet}
@pre: can_add(spritesets, self.feature).
@pre: can_add(spriteset).
"""
assert self.can_add(spritesets, self.feature)
for spriteset in spritesets:
if spriteset not in self.spritesets:
self.spritesets[spriteset] = len(self.spritesets)
assert self.can_add(spriteset)
if spriteset not in self.spritesets:
self.spritesets[spriteset] = len(self.spritesets)

def get_index(self, spriteset):
"""
Expand All @@ -132,7 +128,7 @@ def get_index(self, spriteset):
collection via #add.
"""
assert spriteset in self.spritesets
return self.spritesets[spriteset]
return self.first_set + self.spritesets[spriteset]

def get_action_list(self):
"""
Expand All @@ -142,7 +138,7 @@ def get_action_list(self):
@return: A list of actions needed to represet this collection in a GRF.
@rtype: C{list} of L{BaseAction}
"""
actions = [Action1(self.feature, len(self.spritesets), self.num_sprites_per_spriteset)]
actions = [Action1(self.feature, self.first_set, len(self.spritesets), self.num_sprites_per_spriteset)]
for idx in range(len(self.spritesets)):
for spriteset, spriteset_offset in self.spritesets.items():
if idx == spriteset_offset:
Expand All @@ -151,35 +147,13 @@ def get_action_list(self):
return actions


"""
Statistics about spritesets.
The 1st field of type C{int} contains the largest block of consecutive spritesets.
The 2nd field of type L{Position} contains a positional reference to the largest block of consecutive spritesets.
"""
spriteset_stats = (0, None)


def print_stats():
"""
Print statistics about used ids.
"""
if spriteset_stats[0] > 0:
# NML uses as many concurrent spritesets as possible to prevent sprite duplication.
# So, instead of the actual amount, we rather print the biggest unsplittable block, since that is what matters.
generic.print_info(
"Concurrent spritesets: {}/{} ({})".format(
spriteset_stats[0], max_sprite_block_size, str(spriteset_stats[1])
)
)


"""
The collection which was previoulsy used. add_to_action1 will try to reuse this
collection as long as possible to reduce the duplication of sprites. As soon
as a spriteset with a different feature or amount of sprites is added a new
collection will be created.
"""
last_spriteset_collection = None
spriteset_collections = {}


def add_to_action1(spritesets, feature, pos):
Expand All @@ -202,29 +176,36 @@ def add_to_action1(spritesets, feature, pos):
if not spritesets:
return []

setsize = len(real_sprite.parse_sprite_data(spritesets[0]))
for spriteset in spritesets:
if setsize != len(real_sprite.parse_sprite_data(spriteset)):
raise generic.ScriptError(
"Using spritesets with different sizes in a single sprite group / layout is not possible", pos
)

global spriteset_stats
if spriteset_stats[0] < len(spritesets):
spriteset_stats = (len(spritesets), pos)

global last_spriteset_collection
actions = []
if last_spriteset_collection is None or not last_spriteset_collection.can_add(spritesets, feature):
last_spriteset_collection = SpritesetCollection(feature, len(real_sprite.parse_sprite_data(spritesets[0])))
actions.append(last_spriteset_collection)

last_spriteset_collection.add(spritesets)
global spriteset_collections
if feature not in spriteset_collections:
spriteset_collections[feature] = [
SpritesetCollection(feature, 0, len(real_sprite.parse_sprite_data(spritesets[0])))
]
actions.append(spriteset_collections[feature][-1])

current_collection = spriteset_collections[feature][-1]
for spriteset in spritesets:
for spriteset_collection in spriteset_collections[feature]:
if spriteset in spriteset_collection.spritesets:
continue
if not current_collection.can_add(spriteset):
spriteset_collections[feature].append(
SpritesetCollection(
feature,
current_collection.first_set + len(current_collection.spritesets),
len(real_sprite.parse_sprite_data(spriteset)),
)
)
current_collection = spriteset_collections[feature][-1]
actions.append(current_collection)
current_collection.add(spriteset)

return actions


def get_action1_index(spriteset):
def get_action1_index(spriteset, feature):
"""
Get the index of a spriteset in the action1. The given spriteset must have
been added in the last call to #add_to_action1. Any new calls to
Expand All @@ -234,11 +215,16 @@ def get_action1_index(spriteset):
@param spriteset: The spriteset to get the index of.
@type spriteset: L{SpriteSet}.
@param feature: Feature of the spriteset.
@type feature: C{int}
@return: The index in the action1 of the given spriteset.
@rtype: C{int}
"""
assert last_spriteset_collection is not None
return last_spriteset_collection.get_index(spriteset)
assert feature in spriteset_collections
for spriteset_collection in spriteset_collections[feature]:
if spriteset in spriteset_collection.spritesets:
return spriteset_collection.get_index(spriteset)


def make_cb_failure_action1(feature):
Expand All @@ -253,10 +239,8 @@ def make_cb_failure_action1(feature):
@return: List of actions to append (if any) and action1 index to use
@rtype: C{tuple} of (C{list} of L{BaseAction}, C{int})
"""
global last_spriteset_collection
if last_spriteset_collection is not None and last_spriteset_collection.feature == feature:
if feature in spriteset_collections:
actions = []
else:
last_spriteset_collection = None
actions = [Action1(feature, 1, 0)]
actions = [Action1(feature, 0, 1, 0)]
return (actions, 0) # Index is currently always 0, but will change with ext. A1
4 changes: 2 additions & 2 deletions nml/actions/action2layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ def resolve_spritegroup_ref(self, sg_ref):
"""
spriteset = action2.resolve_spritegroup(sg_ref.name)
offset = self._validate_offset(sg_ref.param_list, spriteset, sg_ref.pos)
num = action1.get_action1_index(spriteset)
num = action1.get_action1_index(spriteset, self.feature)
generic.check_range(num, 0, (1 << 14) - 1, "sprite", sg_ref.pos)
return expression.ConstantNumeric(num), offset

Expand Down Expand Up @@ -653,7 +653,7 @@ def append_mapping(self, mapping, feature, actions, default, custom_spritesets):
feature,
spriteset.name.value + " - feature {:02X}".format(feature),
None,
action1.get_action1_index(spriteset),
action1.get_action1_index(spriteset, feature),
)
actions.append(real_action2)
spriteset.set_action2(real_action2, feature)
Expand Down
4 changes: 2 additions & 2 deletions nml/actions/action2real.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def get_real_action2s(spritegroup, feature):
raise generic.ScriptError("Expected at least one sprite set, encountered 0.", view.pos)
for set_ref in view.spriteset_list:
spriteset = action2.resolve_spritegroup(set_ref.name)
action1_index = action1.get_action1_index(spriteset)
action1_index = action1.get_action1_index(spriteset, feature)
if view.name.value == "loading":
loading_list.append(action1_index)
else:
Expand Down Expand Up @@ -152,7 +152,7 @@ def create_spriteset_actions(spritegroup):
feature,
spriteset.name.value + " - feature {:02X}".format(feature),
spritegroup.pos,
action1.get_action1_index(spriteset),
action1.get_action1_index(spriteset, feature),
)
action_list.append(real_action2)
spriteset.set_action2(real_action2, feature)
Expand Down
1 change: 0 additions & 1 deletion nml/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,6 @@ def nml(
action0.print_stats()
actionF.print_stats()
action7.print_stats()
action1.print_stats()
action2.print_stats()
action6.print_stats()
grf.print_stats()
Expand Down
Binary file modified regression/expected/006_vehicle.grf
Binary file not shown.
2 changes: 1 addition & 1 deletion regression/expected/006_vehicle.nfo
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ FF

11 * 23 04 01 1F 01 FF \wx0059 "Foster Sneltram" 00

12 * 6 01 01 \b1 FF \wx0008
12 * 12 01 01 00 FF \wx0000 FF \wx0001 FF \wx0008

13 opengfx_generic_trams1.pcx 8bpp 48 56 8 18 -3 -10 normal
| opengfx_generic_trams1.png 32bpp 48 56 8 18 -3 -10 normal
Expand Down
Binary file modified regression/expected/010_liveryoverride.grf
Binary file not shown.
2 changes: 1 addition & 1 deletion regression/expected/010_liveryoverride.nfo
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
00
00
2 * 52 08 08 "NML\10" "NML regression test" 00 "A test newgrf testing NML" 00
3 * 6 01 00 \b3 FF \wx0008
3 * 12 01 00 00 FF \wx0000 FF \wx0003 FF \wx0008

4 opengfx_trains_start.pcx 8bpp 142 112 8 22 -3 -10 normal
5 opengfx_trains_start.pcx 8bpp 158 112 21 15 -14 -7 normal
Expand Down
Binary file modified regression/expected/013_train_callback.grf
Binary file not shown.
2 changes: 1 addition & 1 deletion regression/expected/013_train_callback.nfo
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
4 * 28 00 08 \b1 05 FF \wx0000
12 "RAIL" "ELRL" "MONO" "MGLV" "TRPD"

5 * 6 01 00 \b8 FF \wx0004
5 * 12 01 00 00 FF \wx0000 FF \wx0008 FF \wx0004

6 temperate_railwagons.png 8bpp 0 25 8 24 -3 -12 normal
7 temperate_railwagons.png 8bpp 16 25 22 17 -14 -9 normal
Expand Down
Binary file modified regression/expected/016_basic_airporttiles.grf
Binary file not shown.
2 changes: 1 addition & 1 deletion regression/expected/016_basic_airporttiles.nfo
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// Escapes: D= = DR D+ = DF D- = DC Du* = DM D* = DnF Du<< = DnC D<< = DO D& D| Du/ D/ Du% D%
// Format: spritenum imagefile depth xpos ypos xsize ysize xrel yrel zoom flags

0 * 6 01 11 \b1 FF \wx0001
0 * 12 01 11 00 FF \wx0000 FF \wx0001 FF \wx0001

1 opengfx_trains_start.pcx 8bpp 142 112 8 22 -3 -10 normal

Expand Down
Binary file modified regression/expected/017_articulated_tram.grf
Binary file not shown.
2 changes: 1 addition & 1 deletion regression/expected/017_articulated_tram.nfo
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@

5 * 25 04 01 7F 01 FF \wx0058 "Foster Turbo Tram" 00

6 * 6 01 01 \b1 FF \wx0008
6 * 12 01 01 00 FF \wx0000 FF \wx0001 FF \wx0008

7 tram_foster_express.png 8bpp 48 1 8 18 -3 -10 normal
8 tram_foster_express.png 8bpp 64 1 20 18 -14 -5 normal
Expand Down
Binary file modified regression/expected/020_recolour.grf
Binary file not shown.
2 changes: 1 addition & 1 deletion regression/expected/020_recolour.nfo
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
F0 F1 F2 F3 F4 F5 00 00 00 00 00 00 00 00 00 FF

9 * 6 01 01 \b1 FF \wx0008
9 * 12 01 01 00 FF \wx0000 FF \wx0001 FF \wx0008

10 opengfx_generic_trams1.pcx 8bpp 48 56 8 18 -3 -10 normal
11 opengfx_generic_trams1.pcx 8bpp 64 56 20 19 -14 -5 normal
Expand Down
Binary file modified regression/expected/026_asl.grf
Binary file not shown.
4 changes: 2 additions & 2 deletions regression/expected/026_asl.nfo
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
2 * 7 03 11 01 00 \b0
\wx00FF // layout1;

3 * 6 01 0F \b2 FF \wx0003
3 * 12 01 0F 00 FF \wx0000 FF \wx0002 FF \wx0003

4 opengfx_generic_trams1.pcx 8bpp 64 56 20 19 -14 -5 normal
5 * 1 00
Expand Down Expand Up @@ -82,7 +82,7 @@ F0 F1 F2 F3 F4 F5 00 00 00 00 00 00 00 00 00 FF
\wx00FF \dx00000000 \dx00000000
\wx00FF //

12 * 6 01 11 \b2 FF \wx0003
12 * 12 01 11 00 FF \wx0000 FF \wx0002 FF \wx0003

13 opengfx_generic_trams1.pcx 8bpp 64 56 20 19 -14 -5 normal
14 * 1 00
Expand Down
Binary file modified regression/expected/027_airport_layout.grf
Binary file not shown.
2 changes: 1 addition & 1 deletion regression/expected/027_airport_layout.nfo
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// Escapes: D= = DR D+ = DF D- = DC Du* = DM D* = DnF Du<< = DnC D<< = DO D& D| Du/ D/ Du% D%
// Format: spritenum imagefile depth xpos ypos xsize ysize xrel yrel zoom flags

0 * 6 01 11 \b1 FF \wx0001
0 * 12 01 11 00 FF \wx0000 FF \wx0001 FF \wx0001

1 * 1 00

Expand Down
Binary file modified regression/expected/030_house.grf
Binary file not shown.
Loading

0 comments on commit 98b5eff

Please sign in to comment.