def place_special(specials, obj): """ Place at most a single special. Return the number of specials placed. """ # Calculate the conditional probabilities that each special is # placed here given that a special will be placed here. probs = [fo.special_spawn_rate(sp) for sp in specials] total_prob = float(sum(probs)) if total_prob == 0: # This shouldn't happen since special_spawn_rate > 0.0 is checked in distribute_specials() return 0 thresholds = [x / total_prob for x in probs] chance = random.random() for threshold, special in zip(thresholds, specials): if chance > threshold: chance -= threshold continue fo.add_special(obj, special) print "Special", special, "added to", fo.get_name(obj) universe_statistics.specials_summary[special] += 1 return 1 return 0
def place_special(specials, obj): """ Place at most a single special. Return the number of specials placed. """ # Calculate the conditional probabilities that each special is # placed here given that a special will be placed here. probs = [fo.special_spawn_rate(sp) for sp in specials] total_prob = float(sum(probs)) if total_prob == 0: # This shouldn't happen since special_spawn_rate > 0.0 is checked in distribute_specials() return 0 thresholds = [x / total_prob for x in probs] chance = random.random() for threshold, special in zip(thresholds, specials): if chance > threshold: chance -= threshold continue fo.add_special(obj, special) print("Special", special, "added to", fo.get_name(obj)) universe_statistics.specials_summary[special] += 1 return 1 return 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
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)
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)
def populate_monster_fleet(fleet_plan, system): """ Create a monster fleet in ''system'' according to ''fleet_plan'' """ # create monster fleet monster_fleet = fo.create_monster_fleet(system) if monster_fleet == fo.invalid_object(): raise MapGenerationError("Python generate_monsters: unable to create new monster fleet %s" % fleet_plan.name()) # add monsters to fleet for design in fleet_plan.ship_designs(): if fo.create_monster(design, monster_fleet) == fo.invalid_object(): raise MapGenerationError("Python generate_monsters: unable to create monster %s" % design) print "Spawn", fleet_plan.name(), "at", fo.get_name(system)
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
def distribute_specials(specials_freq, universe_objects): """ Adds start-of-game specials to universe objects. """ # get basic chance for occurrence of specials from the universe tables # the values there are integers, so we have to divide them by 10,000 to get the actual basic probability value basic_chance = float(fo.specials_frequency(specials_freq)) / 10000.0 if basic_chance <= 0: return # get a list with all specials that have a spawn rate and limit both > 0 and a location condition defined # (no location condition means a special shouldn't get added at game start) specials = [ sp for sp in fo.get_all_specials() if fo.special_spawn_rate(sp) > 0.0 and fo.special_spawn_limit(sp) > 0 and fo.special_has_location(sp) ] if not specials: return # dump a list of all specials meeting that conditions and their properties to the log print "Specials available for distribution at game start:" for special in specials: print "...", special, ": spawn rate", fo.special_spawn_rate(special),\ "/ spawn limit", fo.special_spawn_limit(special) # attempt to apply a special to each universe object in the list that has been passed to this function # by finding a special that can be applied to it and hasn't been added too many times, and then attempt # to add that special by testing its spawn rate repeat_rate = {1: 0.08, 2: 0.05, 3: 0.01, 4: 0.00} for univ_obj in universe_objects: # for this universe object, find a suitable special # start by shuffling our specials list, so each time the specials are considered in a new random order random.shuffle(specials) num_here = 0 # then, consider each special until one has been found or we run out of specials # (the latter case means that no special is added to this universe object) for special in specials: # check if the spawn limit for this special has already been reached (that is, if this special # has already been added the maximal allowed number of times) if statistics.specials_summary[special] >= fo.special_spawn_limit( special): # if yes, consider next special continue # check if this universe object matches the location condition for this special # (meaning, if this special can be added to this universe object at all) if not fo.special_location(special, univ_obj): # if not, consider next special continue # we have found a special that meets all prerequisites # now do the test if we want to add the selected special to this universe object by making a roll against # the basic probability multiplied by the spawn rate of the special if random.random() > basic_chance * fo.special_spawn_rate(special): # no, test failed, break out of the specials loop and continue with the next universe object statistics.specials_repeat_dist[num_here] += 1 break num_here += 1 # all prerequisites and the test have been met, now add this special to this universe object fo.add_special(univ_obj, special) # increase the statistic counter for this special, so we can keep track of how often it has already # been added (needed for the spawn limit test above, and to dump some statistics to the log later) statistics.specials_summary[special] += 1 print "Special", special, "added to", fo.get_name(univ_obj) # stop attempting to add specials here? give a small chance to try more than one special if random.random() > repeat_rate.get(num_here, 0.0): # sorry, no, break out of the specials loop and continue with the next universe object statistics.specials_repeat_dist[num_here] += 1 break else: statistics.specials_repeat_dist[num_here] += 1
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
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
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() ])
def distribute_specials(specials_freq, universe_objects): """ Adds start-of-game specials to universe objects. """ # get basic chance for occurrence of specials from the universe tables # the values there are integers, so we have to divide them by 10,000 to get the actual basic probability value basic_chance = float(fo.specials_frequency(specials_freq)) / 10000.0 if basic_chance <= 0: return # get a list with all specials that have a spawn rate and limit both > 0 and a location condition defined # (no location condition means a special shouldn't get added at game start) specials = [sp for sp in fo.get_all_specials() if fo.special_spawn_rate(sp) > 0.0 and fo.special_spawn_limit(sp) > 0 and fo.special_has_location(sp)] if not specials: return # dump a list of all specials meeting that conditions and their properties to the log print "Specials available for distribution at game start:" for special in specials: print "...", special, ": spawn rate", fo.special_spawn_rate(special),\ "/ spawn limit", fo.special_spawn_limit(special) # attempt to apply a special to each universe object in the list that has been passed to this function # by finding a special that can be applied to it and hasn't been added too many times, and then attempt # to add that special by testing its spawn rate repeat_rate = {1 : 0.08, 2 : 0.05, 3 : 0.01, 4 : 0.00} for univ_obj in universe_objects: # for this universe object, find a suitable special # start by shuffling our specials list, so each time the specials are considered in a new random order random.shuffle(specials) num_here = 0 # then, consider each special until one has been found or we run out of specials # (the latter case means that no special is added to this universe object) for special in specials: # check if the spawn limit for this special has already been reached (that is, if this special # has already been added the maximal allowed number of times) if statistics.specials_summary[special] >= fo.special_spawn_limit(special): # if yes, consider next special continue # check if this universe object matches the location condition for this special # (meaning, if this special can be added to this universe object at all) if not fo.special_location(special, univ_obj): # if not, consider next special continue # we have found a special that meets all prerequisites # now do the test if we want to add the selected special to this universe object by making a roll against # the basic probability multiplied by the spawn rate of the special if random.random() > basic_chance * fo.special_spawn_rate(special): # no, test failed, break out of the specials loop and continue with the next universe object statistics.specials_repeat_dist[num_here] += 1 break num_here += 1 # all prerequisites and the test have been met, now add this special to this universe object fo.add_special(univ_obj, special) # increase the statistic counter for this special, so we can keep track of how often it has already # been added (needed for the spawn limit test above, and to dump some statistics to the log later) statistics.specials_summary[special] += 1 print "Special", special, "added to", fo.get_name(univ_obj) # stop attempting to add specials here? give a small chance to try more than one special if random.random() > repeat_rate.get(num_here, 0.0): # sorry, no, break out of the specials loop and continue with the next universe object statistics.specials_repeat_dist[num_here] += 1 break else: statistics.specials_repeat_dist[num_here] += 1
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()])
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