Esempio n. 1
0
def log_planet_count_dist(sys_list):

    planet_count_dist = {}
    planet_size_dist = {size: 0 for size in planets.planet_sizes}
    for system in sys_list:
        planet_count = 0
        for planet in fo.sys_get_planets(system):
            this_size = fo.planet_get_size(planet)
            if this_size in planets.planet_sizes:
                planet_count += 1
                planet_size_dist[this_size] += 1
        planet_count_dist.setdefault(planet_count, [0])[0] += 1
    planet_tally = sum(planet_size_dist.values())

    count_distribution_table = Table(
        [Text('planets in system'), Text('num of systems'), Float('% of systems', precession=1)],
        table_name='Planet Count Distribution'
    )
    for planet_count, sys_count in planet_count_dist.items():
        count_distribution_table.add_row((planet_count, sys_count[0], 100.0 * sys_count[0] / len(sys_list)))
    print(count_distribution_table)
    print()

    size_distribution = Table(
        [Text('size'), Text('count'), Float('% of planets', precession=1)],
        table_name='Planet Size Distribution'
    )
    for planet_size, planet_count in sorted(planet_size_dist.items()):
        size_distribution.add_row((planet_size, planet_count, 100.0 * planet_count / planet_tally))
    print(size_distribution)
    print()
Esempio n. 2
0
def log_planets():
    universe = fo.get_universe()

    planets_table = Table([
        Text('id'),
        Text('name'),
        Text('system'),
        Text('type'),
        Sequence('specials'),
        Text('species'),
        Sequence('buildings')
    ],
                          table_name='Planets summary')
    # group planets by system
    for sid in fo.get_systems():
        for pid in fo.sys_get_planets(sid):
            planet = universe.getPlanet(pid)

            planet_type = fo.planet_get_type(pid).name
            planet_size = fo.planet_get_size(pid).name
            if planet_type != planet_size:
                planet_type = '%s %s' % (planet_type, planet_size)

            buildings = [
                universe.getBuilding(x).name for x in planet.buildingIDs
            ]
            planets_table.add_row([
                pid, planet.name, planet.systemID, planet_type,
                list(planet.specials), planet.speciesName, buildings
            ])

    # Printing too much info at once will lead to truncation of text
    for line in planets_table.get_table().split('\n'):
        print line
Esempio n. 3
0
def log_planets():
    universe = fo.get_universe()

    planets_table = Table(
        [Text('id'), Text('name'), Text('system'), Text('type'),
         Sequence('specials'), Text('species'), Sequence('buildings')],
        table_name='Planets summary')
    # group planets by system
    for sid in fo.get_systems():
        for pid in fo.sys_get_planets(sid):
            planet = universe.getPlanet(pid)

            planet_type = fo.planet_get_type(pid).name
            planet_size = fo.planet_get_size(pid).name
            if planet_type != planet_size:
                planet_type = '%s %s' % (planet_type, planet_size)

            buildings = [universe.getBuilding(x).name for x in planet.buildingIDs]
            planets_table.add_row([
                pid, planet.name, planet.systemID, planet_type, list(planet.specials), planet.speciesName, buildings
            ])

    # Printing too much info at once will lead to truncation of text
    for line in planets_table.get_table().split('\n'):
        print line
Esempio n. 4
0
def log_planet_count_dist(sys_list):

    planet_count_dist = {}
    planet_size_dist = {size: 0 for size in planets.planet_sizes}
    for system in sys_list:
        planet_count = 0
        for planet in fo.sys_get_planets(system):
            this_size = fo.planet_get_size(planet)
            if this_size in planets.planet_sizes:
                planet_count += 1
                planet_size_dist[this_size] += 1
        planet_count_dist.setdefault(planet_count, [0])[0] += 1
    planet_tally = sum(planet_size_dist.values())

    count_distribution_table = Table(
        [Text('planets in system'), Text('num of systems'), Float('% of systems', precession=1)],
        table_name='Planet Count Distribution'
    )
    for planet_count, sys_count in planet_count_dist.items():
        count_distribution_table.add_row((planet_count, sys_count[0], 100.0 * sys_count[0] / len(sys_list)))
    print count_distribution_table
    print

    size_distribution = Table(
        [Text('size'), Text('count'), Float('% of planets', precession=1)],
        table_name='Planet Size Distribution'
    )
    for planet_size, planet_count in sorted(planet_size_dist.items()):
        size_distribution.add_row((planet_size, planet_count, 100.0 * planet_count / planet_tally))
    print size_distribution
    print
Esempio n. 5
0
def generate_fields(systems):
    """
    Generates stationary fields in some randomly chosen empty no star systems.
    """
    # filter out all empty no star systems
    candidates = [
        s for s in systems
        if (fo.sys_get_star_type(s) == fo.starType.noStar) and (
            not fo.sys_get_planets(s))
    ]
    # make sure we have at least one empty no star system, otherwise return without creating any fields
    if not candidates:
        print("...no empty no star systems found, no fields created")
        return
    # pick 10-15% of all empty no star systems to create stationary fields in them, but at least one
    accepted = sample(candidates,
                      max(int(len(candidates) * uniform(0.1, 0.15)), 1))
    for system in accepted:
        # randomly pick a field type
        field_type = choice(["FLD_NEBULA_1", "FLD_NEBULA_2", "FLD_NEBULA_3"])
        # and create the field
        if fo.create_field_in_system(field_type, uniform(40, 120),
                                     system) == fo.invalid_object():
            # create field failed, report an error
            report_error(
                "Python generate_fields: create field %s in system %d failed" %
                (field_type, system))
    print("...fields created in %d systems out of %d empty no star systems" %
          (len(accepted), len(candidates)))
Esempio n. 6
0
def compile_home_system_list(num_home_systems, systems):
    """
    Compiles a list with a requested number of home systems.
    """

    # if the list of systems to choose home systems from is empty, report an error and return empty list
    if not systems:
        util.report_error("Python generate_home_system_list: no systems to choose from")
        return []

    # calculate an initial minimal number of jumps that the home systems should be apart,
    # based on the total number of systems to choose from and the requested number of home systems
    min_jumps = max(int(float(len(systems)) / float(num_home_systems * 2)), 5)
    # try to find the home systems, decrease the min jumps until enough systems can be found, or the min jump distance
    # gets reduced to 0 (meaning we don't have enough systems to choose from at all)
    while min_jumps > 0:
        print "Trying to find", num_home_systems, "home systems that are at least", min_jumps, "jumps apart"
        # try to find home systems...
        home_systems = find_systems_with_min_jumps_between(num_home_systems, systems, min_jumps)
        # ...check if we got enough...
        if len(home_systems) >= num_home_systems:
            # ...yes, we got what we need, so let's break out of the loop
            break
        print "Home system min jump conflict: %d systems and %d empires, tried %d min jump and failed"\
              % (len(systems), num_home_systems, min_jumps)
        # ...no, decrease the min jump distance and try again
        min_jumps -= 1

    # check if the loop above delivered a list with enough home systems, or if it exited because the min jump distance
    # has been decreased to 0 without finding enough systems
    # in that case, our galaxy obviously is too crowded, report an error and return an empty list
    if len(home_systems) < num_home_systems:
        util.report_error("Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems"
                          % (num_home_systems, len(systems)))
        return []

    # make sure all our home systems have a "real" star (that is, a star that is not a neutron star, black hole,
    # or even no star at all) and at least one planet in it
    for home_system in home_systems:
        # if this home system has no "real" star, change star type to a randomly selected "real" star
        if fo.sys_get_star_type(home_system) not in starsystems.star_types_real:
            star_type = random.choice(starsystems.star_types_real)
            print "Home system", home_system, "has star type", fo.sys_get_star_type(home_system),\
                  ", changing that to", star_type
            fo.sys_set_star_type(home_system, star_type)

        # if this home system has no planets, create one in a random orbit
        # we take random values for type and size, as these will be set to suitable values later
        if not fo.sys_get_planets(home_system):
            print "Home system", home_system, "has no planets, adding one"
            planet = fo.create_planet(random.choice(planets.planet_sizes_real),
                                      random.choice(planets.planet_types_real),
                                      home_system, random.randint(0, fo.sys_get_num_orbits(home_system) - 1), "")
            # if we couldn't create the planet, report an error and return an empty list
            if planet == fo.invalid_object():
                util.report_error("Python generate_home_system_list: couldn't create planet in home system")
                return []

    return home_systems
Esempio n. 7
0
def execute_turn_events():
    print "Executing turn events for turn", fo.current_turn()

    # creating fields
    systems = fo.get_systems()
    radius = fo.get_universe_width() / 2.0
    if random() < max(0.0003 * radius, 0.03):
        if random() < 0.4:
            field_type = "FLD_MOLECULAR_CLOUD"
            size = 5.0
        else:
            field_type = "FLD_ION_STORM"
            size = 5.0

        x = y = radius
        dist_from_center = 0.0
        while (dist_from_center < radius) or any(hypot(fo.get_x(s) - x, fo.get_y(s) - y) < 50.0 for s in systems):
            angle = random() * 2.0 * pi
            dist_from_center = radius + uniform(min(max(radius * 0.02, 10), 50.0), min(max(radius * 0.05, 20), 100.0))
            x = radius + (dist_from_center * sin(angle))
            y = radius + (dist_from_center * cos(angle))

        print "...creating new", field_type, "field, at distance", dist_from_center, "from center"
        if fo.create_field(field_type, x, y, size) == fo.invalid_object():
            print >> sys.stderr, "Turn events: couldn't create new field"

    # creating monsters
    gsd = fo.get_galaxy_setup_data()
    monster_freq = MONSTER_FREQUENCY[gsd.monsterFrequency]
    # monster freq ranges from 30 (= one monster per 30 systems) to 3 (= one monster per 3 systems)
    # (example: low monsters and 150 Systems results in 150 / 30 * 0.001 = 0.005)
    if monster_freq > 0 and random() < len(systems) / monster_freq * 0.001:
        #only spawn Krill at the moment, other monsters can follow in the future
        if random() < 1:
            monster_type = "SM_KRILL_1"
        else:
            monster_type = "SM_FLOATER"

        # search for systems without planets or fleets
        candidates = [s for s in systems if len(fo.sys_get_planets(s)) <= 0 and len(fo.sys_get_fleets(s)) <= 0]
        if not candidates:
            print >> sys.stderr, "Turn events: unable to find system for monster spawn"
        else:
            system = choice(candidates)
            print "...creating new", monster_type, "at", fo.get_name(system)

            # create monster fleet
            monster_fleet = fo.create_monster_fleet(system)
            # if fleet creation fails, report an error
            if monster_fleet == fo.invalid_object():
                print >> sys.stderr, "Turn events: unable to create new monster fleet"
            else:
                # create monster, if creation fails, report an error
                monster = fo.create_monster(monster_type, monster_fleet)
                if monster == fo.invalid_object():
                    print >> sys.stderr, "Turn events: unable to create monster in fleet"

    return True
Esempio n. 8
0
def log_planet_type_summary(sys_list):
    planet_type_summary = {k: 0 for k in planets.planet_types}
    for system in sys_list:
        for planet in fo.sys_get_planets(system):
            planet_type_summary[fo.planet_get_type(planet)] += 1
    planet_total = sum(planet_type_summary.values())
    print "Planet Type Summary for a total of %d placed planets" % planet_total
    for planet_type, planet_count in planet_type_summary.items():
        print "%-12s %4.1f%%" % (planet_type.name, 100.0 * planet_count / planet_total)
Esempio n. 9
0
def count_planets_in_systems(systems, planet_types_filter=HS_ACCEPTABLE_PLANET_TYPES):
    """
    Return the total number of planets in the specified group of systems,
    only count the planet types specified in planet_types_filter.
    """
    num_planets = 0
    for system in systems:
        num_planets += len([p for p in fo.sys_get_planets(system) if fo.planet_get_type(p) in planet_types_filter])
    return num_planets
Esempio n. 10
0
def count_planets_in_systems(systems, planet_types_filter=HS_ACCEPTABLE_PLANET_TYPES):
    """
    Return the total number of planets in the specified group of systems,
    only count the planet types specified in planet_types_filter.
    """
    num_planets = 0
    for system in systems:
        num_planets += len([p for p in fo.sys_get_planets(system) if fo.planet_get_type(p) in planet_types_filter])
    return num_planets
Esempio n. 11
0
def log_planet_type_summary(sys_list):
    planet_type_summary = {k: 0 for k in planets.planet_types}
    for system in sys_list:
        for planet in fo.sys_get_planets(system):
            planet_type_summary[fo.planet_get_type(planet)] += 1
    planet_total = sum(planet_type_summary.values())
    print "Planet Type Summary for a total of %d placed planets" % planet_total
    for planet_type, planet_count in planet_type_summary.items():
        print "%-12s %4.1f%%" % (planet_type.name,
                                 100.0 * planet_count / planet_total)
Esempio n. 12
0
def log_systems():
    universe = fo.get_universe()

    systems_table = Table(
        [Text('id'), Text('name'), Sequence('planets'), Sequence('connections'), Text('star')],
        table_name='System summary')
    for sid in fo.get_systems():
        system = universe.getSystem(sid)
        systems_table.add_row([
            sid, system.name, fo.sys_get_planets(sid), fo.sys_get_starlanes(sid), system.starType.name
        ])

    # Printing too much info at once will lead to truncation of text
    for line in systems_table.get_table().split('\n'):
        print(line)
Esempio n. 13
0
def log_systems():
    universe = fo.get_universe()

    systems_table = Table(
        [Text('id'), Text('name'), Sequence('planets'), Sequence('connections'), Text('star')],
        table_name='System summary')
    for sid in fo.get_systems():
        system = universe.getSystem(sid)
        systems_table.add_row([
            sid, system.name, fo.sys_get_planets(sid), fo.sys_get_starlanes(sid), system.starType.name
        ])

    # Printing too much info at once will lead to truncation of text
    for line in systems_table.get_table().split('\n'):
        print line
