Beispiel #1
0
    def add_unit(self, repeat=1, **kwargs):
        # how many unique units? (units can be repeated, we are using count for numerid ID, so we want uniques)
        count = len(set(self.units))

        if kwargs.get('type', None) is not None:
            unit = kwargs['type'](consist=self, **kwargs)
        else:
            unit = RoadVehicle(consist=self, **kwargs)

        if count == 0:
            unit.id = self.id # first vehicle gets no numeric id suffix - for compatibility with buy menu list ids etc
        else:
            unit.id = self.id + '_' + str(count)
        unit.numeric_id = self.get_and_verify_numeric_id(count)

        if self.semi_truck_so_redistribute_capacity:
            if count == 0 and kwargs.get('capacity', 0) != 0:
                # guard against lead unit having capacity set in declared props (won't break, just wrong)
                utils.echo_message("Error: " + self.id + ".  First unit of semi-truck must have capacity 0")
            if count == 1:
                # semi-trucks need some capacity moved to lead unit to gain sufficient TE
                # this automagically does that, allowing capacities to be defined simply on the trailer in the vehicle definition
                # sometimes a greater good requires a small evil, although this will probably go wrong eh?
                if repeat != 1:
                    # guard against unintended application of this to anything except first trailer
                    utils.echo_message("Error: " + self.id + ".  Semi-truck cannot repeat first trailer in consist")
                specified_capacities = unit.capacities
                unit.capacities = [int(math.floor(0.5 * capacity)) for capacity in specified_capacities]
                self.units[0].capacities = [int(math.ceil(0.5 * capacity)) for capacity in specified_capacities]

        for repeat_num in range(repeat):
            self.units.append(unit)
Beispiel #2
0
def get_ships_in_buy_menu_order():
    ships = []
    # first compose the buy menu order list
    buy_menu_sort_order = []
    if repo_vars.get('roster', '*') == '*':
        active_rosters = [roster.id for roster in registered_rosters]
    else:
        active_rosters = [repo_vars['roster']]  # make sure it's iterable
    for roster in registered_rosters:
        if roster.id in active_rosters:
            buy_menu_sort_order.extend(roster.buy_menu_sort_order)
            ships.extend(roster.ships_in_buy_menu_order)

    # now guard against any ships missing from buy menu order or vice versa, as that wastes time asking 'wtf?' when they don't appear in game
    ship_id_defender = set([ship.id for ship in ships])
    buy_menu_defender = set(buy_menu_sort_order)
    for id in buy_menu_defender.difference(ship_id_defender):
        utils.echo_message(
            "Warning: ship " + id +
            " in buy_menu_sort_order, but not found in registered ships")
    for id in ship_id_defender.difference(buy_menu_defender):
        utils.echo_message(
            "Warning: ship " + id +
            " in ships, but not in buy_menu_sort_order - won't show in game")
    return ships
Beispiel #3
0
 def get_industry_name(self, industry, economy=None):
     # industries don't store the name directly as a python attr, but in lang - so look it up in base_lang using string id
     name = industry.get_property('name', economy)
     string_id = utils.unwrap_nml_string_declaration(name)
     if string_id not in base_lang_strings:
         utils.echo_message('Warning: string ' + string_id + ' missing for docs')
     return base_lang_strings.get(string_id, 'NO NAME ' + str(name) + ' ' + industry.id)
Beispiel #4
0
 def weight(self):
     mult = self.weight_multiplier
     # trams are 10% heavier per capacity
     if self.roadveh_flag_tram:
         mult = mult + 0.1
     consist_weight = mult * self.total_capacities[1]
     if consist_weight > 63:
         utils.echo_message("Error: consist weight is " + str(consist_weight) + "t for " + self.id + "; must be < 63t")
     return min(consist_weight, 63)
Beispiel #5
0
 def get_reduced_set_of_variant_dates(self):
     # find all the unique dates that will need a switch constructing
     years = set()
     for variant in self.model_variants:
         years.update((variant.intro_date, variant.end_date))
     years = sorted(years)
     # quick integrity check
     if years[0] != 0:
         utils.echo_message(self.id + " doesn't have at least one model variant with intro date 0 (required for nml switches to work)")
     return years
