Skip to content

Instantly share code, notes, and snippets.

@nielsmh
Last active December 25, 2018 13:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nielsmh/0bb2555e75c6b2a65ebfefba08b4fd58 to your computer and use it in GitHub Desktop.
Save nielsmh/0bb2555e75c6b2a65ebfefba08b4fd58 to your computer and use it in GitHub Desktop.
__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.actions import action2, action2var, action6, actionD
from nml import expression, nmlop, global_constants
class Action2Production(action2.Action2):
"""
Class corresponding to Action2Industries (=production CB)
@ivar version: Production CB version. Version 0 uses constants, version 1 uses registers.
@type version: C{int}
@ivar sub_in: Amounts (v0) or registers (v1) to subtract from incoming cargos.
@type sub_in: C{list} of (C{int} or L{VarAction2Var})
@ivar add_out: Amounts (v0) or registers (v1) to add to the output cargos.
@type add_out: C{list} of (C{int} or L{VarAction2Var})
@ivar again: Number (v0) or register (v1), production CB will be run again if nonzero.
@type again C{int} or L{VarAction2Var}
"""
def __init__(self, name, pos, version, sub_in, add_out, again):
action2.Action2.__init__(self, 0x0A, name, pos)
self.version = version
if version in (0, 1):
self.sub_in = sub_in
assert len(self.sub_in) == 3
self.add_out = add_out
assert len(self.add_out) == 2
elif version == 2:
self.sub_in = sub_in
self.add_out = add_out
else:
assert False
self.again = again
def prepare_output(self, sprite_num):
action2.Action2.prepare_output(self, sprite_num)
def write(self, file):
if self.version in (0, 1):
cargo_size = 2 if self.version == 0 else 1
size = 2 + 5 * cargo_size
action2.Action2.write_sprite_start(self, file, size)
file.print_bytex(self.version)
values = self.sub_in + self.add_out + [self.again]
# Read register numbers if needed
if self.version == 1: values = [val.parameter for val in values]
for val in values[:-1]:
file.print_varx(val, cargo_size)
file.print_bytex(values[-1])
elif self.version == 2:
size = 4 + 2 * (len(self.sub_in) + len(self.add_out))
action2.Action2.write_sprite_start(self, file, size)
file.print_bytex(self.version)
file.print_byte(len(self.sub_in))
for cargoindex, value in self.sub_in:
file.print_bytex(cargoindex)
file.print_bytex(value.parameter)
file.print_byte(len(self.add_out))
for cargoindex, value in self.add_out:
file.print_bytex(cargoindex)
file.print_bytex(value.parameter)
file.print_bytex(self.again.parameter)
file.newline()
file.end_sprite()
def resolve_prodcb_register(param, varact2parser):
if isinstance(param, expression.StorageOp) and param.name == 'LOAD_TEMP' and isinstance(param.register, expression.ConstantNumeric):
# We can load a register directly
res = action2var.VarAction2Var(0x7D, 0, 0xFFFFFFFF, param.register.value)
else:
if len(varact2parser.var_list) != 0:
varact2parser.var_list.append(nmlop.VAL2)
varact2parser.var_list_size += 1
varact2parser.parse_expr(action2var.reduce_varaction2_expr(param, 0x0A))
store_tmp = action2var.VarAction2StoreTempVar()
res = action2var.VarAction2LoadTempVar(store_tmp)
varact2parser.var_list.append(nmlop.STO_TMP)
varact2parser.var_list.append(store_tmp)
varact2parser.var_list_size += store_tmp.get_size() + 1 # Add 1 for operator
return res
def finish_production_actions(produce, prod_action, action_list, varact2parser):
action_list.append(prod_action)
if len(varact2parser.var_list) == 0:
produce.set_action2(prod_action, 0x0A)
else:
# Create intermediate varaction2
varaction2 = action2var.Action2Var(0x0A, '{}@registers'.format(produce.name.value), produce.pos, 0x89)
varaction2.var_list = varact2parser.var_list
action_list.extend(varact2parser.extra_actions)
extra_act6 = action6.Action6()
for mod in varact2parser.mods:
extra_act6.modify_bytes(mod.param, mod.size, mod.offset + 4)
if len(extra_act6.modifications) > 0: action_list.append(extra_act6)
ref = expression.SpriteGroupRef(produce.name, [], None, prod_action)
varaction2.ranges.append(action2var.VarAction2Range(expression.ConstantNumeric(0), expression.ConstantNumeric(0), ref, ''))
varaction2.default_result = ref
varaction2.default_comment = ''
# Add two references (default + range)
action2.add_ref(ref, varaction2)
action2.add_ref(ref, varaction2)
produce.set_action2(varaction2, 0x0A)
action_list.append(varaction2)
action6.free_parameters.restore()
return action_list
def get_production_actions(produce):
"""
Get the action list that implements the given produce-block in nfo.
@param produce: Produce-block to parse.
@type produce: L{Produce}
"""
action_list = []
act6 = action6.Action6()
action6.free_parameters.save()
result_list = []
varact2parser = action2var.Varaction2Parser(0x0A)
if all(x.supported_by_actionD(False) for x in produce.param_list):
version = 0
offset = 4
for i, param in enumerate(produce.param_list):
result, offset = actionD.write_action_value(param, action_list, act6, offset, 2 if i < 5 else 1)
result_list.append(result.value)
else:
version = 1
for i, param in enumerate(produce.param_list):
result_list.append(resolve_prodcb_register(param, varact2parser))
if len(act6.modifications) > 0: action_list.append(act6)
prod_action = Action2Production(produce.name.value, produce.pos, version, result_list[0:3], result_list[3:5], result_list[5])
return finish_production_actions(produce, prod_action, action_list, varact2parser)
def get_production_v2_actions(produce):
"""
Get the action list that implements the given produce-block in nfo.
@param produce: Produce-block to parse.
@type produce: L{Produce2}
"""
action_list = []
act6 = action6.Action6()
action6.free_parameters.save()
result_list = []
varact2parser = action2var.Varaction2Parser(0x0A)
def resolve_cargoitem(item):
cargolabel = item.name.value
if cargolabel not in global_constants.cargo_numbers:
raise generic.ScriptError("Cargo label {0} not found in your cargo table".format(cargolabel), pos)
cargoindex = global_constants.cargo_numbers[cargolabel]
valueregister = resolve_prodcb_register(item.value, varact2parser)
return (cargoindex, valueregister)
sub_in = [resolve_cargoitem(item) for item in produce.subtract_in]
add_out = [resolve_cargoitem(item) for item in produce.add_out]
again = resolve_prodcb_register(produce.again, varact2parser)
prod_action = Action2Production(produce.name.value, produce.pos, 2, sub_in, add_out, again)
return finish_production_actions(produce, prod_action, action_list, varact2parser)
def make_empty_production_action2(pos):
"""
Make an empty production action2
For use with failed callbacks
@param pos: Positional context.
@type pos: L{Position}
@return: The created production action2
@rtype: L{Action2Production}
"""
return Action2Production("@CB_FAILED_PROD", pos, 0, [0, 0, 0], [0, 0], 0)
// Automatically generated by GRFCODEC. Do not modify!
// (Info version 32)
// Escapes: 2+ 2- 2< 2> 2u< 2u> 2/ 2% 2u/ 2u% 2* 2& 2| 2^ 2sto = 2s 2rst = 2r 2psto 2ror = 2rot 2cmp 2ucmp 2<< 2u>> 2>>
// Escapes: 71 70 7= 7! 7< 7> 7G 7g 7gG 7GG 7gg 7c 7C
// 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 * 4 \d18
1 * 54 14 "C" "INFO"
"B" "VRSN" \w4 \dx00000000
"B" "MINV" \w4 \dx00000000
"B" "NPAR" \w1 00
"B" "PALS" \w1 "A"
"B" "BLTR" \w1 "8"
00
00
2 * 165 08 08 "NML\04" "NML Example NewGRF: Industry" 00 "\8ENML Example NewGRF: Industry\0D\98This NewGRF is intended to provide a coding example for the high-level NewGRF-coding language NML." 00
3 * 110 04 00 FF 01 \wxD000 "\0DVar 69: \90Foo \0D\98Var 6A: \90Foo \0D\98Var 6B: \90Foo \0D\98Var 6C: \90Foo \0D\98Var 6D: \90Foo \0D\98Var 6E: \90Foo \0D\98Var 6F: \90Foo" 00
4 * 52 00 08 \b1 0B FF \wx0000
09 "PASS" "COAL" "MAIL" "OIL_" "LVST"
"GOOD" "GRAI" "WOOD" "IORE" "STEL"
"VALU"
// Name: test_produce1
5 * 13 02 0A FF 02 \b2 01 80 08 81 \b1 09 82 83
// Name: test_produce1@registers
6 * 72 02 0A FF 89
1A 20 \dx00000005
\2sto 1A 20 \dx00000080
\2r 1A 20 \dx00000007
\2sto 1A 20 \dx00000081
\2r 1A 20 \dx00000005
\2sto 1A 20 \dx00000082
\2r 1A 20 \dx00000001
\2sto 1A 00 \dx00000083
\b1
\wx00FF \dx00000000 \dx00000000
\wx00FF //
7 * 64 00 0A \b6 01 FF \wx0000
08 06
09 06
25 \b4
02 05 09 0A
26 \b4
01 03 08 06
27 \b4
00 00 00 00
28 \b4 \b4
\wx0100 \wx0100 \wx0100 \wx0100
\wx0000 \wx0000 \wx0000 \wx0000
\wx0000 \wx0000 \wx0400 \wx0000
\wx0080 \wx0000 \wx0000 \wx0080
8 * 11 00 0A \b2 01 FF \wx0000
21 02
22 01
// Name: @CB_FAILED_PROD
9 * 15 02 0A FE 00 \wx0000 \wx0000 \wx0000 \wx0000 \wx0000 00
// Name: @CB_FAILED0A
10 * 23 02 0A FE 89
0C 00 \dx0000FFFF
\b1
\wx8000 \dx00000000 \dx00000000 // graphics callback -> return 0
\wx00FE // Non-graphics callback, return graphics result
// Name: @action3_0
11 * 23 02 0A FF 89
18 00 \dx000000FF
\b1
\wx00FF \dx00000000 \dx00000000 // test_produce1;
\wx00FE // @CB_FAILED0A;
// Name: @action3_1
12 * 33 02 0A FE 89
0C 00 \dx0000FFFF
\b2
\wx00FF \dx00000000 \dx00000000 // @action3_0;
\wx8000 \dx0000003A \dx0000003A // return string(STR_INDUSTRY_EXTRA_TEXT);
\wx00FE // @CB_FAILED0A;
13 * 7 03 0A 01 00 \b0
\wx00FE // @action3_1;
14 * 13 00 09 \b3 01 FF \wx0000
08 27
09 27
12 02
15 * 13 00 09 \b3 01 FF \wx0001
08 28
09 28
12 02
16 * 13 00 09 \b3 01 FF \wx0002
08 29
09 29
12 02
17 * 13 00 09 \b3 01 FF \wx0003
08 2A
09 2A
12 02
18 * 26 00 0A \b6 01 FF \wx0001
08 09
09 09
25 \b3
04 06 07
26 \b0
27 \b3
08 0C 04
28 \b0 \b0
/*
* This file is aimed to provide an example on how to code a basic industry in NML.
* To keep the code readable, not every property or variable is documented in
* detail, refer to the object-specific reference in the documentation.
*
* This version shows only how to modify a built-in industry.
*
* Apart from this file, you will also need the following
* - Language files, to be placed in the 'lang' folder.
* Currently english.lng is supplied.
*/
/**********************************************
* Header, containing some general stuff:
**********************************************/
/*
* First, define a grf block. This defines some basic properties of the grf,
* which are required for the grf to be valid and loadable.
*/
grf {
/* This grf is part of NML, therefore "NML" is chosen as the first three
* characters of the GRFID. It is the third real grf defined as part of
* NML, therefore the last character is set to 2. Successive grfs will
* have 3, 4, etc. there, to make sure each example grf has a unique GRFID.
*/
grfid : "NML\04";
name : string(STR_GRF_NAME);
desc : string(STR_GRF_DESCRIPTION);
version : 0; // must be numeric
min_compatible_version : 0;
}
/* this example assumes we're just matching to the default temperate cargos, this wouldn't be the usual case */
cargotable {
PASS, COAL, MAIL, OIL_, LVST, GOOD, GRAI, WOOD, IORE, STEL, VALU
}
produce(test_produce1, [COAL: 5; IORE: 7;], [STEL: 5;], 1)
produce(test_produce2, [COAL: 5; IORE: 7;], [STEL: 5;])
produce(test_produce3, [], [], 0)
/*
* This example extends the cargos accepted and produce by the default temperate factory.
*/
item(FEAT_INDUSTRIES, factory) {
property {
substitute: INDUSTRYTYPE_TEMPERATE_FACTORY;
override: INDUSTRYTYPE_TEMPERATE_FACTORY;
cargo_types: [
accept_cargo("COAL", produce_cargo("MAIL", 1), produce_cargo("GOOD", 1), produce_cargo("STEL", 1), produce_cargo("VALU", 1)),
accept_cargo("OIL_"),
accept_cargo("IORE", produce_cargo("STEL", 4)),
accept_cargo("GRAI", produce_cargo("MAIL", 0.5), produce_cargo("VALU", 0.5))
];
}
graphics {
extra_text_industry: string(STR_INDUSTRY_EXTRA_TEXT);
produce_cargo_arrival: test_produce1;
}
}
item(FEAT_INDUSTRYTILES, factory_tile_1) {
property {
substitute: 39;
override: 39;
special_flags: bitmask(INDTILE_FLAG_ACCEPT_ALL);
}
}
item(FEAT_INDUSTRYTILES, factory_tile_2) {
property {
substitute: 40;
override: 40;
special_flags: bitmask(INDTILE_FLAG_ACCEPT_ALL);
}
}
item(FEAT_INDUSTRYTILES, factory_tile_3) {
property {
substitute: 41;
override: 41;
special_flags: bitmask(INDTILE_FLAG_ACCEPT_ALL);
}
}
item(FEAT_INDUSTRYTILES, factory_tile_4) {
property {
substitute: 42;
override: 42;
special_flags: bitmask(INDTILE_FLAG_ACCEPT_ALL);
}
}
/*
* This example causes the default farm to produce livestock, grain, and wood.
*/
item(FEAT_INDUSTRIES, farm) {
property {
substitute: INDUSTRYTYPE_TEMPERATE_ARCTIC_FARM;
override: INDUSTRYTYPE_TEMPERATE_ARCTIC_FARM;
cargo_types: [
produce_cargo("LVST", 8),
produce_cargo("GRAI", 12),
produce_cargo("WOOD", 4),
];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment