Skip to content

Commit

Permalink
Fix #1629: change the value written for the speed property to that th…
Browse files Browse the repository at this point in the history
…e displayed value in OpenTTD matches the original value
  • Loading branch information
Yexo committed Nov 20, 2011
1 parent 6448dfe commit 736f034
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 23 deletions.
29 changes: 29 additions & 0 deletions nml/actions/action0.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,29 @@
# Features that use an extended byte as ID (vehicles, sounds)
action0_extended_byte_id = [0, 1, 2, 3, 0x0C]

def adjust_value(value, org_value, unit, ottd_convert_func):
"""
Make sure that the property value written to the NewGRF will match exactly
the value as quoted
@param value: The value to check, converted to base units
@type value: L{Expression}
@param org_value: The original value as written in the input file
@type org_value: L{Expression}
@param unit: The unit of the org_value
@type unit: L{Unit} or C{None}
@return: The adjusted value
@rtype: L{Expression}
"""
while ottd_convert_func(value, unit) > org_value.value:
value = expression.ConstantNumeric(int(value.value - 1), value.pos)
while ottd_convert_func(value, unit) < org_value.value:
value = expression.ConstantNumeric(int(value.value + 1), value.pos)
return value

class Action0(base_action.BaseAction):
def __init__(self, feature, id):
self.feature = feature
Expand Down Expand Up @@ -128,6 +151,9 @@ def parse_property(feature, name, value, id, unit):
else: assert False

if unit is None or unit.type != 'nfo':
# Save the original value to test conversion against it
org_value = value

mul = 1
if 'unit_conversion' in prop: mul = prop['unit_conversion']
if unit is not None:
Expand All @@ -139,6 +165,9 @@ def parse_property(feature, name, value, id, unit):
raise generic.ScriptError("Unit conversion specified for property, but no constant value found", value.pos)
value = expression.ConstantNumeric(int(value.value * mul + 0.5), value.pos)

if unit is not None and 'adjust_value' in prop:
value = adjust_value(value, org_value, unit, prop['adjust_value'])

if isinstance(value, expression.ConstantFloat): # Always round floats
value = expression.ConstantNumeric(int(value.value + 0.5), value.pos)

Expand Down
11 changes: 7 additions & 4 deletions nml/actions/action0properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,17 @@ def cargo_list(value, max_num_cargos, prop_num, prop_size):
'name' : {'num': -1, 'string': None},
}

def ottd_display_speed(value, divisor, unit):
return (value.value / divisor * 10 / 16 * unit.ottd_mul) >> unit.ottd_shift

#
# Feature 0x00 (Trains)
#

