/
action5.py
131 lines (109 loc) · 5.73 KB
/
action5.py
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
__license__ = """
NML is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
NML is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
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
class Action5(base_action.BaseAction):
def __init__(self, type, num_sprites, offset):
self.type = type
self.num_sprites = num_sprites
self.offset = offset
def prepare_output(self, sprite_num):
if self.offset is not None:
self.type |= 0x80
def write(self, file):
# <Sprite-number> * <Length> 05 <type> <num-sprites> [<offset>]
size = 5 if self.offset is None else 8
file.start_sprite(size)
file.print_bytex(0x05)
file.print_bytex(self.type)
file.print_bytex(0xFF)
file.print_word(self.num_sprites)
if self.offset is not None:
file.print_bytex(0xFF)
file.print_word(self.offset)
file.newline()
file.end_sprite()
def skip_action7(self):
# skipping with Action7 should work, according to the Action7/9 specs
# However, skipping invalid (OpenTTD-only) Action5s in TTDP can only be done using Action9, else an error occurs
# To be on the safe side, don't allow skipping with Action7 at all
return False
class Action5BlockType:
FIXED = (0,) # fixed number of sprites
ANY = (1,) # any number of sprites
OFFSET = (2,) # flexible number of sprites, offset may be set
action5_table = {
"PRE_SIGNAL": (0x04, 48, Action5BlockType.OFFSET), # deprecated, use "SIGNALS" in all cases
"PRE_SIGNAL_SEMAPHORE": (0x04, 112, Action5BlockType.OFFSET), # deprecated, use "SIGNALS" in all cases
"PRE_SIGNAL_SEMAPHORE_PBS": (0x04, 240, Action5BlockType.OFFSET), # deprecated, use "SIGNALS" in all cases
"SIGNALS": (0x04, 240, Action5BlockType.OFFSET),
"CATENARY": (0x05, 48, Action5BlockType.OFFSET),
"FOUNDATIONS_SLOPES": (0x06, 74, Action5BlockType.FIXED),
"FOUNDATIONS_SLOPES_HALFTILES": (0x06, 90, Action5BlockType.OFFSET),
"TTDP_GUI_25": (0x07, 73, Action5BlockType.FIXED),
"TTDP_GUI": (0x07, 93, Action5BlockType.FIXED),
"CANALS": (0x08, 65, Action5BlockType.OFFSET),
"ONE_WAY_ROAD": (0x09, 18, Action5BlockType.OFFSET),
"COLOURMAP_2CC": (0x0A, 256, Action5BlockType.OFFSET),
"TRAMWAY": (0x0B, 119, Action5BlockType.OFFSET),
"SNOWY_TEMPERATE_TREES": (0x0C, 133, Action5BlockType.FIXED),
"COAST_TILES": (0x0D, 16, Action5BlockType.FIXED),
"COAST_TILES_BASEGFX": (0x0D, 10, Action5BlockType.FIXED),
"COAST_TILES_DIAGONAL": (0x0D, 18, Action5BlockType.FIXED),
"NEW_SIGNALS": (0x0E, 0, Action5BlockType.ANY),
"SLOPED_RAILS": (0x0F, 12, Action5BlockType.OFFSET),
"AIRPORTS": (0x10, 15, Action5BlockType.OFFSET),
"ROAD_STOPS": (0x11, 8, Action5BlockType.OFFSET),
"AQUEDUCTS": (0x12, 8, Action5BlockType.OFFSET),
"AUTORAIL": (0x13, 55, Action5BlockType.OFFSET),
"FLAGS": (0x14, 36, Action5BlockType.OFFSET),
"OTTD_GUI": (0x15, 191, Action5BlockType.OFFSET),
"AIRPORT_PREVIEW": (0x16, 9, Action5BlockType.OFFSET),
"RAILTYPE_TUNNELS": (0x17, 16, Action5BlockType.OFFSET),
"OTTD_RECOLOUR": (0x18, 1, Action5BlockType.OFFSET),
}
def parse_action5(replaces):
real_sprite_list = real_sprite.parse_sprite_data(replaces)
num_sprites = len(real_sprite_list)
if replaces.type.value not in action5_table:
raise generic.ScriptError(replaces.type.value + " is not a valid sprite replacement type", replaces.type.pos)
type_id, num_required, block_type = action5_table[replaces.type.value]
offset = None
if block_type == Action5BlockType.FIXED:
if num_sprites < num_required:
msg = "Invalid sprite count for sprite replacement type '{}', expected {:d}, got {:d}"
msg = msg.format(replaces.type, num_required, num_sprites)
raise generic.ScriptError(msg, replaces.pos)
elif num_sprites > num_required:
msg = (
"Too many sprites specified for sprite replacement type '{}',"
" expected {:d}, got {:d}, extra sprites may be ignored"
).format(replaces.type, num_required, num_sprites)
generic.print_warning(generic.Warning.GENERIC, msg, replaces.pos)
if replaces.offset != 0:
msg = "replacenew parameter 'offset' must be zero for sprite replacement type '{}'".format(replaces.type)
raise generic.ScriptError(msg, replaces.pos)
elif block_type == Action5BlockType.ANY:
if replaces.offset != 0:
msg = "replacenew parameter 'offset' must be zero for sprite replacement type '{}'".format(replaces.type)
raise generic.ScriptError(msg, replaces.pos)
elif block_type == Action5BlockType.OFFSET:
if num_sprites + replaces.offset > num_required:
msg = "Exceeding the limit of {:d} sprites for sprite replacement type '{}', extra sprites may be ignored"
msg = msg.format(num_required, replaces.type)
generic.print_warning(generic.Warning.GENERIC, msg, replaces.pos)
if replaces.offset != 0 or num_sprites != num_required:
offset = replaces.offset
else:
assert 0
return [Action5(type_id, num_sprites, offset)] + real_sprite_list