Esempio n. 14
0
def name_planets(system):
    """
    Sets the names of the planets of the specified system.

    Planet name is system name + planet number (as roman number)
    unless it's an asteroid belt, in that case name is system
    name + 'asteroid belt' (localized).
    """
    # iterate over all planets in the system
    sys_name = fo.get_name(system)
    for planet in fo.sys_get_planets(system):
        name = fo.user_string("NEW_PLANET_NAME")
        name = name.replace("%1%", sys_name)
        name = name.replace("%2%", fo.planet_cardinal_suffix(planet))
        fo.set_name(planet, name)
Esempio n. 15
0
def name_planets(system):
    """
    Sets the names of the planets of the specified system.

    Planet name is system name + planet number (as roman number)
    unless it's an asteroid belt, in that case name is system
    name + 'asteroid belt' (localized).
    """
    # iterate over all planets in the system
    sys_name = fo.get_name(system)
    for planet in fo.sys_get_planets(system):
        name = fo.user_string("NEW_PLANET_NAME")
        name = name.replace("%1%", sys_name)
        name = name.replace("%2%", fo.planet_cardinal_suffix(planet))
        fo.set_name(planet, name)
Esempio n. 16
0
def log_planet_type_summary(sys_list):
    planet_type_summary_table = {k: 0 for k in planets.planet_types}
    for system in sys_list:
        for planet in fo.sys_get_planets(system):
            planet_type_summary_table[fo.planet_get_type(planet)] += 1
    planet_total = sum(planet_type_summary_table.values())

    type_summary_table = Table(
        [Text('planet type', align='<'), Float('% of planets', precession=1)],
        table_name='Planet Type Summary for a total of %d placed planets' % planet_total
    )

    for planet_type, planet_count in sorted(planet_type_summary_table.items()):
        type_summary_table.add_row((planet_type.name, 100.0 * planet_count / planet_total))
    print type_summary_table
    print
Esempio n. 17
0
def log_planet_type_summary(sys_list):
    planet_type_summary_table = {k: 0 for k in planets.planet_types}
    for system in sys_list:
        for planet in fo.sys_get_planets(system):
            planet_type_summary_table[fo.planet_get_type(planet)] += 1
    planet_total = sum(planet_type_summary_table.values())

    type_summary_table = Table(
        [Text('planet type', align='<'), Float('% of planets', precession=1)],
        table_name='Planet Type Summary for a total of %d placed planets' % planet_total
    )

    for planet_type, planet_count in sorted(planet_type_summary_table.items()):
        type_summary_table.add_row((planet_type.name, 100.0 * planet_count / planet_total))
    print(type_summary_table)
    print()
Esempio n. 18
0
def log_planet_count_dist(sys_list):
    planet_count_dist = {}
    planet_size_dist = {size : 0 for size in planets.planet_sizes}
    for system in sys_list:
        planet_count = 0
        for planet in fo.sys_get_planets(system):
            this_size = fo.planet_get_size(planet)
            if this_size in planets.planet_sizes:
                planet_count += 1
                planet_size_dist[this_size] += 1
        planet_count_dist.setdefault(planet_count, [0])[0] += 1
    planet_tally = sum(planet_size_dist.values())
    print "Planet Count Distribution: planets_in_system | num_systems | % of systems"
    for planet_count, sys_count in planet_count_dist.items():
        print "\t\t\t%2d  | %5d | %4.1f%%" % (planet_count, sys_count[0], 100.0 * sys_count[0] / len(sys_list))
    print
    print "Planet Size Distribution: size | count | % of planets"
    for planet_size, planet_count in planet_size_dist.items():
        print "\t\t%-12s | %5d | %4.1f%%" % (planet_size, planet_count, 100.0 * planet_count / planet_tally)
Esempio n. 19
0
def log_planet_count_dist(sys_list):

    planet_count_dist = {}
    planet_size_dist = {size: 0 for size in planets.planet_sizes}
    for system in sys_list:
        planet_count = 0
        for planet in fo.sys_get_planets(system):
            this_size = fo.planet_get_size(planet)
            if this_size in planets.planet_sizes:
                planet_count += 1
                planet_size_dist[this_size] += 1
        planet_count_dist.setdefault(planet_count, [0])[0] += 1
    planet_tally = sum(planet_size_dist.values())

    count_distribution_table = Table(
        Text("planets in system"),
        Text("num of systems"),
        Number("% of systems", precession=1),
        table_name="Planet Count Distribution",
    )
    for planet_count, sys_count in planet_count_dist.items():
        count_distribution_table.add_row(
            planet_count,
            sys_count[0],
            100.0 * sys_count[0] / len(sys_list),
        )
    count_distribution_table.print_table(print)
    print()

    size_distribution = Table(
        Text("size"),
        Text("count"),
        Number("% of planets", precession=1),
        table_name="Planet Size Distribution",
    )
    for planet_size, planet_count in sorted(planet_size_dist.items()):
        size_distribution.add_row(
            planet_size,
            planet_count,
            100.0 * planet_count / planet_tally,
        )
    size_distribution.print_table(print)
    print()
Esempio n. 20
0
def log_planet_type_summary(sys_list):
    planet_type_summary_table = {k: 0 for k in planets.planet_types}
    for system in sys_list:
        for planet in fo.sys_get_planets(system):
            planet_type_summary_table[fo.planet_get_type(planet)] += 1
    planet_total = sum(planet_type_summary_table.values())

    type_summary_table = Table(
        Text("planet type", align="<"),
        Number("% of planets", precession=1),
        table_name="Planet Type Summary for a total of %d placed planets" %
        planet_total,
    )

    for planet_type, planet_count in sorted(planet_type_summary_table.items()):
        type_summary_table.add_row(
            planet_type.name,
            100.0 * planet_count / planet_total,
        )
    type_summary_table.print_table(print)
    print()
Esempio n. 21
0
def log_planet_count_dist(sys_list):
    planet_count_dist = {}
    planet_size_dist = {size: 0 for size in planets.planet_sizes}
    for system in sys_list:
        planet_count = 0
        for planet in fo.sys_get_planets(system):
            this_size = fo.planet_get_size(planet)
            if this_size in planets.planet_sizes:
                planet_count += 1
                planet_size_dist[this_size] += 1
        planet_count_dist.setdefault(planet_count, [0])[0] += 1
    planet_tally = sum(planet_size_dist.values())
    print "Planet Count Distribution: planets_in_system | num_systems | % of systems"
    for planet_count, sys_count in planet_count_dist.items():
        print "\t\t\t%2d  | %5d | %4.1f%%" % (
            planet_count, sys_count[0], 100.0 * sys_count[0] / len(sys_list))
    print
    print "Planet Size Distribution: size | count | % of planets"
    for planet_size, planet_count in planet_size_dist.items():
        print "\t\t%-12s | %5d | %4.1f%%" % (planet_size, planet_count, 100.0 *
                                             planet_count / planet_tally)
Esempio n. 22
0
def log_systems():
    universe = fo.get_universe()

    systems_table = Table(
        Text("id"),
        Text("name"),
        Sequence("planets"),
        Sequence("connections"),
        Text("star"),
        table_name="System summary",
    )
    for sid in fo.get_systems():
        system = universe.getSystem(sid)
        systems_table.add_row(
            sid,
            system.name,
            fo.sys_get_planets(sid),
            fo.sys_get_starlanes(sid),
            system.starType.name,
        )
    systems_table.print_table(print)
Esempio n. 23
0
def name_planets(system):
    """
    Sets the names of the planets of the specified system.

    Planet name is system name + planet number (as roman number)
    unless it's an asteroid belt, in that case name is system
    name + 'asteroid belt' (localized).
    """
    planet_number = 1
    # iterate over all planets in the system
    for planet in fo.sys_get_planets(system):
        # use different naming methods for "normal" planets and asteroid belts
        if fo.planet_get_type(planet) == fo.planetType.asteroids:
            # get localized text from stringtable
            name = fo.user_string("PL_ASTEROID_BELT_OF_SYSTEM")
            # %1% parameter in the localized string is the system name
            name = name.replace("%1%", fo.get_name(system))
        else:
            # set name to system name + planet number as roman number...
            name = fo.get_name(system) + " " + fo.roman_number(planet_number)
            # ...and increase planet number
            planet_number += 1
        # do the actual renaming
        fo.set_name(planet, name)
Esempio n. 24
0
def name_planets(system):
    """
    Sets the names of the planets of the specified system.

    Planet name is system name + planet number (as roman number)
    unless it's an asteroid belt, in that case name is system
    name + 'asteroid belt' (localized).
    """
    planet_number = 1
    # iterate over all planets in the system
    for planet in fo.sys_get_planets(system):
        # use different naming methods for "normal" planets and asteroid belts
        if fo.planet_get_type(planet) == fo.planetType.asteroids:
            # get localized text from stringtable
            name = fo.user_string("PL_ASTEROID_BELT_OF_SYSTEM")
            # %1% parameter in the localized string is the system name
            name = name.replace("%1%", fo.get_name(system))
        else:
            # set name to system name + planet number as roman number...
            name = fo.get_name(system) + " " + fo.roman_number(planet_number)
            # ...and increase planet number
            planet_number += 1
        # do the actual renaming
        fo.set_name(planet, name)
Esempio n. 25
0
def log_planets():
    universe = fo.get_universe()
    planets_table = Table(
        Text("id"),
        Text("name"),
        Text("system"),
        Text("type"),
        Sequence("specials"),
        Text("species"),
        Sequence("buildings"),
        table_name="Planets summary",
    )
    # group planets by system
    for sid in fo.get_systems():
        for pid in fo.sys_get_planets(sid):
            planet = universe.getPlanet(pid)

            planet_type = fo.planet_get_type(pid).name
            planet_size = fo.planet_get_size(pid).name
            if planet_type != planet_size:
                planet_type = "%s %s" % (planet_type, planet_size)

            buildings = [
                universe.getBuilding(x).name for x in planet.buildingIDs
            ]
            planets_table.add_row(
                pid,
                planet.name,
                planet.systemID,
                planet_type,
                list(planet.specials),
                planet.speciesName,
                buildings,
            )

    planets_table.print_table(print)
Esempio n. 26
0
def generate_natives(native_freq, systems, empire_home_systems):
    """
    Adds non-empire-affiliated native populations to planets.
    """

    # first, calculate the chance for natives on a planet based on the native frequency that has been passed
    # get the corresponding value for the specified natives frequency from the universe tables
    native_chance = universe_tables.NATIVE_FREQUENCY[native_freq]
    # a value of 0 means no natives, in this case return immediately
    if native_chance <= 0:
        return

    # compile a list of planets where natives can be placed
    # select only planets sufficiently far away from player home systems
    # list of planets safe for natives
    EMPIRE_TO_NATIVE_MIN_DIST = 2
    empire_exclusions = set(
        itertools.chain.from_iterable(
            fo.systems_within_jumps_unordered(EMPIRE_TO_NATIVE_MIN_DIST, [e])
            for e in empire_home_systems))
    native_safe_planets = set(
        itertools.chain.from_iterable([
            fo.sys_get_planets(s) for s in systems
            if s not in empire_exclusions
        ]))

    print(
        "Number of planets far enough from players for natives to be allowed:",
        len(native_safe_planets))
    # if there are no "native safe" planets at all, we can stop here
    if not native_safe_planets:
        return

    # get all native species
    native_species = fo.get_native_species()
    print("Species that can be added as natives:")
    print("... " + "\n... ".join(native_species))

    # create a map with a list for each planet type containing the species
    # for which this planet type is a good environment
    # we will need this afterwards when picking natives for a planet
    natives_for_planet_type.clear()  # just to be safe
    natives_for_planet_type.update(
        {planet_type: []
         for planet_type in planets.planet_types})
    planet_types_for_natives.clear()
    planet_types_for_natives.update(
        {species: set()
         for species in native_species})
    # iterate over all native species we got
    for species in native_species:
        # check the planet environment for all planet types for this species
        for planet_type in planets.planet_types:
            # if this planet type is a good environment for the species, add it to the list for this planet type
            if fo.species_get_planet_environment(
                    species, planet_type) == fo.planetEnvironment.good:
                natives_for_planet_type[planet_type].append(species)
                planet_types_for_natives[species].add(planet_type)

    # randomly add species to planets
    # iterate over the list of "native safe" planets we compiled earlier
    for candidate in native_safe_planets:
        # select a native species to put on this planet
        planet_type = fo.planet_get_type(candidate)
        # check if we have any native species that like this planet type
        if not natives_for_planet_type[planet_type]:
            # no, continue with next planet
            continue
        universe_statistics.potential_native_planet_summary[planet_type] += 1
        # make a "roll" against the chance for natives to determine if we shall place natives on this planet
        if random.random() > native_chance:
            # no, continue with next planet
            continue
        universe_statistics.settled_native_planet_summary[planet_type] += 1

        # randomly pick one of the native species available for this planet type
        natives = random.choice(natives_for_planet_type[planet_type])

        # put the selected natives on the planet
        fo.planet_set_species(candidate, natives)
        # set planet as homeworld for that species
        fo.species_add_homeworld(natives, candidate)
        # set planet focus
        # check if the preferred focus for the native species is among the foci available on this planet
        available_foci = fo.planet_available_foci(candidate)
        preferred_focus = fo.species_preferred_focus(natives)
        if preferred_focus in available_foci:
            # if yes, set the planet focus to the preferred focus
            fo.planet_set_focus(candidate, preferred_focus)
        elif available_foci:
            # if no, and there is at least one available focus, just take the first of the list
            # otherwise don't set any focus
            fo.planet_set_focus(candidate, available_foci[0])
        print("Added native", natives, "to planet", fo.get_name(candidate))

        # increase the statistics counter for this native species, so a species summary can be dumped to the log later
        universe_statistics.species_summary[natives] += 1
Esempio n. 27
0
def name_star_systems(system_list):
    # choose star types and planet sizes, before choosing names, so naming can have special handling of Deep Space
    star_type_assignments = {}
    planet_assignments = {}
    position_list = []
    for system in system_list:
        star_type = fo.sys_get_star_type(system)
        systemxy = fo.get_pos(system)
        star_type_assignments[systemxy] = star_type
        planet_assignments[systemxy] = fo.sys_get_planets(system)
        position_list.append(systemxy)

    # will name name a portion of stars on a group basis, where the stars of each group share the same base star name,
    # suffixed by different (default greek) letters or characters (options at top of file)
    star_name_map = {}
    star_names = names.get_name_list("STAR_NAMES")
    group_names = names.get_name_list("STAR_GROUP_NAMES")
    potential_group_names = []
    individual_names = []
    stargroup_words[:] = names.get_name_list("STAR_GROUP_WORDS")
    stargroup_chars[:] = names.get_name_list("STAR_GROUP_CHARS")
    stargroup_modifiers[:] = [stargroup_words,
                              stargroup_chars][options.STAR_GROUPS_USE_CHARS]
    for starname in star_names:
        if len(starname) > 6:  # if starname is long, don't allow it for groups
            individual_names.append(starname)
            continue
        # any names that already have a greek letter in them can only be used for individual stars, not groups
        for namepart in starname.split():
            if namepart in greek_letters:
                individual_names.append(starname)
                break
        else:
            potential_group_names.append(starname)

    if not potential_group_names:
        potential_group_names.append("XYZZY")

    # ensure at least a portion of galaxy gets individual starnames
    num_systems = len(system_list)
    choice = num_systems >= options.NAMING_LARGE_GALAXY_SIZE
    target_indiv_ratio = [
        options.TARGET_INDIV_RATIO_SMALL, options.TARGET_INDIV_RATIO_LARGE
    ][choice]
    # TODO improve the following calc to be more likely to hit target_indiv_ratio if more or less than
    # 50% potential_group_names used for groups
    num_individual_stars = int(
        max(
            min(num_systems * target_indiv_ratio,
                len(individual_names) + int(0.5 * len(potential_group_names))),
            num_systems - 0.8 * len(stargroup_modifiers) *
            (len(group_names) + int(0.5 * len(potential_group_names)))))
    star_group_size = 1 + int(
        (num_systems - num_individual_stars) /
        (max(1,
             len(group_names) + int(0.5 * len(potential_group_names)))))
    # make group size a bit bigger than min necessary, at least a trio
    star_group_size = max(3, star_group_size)
    num_star_groups = 1 + int(num_systems / star_group_size)  # initial value

    # first cluster all systems, then remove some to be individually named (otherwise groups can have too many
    # individually named systems in their middle).  First remove any that are too small (only 1 or 2 systems).
    # The clusters with the most systems are generally the most closely spaced, and though they might make good
    # logical candidates for groups, their names are then prone to overlapping on the galaxy map, so after removing
    # small groups, remove the groups with the most systems.
    random.shuffle(position_list)  # just to be sure it is randomized
    init_cluster_assgts = cluster_stars(position_list, num_star_groups)
    star_groups = {}
    for index_pos, index_group in enumerate(init_cluster_assgts):
        systemxy = position_list[index_pos]
        star_groups.setdefault(index_group, []).append(systemxy)
    indiv_systems = []

    # remove groups with only one non-deep-system
    for groupindex, group_list in list(star_groups.items()):
        max_can_transfer = len(potential_group_names) - len(star_groups) + len(
            individual_names) - len(indiv_systems)
        if max_can_transfer <= 0:
            break
        elif max_can_transfer <= len(group_list):
            continue
        not_deep, deep_space = check_deep_space(group_list,
                                                star_type_assignments,
                                                planet_assignments)
        if len(not_deep) > 1:
            continue
        for systemxy in star_groups[groupindex]:
            indiv_systems.append(systemxy)
        del star_groups[groupindex]

    # remove tiny groups
    group_sizes = [(len(group), index) for index, group in star_groups.items()]
    group_sizes.sort()
    while len(indiv_systems) < num_individual_stars and len(group_sizes) > 0:
        groupsize, groupindex = group_sizes.pop()
        max_can_transfer = len(potential_group_names) - len(star_groups) + len(
            individual_names) - len(indiv_systems)
        if (max_can_transfer <= 0) or (groupsize > 2):
            break
        if max_can_transfer <= groupsize:
            continue
        for systemxy in star_groups[groupindex]:
            indiv_systems.append(systemxy)
        del star_groups[groupindex]

    # remove largest (likely most compact) groups
    while len(indiv_systems) < num_individual_stars and len(group_sizes) > 0:
        groupsize, groupindex = group_sizes.pop(-1)
        max_can_transfer = len(potential_group_names) - len(star_groups) + len(
            individual_names) - len(indiv_systems)
        if max_can_transfer <= 0:
            break
        if max_can_transfer <= groupsize:
            continue
        for systemxy in star_groups[groupindex]:
            indiv_systems.append(systemxy)
        del star_groups[groupindex]

    num_star_groups = len(star_groups)
    num_individual_stars = len(indiv_systems)
    random.shuffle(potential_group_names)
    random.shuffle(individual_names)
    random.shuffle(group_names)
    num_for_indiv = min(
        max(
            len(potential_group_names) // 2,
            num_individual_stars + 1 - len(individual_names)),
        len(potential_group_names))
    individual_names.extend(potential_group_names[:num_for_indiv])
    group_names.extend(potential_group_names[num_for_indiv:])

    # print "sampling for %d indiv names from list of %d total indiv names" % (
    #     num_individual_stars, len(individual_names))
    indiv_name_sample = random.sample(individual_names, num_individual_stars)
    # indiv_name_assignments = zip([(pos.x, pos.y) for pos in position_list[:num_individual_stars]], indiv_name_sample)
    star_name_map.update(zip(indiv_systems, indiv_name_sample))
    # print "sampling for %d group names from list of %d total group names"%(num_star_groups, len(group_names))
    if len(group_names) < num_star_groups:
        group_names.extend([
            names.random_name(6)
            for _ in range(num_star_groups - len(group_names))
        ])
    group_name_sample = random.sample(group_names, num_star_groups)
    for index_group, group_list in enumerate(sorted(star_groups.values())):
        star_name_map.update(
            name_group(group_list, group_name_sample[index_group],
                       star_type_assignments, planet_assignments))

    # assign names from star_name_map to star systems
    for system in system_list:
        fo.set_name(
            system,
            star_name_map.get(fo.get_pos(system), "") or random_star_name())
Esempio n. 28
0
def compile_home_system_list(num_home_systems, systems):
    """
    Compiles a list with a requested number of home systems.
    """
    print "Compile home system list:", num_home_systems, "systems requested"

    # if the list of systems to choose home systems from is empty, report an error and return empty list
    if not systems:
        report_error("Python generate_home_system_list: no systems to choose from")
        return []

    # calculate an initial minimal number of jumps that the home systems should be apart,
    # based on the total number of systems to choose from and the requested number of home systems
    # Don't let min_jumps be larger than 10, because a larger number is really not at all needed and with large
    # galaxies an excessive amount of time can be used in failed attempts
    min_jumps = min(10, max(int(float(len(systems)) / float(num_home_systems * 2)), 5))

    # home systems must have a certain minimum of systems in their near vicinity
    # we will try to select our home systems from systems that match this criteria, if that fails, we will select our
    # home systems from all systems and add the missing number planets to the systems in their vicinity afterwards
    # the minimum planet limit and the jump range that defines the "near vicinity" are controlled by the
    # HS_* option constants in options.py (see there)

    # lets start by filtering out all systems from the pool we got passed into this function that match the criteria
    filtered_pool = [s for s in systems if has_min_planets_in_vicinity(s)]
    print "Filtering out systems that meet the minimum planets in the near vicinity condition yielded",\
        len(filtered_pool), "systems"
    print "Using this as the preferred pool for home system selection"
    # now try to pick the requested number of home systems by calling find_home_systems
    # this function takes two pools, a "complete" pool and one with preferred systems
    # it will try to pick the home systems from the preferred pool first, so pass our filtered pool as preferred pool
    home_systems = find_home_systems(num_home_systems, systems, filtered_pool, min_jumps)

    # check if the selection process delivered a list with enough home systems
    # if not, our galaxy obviously is too crowded, report an error and return an empty list
    if len(home_systems) < num_home_systems:
        report_error("Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems"
                     % (num_home_systems, len(systems)))
        return []

    # check if we got more home systems than we requested
    if len(home_systems) > num_home_systems:
        # yes: calculate the number of planets in the near vicinity of each system
        # and store that value with each system in a map
        hs_planets_in_vicinity_map = {s: count_planets_in_systems(get_systems_within_jumps(s, HS_VICINITY_RANGE))
                                      for s in home_systems}
        # sort the home systems by the number of planets in their near vicinity using the map
        # now only pick the number of home systems we need, taking those with the highest number of planets
        home_systems = sorted(home_systems, key=hs_planets_in_vicinity_map.get, reverse=True)[:num_home_systems]

    # make sure all our home systems have a "real" star (that is, a star that is not a neutron star, black hole,
    # or even no star at all) and at least one planet in it
    for home_system in home_systems:
        # if this home system has no "real" star, change star type to a randomly selected "real" star
        if fo.sys_get_star_type(home_system) not in star_types_real:
            star_type = random.choice(star_types_real)
            print "Home system", home_system, "has star type", fo.sys_get_star_type(home_system),\
                  ", changing that to", star_type
            fo.sys_set_star_type(home_system, star_type)

        # if this home system has no planets, create one in a random orbit
        # we take random values for type and size, as these will be set to suitable values later
        if not fo.sys_get_planets(home_system):
            print "Home system", home_system, "has no planets, adding one"
            planet = fo.create_planet(random.choice(planet_sizes_real),
                                      random.choice(planet_types_real),
                                      home_system, random.randint(0, fo.sys_get_num_orbits(home_system) - 1), "")
            # if we couldn't create the planet, report an error and return an empty list
            if planet == fo.invalid_object():
                report_error("Python generate_home_system_list: couldn't create planet in home system")
                return []

    # finally, check again if all home systems meet the criteria of having the required minimum number of planets
    # within their near vicinity, if not, add the missing number of planets
    print "Checking if home systems have the required minimum of planets within the near vicinity..."
    for home_system in home_systems:
        # calculate the number of missing planets, and add them if this number is > 0
        systems_in_vicinity = get_systems_within_jumps(home_system, HS_VICINITY_RANGE)
        num_systems_in_vicinity = len(systems_in_vicinity)
        num_planets_in_vicinity = count_planets_in_systems(systems_in_vicinity)
        num_planets_to_add = min_planets_in_vicinity_limit(num_systems_in_vicinity) - num_planets_in_vicinity
        print "Home system", home_system, "has", num_systems_in_vicinity, "systems and", num_planets_in_vicinity,\
            "planets in the near vicinity, required minimum:", min_planets_in_vicinity_limit(num_systems_in_vicinity)
        if num_planets_to_add > 0:
            systems_in_vicinity.remove(home_system)  # don't add planets to the home system, so remove it from the list
            add_planets_to_vicinity(systems_in_vicinity, num_planets_to_add)

    # as we've sorted the home system list before, lets shuffle it to ensure random order and return
    random.shuffle(home_systems)
    return home_systems
Esempio n. 29
0
def generate_monsters(monster_freq, systems):
    """
    Adds space monsters to systems.
    """
    # first, calculate the basic chance for monster generation in a system
    # based on the monster frequency that has been passed
    # get the corresponding value for the specified monster frequency from the universe tables
    basic_chance = universe_tables.MONSTER_FREQUENCY[monster_freq]
    # a value of 0 means no monsters, in this case return immediately
    if basic_chance <= 0:
        return
    print "Default monster spawn chance:", basic_chance
    expectation_tally = 0.0
    actual_tally = 0

    # get all monster fleets that have a spawn rate and limit both > 0 and at least one monster ship design in it
    # (a monster fleet with no monsters in it is pointless) and store them in a list
    fleet_plans = fo.load_monster_fleet_plan_list()

    # create a map where we store a spawn counter for each monster fleet
    # this counter will be set to the spawn limit initially and decreased every time the monster fleet is spawned
    # this map (dict) needs to be separate from the list holding the fleet plans because the order in which items
    # are stored in a dict is undefined (can be different each time), which would result in different distribution
    # even when using the same seed for the RNG
    spawn_limits = {
        fp: fp.spawn_limit()
        for fp in fleet_plans if fp.spawn_rate() > 0.0 and fp.spawn_limit() > 0
        and fp.ship_designs()
    }

    # map nests to monsters for ease of reporting
    nest_name_map = {
        "KRAKEN_NEST_SPECIAL": "SM_KRAKEN_1",
        "SNOWFLAKE_NEST_SPECIAL": "SM_SNOWFLAKE_1",
        "JUGGERNAUT_NEST_SPECIAL": "SM_JUGGERNAUT_1"
    }
    tracked_plan_tries = {name: 0 for name in nest_name_map.values()}
    tracked_plan_counts = {name: 0 for name in nest_name_map.values()}
    tracked_plan_valid_locations = {
        fp: 0
        for fp in fleet_plans if fp.name() in tracked_plan_counts
    }
    tracked_nest_valid_locations = {nest: 0 for nest in nest_name_map}

    if not fleet_plans:
        return

    universe = fo.get_universe()

    # Fleet plans that include ships capable of altering starlanes.
    ## @content_tag{CAN_ALTER_STARLANES} universe_generator special handling for fleets containing a hull design with this tag.
    fleet_can_alter_starlanes = {
        fp
        for fp in fleet_plans if any([
            universe.getGenericShipDesign(design).hull_type.hasTag(
                "CAN_ALTER_STARLANES") for design in fp.ship_designs()
        ])
    }

    # dump a list of all monster fleets meeting these conditions and their properties to the log
    print "Monster fleets available for generation at game start:"
    for fleet_plan in fleet_plans:
        print "...", fleet_plan.name(), ": spawn rate", fleet_plan.spawn_rate(
        ),
        print "/ spawn limit", fleet_plan.spawn_limit(),
        print "/ effective chance", basic_chance * fleet_plan.spawn_rate(),

        if len(systems) < 100:
            # Note: The WithinStarlaneJumps condition in fp.location()
            # is the most time costly function in universe generation
            print "/ can be spawned at", len(
                [s for s in systems if fleet_plan.location(s)]), "systems"
        else:
            print  # to terminate the print line
        if fleet_plan.name() in nest_name_map.values():
            statistics.tracked_monsters_chance[
                fleet_plan.name()] = basic_chance * fleet_plan.spawn_rate()

    # initialize a manager for monsters that can alter the map
    # required to prevent their placement from disjoining the map
    starlane_altering_monsters = StarlaneAlteringMonsters(systems)

    # for each system in the list that has been passed to this function, find a monster fleet that can be spawned at
    # the system and which hasn't already been added too many times, then attempt to add that monster fleet by
    # testing the spawn rate chance
    for system in systems:
        # collect info for tracked monster nest valid locations
        for planet in fo.sys_get_planets(system):
            for nest in tracked_nest_valid_locations:
                # print "\t tracked monster check planet: %d size: %s for nest: %20s  | result: %s"
                # % (planet, fo.planet_get_size(planet), nest, fo.special_location(nest, planet))
                if fo.special_location(nest, planet):
                    tracked_nest_valid_locations[nest] += 1

        # collect info for tracked monster valid locations
        for fp in tracked_plan_valid_locations:
            if fp.location(system):
                tracked_plan_valid_locations[fp] += 1

        # filter out all monster fleets whose location condition allows this system and whose counter hasn't reached 0.
        # Note: The WithinStarlaneJumps condition in fp.location() is
        # the most time costly function in universe generation.
        suitable_fleet_plans = [
            fp for fp in fleet_plans
            if spawn_limits[fp] and fp.location(system) and (
                fp not in fleet_can_alter_starlanes
                or starlane_altering_monsters.can_place_at(system, fp))
        ]
        # if there are no suitable monster fleets for this system, continue with the next
        if not suitable_fleet_plans:
            continue

        # randomly select one monster fleet out of the suitable ones and then test if we want to add it to this system
        # by making a roll against the basic chance multiplied by the spawn rate of this monster fleet
        expectation_tally += basic_chance * sum(
            [fp.spawn_rate()
             for fp in suitable_fleet_plans]) / len(suitable_fleet_plans)
        fleet_plan = random.choice(suitable_fleet_plans)
        if fleet_plan.name() in tracked_plan_tries:
            tracked_plan_tries[fleet_plan.name()] += 1
        if random.random() > basic_chance * fleet_plan.spawn_rate():
            print(
                "\t\t At system %4d rejected monster fleet %s from %d suitable fleets"
                % (system, fleet_plan.name(), len(suitable_fleet_plans)))
            # no, test failed, continue with the next system
            continue
        actual_tally += 1
        if fleet_plan.name() in tracked_plan_counts:
            tracked_plan_counts[fleet_plan.name()] += 1

        # all prerequisites and the test have been met, now spawn this monster fleet in this system
        # create monster fleet
        try:
            if fleet_plan in fleet_can_alter_starlanes:
                starlane_altering_monsters.place(system, fleet_plan)
            else:
                populate_monster_fleet(fleet_plan, system)
            # decrement counter for this monster fleet
            spawn_limits[fleet_plan] -= 1

        except MapGenerationError as e:
            report_error(str(e))
            continue

    print "Actual # monster fleets placed: %d; Total Placement Expectation: %.1f" % (
        actual_tally, expectation_tally)
    # finally, compile some statistics to be dumped to the log later
    statistics.monsters_summary = [(fp.name(), fp.spawn_limit() - counter)
                                   for fp, counter in spawn_limits.iteritems()]
    statistics.tracked_monsters_tries.update(tracked_plan_tries)
    statistics.tracked_monsters_summary.update(tracked_plan_counts)
    statistics.tracked_monsters_location_summary.update([
        (fp.name(), count)
        for fp, count in tracked_plan_valid_locations.iteritems()
    ])
    statistics.tracked_nest_location_summary.update([
        (nest_name_map[nest], count)
        for nest, count in tracked_nest_valid_locations.items()
    ])
Esempio n. 30
0
def generate_fields(systems):
    """
    Generates stationary fields in some randomly chosen empty no star systems.
    """
    # filter out all empty no star systems
    candidates = [s for s in systems if (fo.sys_get_star_type(s) == fo.starType.noStar) and (not fo.sys_get_planets(s))]
    # make sure we have at least one empty no star system, otherwise return without creating any fields
    if not candidates:
        print "...no empty no star systems found, no fields created"
        return
    # pick 10-20% of all empty no star systems to create stationary fields in them, but at least one
    accepted = sample(candidates, max(int(len(candidates) * uniform(0.1, 0.2)), 1))
    for system in accepted:
        # randomly pick a field type
        field_type = choice(["FLD_NEBULA_1", "FLD_NEBULA_2"])
        # and create the field
        if fo.create_field_in_system(field_type, uniform(40, 120), system) == fo.invalid_object():
            # create field failed, report an error
            report_error("Python generate_fields: create field %s in system %d failed" % (field_type, system))
    print "...fields created in %d systems out of %d empty no star systems" % (len(accepted), len(candidates))
Esempio n. 31
0
def compile_home_system_list(num_home_systems, systems, gsd):
    """
    Compiles a list with a requested number of home systems.
    """
    print "Compile home system list:", num_home_systems, "systems requested"

    # if the list of systems to choose home systems from is empty, report an error and return empty list
    if not systems:
        report_error("Python generate_home_system_list: no systems to choose from")
        return []

    # calculate an initial minimal number of jumps that the home systems should be apart,
    # based on the total number of systems to choose from and the requested number of home systems
    # don't let min_jumps be either:
    # a.) larger than a defined limit, because an unreasonably large number is really not at all needed,
    #     and with large galaxies an excessive amount of time can be used in failed attempts
    # b.) lower than the minimum jump distance limit that should be considered high priority (see options.py),
    #     otherwise no attempt at all would be made to enforce the other requirements for home systems (see below)
    min_jumps = min(HS_MAX_JUMP_DISTANCE_LIMIT, max(int(float(len(systems)) / float(num_home_systems * 2)),
                                                    HS_MIN_DISTANCE_PRIORITY_LIMIT))

    # home systems must have a certain minimum of systems and planets in their near vicinity
    # we will try to select our home systems from systems that match this criteria, if that fails, we will select our
    # home systems from all systems and add the missing number planets to the systems in their vicinity afterwards
    # the minimum system and planet limit and the jump range that defines the "near vicinity" are controlled by the
    # HS_* option constants in options.py (see there)

    # we start by building two additional pools of systems: one that contains all systems that match the criteria
    # completely (meets the min systems and planets limit), and one that contains all systems that match the criteria
    # at least partially (meets the min systems limit)
    pool_matching_sys_and_planet_limit = []
    pool_matching_sys_limit = []
    for system in systems:
        systems_in_vicinity = get_systems_within_jumps(system, HS_VICINITY_RANGE)
        if len(systems_in_vicinity) >= HS_MIN_SYSTEMS_IN_VICINITY:
            pool_matching_sys_limit.append(system)
            if count_planets_in_systems(systems_in_vicinity) >= min_planets_in_vicinity_limit(len(systems_in_vicinity)):
                pool_matching_sys_and_planet_limit.append(system)
    print len(pool_matching_sys_and_planet_limit), "systems meet the min systems and planets in the near vicinity limit"
    print len(pool_matching_sys_limit), "systems meet the min systems in the near vicinity limit"

    # now try to pick the requested number of home systems
    # we will do this by calling find_home_systems, which takes a list of tuples defining the pools from which to pick
    # the home systems; it will use the pools in the order in which they appear in the list, so put better pools first

    # we will make two attempts: the first one with the filtered pools we just created, and tell find_home_systems
    # to start with the min_jumps jumps distance we calculated above, but not to go lower than
    # HS_MIN_DISTANCE_PRIORITY_LIMIT

    print "First attempt: trying to pick home systems from the filtered pools of preferred systems"
    pool_list = [
        # the better pool is of course the one where all systems meet BOTH the min systems and planets limit
        (pool_matching_sys_and_planet_limit, "pool of systems that meet both the min systems and planets limit"),
        # next the less preferred pool where all systems at least meets the min systems limit
        # specify 0 as number of requested home systems to pick as much systems as possible
        (pool_matching_sys_limit, "pool of systems that meet at least the min systems limit"),
    ]
    home_systems = find_home_systems(num_home_systems, pool_list, min_jumps, HS_MIN_DISTANCE_PRIORITY_LIMIT)

    # check if the first attempt delivered a list with enough home systems
    # if not, we make our second attempt, this time disregarding the filtered pools and using all systems, starting
    # again with the min_jumps jump distance limit and specifying 0 as number of required home systems to pick as much
    # systems as possible
    if len(home_systems) < num_home_systems:
        print "Second attempt: trying to pick home systems from all systems"
        home_systems = find_home_systems(num_home_systems, [(systems, "complete pool")], min_jumps, 1)

    # check if the selection process delivered a list with enough home systems
    # if not, our galaxy obviously is too crowded, report an error and return an empty list
    if len(home_systems) < num_home_systems:
        report_error("Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems"
                     % (num_home_systems, len(systems)))
        return []

    # check if we got more home systems than we requested
    if len(home_systems) > num_home_systems:
        # yes: calculate the number of planets in the near vicinity of each system
        # and store that value with each system in a map
        hs_planets_in_vicinity_map = {s: count_planets_in_systems(get_systems_within_jumps(s, HS_VICINITY_RANGE))
                                      for s in home_systems}
        # sort the home systems by the number of planets in their near vicinity using the map
        # now only pick the number of home systems we need, taking those with the highest number of planets
        home_systems = sorted(home_systems, key=hs_planets_in_vicinity_map.get, reverse=True)[:num_home_systems]

    # make sure all our home systems have a "real" star (that is, a star that is not a neutron star, black hole,
    # or even no star at all) and at least one planet in it
    for home_system in home_systems:
        # if this home system has no "real" star, change star type to a randomly selected "real" star
        if fo.sys_get_star_type(home_system) not in star_types_real:
            star_type = random.choice(star_types_real)
            print "Home system", home_system, "has star type", fo.sys_get_star_type(home_system),\
                  ", changing that to", star_type
            fo.sys_set_star_type(home_system, star_type)

        # if this home system has no planets, create one in a random orbit
        # we take random values for type and size, as these will be set to suitable values later
        if not fo.sys_get_planets(home_system):
            print "Home system", home_system, "has no planets, adding one"
            planet = fo.create_planet(random.choice(planet_sizes_real),
                                      random.choice(planet_types_real),
                                      home_system, random.randint(0, fo.sys_get_num_orbits(home_system) - 1), "")
            # if we couldn't create the planet, report an error and return an empty list
            if planet == fo.invalid_object():
                report_error("Python generate_home_system_list: couldn't create planet in home system")
                return []

    # finally, check again if all home systems meet the criteria of having the required minimum number of planets
    # within their near vicinity, if not, add the missing number of planets
    print "Checking if home systems have the required minimum of planets within the near vicinity..."
    for home_system in home_systems:
        # calculate the number of missing planets, and add them if this number is > 0
        systems_in_vicinity = get_systems_within_jumps(home_system, HS_VICINITY_RANGE)
        num_systems_in_vicinity = len(systems_in_vicinity)
        num_planets_in_vicinity = count_planets_in_systems(systems_in_vicinity)
        num_planets_to_add = min_planets_in_vicinity_limit(num_systems_in_vicinity) - num_planets_in_vicinity
        print "Home system", home_system, "has", num_systems_in_vicinity, "systems and", num_planets_in_vicinity,\
            "planets in the near vicinity, required minimum:", min_planets_in_vicinity_limit(num_systems_in_vicinity)
        if num_planets_to_add > 0:
            systems_in_vicinity.remove(home_system)  # don't add planets to the home system, so remove it from the list
            add_planets_to_vicinity(systems_in_vicinity, num_planets_to_add, gsd)

    # as we've sorted the home system list before, lets shuffle it to ensure random order and return
    random.shuffle(home_systems)
    return home_systems