Beispiel #6
0
 def get_and_verify_numeric_id(self, offset):
     numeric_id = self.base_numeric_id + offset
     # guard against the ID being too large to build in an articulated consist
     if numeric_id > 16383:
         utils.echo_message("Error: numeric_id " + str(numeric_id) + " for " + self.id + " can't be used (16383 is max ID for articulated vehicles)")
     # non-blocking guard on duplicate IDs
     for id in numeric_id_defender:
         if id == numeric_id:
             utils.echo_message("Error: consist " + self.id + " unit id collides (" + str(numeric_id) + ") with units in another consist")
     numeric_id_defender.append(numeric_id)
     return numeric_id
Beispiel #7
0
    def __init__(self, id, **kwargs):
        self.id = id
        self.cargo_label = kwargs["cargo_label"]
        self.type_name = kwargs["type_name"]
        self.unit_name = kwargs["unit_name"]
        self.type_abbreviation = kwargs["type_abbreviation"]
        self.sprite = kwargs["sprite"]
        self.weight = kwargs["weight"]
        self.is_freight = kwargs["is_freight"]
        self.cargo_classes = kwargs["cargo_classes"]
        self.town_growth_effect = kwargs["town_growth_effect"]
        self.town_growth_multiplier = kwargs["town_growth_multiplier"]
        self.units_of_cargo = kwargs["units_of_cargo"]
        self.items_of_cargo = kwargs["items_of_cargo"]
        self.penalty_lowerbound = kwargs["penalty_lowerbound"]
        self.single_penalty_length = kwargs["single_penalty_length"]
        self.price_factor = kwargs["price_factor"]
        self.capacity_multiplier = kwargs["capacity_multiplier"]
        # not nml properties
        self.allow_animated_pixels = kwargs.get(
            "allow_animated_pixels",
            False)  # suppress nml warnings about animated pixels
        self.icon_indices = kwargs[
            "icon_indices"]  # icon indices relate to position of icon in cargo icons spritesheet
        self.economy_variations = {}
        for economy in registered_economies:
            if self.id in economy.cargo_ids:
                # create an economy variation
                numeric_id = economy.cargo_ids.index(self.id)
                # As of May 2015, OTTD requires some cargos in specific slots, otherwise default houses break
                mandatory_numeric_ids = {
                    "PASS": 0,
                    "MAIL": 2,
                    "GOOD": 5,
                    "FOOD": 11
                }
                for key, value in mandatory_numeric_ids.items():
                    if self.cargo_label == key and numeric_id != value:
                        raise Exception("Economy " + economy.id +
                                        ": has cargo " + self.id +
                                        " in position " + str(numeric_id) +
                                        "; needs to be in position " +
                                        str(value))
                self.economy_variations[economy] = {"numeric_id": numeric_id}

        # guard against overlapping icon indices, icons should be unique per cargo
        # if two cargos use same icon (1) don't, copy-paste, then adjust some pixels for one of them (2) see 1
        for cargo in registered_cargos:
            if cargo.icon_indices == self.icon_indices:
                utils.echo_message(
                    "Cargo " + self.id +
                    " has overlapping icon_indices with cargo " + cargo.id)
Beispiel #8
0
    def get_engine_cost_points(self):
        # Up to 20 points for power. 1 point per 100hp
        # Power is therefore capped at 2000hp by design, this isn't a hard limit, but raise a warning
        if self.power > 2000:
            utils.echo_message("Consist " + self.id + " has power > 2000hp, which is too much")
        power_cost_points = self.power / 100

        # Up to 30 points for speed above up to 90mph. 1 point per 3mph
        if self.speed > 90:
            utils.echo_message("Consist " + self.id + " has speed > 90, which is too much")
        speed_cost_points = min(self.speed, 90) / 3

        # Up to 20 points for intro date after 1870. 1 point per 8 years.
        # Intro dates capped at 2030, this isn't a hard limit, but raise a warning
        if self.intro_date > 2030:
            utils.echo_message("Consist " + self.id + " has intro_date > 2030, which is too much")
        date_cost_points = max((self.intro_date - 1870), 0) / 8

        # Up to 20 points for capacity. 1 point per 8t.
        # Capacity capped at 160, this isn't a hard limit, but raise a warning
        if self.total_capacities[1] > 160:
            utils.echo_message("Consist " + self.id + " has capacity > 160, which is too much")
        consist_capacity_points = min(self.total_capacities[1], 160)

        return power_cost_points + speed_cost_points + date_cost_points + consist_capacity_points