properties[0x00] = {
'track_type' : {'size': 1, 'num': 0x05},
'ai_special_flag' : {'size': 1, 'num': 0x08},
'speed' : {'size': 2, 'num': 0x09, 'unit_type': 'speed', 'unit_conversion': 3.5790976},
'speed' : {'size': 2, 'num': 0x09, 'unit_type': 'speed', 'unit_conversion': 3.5790976, 'adjust_value': lambda val, unit: ottd_display_speed(val, 1, unit)},
'power' : {'size': 2, 'num': 0x0B, 'unit_type': 'power'},
'running_cost_factor' : {'size': 1, 'num': 0x0D},
'running_cost_base' : {'size': 4, 'num': 0x0E},
Expand Down Expand Up @@ -249,7 +252,7 @@ def roadveh_speed_prop(value):
return props

properties[0x01] = {
'speed' : {'custom_function' : roadveh_speed_prop, 'unit_type': 'speed', 'unit_conversion': 7.1581952},
'speed' : {'custom_function' : roadveh_speed_prop, 'unit_type': 'speed', 'unit_conversion': 7.1581952, 'adjust_value': lambda val, unit: ottd_display_speed(val, 2, unit)},
'running_cost_factor' : {'size': 1, 'num': 0x09},
'running_cost_base' : {'size': 4, 'num': 0x0A},
'sprite_id' : {'size': 1, 'num': 0x0E},
Expand Down Expand Up @@ -290,7 +293,7 @@ def speed_fraction_prop(value, propnr):
'sprite_id' : {'size': 1, 'num': 0x08},
'is_refittable' : {'size': 1, 'num': 0x09},
'cost_factor' : {'size': 1, 'num': 0x0A},
'speed' : {'size': 1, 'num': 0x0B, 'unit_type': 'speed', 'unit_conversion': 7.1581952},
'speed' : {'size': 1, 'num': 0x0B, 'unit_type': 'speed', 'unit_conversion': 7.1581952, 'adjust_value': lambda val, unit: ottd_display_speed(val, 2, unit)},
'cargo_capacity' : {'size': 2, 'num': 0x0D},
'running_cost_factor' : {'size': 1, 'num': 0x0F},
'sound_effect' : {'size': 1, 'num': 0x10},
Expand Down Expand Up @@ -318,7 +321,7 @@ def speed_fraction_prop(value, propnr):
'is_helicopter' : {'size': 1, 'num': 0x09},
'is_large' : {'size': 1, 'num': 0x0A},
'cost_factor' : {'size': 1, 'num': 0x0B},
'speed' : {'size': 1, 'num': 0x0C, 'unit_type': 'speed', 'unit_conversion': 0.279617},
'speed' : {'size': 1, 'num': 0x0C, 'unit_type': 'speed', 'unit_conversion': 0.279617, 'adjust_value': lambda val, unit: ottd_display_speed(val, 1, unit)},
'acceleration' : {'size': 1, 'num': 0x0D},
'running_cost_factor' : {'size': 1, 'num': 0x0E},
'passenger_capacity' : {'size': 2, 'num': 0x0F},
Expand Down
7 changes: 2 additions & 5 deletions nml/actions/action2var.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Action2Var(action2.Action2):
return value based on the result of the computation. The return value can
be either a 15bit integer or a reference to another action2.
@ivar type_byte: The size (byte, word, double word) and access type (own
@ivar type_byte: The size (byte, word, double word) and access type (own
object or related object). 0x89 (own object, double word)
and 0x8A (related object, double word) and the only
supported values.
Expand Down Expand Up @@ -667,10 +667,7 @@ def parse_minmax(value, unit_str, action_list, act6, offset):
"""
check_range = True
if unit_str is not None:
if not isinstance(value, expression.ConstantNumeric):
raise generic.ScriptError("Using a unit is only allowed in combination with a compile-time constant", value.pos)
assert unit_str in unit.units
result = expression.ConstantNumeric(int(value.value / unit.units[unit_str]['convert']))
raise generic.ScriptError("Using a unit is in switch-ranges is not (temporarily) not supported", value.pos)
elif isinstance(value, expression.ConstantNumeric):
result = value
elif isinstance(value, expression.Parameter) and isinstance(value.num, expression.ConstantNumeric):
Expand Down
2 changes: 2 additions & 0 deletions nml/ast/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ def __init__(self, name):
self.name = name
self.type = unit.units[name]['type']
self.convert = unit.units[name]['convert']
self.ottd_mul = unit.units[name]['ottd_mul']
self.ottd_shift = unit.units[name]['ottd_shift']

def __str__(self):
return self.name
Expand Down
2 changes: 1 addition & 1 deletion nml/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def check_range(value, min_value, max_value, name, pos):
@type value: C{int}
@param min_value: Minimum valid value.
@type min_value; C{int}
@type min_value: C{int}
@param max_value: Maximum valid value.
@type max_value: C{int}
Expand Down
4 changes: 2 additions & 2 deletions nml/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def p_generic_assignment(self, t):
t[0] = assignment.Assignment(t[1], t[3], t.lineno(1))

def p_generic_assignment_list(self, t):
'''generic_assignment_list :
'''generic_assignment_list :
| generic_assignment_list generic_assignment'''
t[0] = [] if len(t) == 1 else t[1] + [t[2]]

Expand Down Expand Up @@ -639,7 +639,7 @@ def p_skip_all(self, t):
def p_engine_override(self, t):
'engine_override : ENGINE_OVERRIDE LPAREN expression_list RPAREN SEMICOLON'
t[0] = override.EngineOverride(t[3], t.lineno(1))

def p_sort_vehicles(self, t):
'sort_vehicles : SORT_VEHICLES LPAREN expression_list RPAREN SEMICOLON'
t[0] = sort_vehicles.SortVehicles(t[3], t.lineno(1))
Expand Down
27 changes: 16 additions & 11 deletions nml/unit.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@

units = {}

units['nfo'] = {'type': 'nfo', 'convert': 1} #don't convert, take value literal
units['nfo'] = {'type': 'nfo', 'convert': 1, 'ottd_mul': 1, 'ottd_shift': 0} #don't convert, take value literal

# Conversion factor works like this:
# 1 reference_unit = convert other_unit
# So nfo_value = property_value / convert * property_specific_conversion_factor

# ottd_mul and ottd_shift are the values taken from OpenTTD's src/strings.cpp and
# are used to calculate the displayed value by OpenTTD. If possible, adjust_values
# increases or decreases the NFO value so that the desired display value is actually
# achieved

#Speed (reference: m/s)
units['mph'] = {'type': 'speed', 'convert': 2.236936}
units['km/h'] = {'type': 'speed', 'convert': 3.6}
units['m/s'] = {'type': 'speed', 'convert': 1}
units['mph'] = {'type': 'speed', 'convert': 2.236936, 'ottd_mul': 1, 'ottd_shift': 0}
units['km/h'] = {'type': 'speed', 'convert': 3.6, 'ottd_mul': 103, 'ottd_shift': 6}
units['m/s'] = {'type': 'speed', 'convert': 1, 'ottd_mul': 1831, 'ottd_shift': 12}

#Power (reference: hpI (imperial hp))
units['hp'] = {'type': 'power', 'convert': 1} # Default to imperial hp
units['kW'] = {'type': 'power', 'convert': 0.745699}
units['hpM'] = {'type': 'power', 'convert': 1.013869}
units['hpI'] = {'type': 'power', 'convert': 1}
units['hp'] = {'type': 'power', 'convert': 1, 'ottd_mul': 1, 'ottd_shift': 0} # Default to imperial hp
units['kW'] = {'type': 'power', 'convert': 0.745699, 'ottd_mul': 6109, 'ottd_shift': 13}
units['hpM'] = {'type': 'power', 'convert': 1.013869, 'ottd_mul': 4153, 'ottd_shift': 12}
units['hpI'] = {'type': 'power', 'convert': 1, 'ottd_mul': 1, 'ottd_shift': 0}

#Weight (reference: ton)
units['ton'] = {'type': 'weight', 'convert': 1}
units['tons'] = {'type': 'weight', 'convert': 1}
units['kg'] = {'type': 'weight', 'convert': 1000.0}
units['ton'] = {'type': 'weight', 'convert': 1, 'ottd_mul': 1, 'ottd_shift': 0}
units['tons'] = {'type': 'weight', 'convert': 1, 'ottd_mul': 1, 'ottd_shift': 0}
units['kg'] = {'type': 'weight', 'convert': 1000.0, 'ottd_mul': 1000, 'ottd_shift': 0}

0 comments on commit 736f034

Please sign in to comment.