Esempio n. 32
0
def setup_empire(empire, empire_name, home_system, starting_species, player_name):
    """
    Sets up various aspects of an empire, like empire name, homeworld, etc.
    """

    # set empire name, if no one is given, pick one randomly
    if not empire_name:
        print "No empire name set for player", player_name, ", picking one randomly"
        empire_name = next(empire_name_generator)
    fo.empire_set_name(empire, empire_name)
    print "Empire name for player", player_name, "is", empire_name

    # check starting species, if no one is given, pick one randomly
    if starting_species == "RANDOM" or not starting_species:
        print "Picking random starting species for player", player_name
        starting_species = next(starting_species_pool)
    print "Starting species for player", player_name, "is", starting_species
    statistics.empire_species[starting_species] += 1

    # pick a planet from the specified home system as homeworld
    planet_list = fo.sys_get_planets(home_system)
    # if the system is empty, report an error and return false, indicating failure
    if not planet_list:
        report_error("Python setup_empire: got home system with no planets")
        return False
    homeworld = random.choice(planet_list)

    # set selected planet as empire homeworld with selected starting species
    fo.empire_set_homeworld(empire, homeworld, starting_species)

    # set homeworld focus
    # check if the preferred focus for the starting species is among
    # the foci available on the homeworld planet
    available_foci = fo.planet_available_foci(homeworld)
    preferred_focus = fo.species_preferred_focus(starting_species)
    if preferred_focus in available_foci:
        # if yes, set the homeworld focus to the preferred focus
        print "Player", player_name, ": setting preferred focus", preferred_focus, "on homeworld"
        fo.planet_set_focus(homeworld, preferred_focus)
    elif len(available_foci) > 0:
        # if no, and there is at least one available focus,
        # just take the first of the list
        if preferred_focus == "":
            print "Player", player_name, ": starting species", starting_species, "has no preferred focus, using",\
                  available_foci[0], "instead"
        else:
            print "Player", player_name, ": preferred focus", preferred_focus, "for starting species",\
                  starting_species, "not available on homeworld, using", available_foci[0], "instead"
        fo.planet_set_focus(homeworld, available_foci[0])
    else:
        # if no focus is available on the homeworld, don't set any focus
        print "Player", player_name, ": no available foci on homeworld for starting species", starting_species

    # give homeworld starting buildings
    # use the list provided in scripting/starting_unlocks/buildings.inf
    print "Player", player_name, ": add starting buildings to homeworld"
    for building in load_string_list(os.path.join(fo.get_resource_dir(), "scripting/starting_unlocks/buildings.inf")):
        fo.create_building(building, homeworld, empire)

    # unlock starting techs, buildings, hulls, ship parts, etc.
    # use default content file
    print "Player", player_name, ": add unlocked items"
    for item in fo.load_item_spec_list():
        fo.empire_unlock_item(empire, item.type, item.name)

    # add premade ship designs to empire
    print "Player", player_name, ": add premade ship designs"
    for ship_design in fo.design_get_premade_list():
        fo.empire_add_ship_design(empire, ship_design)

    # add starting fleets to empire
    # use default content file
    print "Player", player_name, ": add starting fleets"
    fleet_plans = fo.load_fleet_plan_list()
    for fleet_plan in fleet_plans:
        # first, create the fleet
        fleet = fo.create_fleet(fleet_plan.name(), home_system, empire)
        # if the fleet couldn't be created, report an error and try to continue with the next fleet plan
        if fleet == fo.invalid_object():
            report_error("Python setup empire: couldn't create fleet %s" % fleet_plan.name())
            continue
        # second, iterate over the list of ship design names in the fleet plan
        for ship_design in fleet_plan.ship_designs():
            # create a ship in the fleet
            # if the ship couldn't be created, report an error and try to continue with the next ship design
            if fo.create_ship("", ship_design, starting_species, fleet) == fo.invalid_object():
                report_error("Python setup empire: couldn't create ship %s for fleet %s"
                             % (ship_design, fleet_plan.name()))
    return True
Esempio n. 33
0
def compile_home_system_list(num_home_systems, systems):
    """
    Compiles a list with a requested number of home systems.
    """

    # if the list of systems to choose home systems from is empty, report an error and return empty list
    if not systems:
        util.report_error(
            "Python generate_home_system_list: no systems to choose from")
        return []

    # calculate an initial minimal number of jumps that the home systems should be apart,
    # based on the total number of systems to choose from and the requested number of home systems
    min_jumps = max(int(float(len(systems)) / float(num_home_systems * 2)), 5)
    # try to find the home systems, decrease the min jumps until enough systems can be found, or the min jump distance
    # gets reduced to 0 (meaning we don't have enough systems to choose from at all)
    while min_jumps > 0:
        print "Trying to find", num_home_systems, "home systems that are at least", min_jumps, "jumps apart"
        # try to find home systems...
        home_systems = find_systems_with_min_jumps_between(
            num_home_systems, systems, min_jumps)
        # ...check if we got enough...
        if len(home_systems) >= num_home_systems:
            # ...yes, we got what we need, so let's break out of the loop
            break
        print "Home system min jump conflict: %d systems and %d empires, tried %d min jump and failed"\
              % (len(systems), num_home_systems, min_jumps)
        # ...no, decrease the min jump distance and try again
        min_jumps -= 1

    # check if the loop above delivered a list with enough home systems, or if it exited because the min jump distance
    # has been decreased to 0 without finding enough systems
    # in that case, our galaxy obviously is too crowded, report an error and return an empty list
    if len(home_systems) < num_home_systems:
        util.report_error(
            "Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems"
            % (num_home_systems, len(systems)))
        return []

    # make sure all our home systems have a "real" star (that is, a star that is not a neutron star, black hole,
    # or even no star at all) and at least one planet in it
    for home_system in home_systems:
        # if this home system has no "real" star, change star type to a randomly selected "real" star
        if fo.sys_get_star_type(
                home_system) not in starsystems.star_types_real:
            star_type = random.choice(starsystems.star_types_real)
            print "Home system", home_system, "has star type", fo.sys_get_star_type(home_system),\
                  ", changing that to", star_type
            fo.sys_set_star_type(home_system, star_type)

        # if this home system has no planets, create one in a random orbit
        # we take random values for type and size, as these will be set to suitable values later
        if not fo.sys_get_planets(home_system):
            print "Home system", home_system, "has no planets, adding one"
            planet = fo.create_planet(
                random.choice(planets.planet_sizes_real),
                random.choice(planets.planet_types_real), home_system,
                random.randint(0,
                               fo.sys_get_num_orbits(home_system) - 1), "")
            # if we couldn't create the planet, report an error and return an empty list
            if planet == fo.invalid_object():
                util.report_error(
                    "Python generate_home_system_list: couldn't create planet in home system"
                )
                return []

    return home_systems
Esempio n. 34
0
def generate_monsters(monster_freq, systems):
    """
    Adds space monsters to systems.
    """

    # first, calculate the basic chance for monster generation in a system
    # based on the monster frequency that has been passed
    # get the corresponding value for the specified monster frequency from the universe tables
    inverse_monster_chance = fo.monster_frequency(monster_freq)
    # as the value in the universe table is higher for a lower frequency, we have to invert it
    # exception: a value of 0 means no monsters, in this case return immediately
    if inverse_monster_chance <= 0:
        return
    basic_chance = 1.0 / float(inverse_monster_chance)
    print "Default monster spawn chance:", basic_chance
    expectation_tally = 0.0
    actual_tally = 0

    # get all monster fleets that have a spawn rate and limit both > 0 and at least one monster ship design in it
    # (a monster fleet with no monsters in it is pointless) and store them with a spawn counter in a dict
    # this counter will be set to the spawn limit initially and decreased every time the monster fleet is spawned
    fleet_plans = {
        fp: fp.spawn_limit()
        for fp in fo.load_monster_fleet_plan_list(
            "space_monster_spawn_fleets.txt") if fp.spawn_rate() > 0.0
        and fp.spawn_limit() > 0 and fp.ship_designs()
    }

    # map nests to monsters for ease of reporting
    nest_name_map = dict(
        zip([
            "KRAKEN_NEST_SPECIAL", "SNOWFLAKE_NEST_SPECIAL",
            "JUGGERNAUT_NEST_SPECIAL"
        ], ["SM_KRAKEN_1", "SM_SNOWFLAKE_1", "SM_JUGGERNAUT_1"]))
    tracked_plan_tries = {name: 0 for name in nest_name_map.values()}
    tracked_plan_counts = {name: 0 for name in nest_name_map.values()}
    tracked_plan_valid_locations = {
        fp: 0
        for fp, limit in fleet_plans.iteritems()
        if fp.name() in tracked_plan_counts
    }
    tracked_nest_valid_locations = {nest: 0 for nest in nest_name_map}

    if not fleet_plans:
        return

    # dump a list of all monster fleets meeting these conditions and their properties to the log
    print "Monster fleets available for generation at game start:"
    for fleet_plan in fleet_plans:
        print "...", fleet_plan.name(), ": spawn rate", fleet_plan.spawn_rate(
        ),
        print "/ spawn limit", fleet_plan.spawn_limit(),
        print "/ effective chance", basic_chance * fleet_plan.spawn_rate(),
        if len(systems) < 1000:
            print "/ can be spawned at", len(
                [s for s in systems if fleet_plan.location(s)]), "systems"
        else:
            print  # to terminate the print line
        if fleet_plan.name() in nest_name_map.values():
            statistics.tracked_monsters_chance[
                fleet_plan.name()] = basic_chance * fleet_plan.spawn_rate()

    # for each system in the list that has been passed to this function, find a monster fleet that can be spawned at
    # the system and which hasn't already been added too many times, then attempt to add that monster fleet by
    # testing the spawn rate chance
    for system in systems:
        # collect info for tracked monster nest valid locations
        for planet in fo.sys_get_planets(system):
            for nest in tracked_nest_valid_locations:
                #print "\t tracked monster check planet: %d size: %s for nest: %20s  | result: %s" % (planet, fo.planet_get_size(planet), nest, fo.special_location(nest, planet))
                if fo.special_location(nest, planet):
                    tracked_nest_valid_locations[nest] += 1

        # collect info for tracked monster valid locations
        for fp in tracked_plan_valid_locations:
            if fp.location(system):
                tracked_plan_valid_locations[fp] += 1

        # filter out all monster fleets whose location condition allows this system and whose counter hasn't reached 0
        suitable_fleet_plans = [
            fp for fp, counter in fleet_plans.iteritems()
            if counter and fp.location(system)
        ]
        # if there are no suitable monster fleets for this system, continue with the next
        if not suitable_fleet_plans:
            continue

        # randomly select one monster fleet out of the suitable ones and then test if we want to add it to this system
        # by making a roll against the basic chance multiplied by the spawn rate of this monster fleet
        expectation_tally += basic_chance * sum(
            [fp.spawn_rate()
             for fp in suitable_fleet_plans]) / len(suitable_fleet_plans)
        fleet_plan = random.choice(suitable_fleet_plans)
        if fleet_plan.name() in tracked_plan_tries:
            tracked_plan_tries[fleet_plan.name()] += 1
        if random.random() > basic_chance * fleet_plan.spawn_rate():
            print "\t\t At system %4d rejected monster fleet %s from %d suitable fleets" % (
                system, fleet_plan.name(), len(suitable_fleet_plans))
            # no, test failed, continue with the next system
            continue
        actual_tally += 1
        if fleet_plan.name() in tracked_plan_counts:
            tracked_plan_counts[fleet_plan.name()] += 1

        # all prerequisites and the test have been met, now spawn this monster fleet in this system
        print "Spawn", fleet_plan.name(), "at", fo.get_name(system)
        # decrement counter for this monster fleet
        fleet_plans[fleet_plan] -= 1
        # create monster fleet
        monster_fleet = fo.create_monster_fleet(system)
        # if fleet creation fails, report an error and try to continue with next system
        if monster_fleet == fo.invalid_object():
            util.report_error(
                "Python generate_monsters: unable to create new monster fleet %s"
                % fleet_plan.name())
            continue
        # add monsters to fleet
        for design in fleet_plan.ship_designs():
            # create monster, if creation fails, report an error and try to continue with the next design
            if fo.create_monster(design, monster_fleet) == fo.invalid_object():
                util.report_error(
                    "Python generate_monsters: unable to create monster %s" %
                    design)

    print "Actual # monster fleets placed: %d; Total Placement Expectation: %.1f" % (
        actual_tally, expectation_tally)
    # finally, compile some statistics to be dumped to the log later
    statistics.monsters_summary = [(fp.name(), fp.spawn_limit() - counter)
                                   for fp, counter in fleet_plans.iteritems()]
    statistics.tracked_monsters_tries.update(tracked_plan_tries)
    statistics.tracked_monsters_summary.update(tracked_plan_counts)
    statistics.tracked_monsters_location_summary.update([
        (fp.name(), count)
        for fp, count in tracked_plan_valid_locations.iteritems()
    ])
    statistics.tracked_nest_location_summary.update([
        (nest_name_map[nest], count)
        for nest, count in tracked_nest_valid_locations.items()
    ])