Beispiel #9
0
 def __init__(self, **kwargs):
     self.id = kwargs.get('id', None)
     self.vehicle_module_path = inspect.stack()[2][1]
     # setup properties for this consist (props either shared for all vehicles, or placed on lead vehicle of consist)
     self._name = kwargs.get('name', None) # private as 'name' is an @property method to add type substring
     self.base_numeric_id = kwargs.get('base_numeric_id', None)
     self.road_type = kwargs.get('road_type', None)
     self.tram_type = kwargs.get('tram_type', None)
     if self.road_type is not None and self.tram_type is not None:
         utils.echo_message("Error: " + self.id + ". Vehicles must not have both road_type and tram_type properties set.  Set one of these only")
     self.roadveh_flag_tram = True if self.tram_type is not None else None
     self.intro_date = kwargs.get('intro_date', None)
     self.vehicle_life = kwargs.get('vehicle_life', None)
     self._power = kwargs.get('power', None)
     # sound effects are faff, they can be set by consist subclass (Bus vs. Coach), or will fall back to a default sound for the vehicle subclass (by power type)
     self._sound_effect = None
     self.default_sound_effect = None # set by unit subclasses
     # suffix for 'Diesel', 'Steam' etc in name string, set by unit subclasses, but stored in consist as it's a consist property
     self._power_type_suffix = None
     # option for multiple default cargos, cascading if first cargo(s) are not available
     self.default_cargos = []
     # semi-trucks need some redistribution of capacity to get correct TE (don't use this of other magic, bad idea)
     self.semi_truck_so_redistribute_capacity = kwargs.get('semi_truck_so_redistribute_capacity', False)
     self._speed = kwargs.get('speed', None)
     self.class_refit_groups = []
     self.label_refits_allowed = [] # no specific labels needed
     self.label_refits_disallowed = []
     self.autorefit = False
     self.loading_speed_multiplier = kwargs.get('loading_speed_multiplier', 1)
     self.cargo_age_period = kwargs.get('cargo_age_period', global_constants.CARGO_AGE_PERIOD)
     # arbitrary adjustments of points that can be applied to adjust buy cost and running cost, over-ride in consist as needed
     # values can be -ve or +ve to dibble specific vehicles (but total calculated points cannot exceed 255)
     self.type_base_buy_cost_points = kwargs.get('type_base_buy_cost_points', 0)
     self.type_base_running_cost_points = kwargs.get('type_base_running_cost_points', 0)
     # multiplier of capacity, used to set consist weight, over-ride in vehicle sub-class as needed
     # set this to the value for road vehicles...trams will be automatically adjusted
     self.weight_multiplier = 0.4
     # create structure to hold the units
     self.units = []
     # create a structure for cargo /livery graphics options
     self.gestalt_graphics = GestaltGraphics()
     # roster is set when the vehicle is registered to a roster, only one roster per vehicle
     self.roster_id = None
Beispiel #10
0
 def pretty_print_cargo_classes(self, cargo):
     result = []
     pretty_names = {'CC_PASSENGERS': 'Passengers',
                     'CC_MAIL': 'Mail',
                     'CC_EXPRESS': 'Express',
                     'CC_ARMOURED': 'Armoured',
                     'CC_BULK': 'Bulk (uncountable)',
                     'CC_PIECE_GOODS': 'Piece Goods (countable)',
                     'CC_LIQUID': 'Liquid',
                     'CC_REFRIGERATED': 'Refrigerated',
                     'CC_HAZARDOUS': 'Hazardous',
                     'CC_COVERED': 'Covered (weather protected)',
                     'CC_NON_POURABLE': 'Not Pourable',
                     }
     cargo_classes_as_literal = cargo.cargo_classes[8:-1]
     cargo_classes = [i.strip() for i in cargo_classes_as_literal.split(',')]
     for cargo_class in cargo_classes:
         if cargo_class not in pretty_names:
             utils.echo_message('cargo class ' + cargo_class + ' is not pretty-printable (used in cargo ' + cargo.id + ')')
         else:
             result.append(pretty_names[cargo_class])
     return ', '.join(result)
 def generic_rows(self):
     utils.echo_message(
         'generic_rows not implemented in GestaltGraphicsCargoSpecificLivery (by design)'
     )
     return None
Beispiel #12
0
 def assert_cargo_labels(self, cargo_labels):
     for i in cargo_labels:
         if i not in global_constants.cargo_labels:
             utils.echo_message("Warning: vehicle " + self.id + " references cargo label " + i + " which is not defined in the cargo table")