`);
36 | }
37 | }
--------------------------------------------------------------------------------
/strings/readme.md:
--------------------------------------------------------------------------------
1 | # Translation tools
2 |
3 | There are two python scripts to help to translate the strings.json file: updateStrings.py and checkStrings.py.
4 |
5 | ## updateStrings.py
6 |
7 | ```shell
8 | python updateStrings.py
9 | ```
10 |
11 | This script reads the strings.json and strings.\.json, and overwrite its contents, putting the keys in the same order as those in strings.json. This doesn't write the keys that are not in strings.json in the output (show as deleted keys count in the shell), and add a 'TRANS:' in the start of every value which key is in strings.json but not was in strings.\.json.
12 |
13 | If there is a last-strings.json file, it will be used to keep track of changes in keys values, which will be format as 'CHANGE:\~FROM:\~TO:\'. In the end of the operation, the strings.json contents will be again copied to last-strings.json.
14 |
15 | ## checkStrings.py
16 |
17 | ```shell
18 | python checkStrings.py
19 | ```
20 | This script reads the strings.json and string.\.json files and outputs every line that:
21 | - is out of json format
22 | - the value is marked with tag 'TRANS:' or 'CHANGE:'
23 | - the number of periods (.) in the strings.json differs from strings.\.json. The translation does not need to have the same number of periods, but this helps find phrases that end with periods that were forgotten in the translation
24 | - the number of leading spaces (spaces before the first character) in the strings.json differs from strings.\.json
25 | - the number of tokens (like %0) in the strings.json differs from strings.\.json
26 | - the numbers in the strings.json differ from strings.\.json
27 |
28 | You can disable some of the checks if you open the script and change the values to ```True``` or ```False```:
29 | ```python
30 | check_tags = True
31 | check_tokens = True
32 | check_leading_space = True
33 | check_periods = True
34 | check_numbers = True
35 | ```
36 |
--------------------------------------------------------------------------------
/src/debug.js:
--------------------------------------------------------------------------------
1 | import { global, breakdown } from './vars.js';
2 | import { deepClone, adjustCosts, messageQueue } from './functions.js';
3 | import { races, traits } from './races.js';
4 | import { craftCost, tradeRatio, atomic_mass, tradeBuyPrice, tradeSellPrice } from './resources.js';
5 | import { actions, checkAffordable } from './actions.js';
6 | import { fuel_adjust, int_fuel_adjust } from './space.js';
7 | import { shipCosts } from './truepath.js';
8 | import { f_rate } from './industry.js';
9 | import { armyRating } from './civics.js';
10 | import { alevel } from './achieve.js';
11 | import { loc } from './locale.js';
12 |
13 | export function enableDebug(){
14 | if (global.settings.expose){
15 | window.evolve = {
16 | actions: deepClone(actions),
17 | races: deepClone(races),
18 | traits: deepClone(traits),
19 | tradeRatio: deepClone(tradeRatio),
20 | craftCost: deepClone(craftCost()),
21 | atomic_mass: deepClone(atomic_mass),
22 | f_rate: deepClone(f_rate),
23 | checkAffordable: deepClone(checkAffordable),
24 | adjustCosts: deepClone(adjustCosts),
25 | armyRating: deepClone(armyRating),
26 | tradeBuyPrice: deepClone(tradeBuyPrice),
27 | tradeSellPrice: deepClone(tradeSellPrice),
28 | fuel_adjust: deepClone(fuel_adjust),
29 | int_fuel_adjust: deepClone(int_fuel_adjust),
30 | alevel: deepClone(alevel),
31 | messageQueue: deepClone(messageQueue),
32 | loc: deepClone(loc),
33 | shipCosts: deepClone(shipCosts),
34 | updateDebugData: deepClone(updateDebugData),
35 | global: {},
36 | breakdown: {},
37 | };
38 | }
39 | }
40 |
41 | export function updateDebugData(){
42 | if (global.settings.expose){
43 | window.evolve.global = deepClone(global);
44 | window.evolve.craftCost = deepClone(craftCost()),
45 | window.evolve.breakdown = deepClone(breakdown);
46 | }
47 | }
--------------------------------------------------------------------------------
/src/wiki/basics.js:
--------------------------------------------------------------------------------
1 | import { loc } from './../locale.js';
2 | import { infoBoxBuilder } from './functions.js';
3 |
4 | export function basicsPage(content){
5 | infoBoxBuilder(content,{ name: 'start', template: 'basics', paragraphs: 2, h_level: 2 });
6 |
7 | infoBoxBuilder(content,{ name: 'prehistoric', template: 'basics', paragraphs: 14, break: [2,6,13], h_level: 2,
8 | para_data: {
9 | 3: [loc(`resource_RNA_name`),loc(`resource_DNA_name`)],
10 | 4: [loc(`evo_organelles_title`),loc(`resource_RNA_name`),loc(`evo_nucleus_title`),loc(`resource_DNA_name`)],
11 | 5: [loc(`evo_membrane_title`),loc(`evo_eukaryotic_title`),loc(`evo_mitochondria_title`)],
12 | 7: [loc('genelab_genus')],
13 | 8: [3],
14 | 13: [loc('evo_sentience_title')],
15 | },
16 | data_link: {
17 | 7: ['wiki.html#races-species']
18 | }
19 | });
20 |
21 | infoBoxBuilder(content,{ name: 'civilization', template: 'basics', paragraphs: 14, break: [2,8,13], h_level: 2,
22 | para_data: {
23 | 3: [loc(`tab_civics`)],
24 | 4: [loc(`resource_Food_name`),loc(`job_farmer`),loc(`job_hunter`)],
25 | 5: ['*'],
26 | 8: [loc(`resource_Food_name`)],
27 | 9: [loc(`resource_Food_name`),loc(`hunger`)],
28 | 10: [loc(`resource_Food_name`)],
29 | 11: [loc(`resource_Food_name`)],
30 | 12: [loc(`resource_Food_name`)],
31 | 13: [loc(`resource_Knowledge_name`)],
32 | 14: [loc(`resource_Knowledge_name`),loc(`city_university`)]
33 | }
34 | });
35 |
36 | infoBoxBuilder(content,{ name: 'government', template: 'basics', paragraphs: 8, break: [4], h_level: 2,
37 | para_data: {
38 | 1: [loc(`tech_government`),loc(`tab_civics`),loc(`govern_anarchy`)],
39 | 2: [loc(`tech_government`),loc(`govern_anarchy`)],
40 | 3: [loc(`tech_government`)],
41 | 4: [loc('morale')],
42 | 5: [loc('morale')],
43 | 6: [loc('morale'),loc('job_entertainer'),loc('morale_stress')],
44 | 7: [loc('morale_tax'),loc('morale')],
45 | 8: [25,100]
46 | }
47 | });
48 |
49 | infoBoxBuilder(content,{ name: 'mad', template: 'basics', paragraphs: 4, h_level: 2,
50 | para_data: {
51 | 1: [loc(`wiki_resets_mad`)],
52 | 2: [loc(`wiki_basics_mad_reset`),loc(`tab_civics`),loc(`tab_military`)],
53 | 3: [loc(`tab_space`)],
54 | },
55 | data_link: {
56 | 2: ['wiki.html#resets-prestige']
57 | }
58 | });
59 | }
60 |
--------------------------------------------------------------------------------
/wiki.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Evolve Wiki
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
50 |
51 |
52 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Evolve
2 |
3 | ## Play
4 |
5 | https://pmotschmann.github.io/Evolve/
6 |
7 | ## About
8 |
9 | An incremental game about evolving a civilization from primordial ooze into a space faring empire.
10 | Evolve combines elements of a clicker with an idler and has lots of micromanagement.
11 |
12 | What will you evolve into?
13 |
14 | ## Submitting Issues
15 | If you think you have discovered a bug or have some other issue with the game then you can open an issue here on Github.
16 | Please do not open Github issues to ask gameplay questions, use Reddit or Discord for that.
17 | Links for both can be found in the footer of the game.
18 |
19 | ## Contributing a Language file
20 | If you are interested in a contributing a new language for Evolve the process is fairly straight forward (although a bit tedious).
21 |
22 | Make a copy of strings/strings.json and name the copy as strings/strings.\.json (EX: strings.es_US.json). The locale format is the language alpha-2 code joined with the country alpha-2 code.
23 |
24 | The strings are stored in a json format and will look like this:
25 | ```
26 | "job_farmer_desc": "Farmers create food to feed your population. Each farmer generates %0 food per second.",
27 | ```
28 | If you are unfamiliar with json the first part is a **key** and cannot be altered, **do not translate or modify the key in any way**. The second part is the string to be translated. Many game strings use **tokens** (**%0**, **%1**, **%2**, etc) to represent game values, as such these tokens must remain in the final translated string. Their position can be moved accordingly, the number represents a specific value and not its position in the string.
29 |
30 | To enable your language translation in the game you must add it to the locales constant in locale.js (bottom of the file).
31 |
32 | Once you feel your translation file is ready send a pull request with it to the Evolve main branch.
33 |
34 |
35 | ## Contributing to the game
36 | Bug fixes, additional translations, themes, or UI improvements can simply be submitted as pull requests; once reviewed and accepted they will be merged into the main game branch. If you want to contribute a new feature it can not arbitrarily make something easier without making something else harder. If your new feature idea simply makes the game easier it will not be accepted.
37 |
38 | ## CSS Changes
39 | Evolve uses LESS to build its CSS, you can not just edit the minified CSS file. You must instead edit src/evolve.less then use the less compiler to rebuild the CSS file.
40 |
41 | ## Build Commands
42 | Assuming you configured your build environment correctly the game can be built using the following scripts
43 | ```
44 | npm run build // Builds the game bundle
45 | npm run dev // Builds the game bundle in debug mode
46 | npm run less // Builds the CSS file
47 | npm run wiki // Builds the wiki bundle
48 | npm run wiki-dev // Builds the wiki bundle in debug mode
49 | npm run wiki-less // Builds the Wiki CSS file
50 | ```
51 |
--------------------------------------------------------------------------------
/strings/updateStrings.py:
--------------------------------------------------------------------------------
1 | import json
2 | from shutil import copyfile
3 | import sys
4 | import os.path as path
5 |
6 | print()
7 |
8 | if len(sys.argv) < 2:
9 | print("inform locale key (python updateString.py )")
10 | else:
11 | locale = sys.argv[1]
12 |
13 | if not path.isfile('strings.{}.json'.format(locale)):
14 | print("'strings.{}.json' not found.\nCreate that file with a line write '{{ }}' if need.".format(locale))
15 | exit()
16 |
17 | with open('strings.json', encoding='utf-8') as default_file, \
18 | open('strings.{}.json'.format(locale), 'r+', encoding='utf-8') as loc_file:#, \
19 |
20 | try:
21 | default_strings = json.load(default_file)
22 | except:
23 | print("the 'strings.json' file is a malformed json file.")
24 | exit()
25 |
26 | try:
27 | locale_strings = json.load(loc_file)
28 | except:
29 | print("the 'strings.{}.json' file is a malformed json file.".format(locale))
30 | exit()
31 |
32 | if path.isfile('last-strings.json'.format(locale)):
33 | last_file = open('last-strings.json', encoding='utf-8')
34 | try:
35 | last_strings = json.load(last_file)
36 | except:
37 | print("the last-strings.json was a malformed json file.")
38 | last_strings = None
39 | last_file.close();
40 | else:
41 | last_strings = None
42 |
43 | writing = {}
44 |
45 | trans_count = 0
46 | change_count = 0
47 |
48 | for key in default_strings:
49 | if key in locale_strings:
50 | if locale_strings[key][0:6] == "TRANS:":
51 | writing[key] = "TRANS:" + default_strings[key]
52 | trans_count+=1
53 | elif last_strings is not None and key in last_strings and default_strings[key] != last_strings[key]:
54 | writing[key] = "CHANGE:" + locale_strings[key] + "~FROM:{}~TO:{}".format(last_strings[key], default_strings[key])
55 | change_count+=1
56 | else:
57 | writing[key] = locale_strings[key]
58 | del locale_strings[key]
59 | else:
60 | writing[key] = "TRANS:" + default_strings[key]
61 | trans_count+=1
62 |
63 | print("{} values are marked with tag 'CHANGE:'".format(change_count))
64 | print("{} values are marked with tag 'TRANS:'".format(trans_count))
65 | if len(locale_strings) > 0:
66 | print("{} keys was deleted: ".format(len(locale_strings)))
67 | for key in locale_strings:
68 | print(key)
69 | else:
70 | print("0 keys was deleted.".format(len(locale_strings)))
71 |
72 | loc_file.seek(0,0);
73 | loc_file.truncate(0);
74 | json.dump(writing, loc_file, ensure_ascii=False, indent=2)
75 |
76 | copyfile("strings.json", "last-strings.json");
77 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Evolve
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
53 |
54 |
55 |
56 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/locale.js:
--------------------------------------------------------------------------------
1 | import { global, save } from './vars.js';
2 |
3 | let strings;
4 | getString(global.settings.locale);
5 |
6 | export function loc(key, variables) {
7 | let string = strings[key];
8 | if (!string) {
9 | if (global.settings.expose){
10 | console.error(`string ${key} not found`);
11 | console.log(strings);
12 | }
13 | return key;
14 | }
15 | if (variables) {
16 | if(variables instanceof Array) {
17 | for (let i = 0; i < variables.length; i++){
18 | let re = new RegExp(`%${i}(?!\\d)`, "g");
19 | if(!re.exec(string)){
20 | if (global.settings.expose){
21 | console.error(`"%${i}" was not found in the string "${key}" to be replace by "${variables[i]}"`);
22 | }
23 | continue;
24 | }
25 | string = string.replace(re, variables[i]);
26 | }
27 | let re = new RegExp("%\\d+(?!\\d)", 'g');
28 | const results = string.match(re);
29 | if(results && global.settings.expose){
30 | console.error(`${results} was found in the string, but there is no variables to make the replacement`);
31 | }
32 | }
33 | else {
34 | if (global.settings.expose){
35 | console.error('"variables" need be a instance of "Array"');
36 | }
37 | }
38 | }
39 | return string;
40 | }
41 |
42 | function getString(locale) {
43 | $.ajaxSetup({ async: false });
44 |
45 | let defaultString;
46 | $.getJSON("strings/strings.json", (data) => { defaultString = data; });
47 |
48 | if (locale != "en-US"){
49 | let localeString;
50 | try {
51 | $.getJSON(`strings/strings.${locale}.json`, (data) => { localeString = data; })
52 | }
53 | catch (e) {
54 | console.error(e,e.stack);
55 | }
56 | const defSize = defaultString.length;
57 |
58 | if (localeString) {
59 | Object.assign(defaultString, localeString);
60 | }
61 |
62 | if(defaultString.length != defSize && global.settings.expose){
63 | console.error(`string.${locale}.json has extra keys.`);
64 | }
65 | }
66 | let string_pack = save.getItem('string_pack') || false;
67 | if (string_pack && global.settings.sPackOn){
68 | let themeString;
69 | try {
70 | themeString = JSON.parse(LZString.decompressFromUTF16(string_pack));
71 | }
72 | catch (e) {
73 | console.error(e,e.stack);
74 | }
75 | const defSize = defaultString.length;
76 |
77 | if (themeString) {
78 | Object.assign(defaultString, themeString);
79 | }
80 |
81 | if (defaultString.length != defSize && global.settings.expose){
82 | console.error(`string pack has extra keys.`);
83 | }
84 | }
85 |
86 | $.ajaxSetup({ async: true });
87 | strings = defaultString;
88 | }
89 |
90 | export const locales = {
91 | 'en-US': 'English (US)',
92 | 'es-ES': 'Spanish (ESP)',
93 | 'pt-BR': 'Português (BR)',
94 | 'zh-CN': '简体中文',
95 | 'zh-TW': '繁體中文',
96 | 'ko-KR': '한국어',
97 | 'cs-CZ': 'Čeština',
98 | 'ru-RU': 'Русский',
99 | 'im-PL': 'Igpay-Atinlay'
100 | };
101 |
--------------------------------------------------------------------------------
/strings/checkStrings.py:
--------------------------------------------------------------------------------
1 | import json
2 | import sys
3 | import os.path as path
4 | import re
5 |
6 | print()
7 |
8 | check_tags = True
9 | check_tokens = True
10 | check_leading_space = True
11 | check_periods = True
12 | check_numbers = True
13 |
14 | def led_spaces(str):
15 | return len(str) - len(str.lstrip(' '))
16 |
17 | if len(sys.argv) < 2:
18 | print("inform locale key (example 'python checkString.py pt-BR')")
19 | else:
20 | locale = sys.argv[1]
21 |
22 | if not path.isfile('strings.{}.json'.format(locale)):
23 | print("'strings.{}.json' not found. Create it before calling this script.".format(locale))
24 | exit()
25 |
26 | with open('strings.json', encoding='utf-8') as default_file, \
27 | open('strings.{}.json'.format(locale), encoding='utf-8') as loc_file:
28 | defstr = json.load(default_file)
29 |
30 | json_regex = re.compile(r'"(?P.+)"\s*:\s"(?P.*)"\s*$')
31 | period_count = re.compile(r'(\.(\D|$))|。')
32 | tokens_regex = re.compile(r'%\d+(?!\d)')
33 | numbers_regex = re.compile(r'\d+')
34 |
35 | for (nl, line) in enumerate(loc_file):
36 | line = line.strip()
37 | if line == '{' or line == '}' or len(line) == 0:
38 | continue
39 |
40 | if line[-1] == ',':
41 | line = line[:-1]
42 |
43 | line = re.search(json_regex, line)
44 |
45 | if line == None:
46 | print('failed parse line {}'.format(nl+1))
47 | continue
48 | line = line.groupdict()
49 |
50 | if not line['key'] in defstr:
51 | print("key '{}' is not found in string.json, from line {}".format(line['key'], nl))
52 | continue
53 | else:
54 | defline = defstr[line['key']]
55 |
56 | if check_tags:
57 | if line['value'][0:6] == "TRANS:":
58 | print("key '{}' is marked with tag 'TRANS:', from line {}".format(line['key'], nl))
59 | if line['value'][0:7] == "CHANGE:":
60 | print("key '{}' is marked with tag 'CHANGE:', from line {}".format(line['key'], nl))
61 |
62 | if check_tokens:
63 | tcdef = len(tokens_regex.findall(defline))
64 | tcloc = len(tokens_regex.findall(line['value']))
65 | if tcdef != tcloc:
66 | print("Number of tokens (like %0) number differ (def: {} != loc: {}), in key '{}', line {}" \
67 | .format(tcdef, tcloc, line['key'], nl+1))
68 |
69 | if check_leading_space:
70 | leddef = led_spaces(defline)
71 | ledloc = led_spaces(line['value'])
72 | if leddef != ledloc:
73 | print("leading spaces differ (def: {} != loc: {}), in key '{}', line {}".format(leddef, ledloc, line['key'], nl+1))
74 |
75 | if check_periods:
76 | pcdef = len(period_count.findall(defline))
77 | pcloc = len(period_count.findall(line['value']))
78 | if pcdef != pcloc:
79 | print("periods number differ (def: {} != loc: {}), in key '{}', line {}" \
80 | .format(pcdef, pcloc, line['key'], nl+1))
81 |
82 | if check_numbers:
83 | pcdef = numbers_regex.findall(defline)
84 | pcloc = numbers_regex.findall(line['value'])
85 | if sorted(pcdef) != sorted(pcloc):
86 | print("Numbers differ (def: {} != loc: {}), in key '{}', line {}" \
87 | .format(pcdef, pcloc, line['key'], nl+1))
--------------------------------------------------------------------------------
/src/wiki/planets.js:
--------------------------------------------------------------------------------
1 | import { global } from './../vars.js';
2 | import { loc } from './../locale.js';
3 | import { planetTraits, biomes } from './../races.js';
4 | import { headerBoxBuilder, infoBoxBuilder } from './functions.js';
5 |
6 | export function planetsPage(content) {
7 | let info = $('');
8 |
9 | let intro = headerBoxBuilder(content,{ name: 'planet', template: 'planet', paragraphs: 4, full: true,
10 | para_data: {
11 | 2: [365,'25%'],
12 | 3: [4],
13 | 4: ['200-600']
14 | }
15 | });
16 | infoBoxBuilder(content,{ name: 'geology', template: 'planet', label: loc('wiki_menu_planets'), paragraphs: 4, h_level: 2,
17 | para_data: {
18 | 2: [2],
19 | 3: ['-10%','+19%'],
20 | 4: [7,'+44%']
21 | }
22 | },intro);
23 |
24 | let planetInfo = infoForFeature(biomes, $(`
${loc('wiki_planet_biome')}
`));
25 | let planetTraitsInfo = infoForFeature(planetTraits, $(`