Esempio n. 35
0
def generate_monsters(monster_freq, systems):
    """
    Adds space monsters to systems.
    """
    # first, calculate the basic chance for monster generation in a system
    # based on the monster frequency that has been passed
    # get the corresponding value for the specified monster frequency from the universe tables
    basic_chance = universe_tables.MONSTER_FREQUENCY[monster_freq]
    # a value of 0 means no monsters, in this case return immediately
    if basic_chance <= 0:
        return
    print "Default monster spawn chance:", basic_chance
    expectation_tally = 0.0
    actual_tally = 0

    # get all monster fleets that have a spawn rate and limit both > 0 and at least one monster ship design in it
    # (a monster fleet with no monsters in it is pointless) and store them in a list
    fleet_plans = fo.load_monster_fleet_plan_list()

    # create a map where we store a spawn counter for each monster fleet
    # this counter will be set to the spawn limit initially and decreased every time the monster fleet is spawned
    # this map (dict) needs to be separate from the list holding the fleet plans because the order in which items
    # are stored in a dict is undefined (can be different each time), which would result in different distribution
    # even when using the same seed for the RNG
    spawn_limits = {fp: fp.spawn_limit() for fp in fleet_plans
                    if fp.spawn_rate() > 0.0 and fp.spawn_limit() > 0 and fp.ship_designs()}

    # map nests to monsters for ease of reporting
    nest_name_map = {"KRAKEN_NEST_SPECIAL": "SM_KRAKEN_1",
                     "SNOWFLAKE_NEST_SPECIAL": "SM_SNOWFLAKE_1",
                     "JUGGERNAUT_NEST_SPECIAL": "SM_JUGGERNAUT_1"}
    tracked_plan_tries = {name: 0 for name in nest_name_map.values()}
    tracked_plan_counts = {name: 0 for name in nest_name_map.values()}
    tracked_plan_valid_locations = {fp: 0 for fp in fleet_plans if fp.name() in tracked_plan_counts}

    if not fleet_plans:
        return

    universe = fo.get_universe()

    # Fleet plans that include ships capable of altering starlanes.
    # @content_tag{CAN_ALTER_STARLANES} universe_generator special handling
    # for fleets containing a hull design with this tag.
    fleet_can_alter_starlanes = {fp for fp in fleet_plans
                                 if any([universe.getGenericShipDesign(design).hull_type.hasTag("CAN_ALTER_STARLANES")
                                         for design in fp.ship_designs()])}

    # dump a list of all monster fleets meeting these conditions and their properties to the log
    print "Monster fleets available for generation at game start:"
    fp_location_cache = {}
    for fleet_plan in fleet_plans:
        print "...", fleet_plan.name(), ": spawn rate", fleet_plan.spawn_rate(),
        print "/ spawn limit", fleet_plan.spawn_limit(),
        print "/ effective chance", basic_chance * fleet_plan.spawn_rate(),
        fp_location_cache[fleet_plan] = set(fleet_plan.locations(systems))
        print ("/ can be spawned at", len(fp_location_cache[fleet_plan]),
               "of", len(systems), "systems")
        if fleet_plan.name() in nest_name_map.values():
            universe_statistics.tracked_monsters_chance[fleet_plan.name()] = basic_chance * fleet_plan.spawn_rate()

    # initialize a manager for monsters that can alter the map
    # required to prevent their placement from disjoining the map
    starlane_altering_monsters = StarlaneAlteringMonsters(systems)

    # collect info for tracked monster nest valid locations
    planets = [p for s in systems for p in fo.sys_get_planets(s)]
    tracked_nest_valid_locations = {nest: len(fo.special_locations(nest, planets))
                                    for nest in nest_name_map}

    # for each system in the list that has been passed to this function, find a monster fleet that can be spawned at
    # the system and which hasn't already been added too many times, then attempt to add that monster fleet by
    # testing the spawn rate chance
    random.shuffle(systems)
    for system in systems:
        # collect info for tracked monster valid locations
        for fp in tracked_plan_valid_locations:
            if system in fp_location_cache[fp]:
                tracked_plan_valid_locations[fp] += 1

        # filter out all monster fleets whose location condition allows this system and whose counter hasn't reached 0.
        suitable_fleet_plans = [fp for fp in fleet_plans
                                if system in fp_location_cache[fp]
                                and spawn_limits.get(fp, 0)
                                and (fp not in fleet_can_alter_starlanes
                                     or starlane_altering_monsters.can_place_at(system, fp))]
        # if there are no suitable monster fleets for this system, continue with the next
        if not suitable_fleet_plans:
            continue

        # randomly select one monster fleet out of the suitable ones and then test if we want to add it to this system
        # by making a roll against the basic chance multiplied by the spawn rate of this monster fleet
        expectation_tally += basic_chance * sum([fp.spawn_rate()
                                                 for fp in suitable_fleet_plans]) / len(suitable_fleet_plans)
        fleet_plan = random.choice(suitable_fleet_plans)
        if fleet_plan.name() in tracked_plan_tries:
            tracked_plan_tries[fleet_plan.name()] += 1
        if random.random() > basic_chance * fleet_plan.spawn_rate():
            print("\t\t At system %4d rejected monster fleet %s from %d suitable fleets"
                  % (system, fleet_plan.name(), len(suitable_fleet_plans)))
            # no, test failed, continue with the next system
            continue
        actual_tally += 1
        if fleet_plan.name() in tracked_plan_counts:
            tracked_plan_counts[fleet_plan.name()] += 1

        # all prerequisites and the test have been met, now spawn this monster fleet in this system
        # create monster fleet
        try:
            if fleet_plan in fleet_can_alter_starlanes:
                starlane_altering_monsters.place(system, fleet_plan)
            else:
                populate_monster_fleet(fleet_plan, system)
            # decrement counter for this monster fleet
            spawn_limits[fleet_plan] -= 1

        except MapGenerationError as err:
            report_error(str(err))
            continue

    print "Actual # monster fleets placed: %d; Total Placement Expectation: %.1f" % (actual_tally, expectation_tally)
    # finally, compile some statistics to be dumped to the log later
    universe_statistics.monsters_summary = [
        (fp.name(), fp.spawn_limit() - counter) for fp, counter in spawn_limits.iteritems()
    ]
    universe_statistics.tracked_monsters_tries.update(tracked_plan_tries)
    universe_statistics.tracked_monsters_summary.update(tracked_plan_counts)
    universe_statistics.tracked_monsters_location_summary.update(
        (fp.name(), count) for fp, count in tracked_plan_valid_locations.iteritems())
    universe_statistics.tracked_nest_location_summary.update(
        (nest_name_map[nest], count) for nest, count in tracked_nest_valid_locations.items())
Esempio n. 36
0
def compile_home_system_list(num_home_systems, systems):
    """
    Compiles a list with a requested number of home systems.
    """
    print "Compile home system list:", num_home_systems, "systems requested"

    # if the list of systems to choose home systems from is empty, report an error and return empty list
    if not systems:
        report_error(
            "Python generate_home_system_list: no systems to choose from")
        return []

    # calculate an initial minimal number of jumps that the home systems should be apart,
    # based on the total number of systems to choose from and the requested number of home systems
    # Don't let min_jumps be larger than 10, because a larger number is really not at all needed and with large
    # galaxies an excessive amount of time can be used in failed attempts
    min_jumps = min(
        10, max(int(float(len(systems)) / float(num_home_systems * 2)), 5))

    # home systems must have a certain minimum of systems in their near vicinity
    # we will try to select our home systems from systems that match this criteria, if that fails, we will select our
    # home systems from all systems and add the missing number planets to the systems in their vicinity afterwards
    # the minimum planet limit and the jump range that defines the "near vicinity" are controlled by the
    # HS_* option constants in options.py (see there)

    # lets start by filtering out all systems from the pool we got passed into this function that match the criteria
    filtered_pool = [s for s in systems if has_min_planets_in_vicinity(s)]
    print "Filtering out systems that meet the minimum planets in the near vicinity condition yielded",\
        len(filtered_pool), "systems"
    print "Using this as the preferred pool for home system selection"
    # now try to pick the requested number of home systems by calling find_home_systems
    # this function takes two pools, a "complete" pool and one with preferred systems
    # it will try to pick the home systems from the preferred pool first, so pass our filtered pool as preferred pool
    home_systems = find_home_systems(num_home_systems, systems, filtered_pool,
                                     min_jumps)

    # check if the selection process delivered a list with enough home systems
    # if not, our galaxy obviously is too crowded, report an error and return an empty list
    if len(home_systems) < num_home_systems:
        report_error(
            "Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems"
            % (num_home_systems, len(systems)))
        return []

    # check if we got more home systems than we requested
    if len(home_systems) > num_home_systems:
        # yes: calculate the number of planets in the near vicinity of each system
        # and store that value with each system in a map
        hs_planets_in_vicinity_map = {
            s: count_planets_in_systems(
                get_systems_within_jumps(s, HS_VICINITY_RANGE))
            for s in home_systems
        }
        # sort the home systems by the number of planets in their near vicinity using the map
        # now only pick the number of home systems we need, taking those with the highest number of planets
        home_systems = sorted(home_systems,
                              key=hs_planets_in_vicinity_map.get,
                              reverse=True)[:num_home_systems]

    # make sure all our home systems have a "real" star (that is, a star that is not a neutron star, black hole,
    # or even no star at all) and at least one planet in it
    for home_system in home_systems:
        # if this home system has no "real" star, change star type to a randomly selected "real" star
        if fo.sys_get_star_type(home_system) not in star_types_real:
            star_type = random.choice(star_types_real)
            print "Home system", home_system, "has star type", fo.sys_get_star_type(home_system),\
                  ", changing that to", star_type
            fo.sys_set_star_type(home_system, star_type)

        # if this home system has no planets, create one in a random orbit
        # we take random values for type and size, as these will be set to suitable values later
        if not fo.sys_get_planets(home_system):
            print "Home system", home_system, "has no planets, adding one"
            planet = fo.create_planet(
                random.choice(planet_sizes_real),
                random.choice(planet_types_real), home_system,
                random.randint(0,
                               fo.sys_get_num_orbits(home_system) - 1), "")
            # if we couldn't create the planet, report an error and return an empty list
            if planet == fo.invalid_object():
                report_error(
                    "Python generate_home_system_list: couldn't create planet in home system"
                )
                return []

    # finally, check again if all home systems meet the criteria of having the required minimum number of planets
    # within their near vicinity, if not, add the missing number of planets
    print "Checking if home systems have the required minimum of planets within the near vicinity..."
    for home_system in home_systems:
        # calculate the number of missing planets, and add them if this number is > 0
        systems_in_vicinity = get_systems_within_jumps(home_system,
                                                       HS_VICINITY_RANGE)
        num_systems_in_vicinity = len(systems_in_vicinity)
        num_planets_in_vicinity = count_planets_in_systems(systems_in_vicinity)
        num_planets_to_add = min_planets_in_vicinity_limit(
            num_systems_in_vicinity) - num_planets_in_vicinity
        print "Home system", home_system, "has", num_systems_in_vicinity, "systems and", num_planets_in_vicinity,\
            "planets in the near vicinity, required minimum:", min_planets_in_vicinity_limit(num_systems_in_vicinity)
        if num_planets_to_add > 0:
            systems_in_vicinity.remove(
                home_system
            )  # don't add planets to the home system, so remove it from the list
            add_planets_to_vicinity(systems_in_vicinity, num_planets_to_add)

    # as we've sorted the home system list before, lets shuffle it to ensure random order and return
    random.shuffle(home_systems)
    return home_systems
Esempio n. 37
0
def name_star_systems(system_list):
    # choose star types and planet sizes, before choosing names, so naming can have special handling of Deep Space
    star_type_assignments = {}
    planet_assignments = {}
    position_list = []
    for system in system_list:
        star_type = fo.sys_get_star_type(system)
        systemxy = fo.get_pos(system)
        star_type_assignments[systemxy] = star_type
        planet_assignments[systemxy] = fo.sys_get_planets(system)
        position_list.append(systemxy)

    # will name name a portion of stars on a group basis, where the stars of each group share the same base star name,
    # suffixed by different (default greek) letters or characters (options at top of file)
    star_name_map = {}
    star_names = names.get_name_list("STAR_NAMES")
    group_names = names.get_name_list("STAR_GROUP_NAMES")
    potential_group_names = []
    individual_names = []
    stargroup_words[:] = names.get_name_list("STAR_GROUP_WORDS")
    stargroup_chars[:] = names.get_name_list("STAR_GROUP_CHARS")
    stargroup_modifiers[:] = [stargroup_words, stargroup_chars][options.STAR_GROUPS_USE_CHARS]
    for starname in star_names:
        if len(starname) > 6:  # if starname is long, don't allow it for groups
            individual_names.append(starname)
            continue
        # any names that already have a greek letter in them can only be used for individual stars, not groups
        for namepart in starname.split():
            if namepart in greek_letters:
                individual_names.append(starname)
                break
        else:
            potential_group_names.append(starname)

    if not potential_group_names:
        potential_group_names.append("XYZZY")

    # ensure at least a portion of galaxy gets individual starnames
    num_systems = len(system_list)
    target_indiv_ratio = [options.TARGET_INDIV_RATIO_SMALL, options.TARGET_INDIV_RATIO_LARGE]\
                         [num_systems >= options.NAMING_LARGE_GALAXY_SIZE]
    # TODO improve the following calc to be more likely to hit target_indiv_ratio if more or less than
    # 50% potential_group_names used for groups
    num_individual_stars = int(max(min(num_systems * target_indiv_ratio,
                                       len(individual_names)+int(0.5 * len(potential_group_names))),
                                   num_systems - 0.8 * len(stargroup_modifiers) *
                                   (len(group_names)+int(0.5 * len(potential_group_names)))))
    star_group_size = 1 + int((num_systems - num_individual_stars) /
                              (max(1, len(group_names)+int(0.5 * len(potential_group_names)))))
    # make group size a bit bigger than min necessary, at least a trio
    star_group_size = max(3, star_group_size)
    num_star_groups = 1 + int(num_systems/star_group_size)  # initial value

    # first cluster all systems, then remove some to be individually named (otherwise groups can have too many
    # individually named systems in their middle).  First remove any that are too small (only 1 or 2 systems).
    # The clusters with the most systems are generally the most closely spaced, and though they might make good
    # logical candidates for groups, their names are then prone to overlapping on the galaxy map, so after removing
    # small groups, remove the groups with the most systems.
    random.shuffle(position_list)  # just to be sure it is randomized
    init_cluster_assgts = cluster_stars(position_list, num_star_groups)
    star_groups = {}
    for index_pos, index_group in enumerate(init_cluster_assgts):
        systemxy = position_list[index_pos]
        star_groups.setdefault(index_group, []).append(systemxy)
    indiv_systems = []

    # remove groups with only one non-deep-system
    for groupindex, group_list in star_groups.items():
        max_can_transfer = len(potential_group_names)-len(star_groups)+len(individual_names)-len(indiv_systems)
        if max_can_transfer <= 0:
            break
        elif max_can_transfer <= len(group_list):
            continue
        not_deep, deep_space = check_deep_space(group_list, star_type_assignments, planet_assignments)
        if len(not_deep) > 1:
            continue
        for systemxy in star_groups[groupindex]:
            indiv_systems.append(systemxy)
        del star_groups[groupindex]

    # remove tiny groups
    group_sizes = [(len(group), index) for index, group in star_groups.items()]
    group_sizes.sort()
    while len(indiv_systems) < num_individual_stars and len(group_sizes) > 0:
        groupsize, groupindex = group_sizes.pop()
        max_can_transfer = len(potential_group_names)-len(star_groups)+len(individual_names)-len(indiv_systems)
        if (max_can_transfer <= 0) or (groupsize > 2):
            break
        if max_can_transfer <= groupsize:
            continue
        for systemxy in star_groups[groupindex]:
            indiv_systems.append(systemxy)
        del star_groups[groupindex]

    # remove largest (likely most compact) groups
    while len(indiv_systems) < num_individual_stars and len(group_sizes) > 0:
        groupsize, groupindex = group_sizes.pop(-1)
        max_can_transfer = len(potential_group_names)-len(star_groups)+len(individual_names)-len(indiv_systems)
        if max_can_transfer <= 0:
            break
        if max_can_transfer <= groupsize:
            continue
        for systemxy in star_groups[groupindex]:
            indiv_systems.append(systemxy)
        del star_groups[groupindex]

    num_star_groups = len(star_groups)
    num_individual_stars = len(indiv_systems)
    random.shuffle(potential_group_names)
    random.shuffle(individual_names)
    random.shuffle(group_names)
    num_for_indiv = min(max(len(potential_group_names)/2, num_individual_stars+1-len(individual_names)),
                        len(potential_group_names))
    individual_names.extend(potential_group_names[:num_for_indiv])
    group_names.extend(potential_group_names[num_for_indiv:])

    #print "sampling for %d indiv names from list of %d total indiv names"%(num_individual_stars, len(individual_names))
    indiv_name_sample = random.sample(individual_names, num_individual_stars)
    #indiv_name_assignments = zip([(pos.x, pos.y) for pos in position_list[:num_individual_stars]], indiv_name_sample)
    indiv_name_assignments = zip(indiv_systems, indiv_name_sample)
    star_name_map.update(indiv_name_assignments)
    #print "sampling for %d group names from list of %d total group names"%(num_star_groups, len(group_names))
    if len(group_names) < num_star_groups:
        group_names.extend([names.random_name(6) for _ in range(num_star_groups - len(group_names))])
    group_name_sample = random.sample(group_names, num_star_groups)
    for index_group, group_list in enumerate(sorted(star_groups.values())):
        star_name_map.update(name_group(group_list, group_name_sample[index_group], star_type_assignments,
                                        planet_assignments))

    # assign names from star_name_map to star systems
    for system in system_list:
        fo.set_name(system, star_name_map.get(fo.get_pos(system), "") or random_star_name())
