def create_universe(): print "Python Universe Generator" # fetch universe and player setup data global gsd, psd_list gsd = fo.get_galaxy_setup_data() psd_list = fo.get_player_setup_data() total_players = len(psd_list) # initialize RNG random.seed(gsd.seed) # store list of possible star system names in global container global star_names star_names = get_name_list("STAR_NAMES") # randomly shuffle the list so we don't get the names always # in the same order when we pop names from the list later random.shuffle(star_names) # make sure there are enough systems for the given number of players print "Universe creation requested with %d systems for %d players" % (gsd.size, total_players) new_size = adjust_universe_size(gsd.size, total_players) if new_size > gsd.size: gsd.size = new_size print "Too few systems for the requested number of players, number of systems adjusted accordingly" print "Creating universe with %d systems for %d players" % (gsd.size, total_players) # get typical width for universe based on number of systems width = fo.calc_typical_universe_width(gsd.size) fo.set_universe_width(width) print "Set universe width to", width # Calling universe generator helper functions to calculate positions # for the requested galaxy shape and number of systems system_positions = fo.SystemPositionVec() if gsd.shape == fo.galaxyShape.random: gsd.shape = random.choice(galaxy_shapes) if gsd.shape == fo.galaxyShape.spiral2: fo.spiral_galaxy_calc_positions(system_positions, 2, gsd.size, width, width) elif gsd.shape == fo.galaxyShape.spiral3: fo.spiral_galaxy_calc_positions(system_positions, 3, gsd.size, width, width) elif gsd.shape == fo.galaxyShape.spiral4: fo.spiral_galaxy_calc_positions(system_positions, 4, gsd.size, width, width) elif gsd.shape == fo.galaxyShape.elliptical: fo.elliptical_galaxy_calc_positions(system_positions, gsd.size, width, width) elif gsd.shape == fo.galaxyShape.cluster: # Typically a galaxy with 100 systems should have ~5 clusters avg_clusters = gsd.size / 20 if avg_clusters < 2: avg_clusters = 2 # Add a bit of random variation (+/- 20%) clusters = random.randint((avg_clusters * 8) / 10, (avg_clusters * 12) / 10) if clusters >= 2: fo.cluster_galaxy_calc_positions(system_positions, clusters, gsd.size, width, width) elif gsd.shape == fo.galaxyShape.ring: fo.ring_galaxy_calc_positions(system_positions, gsd.size, width, width) elif gsd.shape == fo.galaxyShape.test: test_galaxy_calc_positions(system_positions, gsd.size, width) # Check if any positions have been calculated... if len(system_positions) <= 0: # ...if not, fall back on irregular shape gsd.shape = fo.galaxyShape.irregular fo.irregular_galaxy_positions(system_positions, gsd.size, width, width) gsd.size = len(system_positions) print gsd.shape, "galaxy created, final number of systems:", gsd.size # choose star types and planet sizes, before choosting names, so naming can have special handling of Deep Space star_type_assignments = {} planet_size_assignments = {} planet_count_dist = {} for position in system_positions: star_type = pick_star_type() # needed to determine planet size star_type_assignments[(position.x, position.y)] = star_type these_planets = {} planet_count = 0 for orbit in range(0, system_orbits): # check for each orbit if a planet shall be created by determining planet size planet_size = calc_planet_size(star_type, orbit) these_planets[orbit] = planet_size if planet_size in planet_sizes: planet_count += 1 planet_size_assignments[(position.x, position.y)] = these_planets planet_count_dist.setdefault(planet_count, [0])[0] += 1 print "\n Planet Count Distribution: planets_in_system | num_systems" for planet_count, sys_count in planet_count_dist.items(): print "\t\t\t%2d | %5d" % (planet_count, sys_count[0]) print # 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 = {} group_names = get_name_list("STAR_GROUP_NAMES") potential_group_names = [] individual_names = [] stargroup_words[:] = get_name_list("STAR_GROUP_WORDS") stargroup_chars[:] = get_name_list("STAR_GROUP_CHARS") stargroup_modifiers[:] = [stargroup_words, stargroup_chars][ 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 potential_group_names == []: potential_group_names.append("XYZZY") # ensure at least a portion of galaxy gets individual starnames num_systems = len(system_positions) target_indiv_ratio = [target_indiv_ratio_small, target_indiv_ratio_large][ num_systems >= 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 #print "num_individual_stars:", num_individual_stars, "star group size: ", star_group_size, "num_star_groups", num_star_groups #print "num indiv names:", len(individual_names), "num group_names:", len(group_names), "num potential_group_names:", len(potential_group_names) # 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. position_list = list(system_positions) 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): this_pos = position_list[index_pos] star_groups.setdefault(index_group, []).append( (this_pos.x, this_pos.y) ) 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_size_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)) group_name_sample = random.sample(group_names, num_star_groups) for index_group, group_list in enumerate(star_groups.values()): star_name_map.update( name_group(group_list, group_name_sample[index_group], star_type_assignments, planet_size_assignments) ) # Generate and populate systems systems = [] planet_type_summary = {} for position in system_positions: systemxy = (position.x, position.y) star_type = star_type_assignments.get(systemxy, fo.starType.noStar) star_name = star_name_map.get(systemxy, "") or get_star_name() system = fo.create_system(star_type, star_name, position.x, position.y) systems.append(system) these_planets = planet_size_assignments[systemxy] for orbit in range(0, fo.sys_get_num_orbits(system)): # check for each orbit if a planet shall be created by determining planet size planet_size = these_planets.get(orbit, fo.planetSize.noWorld) if planet_size in planet_sizes: # ok, we want a planet, determine planet type and generate the planet planet_type = calc_planet_type(star_type, orbit, planet_size) planet_type_summary.setdefault(planet_type, [0])[0]+=1 generate_planet(planet_size, planet_type, system, orbit) print len(systems), "systems generated and populated" planet_type_names = dict(zip(planet_types_all, ["Swamp", "Radiated", "Toxic", "Inferno", "Barren", "Tundra", "Desert", "Terran", "Ocean", "Asteroids", "Gas Giant"])) planet_total = sum([type_tally[0] for type_tally in planet_type_summary.values()]) print "\nPlanet Type Summary for a total of %d placed planets" % (planet_total) for planet_type in planet_type_summary: print "\t %12s: %.1f%%" % (planet_type_names[planet_type], (100.0*planet_type_summary.get(planet_type, [0])[0])/planet_total) print # generate Starlanes fo.generate_starlanes(gsd.starlaneFrequency) print "Starlanes generated" print "Generate list of home systems..." home_systems = generate_home_system_list(total_players, systems) print "...systems choosen:", home_systems # store list of possible empire names in global container global empire_names print "Load list of empire names..." empire_names = get_name_list("EMPIRE_NAMES") print "...", len(empire_names), "names loaded" # randomly shuffle the list so we don't get the names always # in the same order when we pop names from the list later random.shuffle(empire_names) # set up empires for each player for psd_entry in psd_list: empire = psd_entry.key() psd = psd_entry.data() home_system = home_systems.pop() setup_empire(empire, psd.empire_name, home_system, psd.starting_species, psd.player_name) # iterate over all systems and name their planets # this needs to be done after empire home systems have been set, as # during that process asteroid belts might be changed into planets, # and we need to know the final type of a planet to name it print "Set planet names" for system in systems: name_planets(system) print "Python Universe Generator completed"
def generate_home_system_list(num_home_systems, systems): # id the list of systems to choose home systems from is empty, raise an exception if len(systems) == 0: err_msg = "Python generate_home_system_list: no systems to choose from" print err_msg raise Exception(err_msg) # initialize list of home systems home_systems = [] # loop and get a new home systems until we have the requested number while len(home_systems) < num_home_systems: # try to choose a system until too many attempty failed or a system has been found attempts = 0 found = False while (attempts < 100) and not found: attempts = attempts + 1 # randomly choose one system from the list we got candidate = random.choice(systems) # for the first 50 attempts, only consider systems with "real" stars and at least one planet # if we haven't found a system after 50 attempts, consider all systems if (attempts < 50): if fo.sys_get_star_type(candidate) not in real_star_types: continue if len(fo.sys_get_planets(candidate)) == 0: continue # if our candidate is too close to the already choosen home systems, don't use it if is_too_close_to_other_home_systems(candidate, home_systems): continue # if our candidate passed the above tests, add it to our list home_systems.append(candidate) found = True # if no system could be found, just attempt to find one that's not # already a home system and disregard any other conditions if not found: print "Couldn't find homeworld #", len(home_systems) + 1, "after 100 attempts, just trying to find one now that's not already a home system and disregard any other condition" attempts = 0 while (attempts < 50) and not found: attempts = attempts +1 # again, choose one system from the list we got candidate = random.choice(systems) # but now just check if it has already been choosen as home system if candidate in home_systems: # if yes, try again continue # if our candidate passed the test, add it to our list home_systems.append(candidate) found = True # if we still haven't found a suitable system, our galaxy obviously is too crowded # in that case, throw a fit, em, exception ;) if not found: raise Exception("Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems, aborting" % (num_home_systems, len(systems))) # if choosen system has no "real" star, change star type to a randomly selected "real" star if fo.sys_get_star_type(candidate) not in real_star_types: star_type = random.choice(real_star_types) print "Home system #", len(home_systems), "has star type", fo.sys_get_star_type(candidate), ", changing that to", star_type fo.sys_set_star_type(candidate, star_type) # if choosen 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 len(fo.sys_get_planets(candidate)) == 0: print "Home system #", len(home_systems), "has no planets, adding one" if generate_planet(random.choice(real_planet_sizes), random.choice(planet_types), candidate, random.randint(0, fo.sys_get_num_orbits(candidate) - 1)) == fo.invalid_object(): # generate planet failed, throw an exception raise Exception("Python generate_home_system_list: couldn't create planet in home system") return home_systems