├── README.md
├── browse_sector.py
├── build_db_from_csv.py
├── convert_travmap_to_csv.py
├── covers
├── a.jpg
├── b.jpg
├── c.jpg
├── d.jpg
├── e.jpg
├── f.jpg
├── g.jpg
├── h.jpg
├── i.jpg
├── j.jpg
├── k.jpg
├── l.jpg
├── m.jpg
├── n.jpg
├── o.jpg
└── p.jpg
├── culture.py
├── export_sector.py
├── far_trader.py
├── first_in_generation.py
├── generate_menu.py
├── images
├── agricultural.png
├── asteroid.png
├── barren.png
├── blank.png
├── cold.png
├── corrosive.png
├── exotic.png
├── garden.png
├── gas giant.PNG
├── heavy.png
├── hipop.png
├── hot.png
├── important.png
├── industrial.png
├── light.png
├── lopop.png
├── mask.png
├── moon.png
├── naval.png
├── non_agricultural.png
├── non_industrial.png
├── ocean.png
├── planet.png
├── prison.png
├── scout.png
├── ship.png
├── tbd.png
├── vacuum.png
└── wealthy.png
├── journey_data.py
├── mainworld_calculator.py
├── mainworld_selector.py
├── names.csv
├── non_mw.py
├── routes_short_path.py
├── sector_db
├── example-66.db
├── example-66.db_routes.txt
├── example-66.db_tab.txt
└── traveller_map_poster.html
├── splash.jpg
├── splash_browser.jpg
├── sunburst.ico
├── tables
├── Orbital Eccentricity Table.txt
├── Orbital Separation Table.txt
├── Planet Density Table.txt
├── Star Characteristics III.txt
├── Star Characteristics V.txt
└── World Type Table.txt
├── test_export_def.py
├── trade_goods.csv
├── traveller_functions.py
├── traveller_map.py
├── traveller_master.py
└── travellerization.py
/README.md:
--------------------------------------------------------------------------------
1 | # traveller-universe-creator
2 |
3 | ## aka Bartleby's Sector Builder
4 |
5 | **v 1.1.0 - 2024-05-28**
6 |
7 | Full release complete, including install exe.
8 |
9 | **Program Purpose:**
10 |
11 | The program generates an entire sci-fi sector of stars, planets, moons using "realistic" scientific models
12 |
13 | **How to Run:**
14 |
15 | Option 1: From the installer
16 | - Download and run the install_bartleby_sector_builder_v.1.1.0.exe file.
17 | (This can be found in the "Releases" section on the right).
18 | - Once installed:
19 | - run generate_menu.exe to generate a DB
20 | - find generated DBs in /sector_db
21 | - run browse_sector.exe to view a sector
22 | - optionally use a DB browser (like https://sqlitebrowser.org/) to look at every piece of data for each planet/system
23 | - **please note:** the example-66 database in the /sector_db directory MUST be there for browse_sector to work. Do not remove it.
24 |
25 | Option 2: From the code - requires python run time environment
26 | - run generate_menu.exe to generate a DB
27 | - find generated DBs in /sector_db
28 | - run browse_sector.exe to view a sector
29 | - optionally use a DB browser (like https://sqlitebrowser.org/) to look at every piece of data for each planet/system
30 | - **please note:** the example-66 database in the /sector_db directory MUST be there for browse_sector to work. Do not remove it.
31 |
32 | Requirements for Option 2 (non-EXE installs):
33 |
34 | - Programmed using Python 3.12
35 |
36 | - This is the up to date list of all required imports:
37 |
38 | - pandas v1.4.2 or higher
39 | - numpy v1.22.3 or higher
40 | - networkx v2.8.4 or higher
41 | - matplotlib v3.5.2 or higher
42 | - pillow v9.2 or higher
43 | - PyPDF2 v3.0.1 (latest at this time)
44 | - reportlab v4.2.0 (latest at this time)
45 | - FreeSimpleGUI v5.1.0 (latest at this time)
46 |
47 |
48 |
49 | **v 1.1.0 - Upgrades**
50 |
51 | - Changed from PySimpleGUI to FreeSimpleGUI (until I better understand licensing requirements)
52 | - Added browser functionality to use APIs from travellerworlds.com and travellermap.com
53 | - Added "Rings" value to browser
54 | - Updated PBG value at creation to include PBG numbers beyond primary system
55 | - moved text tables to tables directory
56 |
57 |
58 |
59 | **FAQ:**
60 |
61 | Q: What does the program do?
62 |
63 | A: After setting the parameters you want, it produces a traveller/cepheus sector and provides the information in separate files explained above.
64 |
65 | The text files can be used to visualize the sector using https://travellermap.com/ or PyMapGen at https://github.com/ShawnDriscoll/PyMapGen
66 |
67 | The db file houses information for every star and planet in the sector. It includes UWPs for mainworld and non-mainworlds.
68 | You can browse and search this database using the excellent SQLite browser from: https://sqlitebrowser.org/
69 |
70 | Q: What Traveller rules does it use?
71 |
72 | A: It uses mostly GURPS First In for the science stuff (like stellar details, and planet temperature).
73 | T5 rules for the Traveller stuff (like law level and influence scores).
74 | I have also been using parts of Architect of Worlds (a modern update to First In by the same creator)
75 |
76 | Q: Where do the planet names come from
77 |
78 | A: They come from all over the internet, but most come from: https://simplemaps.com/data/world-cities
79 |
80 | Q: What are these other non-python files?
81 |
82 | A:
83 |
84 | traveller_map_poster.html: a spartan method of using the travellermap API to build sector maps for the sectors you build.
85 | Choose the tab and route file and click submit and an image map of your sector will be created by travellermap.
86 |
87 | names.csv: Used to create planet/moon names and can be modified to add or remove names you prefer or dislike.
88 | Be careful if you remove too many names as the program requires thousands and will crash if it runs out (it does not use the same one twice)
89 |
90 | trade_goods.csv: Copied from T5 and used to show wants and surpluses in each system. Feel free to modify to taste.
91 |
92 | txt files: various First In tables used to create the science stuff. Feel free to modify but be careful - you void the warranty if you mess with this stuff.
93 |
94 | Q: Where can I find more info?
95 |
96 | A: I hang out at COTI (https://www.travellerrpg.com/) from time to time and discuss it there.
97 |
98 | I also made some YouTube vidoes walking through the program: https://www.youtube.com/channel/UCJVDA8TEy3aRHwrGVTsNJBg
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/build_db_from_csv.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Tue May 21 15:16:11 2024
4 |
5 | @author: sean
6 | """
7 |
8 | import pandas as pd
9 | import sqlite3
10 | from traveller_functions import hex_to_int
11 | from traveller_map import build_travellermap_file
12 | from routes_short_path import create_route_xml
13 |
14 | new_sector_db = 'sector_db/solomani_rim.db'
15 | sector_name = 'Solomani Rim'
16 |
17 |
18 | conn_new = sqlite3.connect(new_sector_db)
19 | conn_old = sqlite3.connect('sector_db/example-66.db')
20 |
21 | df = pd.read_csv('sector_db/solomani_rim.csv', dtype=str)
22 |
23 | new_df = pd.DataFrame()
24 |
25 | new_df['location_orb'] = df['Hex']
26 | new_df['location'] = df['Hex']
27 | new_df['system_name'] = df['Name']
28 |
29 | new_df['starport'] = df['UWP'].str[0]
30 |
31 | new_df['size'] =df['UWP'].str[1].apply(hex_to_int)
32 | new_df['atmosphere'] = df['UWP'].str[2].apply(hex_to_int)
33 | new_df['hydrographics'] = df['UWP'].str[3].apply(hex_to_int)
34 |
35 | new_df['population'] = df['UWP'].str[4].apply(hex_to_int)
36 | new_df['government'] = df['UWP'].str[5].apply(hex_to_int)
37 | new_df['law'] = df['UWP'].str[6].apply(hex_to_int)
38 |
39 | new_df['tech_level'] = df['UWP'].str[8].apply(hex_to_int)
40 |
41 | new_df['main_world'] = 1
42 |
43 | new_df.to_sql('traveller_stats', conn_new, if_exists='replace', index_label = 'id')
44 |
45 |
46 |
47 | system_df = pd.DataFrame()
48 | system_df['id'] = df.index
49 | system_df['location'] = df['Hex']
50 | system_df['remarks'] = df['Remarks']
51 |
52 | system_df['ix'] = df['{Ix}']
53 | system_df['ex'] = df['(Ex)']
54 | system_df['cx'] = df['[Cx]']
55 |
56 | system_df['n'] = df['N']
57 | system_df['bases'] = df['B']
58 | system_df['zone'] = df['Z']
59 |
60 | system_df['pbg'] = df['PBG']
61 | system_df['w'] = df['W']
62 | system_df['allegiance'] = df['A']
63 | system_df['stars'] = df['Stellar']
64 |
65 | system_df['remarks'].fillna('None',inplace=True)
66 |
67 |
68 | system_df.to_sql('system_stats', conn_new, if_exists='replace', index=False)
69 |
70 |
71 |
72 |
73 | sql_statement = 'SELECT * FROM '
74 |
75 | table_list = ['far_trader',
76 | 'journey_data',
77 | 'orbital_bodies',
78 | 'perceived_culture',
79 | 'stellar_bodies',
80 | 'main_world_eval']
81 |
82 | for each_table in table_list:
83 | sql_command = sql_statement + each_table
84 | df = pd.read_sql(sql_command, conn_old)
85 | df = pd.DataFrame(columns=df.columns)
86 | df['location'] = new_df['location']
87 | if each_table in ['journey_data', 'orbital_bodies','main_world_eval']:
88 | df['location_orbit'] = new_df['location']
89 | if each_table == 'orbital_bodies':
90 | df['gravity'] = 1
91 | df['wtype'] = 'N/A'
92 | df['size_class'] = 'N/A'
93 | df['atmos_composition'] = 'N/A'
94 | df['temperature'] = 300
95 | df['zone'] = 'Middle Zone'
96 | df['body'] = 'Planet'
97 | df['climate'] = 'N/A'
98 |
99 | elif each_table == 'traveller_stats':
100 | df['location_orb'] = new_df['location']
101 |
102 | elif each_table == 'far_trader':
103 | df['needs'] == 'NA'
104 | df['wants'] == 'NA'
105 |
106 |
107 | if each_table == 'perceived_culture':
108 | df.fillna('N/A', inplace=True)
109 | else:
110 | df.fillna(0, inplace=True)
111 |
112 |
113 | df['id'] = df.index
114 | df.to_sql(each_table, conn_new, if_exists='replace')
115 |
116 |
117 |
118 | build_travellermap_file(new_sector_db, sector_name)
119 | create_route_xml(0,new_sector_db,0)
120 |
121 |
122 |
123 |
124 | conn_old.commit()
125 | conn_new.commit()
126 | conn_new.close()
127 | conn_old.close()
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/convert_travmap_to_csv.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Tue May 21 14:36:01 2024
4 |
5 | @author: sean
6 | """
7 |
8 | import pandas as pd
9 |
10 | df = pd.read_fwf('sector_db/solomani_rim.txt')
11 | df.drop(index=0, inplace=True)
12 | df['Hex'] = df['Hex'].astype(str)
13 | df.to_csv('sector_db/solomani_rim.csv', index=False)
--------------------------------------------------------------------------------
/covers/a.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/a.jpg
--------------------------------------------------------------------------------
/covers/b.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/b.jpg
--------------------------------------------------------------------------------
/covers/c.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/c.jpg
--------------------------------------------------------------------------------
/covers/d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/d.jpg
--------------------------------------------------------------------------------
/covers/e.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/e.jpg
--------------------------------------------------------------------------------
/covers/f.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/f.jpg
--------------------------------------------------------------------------------
/covers/g.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/g.jpg
--------------------------------------------------------------------------------
/covers/h.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/h.jpg
--------------------------------------------------------------------------------
/covers/i.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/i.jpg
--------------------------------------------------------------------------------
/covers/j.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/j.jpg
--------------------------------------------------------------------------------
/covers/k.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/k.jpg
--------------------------------------------------------------------------------
/covers/l.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/l.jpg
--------------------------------------------------------------------------------
/covers/m.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/m.jpg
--------------------------------------------------------------------------------
/covers/n.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/n.jpg
--------------------------------------------------------------------------------
/covers/o.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/o.jpg
--------------------------------------------------------------------------------
/covers/p.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/p.jpg
--------------------------------------------------------------------------------
/culture.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Wed Dec 1 15:44:10 2021
4 |
5 | @author: sean
6 | """
7 | def create_culture_stats(seed_number,db_name):
8 |
9 | import sqlite3
10 | import random
11 |
12 | random.seed(seed_number)
13 |
14 | def create_culture_table():
15 | sql_create_tb_far_trader_table = """CREATE TABLE perceived_culture(
16 | id INTEGER PRIMARY KEY AUTOINCREMENT,
17 | location TEXT,
18 | age TEXT,
19 | appearance TEXT,
20 | tendency TEXT,
21 | materialism TEXT,
22 | honesty TEXT,
23 | bravery TEXT,
24 | social_conflict TEXT,
25 | work_ethic TEXT,
26 | consumerism TEXT,
27 | spiritual_outlook TEXT,
28 | status_quo_outlook TEXT,
29 | custom TEXT,
30 | interests TEXT,
31 | common_skills TEXT)
32 |
33 | ;"""
34 |
35 | c.execute('DROP TABLE IF EXISTS perceived_culture')
36 | c.execute(sql_create_tb_far_trader_table)
37 |
38 | # MAIN PROGRAM
39 |
40 | conn = sqlite3.connect(db_name)
41 | c = conn.cursor()
42 | create_culture_table()
43 |
44 |
45 | age_list = ['infant-centric','infants neither seen nor heard','youth-centric','no youth culture',
46 | 'young adult-centric','adult-centric','adult-centric','adult-centric','revere seniors','revere seniors','seniors reviled']
47 | appearance_list = ['dirty','clean','unkempt','immaculate','casual','formal','average','average','average']
48 | tendency_list = ['perceptive','violent','vain','selfish','kind','careless','capricious','serious','trusting','suspicious',
49 | 'studious','cruel','loquacious','curious','non-curious','intelligent','anti-intellectual','impulsive','empathetic',
50 | 'cheerful','morose','sensitive','insensitive','humble','haughty','even-tempered','hot-tempered','relaxed',
51 | 'black and white - no gray','pragmatic','sexist','obsessed','peaceful','fashionable','war']
52 | materialism_list = ['minimal possessions','average','average','average','modest possessions','covet possessions']
53 | honesty_list = ['scrupulous','honour-bound','truthful','average','average','average','average','deceitful','untrustworthy']
54 | bravery_list = ['average','average','average','foolhardy','brave','cautious','reject bravery as an ideal']
55 | work_ethic_list = ['very relaxed','relaxed','average','average','average','driven','beyond driven']
56 | social_conflict_list = ['average','average','average','conflict adverse','conflict phobic','enjoy conflict','thrive on conflict']
57 | consumerism_list = ['miserly','conservative spender','average','average','spendthrift','wasteful']
58 | spiritual_outlook_list = ['martyr-like','devout','reverent','moderate','moderate','questioning','irreverent']
59 | status_quo_outlook_list = ['radical','progressive','progressive','conservative','conservative','progressive','progressive','conservative',
60 | 'conservative','reactionary']
61 | custom_role_list = ['everyone','everyone','everyone','everyone','everyone','everyone','everyone','everyone','everyone','everyone',
62 | 'natives','citizens','visitors','certain political groups','certain sex','law enforcement','entertainers',
63 | 'heroes','athletes','certain races','religious figures','military figures','certain occupations','political figures',
64 | 'medical professionals','certain age groups','scientists','academics','low social class','high social class',
65 | 'criminals','socialites','celebrities','workers','off-worlders/travellers']
66 | custom_list = ['same clothes for all sexes','unusual clothes','unusual headgear','shaved heads','hair never cut','unusual hair color',
67 | 'unusual hair styles','unusual eyebrows','unusual facial alterations','unusual body alterations','unusual fingernails',
68 | 'unusual toe nails','unusual cosmetics','unusual jewelry','unusual accessories','unusual handgear','tattooing on face',
69 | 'tattooing on body','hidden tattooing','unusual foods','unusual beverages','unusual food preparation','segregated at meals',
70 | 'vegetarian','vegan','certain colored food','certain shaped food','certain food sources','eat in special location',
71 | 'eat only in private','eat in special orientation','eat with unusual utensils','eat only at home','eat at unusual times',
72 | 'eat only at certain times','rituals before eating','rituals after eating','one group eats leftovers',
73 | 'cannabalistic','live privately','live in small groups','live in special locations','live at work',
74 | 'live under special conditions','confined to quarters','live under special care','have extravagant quarters',
75 | 'have minimal quarters','have unusual quarters','quarters must be visited','live with extended families',
76 | 'live in communal housing','live only in certain terrain','must move around','unsual media','unusual starport',
77 | 'unusual lifecycle','unusual social standings','unusual trade','unusual nobility','unusual reproduction','conspiracy-driven']
78 | interests_list = ['religion','philosophy','economics','sports','politics','legends and myth','history','nature','horticulture','handicrafts',
79 | 'foods','wines and spirits','gambling','drugs','art','sculpture','music','theatre',
80 | 'antiques','buttons','coins','guns','swords','hats','insects','sea shells','stones','tools','toys','minerals',
81 | 'airbrushing','blacksmithing','calligraphy','candle-making','ceramics','crocheting','digital arts','photography',
82 | 'drawing','embroidery','fashion','glassblowing','graffiti','illusion','jewelry making','knitting','nail art','needlepoint',
83 | 'origami','painting','pottery','sewing','singing','storytelling','taxidermy','dancing']
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | sql3_select_tb_t5 = """ SELECT s.location,
93 | s.remarks,
94 | t.population
95 | FROM system_stats s
96 | LEFT JOIN traveller_stats t
97 | ON s.location = t.location
98 | WHERE t.main_world = 1"""
99 |
100 | c.execute(sql3_select_tb_t5)
101 | allrows = c.fetchall()
102 |
103 |
104 |
105 | for row in allrows:
106 |
107 |
108 | location = str(row[0])
109 | remarks = str(row[1])
110 | population = int(row[2])
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | if population == 0:
119 | age = 'n/a'
120 | appearance = 'n/a'
121 | tendency = 'n/a'
122 | materialism = 'n/a'
123 | honesty = 'n/a'
124 | bravery = 'n/a'
125 | work_ethic = 'n/a'
126 | social_conflict = 'n/a'
127 | consumerism = 'n/a'
128 | spiritual_outlook = 'n/a'
129 | status_quo_outlook = 'n/a'
130 | custom = 'n/a'
131 | interests = 'n/a'
132 | common_skills = 'n/a'
133 |
134 |
135 |
136 | else:
137 |
138 | age = random.choice(age_list)
139 | appearance = random.choice(appearance_list)
140 | tendency = random.choice(tendency_list)
141 | materialism = random.choice(materialism_list)
142 | honesty = random.choice(honesty_list)
143 | bravery = random.choice(bravery_list)
144 | work_ethic = random.choice(work_ethic_list)
145 | social_conflict = random.choice(social_conflict_list)
146 | consumerism = random.choice(consumerism_list)
147 | spiritual_outlook = random.choice(spiritual_outlook_list)
148 | status_quo_outlook = random.choice(status_quo_outlook_list)
149 | custom = random.choice(custom_role_list) + ': ' + random.choice(custom_list)
150 | interests = random.choice(interests_list)
151 | common_skills = ''
152 |
153 |
154 |
155 |
156 |
157 | dc_common_skills = {
158 | 'Va': 'Vacc Suit-0 ',
159 | 'Ag': 'Animal-0 ',
160 | 'As': 'Zero G-0 ',
161 | 'De': 'Survival-0 ',
162 | 'Fl': 'Hostile Env-0 ',
163 | 'Ga': 'Trader-0 ',
164 | 'He': 'Hostile Env-0 ',
165 | 'Oc': 'Hi G-0 ',
166 | 'Wa': 'Seafarer-0 ',
167 | 'Lo': 'Flyer-0 ',
168 | 'Ni': 'Driver-0 ',
169 | 'Hi': 'Streetwise-0 ',
170 | 'Pa': 'Trader-0 ',
171 | 'Na': 'Survey-0 ',
172 | 'Pi': 'JOT-0 ',
173 | 'In': 'Elec or Mech-0 ',
174 | 'Po': 'Steward-0 ',
175 | 'Pr': 'Craft-0 ',
176 | 'Ri': 'Art-0 '}
177 |
178 | common_skill_list = []
179 | for key in dc_common_skills:
180 | if key in remarks:
181 | common_skill_list.append(dc_common_skills[key])
182 | common_skill_list = set(common_skill_list)
183 | for each in common_skill_list:
184 | common_skills += each
185 |
186 |
187 |
188 |
189 | sqlcommand = ''' INSERT INTO perceived_culture (
190 | location,
191 | age,
192 | appearance,
193 | tendency,
194 | materialism,
195 | honesty,
196 | bravery,
197 | work_ethic,
198 | social_conflict,
199 | consumerism,
200 | spiritual_outlook,
201 | status_quo_outlook,
202 | custom,
203 | interests,
204 | common_skills)
205 | VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) '''
206 |
207 |
208 | body_row = (location,
209 | age,
210 | appearance,
211 | tendency,
212 | materialism,
213 | honesty,
214 | bravery,
215 | work_ethic,
216 | social_conflict,
217 | consumerism,
218 | spiritual_outlook,
219 | status_quo_outlook,
220 | custom,
221 | interests,
222 | common_skills)
223 |
224 |
225 | c.execute(sqlcommand, body_row)
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 | conn.commit()
234 | conn.close()
235 |
236 |
237 |
238 |
239 |
240 |
--------------------------------------------------------------------------------
/far_trader.py:
--------------------------------------------------------------------------------
1 | def generate_far_trader_stats(seed_number,db_name):
2 |
3 | # Far Trader
4 | # by Sean Nelson
5 |
6 | # A program to teach Sean the Python programming language
7 | # The goal is to create a table with mainworld merchant stats
8 | # Using GURPS Far Tarder info
9 |
10 | import sqlite3
11 | import random
12 |
13 |
14 |
15 | def create_tb_far_trader_table():
16 | sql_create_tb_far_trader_table = """CREATE TABLE far_trader(
17 | id INTEGER PRIMARY KEY AUTOINCREMENT,
18 | location TEXT,
19 | wtn INTEGER,
20 | gwp INTEGER,
21 | exchange REAL,
22 | needs TEXT,
23 | wants TEXT)
24 | ;"""
25 |
26 | c.execute('DROP TABLE IF EXISTS far_trader')
27 | c.execute(sql_create_tb_far_trader_table)
28 |
29 | def get_exchange(starport, tech_level):
30 | starport_ex_dict = {'A':1,'B':0.95,'C':0.90,'D':0.85,'E':0.80,'X':0.20}
31 | tech_mod = (15 - tech_level) * 0.05
32 | exchange = starport_ex_dict[starport] - tech_mod
33 | if exchange < 0: exchange = 0
34 | return exchange
35 |
36 | def get_wants_needs():
37 | goods_list = open("trade_goods.csv", "r").readlines()
38 | wants = ''
39 | needs = ''
40 |
41 | for x in range(0,12):
42 | goods_left = len(goods_list)
43 | good_picked = goods_list[random.randrange(0,goods_left)]
44 | good_fixed = good_picked.rstrip('\n')
45 | goods_list.remove(good_picked)
46 | if x < 6:
47 | wants += good_fixed + '; '
48 | else:
49 | needs += good_fixed + '; '
50 |
51 |
52 | return wants, needs
53 |
54 | # MAIN PROGRAM
55 |
56 | conn = sqlite3.connect(db_name)
57 | c = conn.cursor()
58 | create_tb_far_trader_table()
59 |
60 | sql3_select_tb_t5 = """ SELECT t.location,
61 | t.starport,
62 | t.population,
63 | t.tech_level,
64 | s.remarks
65 | FROM traveller_stats t
66 | LEFT JOIN system_stats s
67 | ON t.location = s.location
68 | WHERE main_world = 1 """
69 |
70 | c.execute(sql3_select_tb_t5)
71 | allrows = c.fetchall()
72 |
73 | for row in allrows:
74 | # print (row[0])
75 | location = str(row[0])
76 | starport = str(row[1])
77 | population = int(row[2])
78 | tech_level = int(row[3])
79 | remarks = str(row[4])
80 | if population > 0:
81 |
82 | uwtn = -100
83 | tl_mod = -100
84 | pop_mod = -100
85 | port_mod = -100
86 | wtn = -100
87 |
88 | pop_mod = round(population/2,1)
89 | if tech_level <= 2: tl_mod = -0.5
90 | elif tech_level <= 5: tl_mod = 0
91 | elif tech_level <= 8: tl_mod = 0.5
92 | elif tech_level <= 11: tl_mod = 1
93 | elif tech_level <= 13: tl_mod = 1.5
94 | else: tl_mod = 2
95 |
96 | uwtn = round(tl_mod + pop_mod,2)
97 | # print (uwtn)
98 |
99 | port_dict = { 'A':(1.5,1,1,0.5,0.5,0,0,0),
100 | 'B':(1,1,0.5,0.5,0,0,-0.5,-1),
101 | 'C':(1,0.5,0.5,0,0,-0.5,-1,-1.5),
102 | 'D':(0.5,0.5,0,0,-0.5,-1,-1.5,-2),
103 | 'E':(+0.5,0,0,-0.5,-1,-1.5,-2,-2.5),
104 | 'X':(0,0,-2.5,-3,-3.5,-4,-4.5,-5)}
105 |
106 | mod_uwtn = int(uwtn)
107 | if mod_uwtn > 7: mod_uwtn = 7
108 | elif mod_uwtn < 0: mod_uwtn = 0
109 |
110 | port_mod = port_dict[starport][mod_uwtn]
111 | # print (port_mod)
112 |
113 | wtn = uwtn + port_mod
114 | # print (wtn)
115 |
116 | bpr = -100
117 | bpr_list = (55,85,135,220,350,560,895,1430,2290,3660,5860,9375,15000,24400,40000,60000)
118 | tl_lookup = tech_level
119 | if tl_lookup > 15: tl_lookup = 15
120 | bpr = bpr_list[tl_lookup]
121 | # print(bpr)
122 |
123 | bpr_trade_mod = 1
124 |
125 | if 'Ri' in remarks: bpr_trade_mod = bpr_trade_mod * 1.6
126 | if 'In' in remarks: bpr_trade_mod = bpr_trade_mod * 1.4
127 | if 'Ag' in remarks: bpr_trade_mod = bpr_trade_mod * 1.2
128 | if 'Po' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8
129 | if 'Ni' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8
130 | if 'As' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8
131 | elif 'De' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8
132 | elif 'Fl' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8
133 | elif 'Ic' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8
134 | elif 'Va' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8
135 |
136 | bpr_trade = bpr * bpr_trade_mod
137 | # print(bpr_trade)
138 |
139 | gwp = round(bpr_trade * population * 10,0)
140 | # print(gwp)
141 |
142 | exchange = get_exchange(starport, tech_level)
143 |
144 |
145 | wants, needs = get_wants_needs()
146 |
147 |
148 |
149 | else:
150 | wtn = 0
151 | gwp = 0
152 | exchange = 0
153 | wants = ''
154 | needs = ''
155 |
156 |
157 |
158 | sqlcommand = ''' INSERT INTO far_trader ( location,
159 | wtn,
160 | gwp,
161 | exchange,
162 | wants,
163 | needs)
164 | VALUES(?, ?, ?, ?, ?, ?) '''
165 |
166 |
167 | body_row = (location,
168 | wtn,
169 | gwp,
170 | exchange,
171 | wants,
172 | needs)
173 |
174 |
175 | c.execute(sqlcommand, body_row)
176 |
177 | conn.commit()
178 | conn.close()
--------------------------------------------------------------------------------
/generate_menu.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | v 1.0.0 2022-11-17
4 | v 1.0.1 2024-05-25 - updated version number to reflect changes in first_in_generation
5 | """
6 | import FreeSimpleGUI as sg
7 |
8 | import logging
9 | import warnings
10 | from traveller_master import make_sector_master
11 |
12 |
13 | try:
14 | import pyi_splash
15 | # Update the text on the splash screen
16 | pyi_splash.update_text("Processing...")
17 |
18 | # Close the splash screen. It does not matter when the call
19 | # to this function is made, the splash screen remains open until
20 | # this function is called or the Python program is terminated.
21 | pyi_splash.close()
22 |
23 | except:
24 | logging.debug('Skipped Splash - Running locally')
25 |
26 |
27 | class Creation_Choices:
28 | def __init__(self, random_seed, sector_name, sector_density, settlement_mod):
29 | self.random_seed = random_seed
30 | self.sector_name = sector_name
31 | self.sector_density = sector_density
32 | self.settlement_mod = settlement_mod
33 |
34 | warnings.simplefilter(action='ignore', category=FutureWarning)
35 |
36 | sg.theme('DarkBlue')
37 |
38 | left_layout = [
39 | [sg.Text("PARAMETERS")],
40 | [sg.Text('System Density',tooltip = 'Chance for system in each parsec')],
41 | [sg.Text('Settlement Style',tooltip = 'Optionally modify stats based on location')],
42 | [sg.Text('Sector Name',tooltip = 'Files will be created in /sector_db')],
43 | [sg.Text('Random Seed',tooltip = 'Using the same seed with the same density will produce the same sector')]
44 | ]
45 |
46 | middle_layout = [
47 |
48 | [sg.Text("OPTIONS")],
49 | [sg.Radio('Sparse', "RADIO1", key = '-SPARSE-', tooltip='1 in 6 chance per parsec', default=True),
50 | sg.Radio('Scattered',"RADIO1", key = '-SCATTERED-',tooltip='2 in 6 chance per parsec'),
51 | sg.Radio('Standard', "RADIO1", key = '-STANDARD-', tooltip='3 in 6 chance per parsec')],
52 | [sg.Radio('Normal', "RADIO2", key = '-NORMAL-', tooltip='Normal Traveller Rules', default=True),
53 | sg.Radio('Diminishing',"RADIO2", key = '-DIMINISHING-',tooltip='Population increased in center, lowered in rim')],
54 |
55 | [sg.InputText(size=(10,1), key=('-NAME-'))],
56 | [sg.InputText(size=(4,1),key=('-SEED-'))]
57 | ]
58 |
59 |
60 |
61 | layout = [
62 | [sg.Text("""Generate Window""")],
63 | [sg.HSeparator()],
64 | [sg.Column(left_layout),
65 | sg.VSeparator(),
66 | sg.Column(middle_layout)
67 |
68 | ],
69 | [sg.Button('Generate'), sg.Button('Cancel')]
70 |
71 | ]
72 |
73 | # Create the Window
74 | window = sg.Window("""Bartleby's Sector Builder v1.1""", layout)
75 |
76 |
77 | # Event Loop to process "events" and get the "values" of the inputs
78 | while True:
79 |
80 | event, values = window.read()
81 | if event == sg.WIN_CLOSED or event == 'Cancel': # if user closes window or clicks cancel
82 | print('Close')
83 | break
84 |
85 | else:
86 | print('You entered ', values['-SPARSE-'],
87 | values['-SCATTERED-'],
88 | values['-STANDARD-'],
89 | values['-NAME-'],
90 | values['-SEED-'],
91 | values['-NORMAL-'],
92 | values['-DIMINISHING-'])
93 |
94 | if values['-SPARSE-'] == True:
95 | density_input = 6
96 | elif values['-SCATTERED-'] == True:
97 | density_input = 5
98 | else:
99 | density_input = 4
100 |
101 | if values['-NORMAL-'] is True:
102 | settlement_mod = 0
103 | else:
104 | settlement_mod = 1
105 |
106 |
107 | sector_name_input = values['-NAME-']
108 | random_seed_input = values['-SEED-']
109 |
110 |
111 |
112 | decisions_provided = Creation_Choices(
113 | random_seed_input,
114 | sector_name_input,
115 | density_input,
116 | settlement_mod)
117 |
118 |
119 | make_sector_master(decisions_provided)
120 |
121 | break
122 |
123 |
124 |
125 | window.close()
--------------------------------------------------------------------------------
/images/agricultural.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/agricultural.png
--------------------------------------------------------------------------------
/images/asteroid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/asteroid.png
--------------------------------------------------------------------------------
/images/barren.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/barren.png
--------------------------------------------------------------------------------
/images/blank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/blank.png
--------------------------------------------------------------------------------
/images/cold.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/cold.png
--------------------------------------------------------------------------------
/images/corrosive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/corrosive.png
--------------------------------------------------------------------------------
/images/exotic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/exotic.png
--------------------------------------------------------------------------------
/images/garden.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/garden.png
--------------------------------------------------------------------------------
/images/gas giant.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/gas giant.PNG
--------------------------------------------------------------------------------
/images/heavy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/heavy.png
--------------------------------------------------------------------------------
/images/hipop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/hipop.png
--------------------------------------------------------------------------------
/images/hot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/hot.png
--------------------------------------------------------------------------------
/images/important.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/important.png
--------------------------------------------------------------------------------
/images/industrial.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/industrial.png
--------------------------------------------------------------------------------
/images/light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/light.png
--------------------------------------------------------------------------------
/images/lopop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/lopop.png
--------------------------------------------------------------------------------
/images/mask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/mask.png
--------------------------------------------------------------------------------
/images/moon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/moon.png
--------------------------------------------------------------------------------
/images/naval.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/naval.png
--------------------------------------------------------------------------------
/images/non_agricultural.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/non_agricultural.png
--------------------------------------------------------------------------------
/images/non_industrial.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/non_industrial.png
--------------------------------------------------------------------------------
/images/ocean.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/ocean.png
--------------------------------------------------------------------------------
/images/planet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/planet.png
--------------------------------------------------------------------------------
/images/prison.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/prison.png
--------------------------------------------------------------------------------
/images/scout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/scout.png
--------------------------------------------------------------------------------
/images/ship.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/ship.png
--------------------------------------------------------------------------------
/images/tbd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/tbd.png
--------------------------------------------------------------------------------
/images/vacuum.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/vacuum.png
--------------------------------------------------------------------------------
/images/wealthy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/wealthy.png
--------------------------------------------------------------------------------
/journey_data.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | # Journey Data
4 |
5 |
6 | Created on Wed Oct 20 16:35:07 2021
7 |
8 | @author: sean
9 | """
10 |
11 | def build_journey_table(seed_number,db_name):
12 |
13 | import sqlite3
14 | import pandas as pd
15 | import numpy as np
16 |
17 |
18 | conn = sqlite3.connect(db_name)
19 | c = conn.cursor()
20 |
21 |
22 | try:
23 | sql_orbital = '''SELECT location_orbit, location, stellar_distance, size, stellar_orbit_no from orbital_bodies'''
24 | df_orbital_bodies = pd.read_sql_query(sql_orbital,conn)
25 | except:
26 | print(db_name,'Journey Table: Failed building orbital table')
27 |
28 |
29 | try:
30 | sql_stellar = '''SELECT location, radius as stellar_radius, companion_class from stellar_bodies'''
31 | df_stellar = pd.read_sql_query(sql_stellar,conn)
32 | except:
33 | print(db_name,'Journey Table: Failed building stellar table')
34 |
35 |
36 |
37 | try:
38 |
39 | df_orbital_bodies['left_match'] = df_orbital_bodies.location_orbit.str.slice(0, 6)
40 |
41 |
42 | df_stellar['match'] = df_stellar['location'] + '-' + df_stellar['companion_class']
43 |
44 |
45 | df_journey = pd.merge(df_orbital_bodies,df_stellar, how='left', left_on='left_match', right_on='match')
46 |
47 | except:
48 | print('Journey Table: Failed at df merge')
49 |
50 |
51 |
52 | try:
53 | df_journey['stellar_mask_Mm'] = df_journey['stellar_radius'] * 200 * 149598.073 # convert radius in AU to Mm
54 | stellar_mask_Mm = df_journey['stellar_mask_Mm']
55 |
56 |
57 | df_journey['planetary_mask_Mm'] = round(df_journey['size'] * 1.690 * 100,3)
58 | df_journey['planetary_distance_Mm'] = df_journey['stellar_distance'] * 149598.073
59 |
60 |
61 | planet_Mm = df_journey['planetary_distance_Mm']
62 | planet_mask = df_journey['planetary_mask_Mm']
63 |
64 |
65 | df_journey['planetary_mask_end_Mm'] = planet_Mm + planet_mask
66 | planet_mask_end = df_journey['planetary_mask_end_Mm']
67 |
68 |
69 | # Set the status of any stellar masking
70 | conditions = [ stellar_mask_Mm > planet_mask_end,
71 | (stellar_mask_Mm < planet_mask_end) & (stellar_mask_Mm > planet_Mm),
72 | stellar_mask_Mm < planet_Mm ]
73 | choices = [ 'total', 'partial', 'none' ]
74 | df_journey['planet_stellar_masked'] = np.select(conditions, choices, default=np.nan)
75 |
76 | df_journey['jump_point_Mm'] = np.where(stellar_mask_Mm > planet_mask_end,
77 | stellar_mask_Mm - planet_Mm,
78 | planet_mask_end - planet_Mm)
79 |
80 | except:
81 | print('Journey Table: Failed at calculations')
82 |
83 | try:
84 | for x in range(1,8):
85 | d_a = df_journey['jump_point_Mm']*1000000/(9.8 * x)
86 | sqrt_d_a = d_a**0.5
87 | ix_hrs = 'hrs_' + str(x) + 'g'
88 | df_journey[ix_hrs] = round(sqrt_d_a*2/3600,1)
89 | except:
90 | print('Journey Table: Failed at Acceleration Measurements')
91 |
92 |
93 | df_journey.to_sql('journey_data', conn)
94 |
95 | conn.commit()
96 | c.close()
97 | conn.close()
98 |
99 |
--------------------------------------------------------------------------------
/mainworld_calculator.py:
--------------------------------------------------------------------------------
1 | def generate_mainworld_scores(db_name):
2 | #!/usr/bin/python
3 |
4 | # Mainworld Calculator
5 | # by Sean Nelson
6 |
7 | # The goal is to read the Orbital Bodies table generated from the
8 | # First In program and score the potential for each body being a mainworld
9 |
10 | # The output is a new column in the Orbital Bodies Table. It needs to run after First In.
11 |
12 |
13 |
14 | # Open the SQLite 3 database
15 |
16 | import sqlite3
17 | import pandas as pd
18 | import numpy as np
19 |
20 | conn = sqlite3.connect(db_name)
21 | c = conn.cursor()
22 |
23 |
24 |
25 |
26 | sql3_select = """ SELECT o.location_orbit,
27 | o.location,
28 | o.gravity,
29 | o.hydrographics,
30 | o.wtype,
31 | o.atmos_composition,
32 | o.climate,
33 | o.stellar_distance,
34 | o.temperature,
35 | o.zone,
36 | j.planet_stellar_masked
37 | FROM orbital_bodies o
38 | LEFT JOIN journey_data j
39 | ON o.location_orbit = j.location_orbit"""
40 |
41 | # print('trying to load into DF')
42 | try:
43 | df = pd.read_sql_query(sql3_select,conn)
44 | # print('df load clearly worked')
45 | except:
46 | print('Problem - df failed')
47 |
48 |
49 | # print(df.describe())
50 |
51 |
52 |
53 |
54 | col = 'wtype'
55 | conditions = [ df[col][0] == 'O', df[col][0] == 'H', df[col] == 'Gas Giant', df[col] == 'Belt']
56 | choices = [ 50000, -1000, -1000000, -500]
57 | df["wtype_mod"] = np.select(conditions, choices, default=0)
58 |
59 |
60 |
61 | col = 'gravity'
62 | conditions = [ df[col] > 2, (df[col] <= 2) & (df[col]>= 0.5), df[col] <= 0.5 ]
63 | choices = [ -5000, 1000,0 ]
64 | df["grav_mod"] = np.select(conditions, choices, default=0)
65 |
66 |
67 | col = 'planet_stellar_masked'
68 | conditions = [ df[col] == 'total', df[col] == 'none', df[col] == 'partial' ]
69 | choices = [ -5000, 1000, 200]
70 | df["mask_mod"] = np.select(conditions, choices, default=0)
71 |
72 |
73 | col = 'atmos_composition'
74 | conditions = [ df[col] == 'Standard', df[col]=='Tainted',df[col]=='Corrosive']
75 | choices = [ 50000, 10000,-1000 ]
76 | df["atmos_mod"] = np.select(conditions, choices, default=0)
77 |
78 | col = 'temperature'
79 | conditions = [ (df[col] <= 283) & (df[col]>238),
80 | (df[col] > 283) & (df[col] < 309),
81 | (df[col] >= 309) & (df[col] >= 324)]
82 | choices = [ 1000, 2000,1000 ]
83 | df["temp_mod"] = np.select(conditions, choices, default=-1000)
84 | df["temp_mod"] = df["temp_mod"] - (((df["temperature"] - 283) ** 2)/10)
85 |
86 | # print('after update')
87 | # print(df.describe())
88 | df['mainworld_calc'] = df["wtype_mod"] + df["grav_mod"] + df["atmos_mod"] + df["temp_mod"] + df["mask_mod"]
89 | df['mainworld_status'] = 'N'
90 |
91 | df_mainworld_eval = df[['location',
92 | 'location_orbit',
93 | 'wtype_mod',
94 | 'grav_mod',
95 | 'atmos_mod',
96 | 'temp_mod',
97 | 'mainworld_calc',
98 | 'mainworld_status']]
99 |
100 | # print(df.head())
101 |
102 | try:
103 | df_mainworld_eval.to_sql('main_world_eval', conn, if_exists='replace')
104 | except:
105 | print('Could not write back to sql')
106 |
107 |
108 | conn.commit()
109 | conn.close()
110 |
--------------------------------------------------------------------------------
/mainworld_selector.py:
--------------------------------------------------------------------------------
1 | def choose_mainworld(db_name):
2 | #!/usr/bin/python
3 |
4 | # Mainworld Selector
5 |
6 | # The goal is to read the Orbital Bodies table generated from the
7 | # First In program and adjust by the Mainworld_Calc module
8 |
9 | # The output is a new column in the Orbital Bodies Table. It needs to run after First In and Mainworld_Calc
10 |
11 |
12 |
13 | import sqlite3
14 | import FreeSimpleGUI as sg
15 |
16 | import traceback
17 | import sys
18 |
19 |
20 |
21 |
22 | conn = sqlite3.connect(db_name)
23 | c = conn.cursor()
24 |
25 | sql3_select_loc = """ SELECT location
26 | FROM stellar_bodies
27 | WHERE orbits > 0"""
28 |
29 | sql3_select_locorb = """ SELECT location,
30 | location_orbit,
31 | mainworld_calc
32 | FROM main_world_eval
33 | WHERE location = ? """
34 |
35 | sql3_insert_status = """ UPDATE main_world_eval
36 | SET mainworld_status = 'Y'
37 | WHERE location_orbit = ? """
38 |
39 | loc_list = list()
40 |
41 |
42 | c.execute(sql3_select_loc)
43 | allrows = c.fetchall()
44 | for row in allrows:
45 |
46 | loc_list.append(row[0])
47 |
48 | loc_list = set(loc_list)
49 | loc_len = len(loc_list)
50 | for j,n in enumerate(loc_list):
51 |
52 | sg.one_line_progress_meter('Universe Generation Underway', j+1, loc_len, 'System Count')
53 |
54 |
55 | c.execute(sql3_select_locorb,(n,))
56 | allrows = c.fetchall()
57 | top_calc = -1000000
58 | top_locorb = 0
59 | for row in allrows:
60 | if row[2] > top_calc:
61 | top_calc = row[2]
62 | top_locorb = row[1]
63 |
64 | if top_calc >= -1000000:
65 | # print('Trying to write to DB')
66 | try:
67 | c.execute(sql3_insert_status,[top_locorb,])
68 | conn.commit()
69 | except:
70 | print('Write failed',top_locorb)
71 | print('SQLite traceback: ')
72 | exc_type, exc_value, exc_tb = sys.exc_info()
73 | print(traceback.format_exception(exc_type, exc_value, exc_tb))
74 |
75 | else:
76 | print('Location ERROR!!',top_calc,top_locorb)
77 | #
78 |
79 |
80 | conn.close()
81 |
--------------------------------------------------------------------------------
/names.csv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/names.csv
--------------------------------------------------------------------------------
/non_mw.py:
--------------------------------------------------------------------------------
1 | def generate_non_mainworlds(seed_number,db_name):
2 |
3 |
4 | # The goal is to read the Orbital Bodies table generated from the
5 | # First In program and and build a new table using Traveller 5 stats
6 | # Non Mainworld bodies. Mainworld bodies are produced in Travellerization
7 |
8 |
9 |
10 | import sqlite3
11 | import random
12 | import logging
13 | from traveller_functions import roll_dice
14 |
15 | random.seed(seed_number)
16 |
17 |
18 | def get_system_name(name_list):
19 | names_left = len(name_list)
20 | name_picked = name_list[random.randrange(0,names_left)]
21 | name_fixed = name_picked.rstrip('\n')
22 | name_list.remove(name_picked)
23 | return name_fixed
24 |
25 |
26 | def capture_mainworld_stats():
27 | sql3_select_tb_t5 = """ SELECT t.location,
28 | t.population,
29 | t.government,
30 | g.wtype,
31 | g.zone
32 | FROM traveller_stats t
33 | LEFT JOIN orbital_bodies g
34 | WHERE main_world = 1 """
35 |
36 | c.execute(sql3_select_tb_t5)
37 | allrows = c.fetchall()
38 | mw_dict = {}
39 | for row in allrows:
40 | mw_dict[row[0]] = { 'population' :row[1],
41 | 'government' :row[2],
42 | 'wtype' :row[3],
43 | 'zone' :row[4]}
44 | return mw_dict
45 |
46 |
47 | def get_population(location,mw):
48 |
49 | pop_present = True
50 | pop_mod = -2
51 |
52 | if mw['zone'] == 'Inner Zone':
53 | if 'Hostile' in mw['wtype'] or 'Greenhouse' in mw['wtype']:
54 | pop_present = False
55 | else:
56 | pop_mod = -4
57 |
58 | elif mw['zone'] == 'Outer Zone':
59 | if 'Hostile' in mw['wtype']:
60 | pop_present = False
61 | elif 'Icy' in mw['wtype']:
62 | pop_mod = -6
63 |
64 |
65 |
66 | if pop_present:
67 | dice = roll_dice(2,'Population with mod: '+str(pop_mod),location, conn, c) + pop_mod
68 |
69 | if dice >= mw['population']:
70 | dice = mw['population'] - 1
71 |
72 | if dice < 0: dice = 0
73 |
74 | else:
75 | dice = 0
76 |
77 | return dice
78 |
79 | def get_spaceport(location,population):
80 |
81 | if population == 0:
82 | spaceport = 'Y'
83 | else:
84 | dice = roll_dice(1,'Spaceport',location, conn, c) - population
85 | if dice >= 4: spaceport = 'F'
86 | elif dice == 3: spaceport = 'G'
87 | elif 1 <= dice <=2 : spaceport = 'H'
88 | else: spaceport = 'Y'
89 |
90 |
91 | return spaceport
92 |
93 | def get_atmosphere(pressure,composition):
94 | c_atmosphere = -1
95 | if pressure == 0: c_atmosphere = 0
96 | elif pressure == 0.1: c_atmosphere = 1
97 | elif composition == 'Exotic': c_atmosphere = 10
98 | elif composition == 'Corrosive': c_atmosphere = 11
99 | elif composition == 'GG': c_atmosphere = 1
100 | elif composition == 'Standard':
101 | if pressure < 0.5: c_atmosphere = 3
102 | elif pressure < 0.8: c_atmosphere = 5
103 | elif pressure < 1.2: c_atmosphere = 6
104 | elif pressure < 1.5: c_atmosphere = 8
105 | else: c_atmosphere = 13
106 | elif composition == 'Tainted':
107 | if pressure < 0.5: c_atmosphere = 2
108 | elif pressure < 0.8: c_atmosphere = 4
109 | elif pressure < 1.2: c_atmosphere = 7
110 | elif pressure < 1.5: c_atmosphere = 9
111 | else: c_atmosphere = 12
112 |
113 | return c_atmosphere
114 |
115 |
116 |
117 | def get_government(location, population, mw_government):
118 | if mw_government == '6':
119 | dice = 6
120 | else:
121 | dice = roll_dice(2,'Government',row[0], conn, c) + population - 7
122 | if dice < 0: dice = 0
123 | elif dice > 15: dice = 15
124 | if population == 0: dice = 0
125 | return dice
126 |
127 | def get_law_level(location, government):
128 | dice = roll_dice(2,'Law Level',row[0], conn, c) + government - 7
129 | if dice < 0: dice = 0
130 | elif dice > 15: dice = 15
131 | if population == 0: dice = 0
132 | return dice
133 |
134 | def get_tech_level(location, starport, size, atmosphere, hydrographics, population, government):
135 |
136 | starport_mod = -100
137 | starport_mod_dict = {'A':6, 'B':4, 'C':2, 'X':-4}
138 | if starport in starport_mod_dict.keys():
139 | starport_mod = starport_mod_dict[starport]
140 | else: starport_mod = 0
141 |
142 | size_mod = -100
143 | size_mod_dict = {'0':2, '1':2, '2':1, '3':1, '4':1}
144 | if str(size) in size_mod_dict.keys():
145 | size_mod = size_mod_dict[str(size)]
146 | else: size_mod = 0
147 |
148 | int_atmos = int(atmosphere)
149 | atmosphere_mod = -100
150 | if int_atmos <= 3: atmosphere_mod = 1
151 | elif int_atmos >= 10: atmosphere_mod = 1
152 | else: atmosphere_mod = 0
153 |
154 | hydro_mod = -100
155 | if hydrographics == 9: hydro_mod = 1
156 | elif hydrographics == 10: hydro_mod = 2
157 | else: hydro_mod = 0
158 |
159 | pop_mod = -100
160 | if population <= 5: pop_mod = 1
161 | elif population == 9: pop_mod = 2
162 | elif population >= 10: pop_mod = 4
163 | else: pop_mod = 1
164 |
165 | gov_mod = -100
166 | if government == 0: gov_mod = 1
167 | elif government == 5: gov_mod = 1
168 | elif government == 13: gov_mod = -2
169 | else: gov_mod = 0
170 |
171 | dice = roll_dice(1, 'Tech roll', location, conn, c) \
172 | + starport_mod \
173 | + size_mod \
174 | + atmosphere_mod \
175 | + hydro_mod \
176 | + pop_mod \
177 | + gov_mod
178 | # print ('Tech',starport,dice,starport_mod,size_mod,atmosphere_mod,hydro_mod,pop_mod,gov_mod)
179 | if population == 0: dice = 0
180 | return dice
181 |
182 |
183 |
184 | # MAIN PROGRAM
185 |
186 | print('Generating non-main worlds')
187 | print('processing....')
188 |
189 | name_list = open("names.csv", "r").readlines()
190 |
191 | conn = sqlite3.connect(db_name)
192 | c = conn.cursor()
193 |
194 | try:
195 | mw_dict = capture_mainworld_stats()
196 | except:
197 | logging.debug('Failed getting mainworld stats into dictionary')
198 |
199 | try:
200 |
201 | sql3_select_locorb = """ SELECT m.location_orbit,
202 | m.location,
203 | o.atmos_pressure,
204 | o.atmos_composition,
205 | o.body,
206 | o.hydrographics,
207 | o.size
208 | FROM main_world_eval m
209 | LEFT JOIN orbital_bodies o
210 | ON o.location_orbit = m.location_orbit
211 | WHERE mainworld_status != 'Y' """
212 |
213 | c.execute(sql3_select_locorb)
214 | allrows = c.fetchall()
215 | except:
216 | logging.debug('Problem with selecting from main_world_eval and orbital bodies')
217 |
218 | for row in allrows:
219 |
220 | if row[4] != "Gas Giant":
221 |
222 | try:
223 |
224 | population = get_population(row[0],mw_dict[row[1]])
225 |
226 |
227 |
228 | spaceport = get_spaceport(row[0],population)
229 | atmosphere = get_atmosphere(row[2],row[3])
230 | hydrographics = row[5]
231 | size = row[6]
232 |
233 | government = get_government(row[1], population,mw_dict[row[1]]['government'])
234 | law_level = get_law_level(row[1], government)
235 | tech_level = get_tech_level(row[1], spaceport, size, atmosphere, hydrographics, population, government)
236 |
237 |
238 | except Exception as e:
239 | print(e)
240 | logging.debug('Failed in Non-mainworld generation')
241 |
242 |
243 | else:
244 | population = 0
245 | spaceport = 'Y'
246 | atmosphere = 15
247 | hydrographics = 0
248 | size = row[6]
249 | government = 0
250 | law_level = 0
251 | tech_level = 0
252 |
253 |
254 | main_world = 0
255 |
256 |
257 |
258 |
259 | system_name = get_system_name(name_list)
260 |
261 |
262 | sqlcommand = ''' INSERT INTO traveller_stats(location_orb,
263 | location,
264 | system_name,
265 | starport,
266 | size,
267 | atmosphere,
268 | hydrographics,
269 | population,
270 | government,
271 | law,
272 | tech_level,
273 | main_world)
274 | VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) '''
275 |
276 |
277 | body_row = (str(row[0]),
278 | str(row[1]),
279 | system_name,
280 | spaceport,
281 | size,
282 | atmosphere,
283 | hydrographics,
284 | population,
285 | government,
286 | law_level,
287 | tech_level,
288 | main_world)
289 |
290 |
291 | c.execute(sqlcommand, body_row)
292 |
293 |
294 | conn.commit()
295 | conn.close()
--------------------------------------------------------------------------------
/routes_short_path.py:
--------------------------------------------------------------------------------
1 | def create_route_xml(seed_number,db_name, settlement_mod):
2 |
3 | # Create an xboat route table and extract for traveller map
4 |
5 | import sqlite3
6 | import pandas as pd
7 | import numpy as np
8 | import networkx as nx
9 | import warnings
10 |
11 | def offset_to_cube(location):
12 | x = int(location[0:2])
13 | y = int(location[2:4])
14 | q = x
15 | r = y - (q + (q&1)) / 2
16 | s = -q - r
17 | return(q,r,s)
18 |
19 | def cube_to_offset(location):
20 | x = location[0]
21 | y = int(location[1] + (x + (x&1)) / 2)
22 | if x > 9:
23 | x_string = str(x)
24 | else:
25 | x_string = '0' + str(x)
26 | if y > 9:
27 | y_string = str(y)
28 | else:
29 | y_string = '0' + str(y)
30 |
31 | return (x_string+y_string)
32 |
33 |
34 | def cube_direction(direction):
35 | return cube_direction_vectors[direction]
36 |
37 | def cube_add(cube, vec):
38 | return (cube[0] + vec[0], cube[1] + vec[1], cube[2] + vec[2])
39 |
40 | def cube_neighbor(cube, direction):
41 | return cube_add(cube, cube_direction(direction))
42 |
43 |
44 | def cube_subtract(a, b):
45 | return (a[0] - b[0], a[1] - b[1], a[2] - b[2])
46 |
47 |
48 | def cube_distance(a, b):
49 | vec = cube_subtract(a, b)
50 | return (abs(vec[0]) + abs(vec[1]) + abs(vec[2])) / 2
51 |
52 | def jump_range(center,j_range):
53 | results = []
54 | for q in range(-j_range,j_range+1):
55 | for r in range(-j_range,j_range+1):
56 | for s in range(-j_range,j_range+1):
57 | if q + r + s == 0:
58 | results.append(cube_add(center,[q, r, s]))
59 | return results
60 |
61 |
62 | def off_distance(o_start,o_end):
63 | try:
64 | c_start = offset_to_cube(o_start)
65 | except:
66 | print('C_start failed to convert to cube',o_start)
67 |
68 |
69 | try:
70 | c_end = offset_to_cube(o_end)
71 | except:
72 | print('C_end failed to convert to cube',o_end)
73 |
74 |
75 | return int(cube_distance(c_start,c_end))
76 |
77 |
78 |
79 | ######################################################################
80 |
81 | warnings.simplefilter(action='ignore', category=pd.errors.PerformanceWarning)
82 | warnings.simplefilter(action='ignore', category=FutureWarning)
83 | print('processing...')
84 |
85 |
86 | cube_direction_vectors = [
87 | (+1, 0, -1),
88 | (+1, -1, 0),
89 | (0, -1, +1),
90 | (-1, 0, +1),
91 | (-1, +1, 0),
92 | (0, +1, -1)
93 | ]
94 |
95 |
96 |
97 |
98 |
99 | conn = sqlite3.connect(db_name)
100 | sql3_select = """ SELECT s.location, s.ix
101 | FROM system_stats s
102 | LEFT JOIN traveller_stats t
103 | ON t.location = s.location
104 | WHERE t.main_world = 1"""
105 |
106 | try:
107 | df = pd.read_sql_query(sql3_select,conn,index_col='location')
108 |
109 |
110 | except:
111 | print('Problem - df failed')
112 |
113 | df['ix'] = df['ix'].str.replace('{','')
114 | df['ix'] = df['ix'].str.replace('}','')
115 | df['ix'] = df['ix'].astype(int)
116 |
117 | df['ix_flag'] = np.where(df['ix'] >= 4,1,0)
118 |
119 | #df = df.loc[df['ix_flag'] == 1]
120 |
121 | l_location = list(df.index)
122 |
123 | for l in l_location:
124 | df[l] = df.index
125 | df[l] = df[l].apply(off_distance,args=(l,))
126 |
127 |
128 | df_imp = df.loc[df['ix_flag'] == 1]
129 | l_important = list(df_imp.index)
130 |
131 |
132 | d_distance = df.to_dict('index')
133 |
134 |
135 |
136 | conn.commit()
137 | conn.close()
138 | ########################################################################
139 |
140 |
141 | G = nx.Graph()
142 |
143 | elist = []
144 |
145 | for loc in l_location:
146 | for loc2 in l_location:
147 | if loc < loc2:
148 | if d_distance[loc][loc2] <= 4:
149 | elist.append([loc,loc2])
150 |
151 |
152 | G.add_edges_from(elist)
153 |
154 |
155 | path_list = []
156 | used_list = []
157 | #max_list = 8
158 |
159 | min_list_max = 4
160 | max_list_max = 20
161 | if settlement_mod == 1:
162 | max_list_max = 5 # reduces route connections in diminishing sectors
163 |
164 | for max_list in range(min_list_max,max_list_max):
165 | for loc in l_important:
166 | for loc2 in l_important:
167 | one_chain = []
168 | if loc < loc2 and d_distance[loc][loc2] <= max_list:
169 | if max_list <= 6 or (loc not in used_list):
170 | used_list.append(loc)
171 | one_chain = nx.shortest_path(G,source=loc,target=loc2)
172 | for x,dest in enumerate(one_chain):
173 | if x < (len(one_chain)-1):
174 | path_list.append([dest,one_chain[x+1]])
175 |
176 |
177 | route_text = ''
178 |
179 | for each in path_list:
180 |
181 | route_text += """