Esempio n. 38
0
def setup_empire(empire, empire_name, home_system, starting_species,
                 player_name):
    """
    Sets up various aspects of an empire, like empire name, homeworld, etc.
    """

    # set empire name, if no one is given, pick one randomly
    if not empire_name:
        print "No empire name set for player", player_name, ", picking one randomly"
        empire_name = next(empire_name_generator)
    fo.empire_set_name(empire, empire_name)
    print "Empire name for player", player_name, "is", empire_name

    # check starting species, if no one is given, pick one randomly
    if starting_species == "RANDOM" or not starting_species:
        print "Picking random starting species for player", player_name
        starting_species = next(starting_species_pool)
    print "Starting species for player", player_name, "is", starting_species
    universe_statistics.empire_species[starting_species] += 1

    # pick a planet from the specified home system as homeworld
    planet_list = fo.sys_get_planets(home_system)
    # if the system is empty, report an error and return false, indicating failure
    if not planet_list:
        report_error("Python setup_empire: got home system with no planets")
        return False
    homeworld = random.choice(planet_list)

    # set selected planet as empire homeworld with selected starting species
    fo.empire_set_homeworld(empire, homeworld, starting_species)

    # set homeworld focus
    # check if the preferred focus for the starting species is among
    # the foci available on the homeworld planet
    available_foci = fo.planet_available_foci(homeworld)
    preferred_focus = fo.species_preferred_focus(starting_species)
    if preferred_focus in available_foci:
        # if yes, set the homeworld focus to the preferred focus
        print "Player", player_name, ": setting preferred focus", preferred_focus, "on homeworld"
        fo.planet_set_focus(homeworld, preferred_focus)
    elif len(available_foci) > 0:
        # if no, and there is at least one available focus,
        # just take the first of the list
        if preferred_focus == "":
            print "Player", player_name, ": starting species", starting_species, "has no preferred focus, using",\
                  available_foci[0], "instead"
        else:
            print "Player", player_name, ": preferred focus", preferred_focus, "for starting species",\
                  starting_species, "not available on homeworld, using", available_foci[0], "instead"
        fo.planet_set_focus(homeworld, available_foci[0])
    else:
        # if no focus is available on the homeworld, don't set any focus
        print "Player", player_name, ": no available foci on homeworld for starting species", starting_species

    # give homeworld starting buildings
    print "Player", player_name, ": add starting buildings to homeworld"
    for item in fo.load_starting_buildings():
        fo.create_building(item.name, homeworld, empire)

    # unlock starting techs, buildings, hulls, ship parts, etc.
    # use default content file
    print "Player", player_name, ": add unlocked items"
    for item in fo.load_item_spec_list():
        fo.empire_unlock_item(empire, item.type, item.name)

    # add premade ship designs to empire
    print "Player", player_name, ": add premade ship designs"
    for ship_design in fo.design_get_premade_list():
        fo.empire_add_ship_design(empire, ship_design)

    # add starting fleets to empire
    # use default content file
    print "Player", player_name, ": add starting fleets"
    fleet_plans = fo.load_fleet_plan_list()
    for fleet_plan in fleet_plans:
        # first, create the fleet
        fleet = fo.create_fleet(fleet_plan.name(), home_system, empire)
        # if the fleet couldn't be created, report an error and try to continue with the next fleet plan
        if fleet == fo.invalid_object():
            report_error("Python setup empire: couldn't create fleet %s" %
                         fleet_plan.name())
            continue
        # second, iterate over the list of ship design names in the fleet plan
        for ship_design in fleet_plan.ship_designs():
            # create a ship in the fleet
            # if the ship couldn't be created, report an error and try to continue with the next ship design
            if fo.create_ship("", ship_design, starting_species,
                              fleet) == fo.invalid_object():
                report_error(
                    "Python setup empire: couldn't create ship %s for fleet %s"
                    % (ship_design, fleet_plan.name()))
    return True
Esempio n. 39
0
def execute_turn_events():
    print "Executing turn events for turn", fo.current_turn()

    # creating fields
    systems = fo.get_systems()
    radius = fo.get_universe_width() / 2.0
    if random() < max(0.0003 * radius, 0.03):
        if random() < 0.4:
            field_type = "FLD_MOLECULAR_CLOUD"
            size = 5.0
        else:
            field_type = "FLD_ION_STORM"
            size = 5.0

        x = y = radius
        dist_from_center = 0.0
        while (dist_from_center < radius) or any(
                hypot(fo.get_x(s) - x,
                      fo.get_y(s) - y) < 50.0 for s in systems):
            angle = random() * 2.0 * pi
            dist_from_center = radius + uniform(
                min(max(radius * 0.02, 10), 50.0),
                min(max(radius * 0.05, 20), 100.0))
            x = radius + (dist_from_center * sin(angle))
            y = radius + (dist_from_center * cos(angle))

        print "...creating new", field_type, "field, at distance", dist_from_center, "from center"
        if fo.create_field(field_type, x, y, size) == fo.invalid_object():
            print >> sys.stderr, "Turn events: couldn't create new field"

    # creating monsters
    gsd = fo.get_galaxy_setup_data()
    monster_freq = MONSTER_FREQUENCY[gsd.monsterFrequency]
    # monster freq ranges from 1/30 (= one monster per 30 systems) to 1/3 (= one monster per 3 systems)
    # (example: low monsters and 150 Systems results in 150 / 30 * 0.01 = 0.05)
    if monster_freq > 0 and random() < len(systems) * monster_freq * 0.01:
        #only spawn Krill at the moment, other monsters can follow in the future
        if random() < 1:
            monster_type = "SM_KRILL_1"
        else:
            monster_type = "SM_FLOATER"

        # search for systems without planets or fleets
        candidates = [
            s for s in systems if len(fo.sys_get_planets(s)) <= 0
            and len(fo.sys_get_fleets(s)) <= 0
        ]
        if not candidates:
            print >> sys.stderr, "Turn events: unable to find system for monster spawn"
        else:
            system = choice(candidates)
            print "...creating new", monster_type, "at", fo.get_name(system)

            # create monster fleet
            monster_fleet = fo.create_monster_fleet(system)
            # if fleet creation fails, report an error
            if monster_fleet == fo.invalid_object():
                print >> sys.stderr, "Turn events: unable to create new monster fleet"
            else:
                # create monster, if creation fails, report an error
                monster = fo.create_monster(monster_type, monster_fleet)
                if monster == fo.invalid_object():
                    print >> sys.stderr, "Turn events: unable to create monster in fleet"

    return True
Esempio n. 40
0
def compile_home_system_list(num_home_systems, systems, gsd):
    """
    Compiles a list with a requested number of home systems.
    """
    print "Compile home system list:", num_home_systems, "systems requested"

    # if the list of systems to choose home systems from is empty, report an error and return empty list
    if not systems:
        report_error(
            "Python generate_home_system_list: no systems to choose from")
        return []

    # calculate an initial minimal number of jumps that the home systems should be apart,
    # based on the total number of systems to choose from and the requested number of home systems
    # don't let min_jumps be either:
    # a.) larger than a defined limit, because an unreasonably large number is really not at all needed,
    #     and with large galaxies an excessive amount of time can be used in failed attempts
    # b.) lower than the minimum jump distance limit that should be considered high priority (see options.py),
    #     otherwise no attempt at all would be made to enforce the other requirements for home systems (see below)
    min_jumps = min(
        HS_MAX_JUMP_DISTANCE_LIMIT,
        max(int(float(len(systems)) / float(num_home_systems * 2)),
            HS_MIN_DISTANCE_PRIORITY_LIMIT))

    # home systems must have a certain minimum of systems and planets in their near vicinity
    # we will try to select our home systems from systems that match this criteria, if that fails, we will select our
    # home systems from all systems and add the missing number planets to the systems in their vicinity afterwards
    # the minimum system and planet limit and the jump range that defines the "near vicinity" are controlled by the
    # HS_* option constants in options.py (see there)

    # we start by building two additional pools of systems: one that contains all systems that match the criteria
    # completely (meets the min systems and planets limit), and one that contains all systems that match the criteria
    # at least partially (meets the min systems limit)
    pool_matching_sys_and_planet_limit = []
    pool_matching_sys_limit = []
    for system in systems:
        systems_in_vicinity = fo.systems_within_jumps_unordered(
            HS_VICINITY_RANGE, [system])
        if len(systems_in_vicinity) >= HS_MIN_SYSTEMS_IN_VICINITY:
            pool_matching_sys_limit.append(system)
            if count_planets_in_systems(
                    systems_in_vicinity) >= min_planets_in_vicinity_limit(
                        len(systems_in_vicinity)):
                pool_matching_sys_and_planet_limit.append(system)
    print(
        len(pool_matching_sys_and_planet_limit),
        "systems meet the min systems and planets in the near vicinity limit")
    print len(pool_matching_sys_limit
              ), "systems meet the min systems in the near vicinity limit"

    # now try to pick the requested number of home systems
    # we will do this by calling find_home_systems, which takes a list of tuples defining the pools from which to pick
    # the home systems; it will use the pools in the order in which they appear in the list, so put better pools first

    # we will make two attempts: the first one with the filtered pools we just created, and tell find_home_systems
    # to start with the min_jumps jumps distance we calculated above, but not to go lower than
    # HS_MIN_DISTANCE_PRIORITY_LIMIT

    print "First attempt: trying to pick home systems from the filtered pools of preferred systems"
    pool_list = [
        # the better pool is of course the one where all systems meet BOTH the min systems and planets limit
        (pool_matching_sys_and_planet_limit,
         "pool of systems that meet both the min systems and planets limit"),
        # next the less preferred pool where all systems at least meets the min systems limit
        # specify 0 as number of requested home systems to pick as much systems as possible
        (pool_matching_sys_limit,
         "pool of systems that meet at least the min systems limit"),
    ]
    home_systems = find_home_systems(num_home_systems, pool_list, min_jumps,
                                     HS_MIN_DISTANCE_PRIORITY_LIMIT)

    # check if the first attempt delivered a list with enough home systems
    # if not, we make our second attempt, this time disregarding the filtered pools and using all systems, starting
    # again with the min_jumps jump distance limit and specifying 0 as number of required home systems to pick as much
    # systems as possible
    if len(home_systems) < num_home_systems:
        print "Second attempt: trying to pick home systems from all systems"
        home_systems = find_home_systems(num_home_systems,
                                         [(systems, "complete pool")],
                                         min_jumps, 1)

    # check if the selection process delivered a list with enough home systems
    # if not, our galaxy obviously is too crowded, report an error and return an empty list
    if len(home_systems) < num_home_systems:
        report_error(
            "Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems"
            % (num_home_systems, len(systems)))
        return []

    # check if we got more home systems than we requested
    if len(home_systems) > num_home_systems:
        # yes: calculate the number of planets in the near vicinity of each system
        # and store that value with each system in a map
        hs_planets_in_vicinity_map = {
            s: calculate_home_system_merit(s)
            for s in home_systems
        }
        # sort the home systems by the number of planets in their near vicinity using the map
        # now only pick the number of home systems we need, taking those with the highest number of planets
        home_systems = sorted(home_systems,
                              key=hs_planets_in_vicinity_map.get,
                              reverse=True)[:num_home_systems]

    # make sure all our home systems have a "real" star (that is, a star that is not a neutron star, black hole,
    # or even no star at all) and at least one planet in it
    for home_system in home_systems:
        # if this home system has no "real" star, change star type to a randomly selected "real" star
        if fo.sys_get_star_type(home_system) not in star_types_real:
            star_type = random.choice(star_types_real)
            print "Home system", home_system, "has star type", fo.sys_get_star_type(home_system),\
                  ", changing that to", star_type
            fo.sys_set_star_type(home_system, star_type)

        # if this home system has no planets, create one in a random orbit
        # we take random values for type and size, as these will be set to suitable values later
        if not fo.sys_get_planets(home_system):
            print "Home system", home_system, "has no planets, adding one"
            planet = fo.create_planet(
                random.choice(planet_sizes_real),
                random.choice(planet_types_real), home_system,
                random.randint(0,
                               fo.sys_get_num_orbits(home_system) - 1), "")
            # if we couldn't create the planet, report an error and return an empty list
            if planet == fo.invalid_object():
                report_error(
                    "Python generate_home_system_list: couldn't create planet in home system"
                )
                return []

    # finally, check again if all home systems meet the criteria of having the required minimum number of planets
    # within their near vicinity, if not, add the missing number of planets
    print "Checking if home systems have the required minimum of planets within the near vicinity..."
    for home_system in home_systems:
        # calculate the number of missing planets, and add them if this number is > 0
        systems_in_vicinity = fo.systems_within_jumps_unordered(
            HS_VICINITY_RANGE, [home_system])
        num_systems_in_vicinity = len(systems_in_vicinity)
        num_planets_in_vicinity = count_planets_in_systems(systems_in_vicinity)
        num_planets_to_add = min_planets_in_vicinity_limit(
            num_systems_in_vicinity) - num_planets_in_vicinity
        print "Home system", home_system, "has", num_systems_in_vicinity, "systems and", num_planets_in_vicinity,\
            "planets in the near vicinity, required minimum:", min_planets_in_vicinity_limit(num_systems_in_vicinity)
        if num_planets_to_add > 0:
            systems_in_vicinity.remove(
                home_system
            )  # don't add planets to the home system, so remove it from the list
            # sort the systems_in_vicinity before adding, since the C++ engine doesn't guarrantee the same
            # platform independence as python.
            add_planets_to_vicinity(sorted(systems_in_vicinity),
                                    num_planets_to_add, gsd)

    # as we've sorted the home system list before, lets shuffle it to ensure random order and return
    random.shuffle(home_systems)
    return home_systems
Esempio n. 41
0
def execute_turn_events():
    print("Executing turn events for turn", fo.current_turn())

    # creating fields
    systems = fo.get_systems()
    radius = fo.get_universe_width() / 2.0
    field_types = [
        "FLD_MOLECULAR_CLOUD", "FLD_ION_STORM", "FLD_NANITE_SWARM",
        "FLD_METEOR_BLIZZARD", "FLD_VOID_RIFT"
    ]

    if random() < max(0.00015 * radius, 0.03):
        field_type = choice(field_types)
        size = 5.0
        x = y = radius
        dist_from_center = uniform(0.35, 1.0) * radius
        angle = random() * 2.0 * pi
        x = radius + (dist_from_center * sin(angle))
        y = radius + (dist_from_center * cos(angle))

        print("...creating new", field_type, "field, at distance",
              dist_from_center, "from center")
        if fo.create_field(field_type, x, y, size) == fo.invalid_object():
            print("Turn events: couldn't create new field", file=sys.stderr)

    # creating monsters
    gsd = fo.get_galaxy_setup_data()
    monster_freq = MONSTER_FREQUENCY[gsd.monsterFrequency]
    # monster freq ranges from 1/30 (= one monster per 30 systems) to 1/3 (= one monster per 3 systems)
    # (example: low monsters and 150 Systems results in 150 / 30 * 0.01 = 0.05)
    if monster_freq > 0 and random() < len(systems) * monster_freq * 0.01:
        # only spawn Krill at the moment, other monsters can follow in the future
        if random() < 1:
            monster_type = "SM_KRILL_1"
        else:
            monster_type = "SM_FLOATER"

        # search for systems without planets or fleets
        candidates = [
            s for s in systems if len(fo.sys_get_planets(s)) <= 0
            and len(fo.sys_get_fleets(s)) <= 0
        ]
        if not candidates:
            print("Turn events: unable to find system for monster spawn",
                  file=sys.stderr)
        else:
            system = choice(candidates)
            print("...creating new", monster_type, "at", fo.get_name(system))

            # create monster fleet
            monster_fleet = fo.create_monster_fleet(system)
            # if fleet creation fails, report an error
            if monster_fleet == fo.invalid_object():
                print("Turn events: unable to create new monster fleet",
                      file=sys.stderr)
            else:
                # create monster, if creation fails, report an error
                monster = fo.create_monster(monster_type, monster_fleet)
                if monster == fo.invalid_object():
                    print("Turn events: unable to create monster in fleet",
                          file=sys.stderr)

    return True
Esempio n. 42
0
def generate_natives(native_freq, systems, empire_home_systems):
    """
    Adds non-empire-affiliated native populations to planets.
    """

    # first, calculate the chance for natives on a planet based on the native frequency that has been passed
    # get the corresponding value for the specified natives frequency from the universe tables
    inverse_native_chance = fo.native_frequency(native_freq)
    # as the value in the universe table is higher for a lower frequency, we have to invert it
    # exception: a value of 0 means no natives, in this case return immediately
    if inverse_native_chance <= 0:
        return
    native_chance = 1.0 / float(inverse_native_chance)

    # compile a list of planets where natives can be placed
    # select only planets sufficiently far away from player home systems
    native_safe_planets = []  # list of planets safe for natives
    for candidate in systems:
        if not is_too_close_to_empire_home_systems(candidate, empire_home_systems):
            # this system is sufficiently far away from all player homeworlds, so add it's planets to our list
            native_safe_planets += fo.sys_get_planets(candidate)
    print "Number of planets far enough from players for natives to be allowed:", len(native_safe_planets)
    # if there are no "native safe" planets at all, we can stop here
    if not native_safe_planets:
        return

    # get all native species
    native_species = fo.get_native_species()
    print "Species that can be added as natives:"
    print "... " + "\n... ".join(native_species)

    # create a map with a list for each planet type containing the species
    # for which this planet type is a good environment
    # we will need this afterwards when picking natives for a planet
    natives_for_planet_type.clear()  # just to be safe
    natives_for_planet_type.update( {planet_type: [] for planet_type in planets.planet_types} )
    planet_types_for_natives.clear()
    planet_types_for_natives.update( {species: set() for species in native_species} )
    # iterate over all native species we got
    for species in native_species:
        # check the planet environment for all planet types for this species
        for planet_type in planets.planet_types:
            # if this planet type is a good environment for the species, add it to the list for this planet type
            if fo.species_get_planet_environment(species, planet_type) == fo.planetEnvironment.good:
                natives_for_planet_type[planet_type].append(species)
                planet_types_for_natives[species].add(planet_type)

    # randomly add species to planets
    # iterate over the list of "native safe" planets we compiled earlier
    for candidate in native_safe_planets:
        # select a native species to put on this planet
        planet_type = fo.planet_get_type(candidate)
        # check if we have any native species that like this planet type
        if not natives_for_planet_type[planet_type]:
            # no, continue with next planet
            continue
        statistics.potential_native_planet_summary[planet_type] += 1
        # make a "roll" against the chance for natives to determine if we shall place natives on this planet
        if random.random() > native_chance:
            # no, continue with next planet
            continue
        statistics.settled_native_planet_summary[planet_type] += 1

        # randomly pick one of the native species available for this planet type
        natives = random.choice(natives_for_planet_type[planet_type])

        # put the selected natives on the planet
        fo.planet_set_species(candidate, natives)
        # set planet as homeworld for that species
        fo.species_add_homeworld(natives, candidate)
        # set planet focus
        # check if the preferred focus for the native species is among the foci available on this planet
        available_foci = fo.planet_available_foci(candidate)
        preferred_focus = fo.species_preferred_focus(natives)
        if preferred_focus in available_foci:
            # if yes, set the planet focus to the preferred focus
            fo.planet_set_focus(candidate, preferred_focus)
        elif available_foci:
            # if no, and there is at least one available focus, just take the first of the list
            # otherwise don't set any focus
            fo.planet_set_focus(candidate, available_foci[0])
        print "Added native", natives, "to planet", fo.get_name(candidate)

        # increase the statistics counter for this native species, so a species summary can be dumped to the log later
        statistics.species_summary[natives] += 1
Esempio n. 43
0
def generate_monsters(monster_freq, systems):
    """
    Adds space monsters to systems.
    """

    # first, calculate the basic chance for monster generation in a system
    # based on the monster frequency that has been passed
    # get the corresponding value for the specified monster frequency from the universe tables
    basic_chance = universe_tables.MONSTER_FREQUENCY[monster_freq]
    # a value of 0 means no monsters, in this case return immediately
    if basic_chance <= 0:
        return
    print "Default monster spawn chance:", basic_chance
    expectation_tally = 0.0
    actual_tally = 0

    # get all monster fleets that have a spawn rate and limit both > 0 and at least one monster ship design in it
    # (a monster fleet with no monsters in it is pointless) and store them in a list
    fleet_plans = fo.load_monster_fleet_plan_list()

    # create a map where we store a spawn counter for each monster fleet
    # this counter will be set to the spawn limit initially and decreased every time the monster fleet is spawned
    # this map (dict) needs to be separate from the list holding the fleet plans because the order in which items
    # are stored in a dict is undefined (can be different each time), which would result in different distribution
    # even when using the same seed for the RNG
    spawn_limits = {fp: fp.spawn_limit() for fp in fleet_plans
                    if fp.spawn_rate() > 0.0 and fp.spawn_limit() > 0 and fp.ship_designs()}

    # map nests to monsters for ease of reporting
    nest_name_map = dict(zip(["KRAKEN_NEST_SPECIAL", "SNOWFLAKE_NEST_SPECIAL", "JUGGERNAUT_NEST_SPECIAL"], ["SM_KRAKEN_1", "SM_SNOWFLAKE_1", "SM_JUGGERNAUT_1"]))
    tracked_plan_tries = {name: 0 for name in nest_name_map.values()}
    tracked_plan_counts = {name: 0 for name in nest_name_map.values()}
    tracked_plan_valid_locations = {fp: 0 for fp in fleet_plans if fp.name() in tracked_plan_counts}
    tracked_nest_valid_locations = {nest: 0 for nest in nest_name_map}

    if not fleet_plans:
        return

    # dump a list of all monster fleets meeting these conditions and their properties to the log
    print "Monster fleets available for generation at game start:"
    for fleet_plan in fleet_plans:
        print "...", fleet_plan.name(), ": spawn rate", fleet_plan.spawn_rate(),
        print "/ spawn limit", fleet_plan.spawn_limit(),
        print "/ effective chance", basic_chance * fleet_plan.spawn_rate(),
        if len(systems) < 1000:
            print "/ can be spawned at", len([s for s in systems if fleet_plan.location(s)]), "systems"
        else:
            print  # to terminate the print line
        if fleet_plan.name() in nest_name_map.values():
            statistics.tracked_monsters_chance[fleet_plan.name()] = basic_chance * fleet_plan.spawn_rate()

    # for each system in the list that has been passed to this function, find a monster fleet that can be spawned at
    # the system and which hasn't already been added too many times, then attempt to add that monster fleet by
    # testing the spawn rate chance
    for system in systems:
        # collect info for tracked monster nest valid locations
        for planet in fo.sys_get_planets(system):
            for nest in tracked_nest_valid_locations:
                #print "\t tracked monster check planet: %d size: %s for nest: %20s  | result: %s" % (planet, fo.planet_get_size(planet), nest, fo.special_location(nest, planet))
                if fo.special_location(nest, planet):
                    tracked_nest_valid_locations[nest] += 1

        # collect info for tracked monster valid locations
        for fp in tracked_plan_valid_locations:
            if fp.location(system):
                tracked_plan_valid_locations[fp] += 1

        # filter out all monster fleets whose location condition allows this system and whose counter hasn't reached 0
        suitable_fleet_plans = [fp for fp in fleet_plans if spawn_limits[fp] and fp.location(system)]
        # if there are no suitable monster fleets for this system, continue with the next
        if not suitable_fleet_plans:
            continue

        # randomly select one monster fleet out of the suitable ones and then test if we want to add it to this system
        # by making a roll against the basic chance multiplied by the spawn rate of this monster fleet
        expectation_tally += basic_chance * sum([fp.spawn_rate() for fp in suitable_fleet_plans]) / len(suitable_fleet_plans)
        fleet_plan = random.choice(suitable_fleet_plans)
        if fleet_plan.name() in tracked_plan_tries:
            tracked_plan_tries[fleet_plan.name()] += 1
        if random.random() > basic_chance * fleet_plan.spawn_rate():
            print "\t\t At system %4d rejected monster fleet %s from %d suitable fleets" % (system, fleet_plan.name(), len(suitable_fleet_plans))
            # no, test failed, continue with the next system
            continue
        actual_tally += 1
        if fleet_plan.name() in tracked_plan_counts:
            tracked_plan_counts[fleet_plan.name()] += 1

        # all prerequisites and the test have been met, now spawn this monster fleet in this system
        print "Spawn", fleet_plan.name(), "at", fo.get_name(system)
        # decrement counter for this monster fleet
        spawn_limits[fleet_plan] -= 1
        # create monster fleet
        monster_fleet = fo.create_monster_fleet(system)
        # if fleet creation fails, report an error and try to continue with next system
        if monster_fleet == fo.invalid_object():
            util.report_error("Python generate_monsters: unable to create new monster fleet %s" % fleet_plan.name())
            continue
        # add monsters to fleet
        for design in fleet_plan.ship_designs():
            # create monster, if creation fails, report an error and try to continue with the next design
            if fo.create_monster(design, monster_fleet) == fo.invalid_object():
                util.report_error("Python generate_monsters: unable to create monster %s" % design)

    print "Actual # monster fleets placed: %d; Total Placement Expectation: %.1f" % (actual_tally, expectation_tally)
    # finally, compile some statistics to be dumped to the log later
    statistics.monsters_summary = [(fp.name(), fp.spawn_limit() - counter) for fp, counter in spawn_limits.iteritems()]
    statistics.tracked_monsters_tries.update(tracked_plan_tries)
    statistics.tracked_monsters_summary.update(tracked_plan_counts)
    statistics.tracked_monsters_location_summary.update([(fp.name(), count) for fp, count in tracked_plan_valid_locations.iteritems()])
    statistics.tracked_nest_location_summary.update([(nest_name_map[nest], count) for nest, count in tracked_nest_valid_locations.items()])