├── anno1800assistant ├── src │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── styles.css │ ├── tsconfig.app.json │ ├── tslint.json │ ├── tsconfig.spec.json │ ├── main.ts │ ├── browserslist │ ├── test.ts │ ├── app │ │ ├── app.module.ts │ │ ├── app.component.spec.ts │ │ ├── data │ │ │ ├── region.ts │ │ │ ├── products.ts │ │ │ ├── populations.ts │ │ │ └── factories.ts │ │ ├── app.component.css │ │ ├── app.component.ts │ │ ├── app.component.html │ │ └── models │ │ │ └── island.ts │ ├── index.html │ ├── karma.conf.js │ └── polyfills.ts ├── e2e │ ├── tsconfig.e2e.json │ ├── src │ │ ├── app.po.ts │ │ └── app.e2e-spec.ts │ └── protractor.conf.js ├── .editorconfig ├── tsconfig.json ├── README.md ├── package.json ├── tslint.json └── angular.json ├── docs ├── styles.14568735a6491180fd40.css ├── favicon.ico ├── index.html ├── runtime.26209474bfa8dc87a77c.js ├── 3rdpartylicenses.txt └── polyfills.8bbb231b43165d65d357.js ├── AnnoXMLParser ├── AnnoXMLParser │ ├── App.config │ ├── packages.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── AnnoXMLParser.csproj │ └── Program.cs └── AnnoXMLParser.sln ├── .gitignore ├── LICENSE.md └── README.md /anno1800assistant/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/styles.14568735a6491180fd40.css: -------------------------------------------------------------------------------- 1 | body{background-color:#c1f2ff} -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beesbeesbeesbees/anno1800assistant/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /anno1800assistant/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /anno1800assistant/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beesbeesbeesbees/anno1800assistant/HEAD/anno1800assistant/src/favicon.ico -------------------------------------------------------------------------------- /anno1800assistant/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | body { 3 | background-color: #c1f2ff; 4 | } -------------------------------------------------------------------------------- /AnnoXMLParser/AnnoXMLParser/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /anno1800assistant/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /AnnoXMLParser/AnnoXMLParser/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /anno1800assistant/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /anno1800assistant/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /anno1800assistant/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /anno1800assistant/src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /anno1800assistant/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /anno1800assistant/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /anno1800assistant/src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /anno1800assistant/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": false, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /anno1800assistant/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /anno1800assistant/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule} from '@angular/forms'; 4 | 5 | import { AppComponent } from './app.component'; 6 | import { PopulationService } from 'src/app/data/populations'; 7 | import { RegionService } from './data/region'; 8 | import { ProductsService, Product } from './data/products'; 9 | 10 | @NgModule({ 11 | declarations: [ 12 | AppComponent 13 | ], 14 | imports: [ 15 | BrowserModule, 16 | FormsModule 17 | ], 18 | providers: [ 19 | PopulationService, 20 | RegionService, 21 | ProductsService, 22 | ], 23 | bootstrap: [AppComponent] 24 | }) 25 | export class AppModule { } 26 | -------------------------------------------------------------------------------- /anno1800assistant/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /anno1800assistant/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to anno1800assistant!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /anno1800assistant/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | **/dist 5 | **/tmp 6 | **/bin 7 | **/obj 8 | **/out-tsc 9 | # Only exists if Bazel was run 10 | **/bazel-out 11 | 12 | # dependencies 13 | **/node_modules 14 | **/packages 15 | 16 | # profiling files 17 | chrome-profiler-events.json 18 | speed-measure-plugin.json 19 | 20 | # IDEs and editors 21 | **/.vs 22 | /.idea 23 | .project 24 | .classpath 25 | .c9/ 26 | *.launch 27 | .settings/ 28 | *.sublime-workspace 29 | 30 | # IDE - VSCode 31 | .vscode/* 32 | !.vscode/settings.json 33 | !.vscode/tasks.json 34 | !.vscode/launch.json 35 | !.vscode/extensions.json 36 | .history/* 37 | 38 | # misc 39 | /.sass-cache 40 | /connect.lock 41 | /coverage 42 | /libpeerconnection.log 43 | npm-debug.log 44 | yarn-error.log 45 | testem.log 46 | /typings 47 | 48 | # System Files 49 | .DS_Store 50 | Thumbs.db 51 | -------------------------------------------------------------------------------- /anno1800assistant/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Anno 1800 Assistant 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | View on Github 17 | 18 | 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Beesbeesbeesbees 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /anno1800assistant/README.md: -------------------------------------------------------------------------------- 1 | # Anno1800assistant 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.3.8. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /anno1800assistant/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.debugElement.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'anno1800assistant'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.debugElement.componentInstance; 22 | expect(app.title).toEqual('anno1800assistant'); 23 | }); 24 | 25 | it('should render title in a h1 tag', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.debugElement.nativeElement; 29 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to anno1800assistant!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /anno1800assistant/src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage/anno1800assistant'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /AnnoXMLParser/AnnoXMLParser.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2015 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnnoXMLParser", "AnnoXMLParser\AnnoXMLParser.csproj", "{EC222549-A31A-49B8-9EDC-59621B9A0CF4}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {EC222549-A31A-49B8-9EDC-59621B9A0CF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {EC222549-A31A-49B8-9EDC-59621B9A0CF4}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {EC222549-A31A-49B8-9EDC-59621B9A0CF4}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {EC222549-A31A-49B8-9EDC-59621B9A0CF4}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {80395BA3-B2F7-4CEC-9B30-370CDBB5911E} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Anno 1800 Assistant 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | View on Github 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/runtime.26209474bfa8dc87a77c.js: -------------------------------------------------------------------------------- 1 | !function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EC222549-A31A-49B8-9EDC-59621B9A0CF4} 8 | Exe 9 | AnnoXMLParser 10 | AnnoXMLParser 11 | v4.6.1 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /anno1800assistant/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /anno1800assistant/src/app/data/region.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { PopulationService } from './populations'; 3 | 4 | export type Region = 'OldWorld' | 'NewWorld' | 'Arctic' | 'Enbesa'; 5 | 6 | @Injectable() 7 | export class RegionService { 8 | static regionFriendlyNameMap: { label: string, value: Region }[] = [ 9 | { label: 'Old World', value: 'OldWorld' }, 10 | { label: 'New World', value: 'NewWorld' }, 11 | { label: 'Arctic', value: 'Arctic' }, 12 | { label: 'Enbesa', value: 'Enbesa' } 13 | ]; 14 | 15 | static regionColorMap: { [key in Region]: string } = { 16 | 'OldWorld': '#ffeec4', 17 | 'NewWorld': '#75e069', 18 | 'Arctic': '#ededed', 19 | 'Enbesa': '#ffd336', 20 | } 21 | 22 | constructor(private populationService: PopulationService) { 23 | 24 | } 25 | 26 | static regionFactories: { [key in Region ]: number[] } = { 27 | 'OldWorld': [ 28 | // Farmers 29 | 1010278, // Fishery 30 | 1010294, // Schnapps 31 | 1010315, // Knitter 32 | 33 | // Workers 34 | 1010316, // Sausages 35 | 1010291, // Bread 36 | 1010281, // Soap 37 | 1010292, // Beer 38 | 1010360, // School 39 | 40 | // Artisans 41 | 1010295, // Canned Food 42 | 1010284, // Sewing Machines 43 | 1010340, // Rum 44 | 1010325, // Fur Coats 45 | 1010362, // University 46 | 47 | // Engineers 48 | 101250, // Glasses 49 | 1010323, // High Wheelers 50 | 1, // Electricity 51 | 101252, // Coffee 52 | 1010324, // Pocket Watches 53 | 1010286, // Light Bulbs 54 | 1010365, // Bank 55 | 56 | // Investors 57 | 100659, // Champagne 58 | 1010342, // Cigars 59 | 1010364, // Club House 60 | 1010341, // Chocolate 61 | 1010328, // Jewelry 62 | 1010326, // Gramophones 63 | 1010303, // Steam Carriages 64 | 65 | // Scholars 66 | 118733, // Bootmaker 67 | 118734, // Tailor's Shop 68 | 101273, // Bowler Hats 69 | 114468, // Tea Spicer 70 | 114471, // Wat Kitchen 71 | 114469, // Tapestry Looms 72 | 118735, // Telephone Manufacturer 73 | 114472, // Pipe Maker 74 | 75 | 76 | ], 77 | 'NewWorld': [ 78 | // Jornaleros 79 | 101264, // Fried Plantains 80 | 1010340, // Rum 81 | 101266, // Ponchos 82 | 2, // Chapel 83 | 84 | // Obreros 85 | 101271, // Tortillas 86 | 101252, // Coffee 87 | 3, // Boxing Arena 88 | 101273, // Bowler Hats 89 | 1010292, // Beer 90 | 1010284, // Sewing Machines 91 | 1010342, // Cigars 92 | ], 93 | 'Arctic': [ 94 | // Explorers 95 | 112668, // Pemmican 96 | 112675, // Sleeping Bag Factory 97 | 112679, // Oil Lamp Factory 98 | 1010294, // Schnapps Distillery 99 | 100 | // Technicians 101 | 1010295, // Cannery 102 | 112672, // Parka Factory 103 | 112680, // Husky Sled Factory 104 | 101252, // Coffee Roaster 105 | ], 106 | 'Enbesa': [ 107 | // Shepherds 108 | 114456, // Goat Farm 109 | 114466, // Embroiderer 110 | 114519, // Musician's Court 111 | 114444, // Dry-House 112 | 114468, // Tea Spicer 113 | 114 | // Elders 115 | 118725, // Ceramics Workshop 116 | 114469, // Tapestry Looms 117 | 114471, // Wat Kitchen 118 | 114472, // Pipe Maker 119 | 114470, // Luminer 120 | 101250, // Spectacle Factory 121 | 114464, // Lanternsmith 122 | ] 123 | }; 124 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Launch Anno 1800 Assistant In-Browser](https://beesbeesbeesbees.github.io/anno1800assistant/) 2 | 3 | # anno1800assistant 4 | Anno 1800 Assistant is a tool to compute consumption and production rates of consumer goods in Anno 1800. It is designed to aid with tracking population and building counts in moment-to-moment gameplay. At a glance the user should be able to see what effect building or promoting residences will have in their goods consumption and when new production chains will be required. It also allows modeling of multiple islands at once and trade interactions between them. 5 | 6 | ## Info 7 | The rates in calculations in the app are based on independent research into the games' files and experiments in the retail version of the game. Like in previous anno games, the consumption model appears to be based on number of residences per population level per second, rather than being based on population as one might expect. 8 | 9 | ## Instructions 10 | Input the number of residences per population level of an island into the top row. Promotion buttons are added for convenience when promoting from one level to the next. Use shift or control modifier keys to promote multiple residences at a time. 11 | 12 | The bottom section contains all relevant production buildings for the island, with the number of factories required to fully satisfy the population listed in the 'Required' column. Once a production chain is marked as 'Enabled', you will be able to input the number of production buildings constructed on the island, and the row will be color coded to show whether the current production is enough to satisfy the demand. Once enabled, the production chain will also be reflected in the 'Population' row in the top section for population/workforce calculation. 13 | 14 | The 'Productivity' column can be used to reflect increases or decreases in productivity due to island bonuses or penalties. Adjusting it should adust the 'Required' column accordingly, and adjusting it for intermediate products in production chains should properly adjust all earlier products. 15 | 16 | The 'Trade' column can be used to model trading goods from one island to another. The value will be added or subtracted to the 'Built' column to determine goods satisfaction for the island. Trade is measured in production buildings (so a value of 1 in the 'Trade' column would satisfy the same demand as a value of 1 in the 'Built' column.) The trade input box will show red if the global balance of trade goods for a particular product is inbalanced. The tooltip for the input box will show the global trade balance. Note that trade balance is affected by productivity. For example, an island exporting a single production facility at 200% productivity to an island which also has a single production facility of the same type running at 100% productivity should require Trade values of -1 and +2, respectively to be balanced, to reflect that the total goods available on the second island will be triple the output of a factory running at 100%. 17 | 18 | ## Building and Debugging 19 | ### One-Time Prerequisites 20 | * Node.js (https://nodejs.org/en/) 21 | * Install Angular/CLI locally (per https://angular.io/guide/quickstart#step-1-install-the-angular-cli) with the command: 22 | ``` 23 | npm install -g @angular/cli 24 | ``` 25 | * From the anno1800assistant application folder (containing package.json), install required packages with the command: 26 | ``` 27 | npm install 28 | ``` 29 | This step may need to be performed again after cloning the repo to new locations or after updating dependencies. 30 | 31 | ### Debugging locally 32 | From the anno1800assistant application folder (containing package.json): 33 | ``` 34 | ng serve --open 35 | ``` 36 | 37 | ### Building 38 | From the anno1800assistant application folder (containing package.json): 39 | ``` 40 | ng build --prod 41 | ``` 42 | Which outputs to the /dist/ folder 43 | 44 | ## AnnoXMLParser 45 | I also included in the repo a crude tool that I used to parse information out of the game's assets.xml file, which can be extracted using the excllent RDA Explorer tool (https://github.com/lysannschlegel/RDAExplorer) 46 | 47 | It is not at all required to run the app, but I've included it for learning purposes, and it may be useful for parsing out new data for future versions of the game or creating translations. 48 | 49 | The tool is built in C# .NET and can be run using Visual Studio 50 | -------------------------------------------------------------------------------- /anno1800assistant/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "anno1800assistant": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/anno1800assistant", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "src/tsconfig.app.json", 21 | "assets": [ 22 | "src/favicon.ico", 23 | "src/assets" 24 | ], 25 | "styles": [ 26 | "src/styles.css" 27 | ], 28 | "scripts": [], 29 | "es5BrowserSupport": true 30 | }, 31 | "configurations": { 32 | "production": { 33 | "fileReplacements": [ 34 | { 35 | "replace": "src/environments/environment.ts", 36 | "with": "src/environments/environment.prod.ts" 37 | } 38 | ], 39 | "optimization": true, 40 | "outputHashing": "all", 41 | "sourceMap": false, 42 | "extractCss": true, 43 | "namedChunks": false, 44 | "aot": true, 45 | "extractLicenses": true, 46 | "vendorChunk": false, 47 | "buildOptimizer": true, 48 | "budgets": [ 49 | { 50 | "type": "initial", 51 | "maximumWarning": "2mb", 52 | "maximumError": "5mb" 53 | } 54 | ] 55 | } 56 | } 57 | }, 58 | "serve": { 59 | "builder": "@angular-devkit/build-angular:dev-server", 60 | "options": { 61 | "browserTarget": "anno1800assistant:build" 62 | }, 63 | "configurations": { 64 | "production": { 65 | "browserTarget": "anno1800assistant:build:production" 66 | } 67 | } 68 | }, 69 | "extract-i18n": { 70 | "builder": "@angular-devkit/build-angular:extract-i18n", 71 | "options": { 72 | "browserTarget": "anno1800assistant:build" 73 | } 74 | }, 75 | "test": { 76 | "builder": "@angular-devkit/build-angular:karma", 77 | "options": { 78 | "main": "src/test.ts", 79 | "polyfills": "src/polyfills.ts", 80 | "tsConfig": "src/tsconfig.spec.json", 81 | "karmaConfig": "src/karma.conf.js", 82 | "styles": [ 83 | "src/styles.css" 84 | ], 85 | "scripts": [], 86 | "assets": [ 87 | "src/favicon.ico", 88 | "src/assets" 89 | ] 90 | } 91 | }, 92 | "lint": { 93 | "builder": "@angular-devkit/build-angular:tslint", 94 | "options": { 95 | "tsConfig": [ 96 | "src/tsconfig.app.json", 97 | "src/tsconfig.spec.json" 98 | ], 99 | "exclude": [ 100 | "**/node_modules/**" 101 | ] 102 | } 103 | } 104 | } 105 | }, 106 | "anno1800assistant-e2e": { 107 | "root": "e2e/", 108 | "projectType": "application", 109 | "prefix": "", 110 | "architect": { 111 | "e2e": { 112 | "builder": "@angular-devkit/build-angular:protractor", 113 | "options": { 114 | "protractorConfig": "e2e/protractor.conf.js", 115 | "devServerTarget": "anno1800assistant:serve" 116 | }, 117 | "configurations": { 118 | "production": { 119 | "devServerTarget": "anno1800assistant:serve:production" 120 | } 121 | } 122 | }, 123 | "lint": { 124 | "builder": "@angular-devkit/build-angular:tslint", 125 | "options": { 126 | "tsConfig": "e2e/tsconfig.e2e.json", 127 | "exclude": [ 128 | "**/node_modules/**" 129 | ] 130 | } 131 | } 132 | } 133 | } 134 | }, 135 | "defaultProject": "anno1800assistant" 136 | } -------------------------------------------------------------------------------- /anno1800assistant/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | .addIsland, .island { 2 | margin: 5px 0px 5px 5px; 3 | display: inline-block; 4 | vertical-align: top; 5 | } 6 | 7 | .buttonTray > * { 8 | margin-left: 4px; 9 | } 10 | 11 | .island { 12 | border: 1px solid gray; 13 | padding: 5px; 14 | } 15 | 16 | .islandName { 17 | width: 50%; 18 | display: inline-block; 19 | text-align: center; 20 | } 21 | 22 | .islandRegion { 23 | width: 20%; 24 | display: inline-block; 25 | text-align: center; 26 | margin-left: 20px; 27 | } 28 | 29 | .islandNameContainer, .islandHeaderWrapper { 30 | text-align: center; 31 | } 32 | 33 | .islandHeader { 34 | text-align: center; 35 | display: inline-block; 36 | width: 100%; 37 | } 38 | 39 | .populationGroup { 40 | display: inline-block; 41 | text-align: left; 42 | } 43 | .populationGroup:not(:first-of-type) { 44 | margin-left: 30px; 45 | } 46 | 47 | input[type="number"] { 48 | width: 80px; 49 | display: inline-block; 50 | } 51 | 52 | .popLabelColumn { 53 | width: 87px; 54 | display: inline-block; 55 | text-align: right; 56 | padding-right: 10px; 57 | } 58 | 59 | .showUnusedToggle { 60 | margin-left: 5px; 61 | } 62 | .showUnusedToggle, .showUnusedToggle * { 63 | cursor: pointer; 64 | z-index: 999999999; 65 | } 66 | 67 | .houseCountColumn { 68 | width: auto; 69 | display: inline-block; 70 | } 71 | .houseCountColumn.hasAddButton .houseCountColumnLabel { 72 | margin-left: 63px; 73 | } 74 | .houseCountColumn .houseCountColumnLabel { 75 | margin-left: 3px; 76 | } 77 | .houseCountColumn .populationCountColumnLabel { 78 | margin-left: 13px; 79 | } 80 | .houseCountColumn.hasAddButton .populationCountColumnLabel { 81 | margin-left: 73px; 82 | } 83 | .houseCountColumn.header { 84 | text-align: right; 85 | padding-right: 15px; 86 | font-weight: bold; 87 | position: relative; 88 | } 89 | .houseCountColumn.populationCount { 90 | padding-left: 68px; 91 | } 92 | .houseCountColumn > * { 93 | display: inline-block; 94 | vertical-align: middle; 95 | } 96 | .houseCountColumn > .btn { 97 | margin-left: 5px; 98 | margin-right: 5px; 99 | } 100 | .houseCountColumn:last-child { 101 | width: 50px; 102 | } 103 | 104 | .addHouseButton { 105 | margin-right: 5px; 106 | } 107 | .addHouseButton, .houseCountColumn > .btn { 108 | text-align: left; 109 | width: 50px; 110 | } 111 | 112 | .factories { 113 | text-align: center; 114 | display: inline-block; 115 | line-height: 1; 116 | } 117 | 118 | .factories .factoryNameColumn { 119 | width: 200px; 120 | display: inline-block; 121 | text-align: left; 122 | position: relative; 123 | } 124 | 125 | .factoryHeader { 126 | font-weight: bold; 127 | text-align: center; 128 | padding: 1px 2px 1px 9px; 129 | margin-bottom: 5px; 130 | } 131 | 132 | .factories input[type="number"] { 133 | padding: 1px 5px; 134 | height: initial; 135 | } 136 | 137 | .factoryColumn { 138 | display: inline-block; 139 | vertical-align: top; 140 | } 141 | 142 | .factoryColumn:not(:last-child) { 143 | margin-right: 10px; 144 | } 145 | 146 | .factory { 147 | border-top: 1px solid gray; 148 | border-left: 1px solid gray; 149 | border-right: 1px solid gray; 150 | border-radius: 3px; 151 | padding: 1px 1px 0px 8px; 152 | background-color: white; 153 | } 154 | .factory:last-child { 155 | border-bottom: 1px solid gray; 156 | } 157 | .factory.unsatisfied { 158 | background-color: #FDA8AB; 159 | } 160 | .factory.satisfied { 161 | background-color: #D5EF9F; 162 | } 163 | .factory.slightlyUnsatisfied { 164 | background-color: #fff26d; 165 | } 166 | .factory.focusedFactory { 167 | box-shadow: inset 0px 0px 5px #e756f9 168 | } 169 | 170 | .productivityColumn { 171 | width: 100px; 172 | display: inline-block; 173 | text-align: center; 174 | } 175 | 176 | .enabledColumn { 177 | width: 60px; 178 | display: inline-block; 179 | text-align: center; 180 | } 181 | 182 | .requiredColumn { 183 | width: 70px; 184 | display: inline-block; 185 | text-align: center; 186 | } 187 | 188 | .builtColumn { 189 | width: 85px; 190 | display: inline-block; 191 | } 192 | 193 | .tradeColumn { 194 | width: 85px; 195 | display: inline-block; 196 | } 197 | 198 | .indent { 199 | width: 20px; 200 | display: inline-block; 201 | } 202 | 203 | .publicService .hiddenForPublicService { 204 | visibility: hidden; 205 | } 206 | 207 | .unbalancedTrade { 208 | background-color: #FDA8AB; 209 | } 210 | 211 | .bottomButton { 212 | margin-left: 5px; 213 | } 214 | 215 | .brightHarvestToggle { 216 | padding: .15rem .3rem !important; 217 | font-size: .8rem !important; 218 | line-height: 1 !important; 219 | position: absolute; 220 | cursor: pointer; 221 | right: 0px; 222 | top: 0px; 223 | } 224 | .brightHarvestToggle.disabled { 225 | cursor: not-allowed; 226 | } -------------------------------------------------------------------------------- /anno1800assistant/src/app/data/products.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class ProductsService { 5 | AllProducts: Product[]; 6 | 7 | constructor() { 8 | this.AllProducts = [{"ID":119392,"Name":"Research Points"},{"ID":114431,"Name":"Telephones"},{"ID":114430,"Name":"Tailored Suits"},{"ID":114428,"Name":"Leather Boots"},{"ID":117699,"Name":"Lanterns"},{"ID":117698,"Name":"Illuminated Script"},{"ID":114414,"Name":"Clay Pipe"},{"ID":114410,"Name":"Seafood Stew"},{"ID":114404,"Name":"Tapestries"},{"ID":118724,"Name":"Ceramics"},{"ID":114402,"Name":"Mud Bricks"},{"ID":114390,"Name":"Hibiscus Tea"},{"ID":114401,"Name":"Finery"},{"ID":114359,"Name":"Dried Meat"},{"ID":114427,"Name":"Decorative Cloth"},{"ID":114433,"Name":"Essential Oil"},{"ID":114429,"Name":"Ammunition"},{"ID":117701,"Name":"Ornate Candles"},{"ID":114408,"Name":"Spiced Flour"},{"ID":114391,"Name":"Linen"},{"ID":118728,"Name":"Lobster"},{"ID":117702,"Name":"Paper"},{"ID":114358,"Name":"Salt"},{"ID":114371,"Name":"Goat Milk"},{"ID":114357,"Name":"Sanga Cow"},{"ID":114370,"Name":"Beeswax"},{"ID":114369,"Name":"Spices"},{"ID":114368,"Name":"Indigo Dye"},{"ID":114367,"Name":"Teff"},{"ID":114365,"Name":"Linseed"},{"ID":114364,"Name":"Hibiscus Petals"},{"ID":114356,"Name":"Wanza Timber"},{"ID":114425,"Name":"Radio Tower"},{"ID":114362,"Name":"Monastery"},{"ID":114361,"Name":"Musicians' Court"},{"ID":114355,"Name":"Marketplace"},{"ID":124478,"Name":"Scholar Workforce"},{"ID":114341,"Name":"Elder Workforce"},{"ID":114340,"Name":"Shepherd Workforce"},{"ID":270042,"Name":"Fuel"},{"ID":269751,"Name":"Fuel"},{"ID":115980,"Name":"Lost Expedition Scrap"},{"ID":112706,"Name":"Gas"},{"ID":112703,"Name":"Husky Sleds"},{"ID":112704,"Name":"Sleds"},{"ID":112700,"Name":"Parkas"},{"ID":112701,"Name":"Sleeping Bags"},{"ID":112702,"Name":"Oil Lamps"},{"ID":112705,"Name":"Pemmican"},{"ID":112696,"Name":"Seal Skin"},{"ID":112699,"Name":"Whale Oil"},{"ID":112698,"Name":"Huskies"},{"ID":112695,"Name":"Bear Fur"},{"ID":112697,"Name":"Goose Feathers"},{"ID":112694,"Name":"Caribou Meat"},{"ID":114890,"Name":"Canteen"},{"ID":112693,"Name":"Post Office"},{"ID":112708,"Name":"Heat"},{"ID":112654,"Name":"Technicians Workforce"},{"ID":112653,"Name":"Explorer Workforce"},{"ID":112523,"Name":"Special Scrap"},{"ID":112520,"Name":"Nice Scrap"},{"ID":112518,"Name":"Scrap"},{"ID":120022,"Name":"Electricity"},{"ID":1010566,"Name":"Oil"},{"ID":120021,"Name":"Coal"},{"ID":1010259,"Name":"Cigars"},{"ID":1010240,"Name":"Cotton Fabric"},{"ID":1010239,"Name":"Sugar"},{"ID":120035,"Name":"Tortillas"},{"ID":120033,"Name":"Fried Plantains"},{"ID":120032,"Name":"Coffee"},{"ID":1010258,"Name":"Chocolate"},{"ID":1010257,"Name":"Rum"},{"ID":120037,"Name":"Bombins"},{"ID":120044,"Name":"Felt"},{"ID":120043,"Name":"Ponchos"},{"ID":120042,"Name":"Fish Oil"},{"ID":1010256,"Name":"Pearls"},{"ID":120041,"Name":"Plantains"},{"ID":120036,"Name":"Alpaca Wool"},{"ID":120034,"Name":"Corn"},{"ID":120031,"Name":"Coffee Beans"},{"ID":1010255,"Name":"Caoutchouc"},{"ID":1010254,"Name":"Cocoa"},{"ID":1010253,"Name":"Cotton"},{"ID":1010252,"Name":"Tobacco"},{"ID":1010251,"Name":"Sugar Cane"},{"ID":1010250,"Name":"Jewelry"},{"ID":1010249,"Name":"Gold"},{"ID":1010248,"Name":"Gramophones"},{"ID":120030,"Name":"Spectacles"},{"ID":1010246,"Name":"Pocket Watches"},{"ID":1010245,"Name":"Penny Farthings"},{"ID":1010243,"Name":"Filaments"},{"ID":1010242,"Name":"Wood Veneers"},{"ID":1010241,"Name":"Glass"},{"ID":1010237,"Name":"Work Clothes"},{"ID":1010236,"Name":"Malt"},{"ID":1010235,"Name":"Flour"},{"ID":1010234,"Name":"Tallow"},{"ID":1010233,"Name":"Gold Ore"},{"ID":1010231,"Name":"Cement"},{"ID":1010230,"Name":"Copper"},{"ID":1010229,"Name":"Zinc"},{"ID":1010227,"Name":"Iron"},{"ID":1010226,"Name":"Coal"},{"ID":1010204,"Name":"Brass"},{"ID":1010225,"Name":"Steam Carriages"},{"ID":1010224,"Name":"Steam Motors"},{"ID":1010223,"Name":"Advanced Weapons"},{"ID":1010222,"Name":"Dynamite"},{"ID":1010221,"Name":"Weapons"},{"ID":1010219,"Name":"Steel"},{"ID":1010218,"Name":"Steel Beams"},{"ID":120016,"Name":"Champagne"},{"ID":1010238,"Name":"Sausages"},{"ID":1010216,"Name":"Schnapps"},{"ID":1010217,"Name":"Canned Food"},{"ID":1010215,"Name":"Goulash"},{"ID":1010214,"Name":"Beer"},{"ID":1010213,"Name":"Bread"},{"ID":1010206,"Name":"Sewing Machines"},{"ID":1010201,"Name":"Clay"},{"ID":1010211,"Name":"Chassis"},{"ID":1010210,"Name":"Sails"},{"ID":1010208,"Name":"Light Bulbs"},{"ID":1010207,"Name":"Windows"},{"ID":1010247,"Name":"Fur Coats"},{"ID":1010205,"Name":"Bricks"},{"ID":1010196,"Name":"Timber"},{"ID":1010203,"Name":"Soap"},{"ID":1010202,"Name":"Reinforced Concrete"},{"ID":1010228,"Name":"Quartz Sand"},{"ID":1010232,"Name":"Saltpeter"},{"ID":1010200,"Name":"Fish"},{"ID":1010198,"Name":"Red Peppers"},{"ID":120014,"Name":"Grapes"},{"ID":1010209,"Name":"Furs"},{"ID":1010199,"Name":"Pigs"},{"ID":1010197,"Name":"Wool"},{"ID":120008,"Name":"Wood"},{"ID":1010195,"Name":"Potatoes"},{"ID":1010194,"Name":"Hops"},{"ID":1010193,"Name":"Beef"},{"ID":1010192,"Name":"Grain"},{"ID":1010348,"Name":"Boxing Arena"},{"ID":120050,"Name":"Chapel"},{"ID":1010356,"Name":"Bank"},{"ID":1010355,"Name":"Members Club"},{"ID":1010354,"Name":"Electricity"},{"ID":1010353,"Name":"University"},{"ID":1010352,"Name":"Variety Theatre"},{"ID":1010351,"Name":"School"},{"ID":1010350,"Name":"Church"},{"ID":1010349,"Name":"Pub"},{"ID":120020,"Name":"Market"},{"ID":1010367,"Name":"Obrero Workforce"},{"ID":1010366,"Name":"Jornalero Workforce"},{"ID":1010128,"Name":"Investor Workforce"},{"ID":1010117,"Name":"Engineer Workforce"},{"ID":1010116,"Name":"Artisan Workforce"},{"ID":1010115,"Name":"Worker Workforce"},{"ID":1010052,"Name":"Farmer Workforce"},{"ID":1010190,"Name":"Influence"},{"ID":1010017,"Name":"Coins"}]; 9 | 10 | // Manually adding products to represent usage of Bright Harvest DLC silos and tractor barns 11 | this.AllProducts.push({ "ID": 1, "Name":"Silo Unit" }); 12 | this.AllProducts.push({ "ID": 2, "Name":"Tractor Barn Unit" }); 13 | } 14 | } 15 | 16 | export class Product { 17 | ID: number 18 | Name: string 19 | } -------------------------------------------------------------------------------- /anno1800assistant/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, HostListener, OnChanges, ElementRef, ViewChild } from '@angular/core'; 2 | import { PopulationLevel, PopulationLevelSaveInfo, PopulationService } from './data/populations'; 3 | import { Factory, Factories, FactorySaveInfo } from './data/factories'; 4 | import { RegionService, Region } from './data/region'; 5 | import { IslandSaveInfo, Island } from './models/island'; 6 | 7 | @Component({ 8 | selector: 'app-root', 9 | templateUrl: './app.component.html', 10 | styleUrls: ['./app.component.css'] 11 | }) 12 | export class AppComponent implements OnInit { 13 | constructor( 14 | public regionService: RegionService, 15 | public populationService: PopulationService 16 | ) { } 17 | 18 | regionOptions = RegionService.regionFriendlyNameMap; 19 | regionColorMap = RegionService.regionColorMap; 20 | Islands: Island[] 21 | Shift_key_held: boolean = false; 22 | Ctrl_key_held: boolean = false; 23 | Autosave_throttle: Date; 24 | FocusedFactoryID: number; 25 | 26 | ngOnInit() { 27 | this.Autosave_throttle = new Date(new Date().getTime() + 100) 28 | let saveData = window.localStorage.getItem('anno1800assistantsave'); 29 | 30 | if (!saveData) { 31 | this.Reset(); 32 | return; 33 | } 34 | 35 | try { 36 | let saveObject = JSON.parse(saveData) as SaveData; 37 | this.LoadData(saveObject); 38 | } 39 | catch { 40 | this.Reset(); 41 | } 42 | } 43 | 44 | ngDoCheck() { 45 | this.Autosave(); 46 | } 47 | 48 | ResetButton() { 49 | if (confirm('Are you sure you want to reset? This will erase all of your world data. You can save a backup of your world data to your computer with the Save button.')) { 50 | this.Reset(); 51 | } 52 | } 53 | 54 | Reset() { 55 | this.Islands = []; 56 | this.AddIsland(); 57 | } 58 | 59 | AddIsland() { 60 | this.Islands.push(new Island("Island " + (this.Islands.length + 1), this.populationService)); 61 | } 62 | 63 | MoveIslandUp(index: number) { 64 | let island = this.Islands[index]; 65 | this.Islands.splice(index, 1); 66 | this.Islands.splice(index - 1, 0, island); 67 | } 68 | 69 | MoveIslandDown(index: number) { 70 | let island = this.Islands[index]; 71 | this.Islands.splice(index, 1); 72 | this.Islands.splice(index + 1, 0, island); 73 | } 74 | 75 | SetFocusedFactoryID(factoryID: number) { 76 | this.FocusedFactoryID = factoryID; 77 | } 78 | 79 | ClearFocusedFactoryID() { 80 | this.FocusedFactoryID = null; 81 | } 82 | 83 | Autosave() { 84 | if (this.Autosave_throttle && new Date() < this.Autosave_throttle) { 85 | return; 86 | } 87 | 88 | window.localStorage.setItem('anno1800assistantsave', JSON.stringify(this.GenerateSaveData())); 89 | this.Autosave_throttle = new Date(new Date().getTime() + 100) 90 | } 91 | 92 | ManualSave() { 93 | var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(this.GenerateSaveData())); 94 | var downloadAnchorNode = document.createElement('a'); 95 | downloadAnchorNode.setAttribute("href", dataStr); 96 | downloadAnchorNode.setAttribute("download", "anno1800AssistantSaveData.json"); 97 | document.body.appendChild(downloadAnchorNode); // required for firefox 98 | downloadAnchorNode.click(); 99 | downloadAnchorNode.remove(); 100 | } 101 | 102 | TriggerFileUpload() { 103 | let el: HTMLElement = this.loadInput.nativeElement as HTMLElement; 104 | el.click(); 105 | } 106 | 107 | ManualLoad(event: any) { 108 | let self = this; 109 | var reader = new FileReader(); 110 | 111 | reader.onload = function(onLoadEvent: any) { 112 | let data = JSON.parse(onLoadEvent.target.result) as SaveData; 113 | self.LoadData(data); 114 | self.Autosave_throttle = null; 115 | self.Autosave(); 116 | } 117 | 118 | reader.readAsText(event.target.files[0]); 119 | } 120 | 121 | GenerateSaveData(): SaveData { 122 | let save: IslandSaveInfo[] = []; 123 | for (var i = 0; i < this.Islands.length; i++) { 124 | let island = this.Islands[i]; 125 | let populationLevels: PopulationLevelSaveInfo[] = []; 126 | let factories: FactorySaveInfo[] = []; 127 | 128 | for (var pop = 0; pop < island.PopulationLevels.length; pop++) { 129 | populationLevels.push({ 130 | HouseCount: island.PopulationLevels[pop].HouseCount, 131 | ShowUnused: island.PopulationLevels[pop].ShowUnused 132 | }); 133 | } 134 | 135 | for (var fact = 0; fact < island.Factories.length; fact++) { 136 | let factory = island.Factories[fact]; 137 | factories.push({ 138 | FactoryID: factory.ID, 139 | ParentFactoryID: factory.ParentFactoryOrThisRecursive.ID, 140 | BuiltCount: factory.BuiltCount, 141 | Productivity: factory.Productivity, 142 | Enabled: factory.Enabled, 143 | TradeBalance: factory.TradeBalance, 144 | UseSilo: factory.UseSilo, 145 | UseTractorBarn: factory.UseTractorBarn, 146 | }); 147 | } 148 | 149 | save.push({ 150 | Name: island.Name, 151 | Region: island.Region, 152 | PopulationLevels: populationLevels, 153 | Factories: factories, 154 | IsMinimized: island.IsMinimized, 155 | }); 156 | } 157 | 158 | return { Islands: save }; 159 | } 160 | 161 | LoadData(data: SaveData) { 162 | this.Islands = []; 163 | for (var i = 0; i < data.Islands.length; i++) { 164 | this.Islands.push(new Island(data.Islands[i].Name, this.populationService, data.Islands[i])); 165 | } 166 | } 167 | 168 | @ViewChild('loadInput') loadInput: ElementRef; 169 | 170 | @HostListener('window:keydown', ['$event']) 171 | keyDownEvent(event: KeyboardEvent) { 172 | if (event.keyCode === 16) { 173 | this.Shift_key_held = true; 174 | } 175 | 176 | if (event.keyCode === 17) { 177 | this.Ctrl_key_held = true; 178 | } 179 | } 180 | 181 | @HostListener('window:keyup', ['$event']) 182 | keyUpEvent(event: KeyboardEvent) { 183 | if (event.keyCode === 16) { 184 | this.Shift_key_held = false; 185 | } 186 | 187 | if (event.keyCode === 17) { 188 | this.Ctrl_key_held = false; 189 | } 190 | } 191 | 192 | PromotionCount(): number { 193 | return 1 * (this.Ctrl_key_held ? 5 : 1) * (this.Shift_key_held ? 10 : 1); 194 | } 195 | 196 | GetTradeBalance(factoryID: number): number { 197 | let balance = 0; 198 | 199 | for (var i = 0; i < this.Islands.length; i++) { 200 | for (var k = 0; k < this.Islands[i].Factories.length; k++) { 201 | if (this.Islands[i].Factories[k].ID === factoryID) { 202 | balance += (this.Islands[i].Factories[k].Productivity * this.Islands[i].Factories[k].TradeBalance / 100); 203 | } 204 | } 205 | } 206 | 207 | return balance; 208 | } 209 | 210 | IsTradeBalanced(factoryID: number): boolean { 211 | return Math.abs(this.GetTradeBalance(factoryID)) < 0.02; 212 | } 213 | } 214 | 215 | export class SaveData { 216 | Islands: IslandSaveInfo[] 217 | } -------------------------------------------------------------------------------- /anno1800assistant/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 |
21 | 22 | 23 | 24 | 25 |
26 | Residences
27 | Population 28 |
29 | 30 | {{ popLevel.Name }} 31 | 32 | 33 | 34 | 35 |
36 | 37 | 38 | 39 | 40 | 41 | {{ PromotionCount() }} 42 | 43 | 44 | 45 | 46 | 47 | {{ PromotionCount() }} 48 | 49 |
50 | 51 | 52 | {{ popLevel.GetPopulation(island.Factories) }} 53 |
54 |
55 |
56 |
57 |
58 | 59 | 60 |
61 | 62 |
63 | 64 | Enable 65 | Productivity 66 | Required 67 | Built 68 | Trade 69 |
70 |
72 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | {{ factory.GetRequiredCount(island) }} 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 |
116 |
117 |
118 |
119 | 120 | 121 | 122 | Add Island 123 | 124 |
125 | 126 | 127 | 128 | Save 129 | 130 | 131 | 132 | 133 | Load 134 | 135 | 136 | 137 | 138 | Reset 139 | 140 | 141 | -------------------------------------------------------------------------------- /anno1800assistant/src/app/models/island.ts: -------------------------------------------------------------------------------- 1 | import { Region, RegionService } from '../data/region'; 2 | import { PopulationLevel, PopulationService, PopulationLevelSaveInfo } from '../data/populations'; 3 | import { Factory, FactorySaveInfo, Factories } from '../data/factories'; 4 | 5 | export class Island { 6 | Name: string 7 | Region: Region; 8 | IsMinimized: boolean 9 | PopulationService: PopulationService; 10 | PopulationLevels: PopulationLevel[]; 11 | Factories: Factory[]; 12 | FactoryGroups: Factory[][]; 13 | 14 | constructor(name: string, populationService: PopulationService, saveInfo?: IslandSaveInfo) { 15 | this.PopulationService = populationService; 16 | this.Name = name; 17 | this.Region = 'OldWorld'; 18 | 19 | this.RegionChanged(saveInfo); 20 | } 21 | 22 | RegionChanged(saveInfo?: IslandSaveInfo) { 23 | this.Factories = []; 24 | this.FactoryGroups = []; 25 | 26 | if (saveInfo) { 27 | if (!saveInfo.Region) { 28 | // This save is from before the DLC update! We'll attempt to guess at its type based on the old convention. 29 | const oldWorldCount = saveInfo.PopulationLevels[0].HouseCount + saveInfo.PopulationLevels[1].HouseCount + saveInfo.PopulationLevels[2].HouseCount 30 | + saveInfo.PopulationLevels[3].HouseCount + saveInfo.PopulationLevels[4].HouseCount; 31 | const newWorldCount = saveInfo.PopulationLevels[5].HouseCount + saveInfo.PopulationLevels[6].HouseCount; 32 | 33 | if (newWorldCount > oldWorldCount) { 34 | saveInfo.Region = 'NewWorld'; 35 | saveInfo.PopulationLevels = [ 36 | saveInfo.PopulationLevels[5], 37 | saveInfo.PopulationLevels[6], 38 | ]; 39 | } 40 | else { 41 | saveInfo.Region = 'OldWorld'; 42 | saveInfo.PopulationLevels = [ 43 | saveInfo.PopulationLevels[0], 44 | saveInfo.PopulationLevels[1], 45 | saveInfo.PopulationLevels[2], 46 | saveInfo.PopulationLevels[3], 47 | saveInfo.PopulationLevels[4], 48 | ]; 49 | } 50 | 51 | // For the grain silo update, grain farm was updated from factory ID 1010262 to 269851. 52 | for (let i = 0; i < saveInfo.Factories.length; i++) { 53 | if (saveInfo.Factories[i].FactoryID === 1010262) { 54 | saveInfo.Factories[i].FactoryID = 269851; 55 | } 56 | } 57 | } 58 | 59 | this.Region = saveInfo.Region || this.Region; 60 | this.IsMinimized = saveInfo.IsMinimized; 61 | this.PopulationLevels = this.PopulationService.getNewPopulationForRegion(this.Region); 62 | 63 | for (var i = 0; i < saveInfo.PopulationLevels.length; i++) { 64 | this.PopulationLevels[i].HouseCount = saveInfo.PopulationLevels[i].HouseCount; 65 | this.PopulationLevels[i].ShowUnused = saveInfo.PopulationLevels[i].ShowUnused; 66 | } 67 | } 68 | else { 69 | this.PopulationLevels = this.PopulationService.getNewPopulationForRegion(this.Region); 70 | } 71 | 72 | RegionService.regionFactories[this.Region].forEach(regionFactoryID => { 73 | this.AddFactoryChain(regionFactoryID, saveInfo); 74 | }) 75 | 76 | if (this.Region === 'OldWorld') { 77 | // Manually add grain silos and tractor barns to old world 78 | this.AddFactoryChain(101, saveInfo); 79 | this.AddFactoryChain(103, saveInfo); 80 | } 81 | else if (this.Region === 'NewWorld') { 82 | // Manually add corn silos and tractor barns to new world 83 | this.AddFactoryChain(102, saveInfo); 84 | this.AddFactoryChain(103, saveInfo); 85 | } 86 | else if (this.Region === 'Enbesa') { 87 | // Manually add teff silos and tractor barns to Enbesa 88 | this.AddFactoryChain(104, saveInfo); 89 | this.AddFactoryChain(103, saveInfo); 90 | } 91 | } 92 | 93 | 94 | AddFactoryChain(factoryID: number, saveInfo: IslandSaveInfo) { 95 | let factory = new Factory(new Factories().AllFactories.filter(f => f.ID === factoryID)[0]); 96 | 97 | if (saveInfo) { 98 | let savedFactoryInfos = saveInfo.Factories.filter(f => f.FactoryID === factory.ID); 99 | let savedFactoryInfo = savedFactoryInfos[0]; 100 | 101 | if (savedFactoryInfos.length > 1) { 102 | let matchingSavedFactoryInfo = savedFactoryInfos.filter(f => f.ParentFactoryID === factory.ParentFactoryOrThisRecursive.ID)[0]; 103 | 104 | // Doing a check here to make sure we matched to be backwards-compatible with old saves. This can eventually be removed. 105 | if (matchingSavedFactoryInfo) { 106 | savedFactoryInfo = matchingSavedFactoryInfo; 107 | } 108 | } 109 | 110 | if (savedFactoryInfo) { 111 | factory.Enabled = savedFactoryInfo.Enabled; 112 | factory.BuiltCount = savedFactoryInfo.BuiltCount; 113 | factory.Productivity = savedFactoryInfo.Productivity; 114 | factory.TradeBalance = savedFactoryInfo.TradeBalance; 115 | factory.UseSilo = savedFactoryInfo.UseSilo; 116 | factory.UseTractorBarn = savedFactoryInfo.UseTractorBarn; 117 | } 118 | } 119 | 120 | this.Factories.push(factory); 121 | let group = [factory]; 122 | this.ProcessChildFactories(factory, group, saveInfo); 123 | this.FactoryGroups.push(group); 124 | } 125 | 126 | 127 | ProcessChildFactories(parentFactory: Factory, group: Factory[], saveInfo: IslandSaveInfo) { 128 | for (var i = 0; i < parentFactory.Inputs.length; i++) { 129 | let matchedRawFactories = new Factories().AllFactories.filter(f => 130 | f.Outputs.filter(output => output.ProductID === parentFactory.Inputs[i].ProductID).length > 0 131 | ); 132 | 133 | // Logic for matching factories: 134 | // First, we want to match on 'new world' and 'old world' flags to account for new world vs old world variants of factories 135 | // Then, if we have multiple results, we want to use the one that has a valid cycle time (to account for odd entries like fuel depots that have multiple entries in the game data) 136 | if (matchedRawFactories.length > 1) { 137 | matchedRawFactories.sort((a, b) => { 138 | if (a.IsNewWorld === parentFactory.IsNewWorld && b.IsNewWorld !== parentFactory.IsNewWorld) { 139 | return -1; 140 | } 141 | if (a.IsNewWorld !== parentFactory.IsNewWorld && b.IsNewWorld === parentFactory.IsNewWorld) { 142 | return 1; 143 | } 144 | if (a.CycleTime && !b.CycleTime) { 145 | return -1; 146 | } 147 | if (!a.CycleTime && b.CycleTime) { 148 | return 1; 149 | } 150 | 151 | return 0; 152 | }); 153 | } 154 | 155 | const matchedRawFactory = matchedRawFactories[0]; 156 | 157 | if (matchedRawFactory) { 158 | let newFactory = new Factory(matchedRawFactory); 159 | 160 | if (saveInfo) { 161 | let savedFactoryInfos = saveInfo.Factories.filter(f => f.FactoryID === newFactory.ID); 162 | let savedFactoryInfo = savedFactoryInfos[0]; 163 | 164 | if (savedFactoryInfos.length > 1) { 165 | let matchingSavedFactoryInfo = savedFactoryInfos.filter(f => f.ParentFactoryID === parentFactory.ParentFactoryOrThisRecursive.ID)[0]; 166 | 167 | // Doing a check here to make sure we matched to be backwards-compatible with old saves. This can eventually be removed. 168 | if (matchingSavedFactoryInfo) { 169 | savedFactoryInfo = matchingSavedFactoryInfo; 170 | } 171 | } 172 | 173 | if (savedFactoryInfo) { 174 | newFactory.Enabled = savedFactoryInfo.Enabled; 175 | newFactory.BuiltCount = savedFactoryInfo.BuiltCount; 176 | newFactory.Productivity = savedFactoryInfo.Productivity; 177 | newFactory.TradeBalance = savedFactoryInfo.TradeBalance; 178 | newFactory.UseSilo = savedFactoryInfo.UseSilo; 179 | newFactory.UseTractorBarn = savedFactoryInfo.UseTractorBarn; 180 | } 181 | } 182 | 183 | parentFactory.ChildFactories.push(newFactory); 184 | newFactory.ParentFactory = parentFactory; 185 | this.Factories.push(newFactory); 186 | group.push(newFactory); 187 | this.ProcessChildFactories(newFactory, group, saveInfo); 188 | } 189 | } 190 | } 191 | 192 | EnabledFactoryGroups() { 193 | return this.FactoryGroups.filter(f => f[0].IsInUse(this)); 194 | } 195 | 196 | GetColumnLayouts() { 197 | let columnCount = 2; 198 | let columns = []; 199 | for (var i = 0; i < columnCount; i++) { 200 | columns[i] = []; 201 | } 202 | 203 | let enabledGroups = this.EnabledFactoryGroups(); 204 | for (var i = 0; i < enabledGroups.length; i++) { 205 | let smallest = columns[0]; 206 | 207 | for (var k = 0; k < columnCount; k++) { 208 | if (columns[k].length < smallest.length) { 209 | smallest = columns[k]; 210 | } 211 | } 212 | 213 | for (var k = 0; k < enabledGroups[i].length; k++) { 214 | smallest.push(enabledGroups[i][k]); 215 | } 216 | } 217 | 218 | return columns; 219 | } 220 | 221 | GetTotalHouseCount(): number { 222 | let result = 0; 223 | 224 | if (!this.PopulationLevels) { 225 | return; 226 | } 227 | 228 | for (let i = 0; i < this.PopulationLevels.length; i++) { 229 | result += this.PopulationLevels[i].HouseCount; 230 | } 231 | 232 | return result; 233 | } 234 | 235 | ToggleMinimized() { 236 | this.IsMinimized = !this.IsMinimized; 237 | } 238 | } 239 | 240 | export class IslandSaveInfo { 241 | Name: string 242 | Region: Region 243 | IsMinimized: boolean 244 | PopulationLevels: PopulationLevelSaveInfo[] 245 | Factories: FactorySaveInfo[] 246 | } -------------------------------------------------------------------------------- /anno1800assistant/src/app/data/populations.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from "./factories"; 2 | import { Injectable } from '@angular/core'; 3 | import { Region } from './region'; 4 | 5 | @Injectable() 6 | export class PopulationService { 7 | private rawData: PopulationLevelRaw[] = [ 8 | {"Name":"Scholars","Inputs":[{"ProductID":1010353,"Amount":0.0,"SupplyWeight":12,"MoneyValue":0},{"ProductID":1010217,"Amount":0.001,"SupplyWeight":19,"MoneyValue":0},{"ProductID":114428,"Amount":0.002,"SupplyWeight":0,"MoneyValue":0},{"ProductID":1010257,"Amount":0.0008,"SupplyWeight":0,"MoneyValue":0},{"ProductID":114430,"Amount":0.00275,"SupplyWeight":23,"MoneyValue":0},{"ProductID":120037,"Amount":0.0008,"SupplyWeight":0,"MoneyValue":0},{"ProductID":114390,"Amount":0.00175,"SupplyWeight":0,"MoneyValue":0},{"ProductID":1010354,"Amount":0.0,"SupplyWeight":12,"MoneyValue":0},{"ProductID":114410,"Amount":0.0015,"SupplyWeight":22,"MoneyValue":0},{"ProductID":114404,"Amount":0.00125,"SupplyWeight":0,"MoneyValue":0},{"ProductID":114414,"Amount":0.00075,"SupplyWeight":0,"MoneyValue":0},{"ProductID":114431,"Amount":0.0015,"SupplyWeight":20,"MoneyValue":0},{"ProductID":114425,"Amount":0.0,"SupplyWeight":12,"MoneyValue":0},{"ProductID":1010248,"Amount":0.0007,"SupplyWeight":0,"MoneyValue":0}]}, 9 | {"Name":"Elders","Inputs":[{"ProductID":120020,"Amount":0.0,"SupplyWeight":3,"MoneyValue":0},{"ProductID":114371,"Amount":0.001,"SupplyWeight":3,"MoneyValue":12},{"ProductID":114401,"Amount":0.0005,"SupplyWeight":2,"MoneyValue":12},{"ProductID":114359,"Amount":0.0007,"SupplyWeight":2,"MoneyValue":15},{"ProductID":114390,"Amount":0.0004,"SupplyWeight":0,"MoneyValue":20},{"ProductID":118724,"Amount":0.0004,"SupplyWeight":2,"MoneyValue":10},{"ProductID":114404,"Amount":0.00035,"SupplyWeight":0,"MoneyValue":25},{"ProductID":114410,"Amount":0.00037,"SupplyWeight":3,"MoneyValue":15},{"ProductID":114414,"Amount":0.00015,"SupplyWeight":0,"MoneyValue":24},{"ProductID":117698,"Amount":0.00018,"SupplyWeight":2,"MoneyValue":16},{"ProductID":120030,"Amount":0.00012,"SupplyWeight":0,"MoneyValue":34},{"ProductID":117699,"Amount":0.0003,"SupplyWeight":3,"MoneyValue":25}]}, 10 | {"Name":"Shepherds","Inputs":[{"ProductID":120020,"Amount":0.0,"SupplyWeight":3,"MoneyValue":0},{"ProductID":114371,"Amount":0.0005,"SupplyWeight":3,"MoneyValue":12},{"ProductID":114401,"Amount":0.00025,"SupplyWeight":2,"MoneyValue":12},{"ProductID":114359,"Amount":0.00035,"SupplyWeight":2,"MoneyValue":15},{"ProductID":114390,"Amount":0.0002,"SupplyWeight":0,"MoneyValue":20}]}, 11 | {"Name":"Technicians","Inputs":[{"ProductID":114890,"Amount":0.0,"SupplyWeight":4,"MoneyValue":20},{"ProductID":112705,"Amount":0.0004,"SupplyWeight":3,"MoneyValue":40},{"ProductID":112702,"Amount":0.0002,"SupplyWeight":3,"MoneyValue":70},{"ProductID":112693,"Amount":0.0,"SupplyWeight":3,"MoneyValue":30},{"ProductID":1010217,"Amount":0.0002,"SupplyWeight":3,"MoneyValue":50},{"ProductID":112703,"Amount":0.0003,"SupplyWeight":4,"MoneyValue":80},{"ProductID":112701,"Amount":0.0003,"SupplyWeight":0,"MoneyValue":50},{"ProductID":1010216,"Amount":0.0005,"SupplyWeight":0,"MoneyValue":35},{"ProductID":112700,"Amount":0.0004,"SupplyWeight":0,"MoneyValue":45},{"ProductID":120032,"Amount":0.0004,"SupplyWeight":0,"MoneyValue":60}]}, 12 | {"Name":"Explorers","Inputs":[{"ProductID":114890,"Amount":0.0,"SupplyWeight":4,"MoneyValue":20},{"ProductID":112705,"Amount":0.0002,"SupplyWeight":3,"MoneyValue":40},{"ProductID":112702,"Amount":0.0001,"SupplyWeight":3,"MoneyValue":70},{"ProductID":112701,"Amount":0.00015,"SupplyWeight":0,"MoneyValue":50},{"ProductID":1010216,"Amount":0.00025,"SupplyWeight":0,"MoneyValue":35}]}, 13 | {"Name":"Obreros","Inputs":[{"ProductID":120020,"Amount":0.0,"SupplyWeight":5,"MoneyValue":0},{"ProductID":120033,"Amount":0.000952381,"SupplyWeight":3,"MoneyValue":25},{"ProductID":1010257,"Amount":0.000476191,"SupplyWeight":0,"MoneyValue":25},{"ProductID":120043,"Amount":0.000833333,"SupplyWeight":2,"MoneyValue":25},{"ProductID":120035,"Amount":0.00047619,"SupplyWeight":4,"MoneyValue":10},{"ProductID":120032,"Amount":0.000196079,"SupplyWeight":2,"MoneyValue":10},{"ProductID":120037,"Amount":0.000444444,"SupplyWeight":2,"MoneyValue":10},{"ProductID":1010214,"Amount":0.000444444,"SupplyWeight":0,"MoneyValue":30},{"ProductID":1010259,"Amount":0.000185185,"SupplyWeight":0,"MoneyValue":35},{"ProductID":1010206,"Amount":0.000416667,"SupplyWeight":2,"MoneyValue":25}]}, 14 | {"Name":"Jornaleros","Inputs":[{"ProductID":120020,"Amount":0.0,"SupplyWeight":5,"MoneyValue":0},{"ProductID":120033,"Amount":0.00047619,"SupplyWeight":3,"MoneyValue":25},{"ProductID":1010257,"Amount":0.000238095,"SupplyWeight":0,"MoneyValue":25},{"ProductID":120043,"Amount":0.000416667,"SupplyWeight":2,"MoneyValue":25}]}, 15 | {"Name":"Investors","Inputs":[{"ProductID":120030,"Amount":0.000296296,"SupplyWeight":16,"MoneyValue":100},{"ProductID":1010245,"Amount":0.000833333,"SupplyWeight":0,"MoneyValue":140},{"ProductID":120032,"Amount":0.001568627,"SupplyWeight":8,"MoneyValue":80},{"ProductID":1010246,"Amount":0.000261438,"SupplyWeight":0,"MoneyValue":180},{"ProductID":1010354,"Amount":0.0,"SupplyWeight":8,"MoneyValue":0},{"ProductID":1010208,"Amount":0.000416667,"SupplyWeight":8,"MoneyValue":140},{"ProductID":120016,"Amount":0.000392,"SupplyWeight":2,"MoneyValue":50},{"ProductID":1010259,"Amount":0.00037037,"SupplyWeight":2,"MoneyValue":50},{"ProductID":1010258,"Amount":0.000888889,"SupplyWeight":2,"MoneyValue":50},{"ProductID":1010250,"Amount":0.000350877,"SupplyWeight":0,"MoneyValue":250},{"ProductID":1010248,"Amount":0.0000877,"SupplyWeight":0,"MoneyValue":150},{"ProductID":1010225,"Amount":0.000111111,"SupplyWeight":4,"MoneyValue":300}]}, 16 | {"Name":"Engineers","Inputs":[{"ProductID":1010217,"Amount":0.00034188,"SupplyWeight":12,"MoneyValue":40},{"ProductID":1010206,"Amount":0.000952381,"SupplyWeight":6,"MoneyValue":80},{"ProductID":1010257,"Amount":0.001904762,"SupplyWeight":0,"MoneyValue":100},{"ProductID":1010247,"Amount":0.000888889,"SupplyWeight":6,"MoneyValue":120},{"ProductID":1010353,"Amount":0.0,"SupplyWeight":6,"MoneyValue":0},{"ProductID":120030,"Amount":0.000148148,"SupplyWeight":4,"MoneyValue":50},{"ProductID":1010245,"Amount":0.000416667,"SupplyWeight":0,"MoneyValue":70},{"ProductID":120032,"Amount":0.000784314,"SupplyWeight":2,"MoneyValue":40},{"ProductID":1010246,"Amount":0.000130719,"SupplyWeight":0,"MoneyValue":90},{"ProductID":1010354,"Amount":0.0,"SupplyWeight":2,"MoneyValue":0},{"ProductID":1010208,"Amount":0.000208333,"SupplyWeight":2,"MoneyValue":70}]}, 17 | {"Name":"Artisans","Inputs":[{"ProductID":1010238,"Amount":0.000666667,"SupplyWeight":6,"MoneyValue":40},{"ProductID":1010213,"Amount":0.000606061,"SupplyWeight":6,"MoneyValue":40},{"ProductID":1010203,"Amount":0.000277778,"SupplyWeight":4,"MoneyValue":40},{"ProductID":1010214,"Amount":0.000512821,"SupplyWeight":0,"MoneyValue":100},{"ProductID":1010351,"Amount":0.0,"SupplyWeight":4,"MoneyValue":0},{"ProductID":1010217,"Amount":0.00017094,"SupplyWeight":4,"MoneyValue":20},{"ProductID":1010206,"Amount":0.00047619,"SupplyWeight":2,"MoneyValue":40},{"ProductID":1010257,"Amount":0.000952381,"SupplyWeight":0,"MoneyValue":50},{"ProductID":1010247,"Amount":0.000444444,"SupplyWeight":2,"MoneyValue":60},{"ProductID":1010353,"Amount":0.0,"SupplyWeight":2,"MoneyValue":0}]}, 18 | {"Name":"Workers","Inputs":[{"ProductID":120020,"Amount":0.0,"SupplyWeight":5,"MoneyValue":0},{"ProductID":1010200,"Amount":0.0008333334,"SupplyWeight":3,"MoneyValue":10},{"ProductID":1010216,"Amount":0.001111112,"SupplyWeight":0,"MoneyValue":30},{"ProductID":1010237,"Amount":0.001025642,"SupplyWeight":2,"MoneyValue":30},{"ProductID":1010238,"Amount":0.000333334,"SupplyWeight":3,"MoneyValue":20},{"ProductID":1010213,"Amount":0.00030303,"SupplyWeight":3,"MoneyValue":20},{"ProductID":1010203,"Amount":0.000138889,"SupplyWeight":2,"MoneyValue":20},{"ProductID":1010214,"Amount":0.00025641,"SupplyWeight":0,"MoneyValue":50},{"ProductID":1010351,"Amount":0.0,"SupplyWeight":2,"MoneyValue":0}]}, 19 | {"Name":"Farmers","Inputs":[{"ProductID":120020,"Amount":0.0,"SupplyWeight":5,"MoneyValue":0},{"ProductID":1010200,"Amount":0.0004166667,"SupplyWeight":3,"MoneyValue":10},{"ProductID":1010216,"Amount":0.000555556,"SupplyWeight":0,"MoneyValue":30},{"ProductID":1010237,"Amount":0.000512821,"SupplyWeight":2,"MoneyValue":30}]} 20 | ]; 21 | 22 | getNewPopulationForRegion(region: Region): PopulationLevel[] { 23 | return this.createMethods[region](); 24 | } 25 | 26 | private createMethods: { [key in Region]: () => PopulationLevel[] } = { 27 | 'OldWorld': () => { 28 | const result: PopulationLevel[] = [ 29 | new PopulationLevel(this.rawData.filter(x => x.Name === 'Farmers')[0]), 30 | new PopulationLevel(this.rawData.filter(x => x.Name === 'Workers')[0]), 31 | new PopulationLevel(this.rawData.filter(x => x.Name === 'Artisans')[0]), 32 | new PopulationLevel(this.rawData.filter(x => x.Name === 'Engineers')[0]), 33 | new PopulationLevel(this.rawData.filter(x => x.Name === 'Investors')[0]), 34 | new PopulationLevel(this.rawData.filter(x => x.Name === 'Scholars')[0]), 35 | ]; 36 | 37 | result[0].PromotionTarget = result[1]; 38 | result[1].PromotionTarget = result[2]; 39 | result[2].PromotionTarget = result[3]; 40 | result[3].PromotionTarget = result[4]; 41 | result[4].PromotionTarget = result[5]; 42 | 43 | return result; 44 | }, 'NewWorld': () => { 45 | const result: PopulationLevel[] = [ 46 | new PopulationLevel(this.rawData.filter(x => x.Name === 'Jornaleros')[0]), 47 | new PopulationLevel(this.rawData.filter(x => x.Name === 'Obreros')[0]), 48 | ]; 49 | 50 | result[0].PromotionTarget = result[1]; 51 | 52 | return result; 53 | }, 'Arctic': () => { 54 | const result: PopulationLevel[] = [ 55 | new PopulationLevel(this.rawData.filter(x => x.Name === 'Explorers')[0]), 56 | new PopulationLevel(this.rawData.filter(x => x.Name === 'Technicians')[0]), 57 | ]; 58 | 59 | result[0].PromotionTarget = result[1]; 60 | 61 | return result; 62 | }, 'Enbesa': () => { 63 | const result: PopulationLevel[] = [ 64 | new PopulationLevel(this.rawData.filter(x => x.Name === 'Shepherds')[0]), 65 | new PopulationLevel(this.rawData.filter(x => x.Name === 'Elders')[0]), 66 | ]; 67 | 68 | result[0].PromotionTarget = result[1]; 69 | 70 | return result; 71 | } 72 | } 73 | } 74 | 75 | class PopulationLevelRaw { 76 | Name: string 77 | Inputs: PopulationInput[] 78 | } 79 | 80 | 81 | 82 | export class PopulationLevel extends PopulationLevelRaw { 83 | constructor(raw: PopulationLevelRaw) { 84 | super(); 85 | this.Name = raw.Name; 86 | this.Inputs = raw.Inputs; 87 | this.UsesMarketplace = ['Farmers', 'Workers', 'Jornaleros', 'Obreros'].includes(this.Name); 88 | } 89 | 90 | HouseCount: number = 0 91 | PromotionTarget: PopulationLevel 92 | ShowUnused: boolean 93 | UsesMarketplace: boolean 94 | 95 | GetPopulation(factories: Factory[]): number { 96 | let supplyWeight = 0; 97 | if (this.UsesMarketplace) { 98 | supplyWeight += 5; 99 | } 100 | 101 | let enabledOutputProductIDs = {}; 102 | for (var i = 0; i < factories.length; i++) { 103 | if (factories[i].Enabled) { 104 | enabledOutputProductIDs[factories[i].Outputs[0].ProductID] = true; 105 | } 106 | } 107 | 108 | for (var i = 0; i < this.Inputs.length; i++) { 109 | let enabled = enabledOutputProductIDs[this.Inputs[i].ProductID]; 110 | if (enabled) { 111 | supplyWeight += this.Inputs[i].SupplyWeight; 112 | } 113 | } 114 | 115 | return supplyWeight * this.HouseCount; 116 | } 117 | 118 | Promote(promotionCount: number): void { 119 | if (!this.PromotionTarget) { 120 | return; 121 | } 122 | 123 | if (this.HouseCount - promotionCount < 0){ 124 | promotionCount = this.HouseCount; 125 | } 126 | 127 | this.HouseCount -= promotionCount; 128 | this.PromotionTarget.HouseCount += promotionCount; 129 | } 130 | 131 | ToggleShowUnused(): void { 132 | this.ShowUnused = !this.ShowUnused; 133 | } 134 | 135 | AddHouses(houseCount: number): void { 136 | this.HouseCount += houseCount; 137 | } 138 | 139 | GetProductRequirement(productID: number): number { 140 | let input = this.Inputs.filter(i => i.ProductID === productID)[0]; 141 | if (input) { 142 | return input.Amount * this.HouseCount; 143 | } 144 | 145 | return 0; 146 | } 147 | 148 | Save(): PopulationLevelSaveInfo { 149 | return { 150 | HouseCount: this.HouseCount, 151 | ShowUnused: this.ShowUnused 152 | }; 153 | } 154 | } 155 | 156 | export class PopulationInput { 157 | ProductID: number 158 | Amount: number 159 | SupplyWeight: number 160 | MoneyValue: number 161 | } 162 | 163 | export class PopulationLevelSaveInfo { 164 | HouseCount: number 165 | ShowUnused: boolean 166 | } 167 | -------------------------------------------------------------------------------- /AnnoXMLParser/AnnoXMLParser/Program.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml.Linq; 9 | 10 | namespace AnnoXMLParser { 11 | class Program { 12 | static void Main(string[] args) { 13 | string assetsXmlPath = @"C:\Path\assets.xml"; 14 | string outputFolder = @"C:\Path\"; 15 | 16 | 17 | XDocument doc = XDocument.Load(assetsXmlPath); 18 | Stack queue = new Stack(doc.Elements()); 19 | List factoryXMLs = new List(); 20 | List productXMLs = new List(); 21 | List populationXMLs = new List(); 22 | List newWorldVariants = new List(); 23 | 24 | while (queue.Any()) { 25 | var element = queue.Pop(); 26 | 27 | if (element.Name == "Template" && ( 28 | element.Value == "FactoryBuilding7" 29 | || element.Value == "FactoryBuilding7_Arctic" 30 | || element.Value == "FarmBuilding" 31 | || element.Value == "FarmBuilding_Arctic" 32 | || element.Value == "HeavyFactoryBuilding" 33 | || element.Value == "PublicServiceBuilding" 34 | || element.Value == "SlotFactoryBuilding7" 35 | || element.Value == "FreeAreaBuilding" 36 | || element.Value == "FreeAreaBuilding_Arctic" 37 | )) { 38 | factoryXMLs.Add(element.Parent); 39 | } 40 | 41 | if (element.Name == "Template" && element.Value == "Product") { 42 | productXMLs.Add(element.Parent); 43 | } 44 | 45 | if (element.Name == "Template" && element.Value == "PopulationLevel7") { 46 | populationXMLs.Add(element.Parent); 47 | } 48 | 49 | if (element.Name == "BaseAssetGUID") { 50 | newWorldVariants.Add(element.Parent); 51 | } 52 | 53 | foreach (var add in element.Elements()) { 54 | queue.Push(add); 55 | } 56 | } 57 | 58 | var products = new List(); 59 | foreach (var productXML in productXMLs) { 60 | int id = int.Parse(productXML.Elements() 61 | .Single(x => x.Name == "Values").Elements() 62 | .Single(x => x.Name == "Standard").Elements() 63 | .Single(x => x.Name == "GUID").Value); 64 | string name = productXML.Elements() 65 | .Single(x => x.Name == "Values").Elements() 66 | .Single(x => x.Name == "Text").Elements() 67 | .Single(x => x.Name == "LocaText").Elements() 68 | .Single(x => x.Name == "English").Elements() 69 | .Single(x => x.Name == "Text").Value; 70 | 71 | products.Add(new Product { ID = id, Name = name }); 72 | } 73 | 74 | var factories = new List(); 75 | foreach (var factoryXML in factoryXMLs) { 76 | bool isPublicService = factoryXML.Elements().Single(x => x.Name == "Template").Value == "PublicServiceBuilding"; 77 | 78 | int id = int.Parse(factoryXML.Elements() 79 | .Single(x => x.Name == "Values").Elements() 80 | .Single(x => x.Name == "Standard").Elements() 81 | .Single(x => x.Name == "GUID").Value); 82 | 83 | int cycleTime = int.Parse(factoryXML.Elements() 84 | ?.SingleOrDefault(x => x.Name == "Values")?.Elements() 85 | ?.SingleOrDefault(x => x.Name == "FactoryBase")?.Elements() 86 | ?.SingleOrDefault(x => x.Name == "CycleTime")?.Value ?? "0"); 87 | 88 | string name = factoryXML.Elements() 89 | .Single(x => x.Name == "Values").Elements() 90 | .Single(x => x.Name == "Text").Elements() 91 | .Single(x => x.Name == "LocaText").Elements() 92 | .Single(x => x.Name == "English").Elements() 93 | .Single(x => x.Name == "Text").Value; 94 | 95 | List inputs = new List(); 96 | 97 | if (!isPublicService) { 98 | var inputXMLs = factoryXML.Elements() 99 | ?.SingleOrDefault(x => x.Name == "Values")?.Elements() 100 | ?.SingleOrDefault(x => x.Name == "FactoryBase")?.Elements() 101 | ?.SingleOrDefault(x => x.Name == "FactoryInputs")?.Elements(); 102 | 103 | foreach (var inputXML in inputXMLs ?? Enumerable.Empty()) { 104 | int productID = int.Parse(inputXML.Elements() 105 | .Single(x => x.Name == "Product") 106 | .Value); 107 | 108 | int amount = int.Parse(inputXML.Elements() 109 | .Single(x => x.Name == "Amount") 110 | .Value); 111 | 112 | inputs.Add(new FactoryIngredient() { ProductID = productID, Amount = amount }); 113 | } 114 | } 115 | 116 | List outputs = new List(); 117 | if (isPublicService) { 118 | var outputXMLs = factoryXML.Elements() 119 | ?.SingleOrDefault(x => x.Name == "Values")?.Elements() 120 | ?.SingleOrDefault(x => x.Name == "PublicService")?.Elements() 121 | ?.SingleOrDefault(x => x.Name == "PublicServiceOutputs")?.Elements(); 122 | 123 | foreach (var outputXML in outputXMLs ?? Enumerable.Empty()) { 124 | int productID = int.Parse(outputXML.Elements() 125 | .Single(x => x.Name == "Product") 126 | .Value); 127 | 128 | outputs.Add(new FactoryIngredient() { ProductID = productID, Amount = 0 }); 129 | } 130 | } 131 | else { 132 | var outputXMLs = factoryXML.Elements() 133 | ?.SingleOrDefault(x => x.Name == "Values")?.Elements() 134 | ?.SingleOrDefault(x => x.Name == "FactoryBase")?.Elements() 135 | ?.SingleOrDefault(x => x.Name == "FactoryOutputs")?.Elements(); 136 | 137 | foreach (var outputXML in outputXMLs ?? Enumerable.Empty()) { 138 | int productID = int.Parse(outputXML.Elements() 139 | .Single(x => x.Name == "Product") 140 | .Value); 141 | 142 | int amount = int.Parse(outputXML.Elements() 143 | .Single(x => x.Name == "Amount") 144 | .Value); 145 | 146 | outputs.Add(new FactoryIngredient() { ProductID = productID, Amount = amount }); 147 | } 148 | } 149 | 150 | var factory = new Factory() { ID = id, Name = name, CycleTime = cycleTime, Inputs = inputs.ToArray(), Outputs = outputs.ToArray() }; 151 | 152 | string associatedRegions = factoryXML.Elements() 153 | ?.SingleOrDefault(x => x.Name == "Values")?.Elements() 154 | ?.SingleOrDefault(x => x.Name == "Building")?.Elements() 155 | ?.SingleOrDefault(x => x.Name == "AssociatedRegions")?.Value ?? ""; 156 | 157 | if (associatedRegions.IndexOf("Moderate", StringComparison.OrdinalIgnoreCase) >= 0) { 158 | factory.IsOldWorld = true; 159 | } 160 | 161 | if (associatedRegions.IndexOf("Colony01", StringComparison.OrdinalIgnoreCase) >= 0) { 162 | factory.IsNewWorld = true; 163 | } 164 | 165 | string motorizable = factoryXML.Elements() 166 | ?.SingleOrDefault(x => x.Name == "Values")?.Elements() 167 | ?.SingleOrDefault(x => x.Name == "Motorizable")?.Elements() 168 | ?.SingleOrDefault(x => x.Name == "MotorizableType")?.Value ?? ""; 169 | 170 | if (string.Equals(motorizable, "Silo")) { 171 | factory.CanHaveSilo = true; 172 | } 173 | else if (string.Equals(motorizable, "Tractor")) { 174 | factory.CanHaveTractorBarn = true; 175 | } 176 | 177 | factories.Add(factory); 178 | } 179 | 180 | var populationLevels = new List(); 181 | foreach (var populationXML in populationXMLs) { 182 | string name = populationXML.Elements() 183 | .Single(x => x.Name == "Values").Elements() 184 | .Single(x => x.Name == "Text").Elements() 185 | .Single(x => x.Name == "LocaText").Elements() 186 | .Single(x => x.Name == "English").Elements() 187 | .Single(x => x.Name == "Text").Value; 188 | 189 | var inputXMLs = populationXML.Elements() 190 | .Single(x => x.Name == "Values").Elements() 191 | .Single(x => x.Name == "PopulationLevel7").Elements() 192 | .Single(x => x.Name == "PopulationInputs").Elements(); 193 | 194 | List inputs = new List(); 195 | foreach (var inputXML in inputXMLs) { 196 | string sProductID = inputXML.Elements().SingleOrDefault(x => x.Name == "Product")?.Value; 197 | string sSupplyWeight = inputXML.Elements().SingleOrDefault(x => x.Name == "SupplyWeight")?.Value; 198 | string sAmount = inputXML.Elements().SingleOrDefault(x => x.Name == "Amount")?.Value; 199 | string sMoneyValue = inputXML.Elements().SingleOrDefault(x => x.Name == "MoneyValue")?.Value; 200 | 201 | if (!string.IsNullOrEmpty(sProductID) && (!string.IsNullOrEmpty(sAmount) || !string.IsNullOrEmpty(sSupplyWeight))) { 202 | PopulationInput input = new PopulationInput() { ProductID = int.Parse(sProductID) }; 203 | inputs.Add(input); 204 | 205 | if (!string.IsNullOrEmpty(sAmount)) { 206 | decimal amount = 0m; 207 | if (!decimal.TryParse(sAmount, out amount)) { 208 | amount = decimal.Parse(sAmount, System.Globalization.NumberStyles.Float); 209 | } 210 | input.Amount = amount; 211 | } 212 | 213 | if (!string.IsNullOrEmpty(sSupplyWeight)) { 214 | input.SupplyWeight = int.Parse(sSupplyWeight); 215 | } 216 | 217 | if (!string.IsNullOrEmpty(sMoneyValue)) { 218 | input.MoneyValue = int.Parse(sMoneyValue); 219 | } 220 | } 221 | } 222 | 223 | populationLevels.Add(new PopulationLevel() { Name = name, Inputs = inputs.ToArray() }); 224 | } 225 | 226 | foreach (var variantXML in newWorldVariants) { 227 | string baseIDString = variantXML.Elements() 228 | .Single(x => x.Name == "BaseAssetGUID").Value; 229 | 230 | if (!int.TryParse(baseIDString, out int baseID)) { 231 | continue; 232 | } 233 | 234 | var baseFactory = factories.SingleOrDefault(x => x.ID == baseID); 235 | if (baseFactory == null) { 236 | continue; 237 | } 238 | 239 | int newID = int.Parse(variantXML.Elements() 240 | .Single(x => x.Name == "Values").Elements() 241 | .Single(x => x.Name == "Standard").Elements() 242 | .Single(x => x.Name == "GUID").Value); 243 | 244 | var newFactory = new Factory() { 245 | ID = newID, 246 | Name = baseFactory.Name, 247 | CycleTime = baseFactory.CycleTime, 248 | Inputs = baseFactory.Inputs, 249 | Outputs = baseFactory.Outputs, 250 | IsNewWorld = true, 251 | IsOldWorld = false 252 | }; 253 | 254 | string newCycleTimeString = variantXML.Elements() 255 | ?.SingleOrDefault(x => x.Name == "Values")?.Elements() 256 | ?.SingleOrDefault(x => x.Name == "FactoryBase")?.Elements() 257 | ?.SingleOrDefault(x => x.Name == "CycleTime")?.Value; 258 | 259 | if (!string.IsNullOrEmpty(newCycleTimeString) && int.TryParse(newCycleTimeString, out int newCycleTime)) { 260 | newFactory.CycleTime = newCycleTime; 261 | } 262 | 263 | factories.Add(newFactory); 264 | } 265 | 266 | var productsJSON = JsonConvert.SerializeObject(products.ToArray()); 267 | File.WriteAllText(outputFolder + "products.json", productsJSON); 268 | 269 | var factoriesJSON = JsonConvert.SerializeObject(factories.ToArray()); 270 | File.WriteAllText(outputFolder + "factories.json", factoriesJSON); 271 | 272 | var populationJSON = JsonConvert.SerializeObject(populationLevels.ToArray()); 273 | File.WriteAllText(outputFolder + "populations.json", populationJSON); 274 | } 275 | } 276 | 277 | public class Product { 278 | public int ID { get; set; } 279 | public string Name { get; set; } 280 | } 281 | 282 | public class Factory { 283 | public int ID { get; set; } 284 | public string Name { get; set; } 285 | public int CycleTime { get; set; } 286 | public FactoryIngredient[] Inputs { get; set; } 287 | public FactoryIngredient[] Outputs { get; set; } 288 | public bool IsOldWorld { get; set; } 289 | public bool IsNewWorld { get; set; } 290 | public bool CanHaveSilo { get; set; } 291 | public bool CanHaveTractorBarn { get; set; } 292 | } 293 | 294 | public class FactoryIngredient { 295 | public int ProductID { get; set; } 296 | public int Amount { get; set; } 297 | } 298 | 299 | public class PopulationLevel { 300 | public string Name { get; set; } 301 | public PopulationInput[] Inputs { get; set; } 302 | } 303 | 304 | public class PopulationInput { 305 | public int ProductID { get; set; } 306 | public decimal Amount { get; set; } 307 | public int SupplyWeight { get; set; } 308 | public int MoneyValue { get; set; } 309 | } 310 | } -------------------------------------------------------------------------------- /docs/3rdpartylicenses.txt: -------------------------------------------------------------------------------- 1 | @angular-devkit/build-angular 2 | MIT 3 | The MIT License 4 | 5 | Copyright (c) 2017 Google, Inc. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | 26 | @angular/common 27 | MIT 28 | 29 | @angular/core 30 | MIT 31 | 32 | @angular/forms 33 | MIT 34 | 35 | @angular/platform-browser 36 | MIT 37 | 38 | core-js 39 | MIT 40 | Copyright (c) 2014-2019 Denis Pushkarev 41 | 42 | Permission is hereby granted, free of charge, to any person obtaining a copy 43 | of this software and associated documentation files (the "Software"), to deal 44 | in the Software without restriction, including without limitation the rights 45 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 46 | copies of the Software, and to permit persons to whom the Software is 47 | furnished to do so, subject to the following conditions: 48 | 49 | The above copyright notice and this permission notice shall be included in 50 | all copies or substantial portions of the Software. 51 | 52 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 53 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 54 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 55 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 56 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 57 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 58 | THE SOFTWARE. 59 | 60 | 61 | rxjs 62 | Apache-2.0 63 | Apache License 64 | Version 2.0, January 2004 65 | http://www.apache.org/licenses/ 66 | 67 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 68 | 69 | 1. Definitions. 70 | 71 | "License" shall mean the terms and conditions for use, reproduction, 72 | and distribution as defined by Sections 1 through 9 of this document. 73 | 74 | "Licensor" shall mean the copyright owner or entity authorized by 75 | the copyright owner that is granting the License. 76 | 77 | "Legal Entity" shall mean the union of the acting entity and all 78 | other entities that control, are controlled by, or are under common 79 | control with that entity. For the purposes of this definition, 80 | "control" means (i) the power, direct or indirect, to cause the 81 | direction or management of such entity, whether by contract or 82 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 83 | outstanding shares, or (iii) beneficial ownership of such entity. 84 | 85 | "You" (or "Your") shall mean an individual or Legal Entity 86 | exercising permissions granted by this License. 87 | 88 | "Source" form shall mean the preferred form for making modifications, 89 | including but not limited to software source code, documentation 90 | source, and configuration files. 91 | 92 | "Object" form shall mean any form resulting from mechanical 93 | transformation or translation of a Source form, including but 94 | not limited to compiled object code, generated documentation, 95 | and conversions to other media types. 96 | 97 | "Work" shall mean the work of authorship, whether in Source or 98 | Object form, made available under the License, as indicated by a 99 | copyright notice that is included in or attached to the work 100 | (an example is provided in the Appendix below). 101 | 102 | "Derivative Works" shall mean any work, whether in Source or Object 103 | form, that is based on (or derived from) the Work and for which the 104 | editorial revisions, annotations, elaborations, or other modifications 105 | represent, as a whole, an original work of authorship. For the purposes 106 | of this License, Derivative Works shall not include works that remain 107 | separable from, or merely link (or bind by name) to the interfaces of, 108 | the Work and Derivative Works thereof. 109 | 110 | "Contribution" shall mean any work of authorship, including 111 | the original version of the Work and any modifications or additions 112 | to that Work or Derivative Works thereof, that is intentionally 113 | submitted to Licensor for inclusion in the Work by the copyright owner 114 | or by an individual or Legal Entity authorized to submit on behalf of 115 | the copyright owner. For the purposes of this definition, "submitted" 116 | means any form of electronic, verbal, or written communication sent 117 | to the Licensor or its representatives, including but not limited to 118 | communication on electronic mailing lists, source code control systems, 119 | and issue tracking systems that are managed by, or on behalf of, the 120 | Licensor for the purpose of discussing and improving the Work, but 121 | excluding communication that is conspicuously marked or otherwise 122 | designated in writing by the copyright owner as "Not a Contribution." 123 | 124 | "Contributor" shall mean Licensor and any individual or Legal Entity 125 | on behalf of whom a Contribution has been received by Licensor and 126 | subsequently incorporated within the Work. 127 | 128 | 2. Grant of Copyright License. Subject to the terms and conditions of 129 | this License, each Contributor hereby grants to You a perpetual, 130 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 131 | copyright license to reproduce, prepare Derivative Works of, 132 | publicly display, publicly perform, sublicense, and distribute the 133 | Work and such Derivative Works in Source or Object form. 134 | 135 | 3. Grant of Patent License. Subject to the terms and conditions of 136 | this License, each Contributor hereby grants to You a perpetual, 137 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 138 | (except as stated in this section) patent license to make, have made, 139 | use, offer to sell, sell, import, and otherwise transfer the Work, 140 | where such license applies only to those patent claims licensable 141 | by such Contributor that are necessarily infringed by their 142 | Contribution(s) alone or by combination of their Contribution(s) 143 | with the Work to which such Contribution(s) was submitted. If You 144 | institute patent litigation against any entity (including a 145 | cross-claim or counterclaim in a lawsuit) alleging that the Work 146 | or a Contribution incorporated within the Work constitutes direct 147 | or contributory patent infringement, then any patent licenses 148 | granted to You under this License for that Work shall terminate 149 | as of the date such litigation is filed. 150 | 151 | 4. Redistribution. You may reproduce and distribute copies of the 152 | Work or Derivative Works thereof in any medium, with or without 153 | modifications, and in Source or Object form, provided that You 154 | meet the following conditions: 155 | 156 | (a) You must give any other recipients of the Work or 157 | Derivative Works a copy of this License; and 158 | 159 | (b) You must cause any modified files to carry prominent notices 160 | stating that You changed the files; and 161 | 162 | (c) You must retain, in the Source form of any Derivative Works 163 | that You distribute, all copyright, patent, trademark, and 164 | attribution notices from the Source form of the Work, 165 | excluding those notices that do not pertain to any part of 166 | the Derivative Works; and 167 | 168 | (d) If the Work includes a "NOTICE" text file as part of its 169 | distribution, then any Derivative Works that You distribute must 170 | include a readable copy of the attribution notices contained 171 | within such NOTICE file, excluding those notices that do not 172 | pertain to any part of the Derivative Works, in at least one 173 | of the following places: within a NOTICE text file distributed 174 | as part of the Derivative Works; within the Source form or 175 | documentation, if provided along with the Derivative Works; or, 176 | within a display generated by the Derivative Works, if and 177 | wherever such third-party notices normally appear. The contents 178 | of the NOTICE file are for informational purposes only and 179 | do not modify the License. You may add Your own attribution 180 | notices within Derivative Works that You distribute, alongside 181 | or as an addendum to the NOTICE text from the Work, provided 182 | that such additional attribution notices cannot be construed 183 | as modifying the License. 184 | 185 | You may add Your own copyright statement to Your modifications and 186 | may provide additional or different license terms and conditions 187 | for use, reproduction, or distribution of Your modifications, or 188 | for any such Derivative Works as a whole, provided Your use, 189 | reproduction, and distribution of the Work otherwise complies with 190 | the conditions stated in this License. 191 | 192 | 5. Submission of Contributions. Unless You explicitly state otherwise, 193 | any Contribution intentionally submitted for inclusion in the Work 194 | by You to the Licensor shall be under the terms and conditions of 195 | this License, without any additional terms or conditions. 196 | Notwithstanding the above, nothing herein shall supersede or modify 197 | the terms of any separate license agreement you may have executed 198 | with Licensor regarding such Contributions. 199 | 200 | 6. Trademarks. This License does not grant permission to use the trade 201 | names, trademarks, service marks, or product names of the Licensor, 202 | except as required for reasonable and customary use in describing the 203 | origin of the Work and reproducing the content of the NOTICE file. 204 | 205 | 7. Disclaimer of Warranty. Unless required by applicable law or 206 | agreed to in writing, Licensor provides the Work (and each 207 | Contributor provides its Contributions) on an "AS IS" BASIS, 208 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 209 | implied, including, without limitation, any warranties or conditions 210 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 211 | PARTICULAR PURPOSE. You are solely responsible for determining the 212 | appropriateness of using or redistributing the Work and assume any 213 | risks associated with Your exercise of permissions under this License. 214 | 215 | 8. Limitation of Liability. In no event and under no legal theory, 216 | whether in tort (including negligence), contract, or otherwise, 217 | unless required by applicable law (such as deliberate and grossly 218 | negligent acts) or agreed to in writing, shall any Contributor be 219 | liable to You for damages, including any direct, indirect, special, 220 | incidental, or consequential damages of any character arising as a 221 | result of this License or out of the use or inability to use the 222 | Work (including but not limited to damages for loss of goodwill, 223 | work stoppage, computer failure or malfunction, or any and all 224 | other commercial damages or losses), even if such Contributor 225 | has been advised of the possibility of such damages. 226 | 227 | 9. Accepting Warranty or Additional Liability. While redistributing 228 | the Work or Derivative Works thereof, You may choose to offer, 229 | and charge a fee for, acceptance of support, warranty, indemnity, 230 | or other liability obligations and/or rights consistent with this 231 | License. However, in accepting such obligations, You may act only 232 | on Your own behalf and on Your sole responsibility, not on behalf 233 | of any other Contributor, and only if You agree to indemnify, 234 | defend, and hold each Contributor harmless for any liability 235 | incurred by, or claims asserted against, such Contributor by reason 236 | of your accepting any such warranty or additional liability. 237 | 238 | END OF TERMS AND CONDITIONS 239 | 240 | APPENDIX: How to apply the Apache License to your work. 241 | 242 | To apply the Apache License to your work, attach the following 243 | boilerplate notice, with the fields enclosed by brackets "[]" 244 | replaced with your own identifying information. (Don't include 245 | the brackets!) The text should be enclosed in the appropriate 246 | comment syntax for the file format. We also recommend that a 247 | file or class name and description of purpose be included on the 248 | same "printed page" as the copyright notice for easier 249 | identification within third-party archives. 250 | 251 | Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors 252 | 253 | Licensed under the Apache License, Version 2.0 (the "License"); 254 | you may not use this file except in compliance with the License. 255 | You may obtain a copy of the License at 256 | 257 | http://www.apache.org/licenses/LICENSE-2.0 258 | 259 | Unless required by applicable law or agreed to in writing, software 260 | distributed under the License is distributed on an "AS IS" BASIS, 261 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 262 | See the License for the specific language governing permissions and 263 | limitations under the License. 264 | 265 | 266 | 267 | tslib 268 | Apache-2.0 269 | Apache License 270 | 271 | Version 2.0, January 2004 272 | 273 | http://www.apache.org/licenses/ 274 | 275 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 276 | 277 | 1. Definitions. 278 | 279 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 280 | 281 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 282 | 283 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 284 | 285 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 286 | 287 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 288 | 289 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 290 | 291 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 292 | 293 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 294 | 295 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 296 | 297 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 298 | 299 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 300 | 301 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 302 | 303 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 304 | 305 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 306 | 307 | You must cause any modified files to carry prominent notices stating that You changed the files; and 308 | 309 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 310 | 311 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 312 | 313 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 314 | 315 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 316 | 317 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 318 | 319 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 320 | 321 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 322 | 323 | END OF TERMS AND CONDITIONS 324 | 325 | 326 | zone.js 327 | MIT 328 | The MIT License 329 | 330 | Copyright (c) 2016-2018 Google, Inc. 331 | 332 | Permission is hereby granted, free of charge, to any person obtaining a copy 333 | of this software and associated documentation files (the "Software"), to deal 334 | in the Software without restriction, including without limitation the rights 335 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 336 | copies of the Software, and to permit persons to whom the Software is 337 | furnished to do so, subject to the following conditions: 338 | 339 | The above copyright notice and this permission notice shall be included in 340 | all copies or substantial portions of the Software. 341 | 342 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 343 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 344 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 345 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 346 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 347 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 348 | THE SOFTWARE. 349 | -------------------------------------------------------------------------------- /docs/polyfills.8bbb231b43165d65d357.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[3],{"0TWp":function(e,t,n){!function(){"use strict";!function(e){var t=e.performance;function n(e){t&&t.mark&&t.mark(e)}function r(e,n){t&&t.measure&&t.measure(e,n)}n("Zone");var o=!0===e.__zone_symbol__forceDuplicateZoneCheck;if(e.Zone){if(o||"function"!=typeof e.Zone.__symbol__)throw new Error("Zone already loaded.");return e.Zone}var a,i=function(){function t(e,t){this._parent=e,this._name=t?t.name||"unnamed":"",this._properties=t&&t.properties||{},this._zoneDelegate=new c(this,this._parent&&this._parent._zoneDelegate,t)}return t.assertZonePatched=function(){if(e.Promise!==Z.ZoneAwarePromise)throw new Error("Zone.js has detected that ZoneAwarePromise `(window|global).Promise` has been overwritten.\nMost likely cause is that a Promise polyfill has been loaded after Zone.js (Polyfilling Promise api is not necessary when zone.js is loaded. If you must load one, do so before loading zone.js.)")},Object.defineProperty(t,"root",{get:function(){for(var e=t.current;e.parent;)e=e.parent;return e},enumerable:!0,configurable:!0}),Object.defineProperty(t,"current",{get:function(){return O.zone},enumerable:!0,configurable:!0}),Object.defineProperty(t,"currentTask",{get:function(){return P},enumerable:!0,configurable:!0}),t.__load_patch=function(a,i){if(Z.hasOwnProperty(a)){if(o)throw Error("Already loaded patch: "+a)}else if(!e["__Zone_disable_"+a]){var s="Zone:"+a;n(s),Z[a]=i(e,t,z),r(s,s)}},Object.defineProperty(t.prototype,"parent",{get:function(){return this._parent},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"name",{get:function(){return this._name},enumerable:!0,configurable:!0}),t.prototype.get=function(e){var t=this.getZoneWith(e);if(t)return t._properties[e]},t.prototype.getZoneWith=function(e){for(var t=this;t;){if(t._properties.hasOwnProperty(e))return t;t=t._parent}return null},t.prototype.fork=function(e){if(!e)throw new Error("ZoneSpec required!");return this._zoneDelegate.fork(this,e)},t.prototype.wrap=function(e,t){if("function"!=typeof e)throw new Error("Expecting function got: "+e);var n=this._zoneDelegate.intercept(this,e,t),r=this;return function(){return r.runGuarded(n,this,arguments,t)}},t.prototype.run=function(e,t,n,r){O={parent:O,zone:this};try{return this._zoneDelegate.invoke(this,e,t,n,r)}finally{O=O.parent}},t.prototype.runGuarded=function(e,t,n,r){void 0===t&&(t=null),O={parent:O,zone:this};try{try{return this._zoneDelegate.invoke(this,e,t,n,r)}catch(o){if(this._zoneDelegate.handleError(this,o))throw o}}finally{O=O.parent}},t.prototype.runTask=function(e,t,n){if(e.zone!=this)throw new Error("A task can only be run in the zone of creation! (Creation: "+(e.zone||y).name+"; Execution: "+this.name+")");if(e.state!==m||e.type!==D&&e.type!==S){var r=e.state!=b;r&&e._transitionTo(b,_),e.runCount++;var o=P;P=e,O={parent:O,zone:this};try{e.type==S&&e.data&&!e.data.isPeriodic&&(e.cancelFn=void 0);try{return this._zoneDelegate.invokeTask(this,e,t,n)}catch(a){if(this._zoneDelegate.handleError(this,a))throw a}}finally{e.state!==m&&e.state!==w&&(e.type==D||e.data&&e.data.isPeriodic?r&&e._transitionTo(_,b):(e.runCount=0,this._updateTaskCount(e,-1),r&&e._transitionTo(m,b,m))),O=O.parent,P=o}}},t.prototype.scheduleTask=function(e){if(e.zone&&e.zone!==this)for(var t=this;t;){if(t===e.zone)throw Error("can not reschedule task to "+this.name+" which is descendants of the original zone "+e.zone.name);t=t.parent}e._transitionTo(k,m);var n=[];e._zoneDelegates=n,e._zone=this;try{e=this._zoneDelegate.scheduleTask(this,e)}catch(r){throw e._transitionTo(w,k,m),this._zoneDelegate.handleError(this,r),r}return e._zoneDelegates===n&&this._updateTaskCount(e,1),e.state==k&&e._transitionTo(_,k),e},t.prototype.scheduleMicroTask=function(e,t,n,r){return this.scheduleTask(new l(E,e,t,n,r,void 0))},t.prototype.scheduleMacroTask=function(e,t,n,r,o){return this.scheduleTask(new l(S,e,t,n,r,o))},t.prototype.scheduleEventTask=function(e,t,n,r,o){return this.scheduleTask(new l(D,e,t,n,r,o))},t.prototype.cancelTask=function(e){if(e.zone!=this)throw new Error("A task can only be cancelled in the zone of creation! (Creation: "+(e.zone||y).name+"; Execution: "+this.name+")");e._transitionTo(T,_,b);try{this._zoneDelegate.cancelTask(this,e)}catch(t){throw e._transitionTo(w,T),this._zoneDelegate.handleError(this,t),t}return this._updateTaskCount(e,-1),e._transitionTo(m,T),e.runCount=0,e},t.prototype._updateTaskCount=function(e,t){var n=e._zoneDelegates;-1==t&&(e._zoneDelegates=null);for(var r=0;r0,macroTask:n.macroTask>0,eventTask:n.eventTask>0,change:e})},e}(),l=function(){function t(n,r,o,a,i,s){this._zone=null,this.runCount=0,this._zoneDelegates=null,this._state="notScheduled",this.type=n,this.source=r,this.data=a,this.scheduleFn=i,this.cancelFn=s,this.callback=o;var c=this;this.invoke=n===D&&a&&a.useG?t.invokeTask:function(){return t.invokeTask.call(e,c,this,arguments)}}return t.invokeTask=function(e,t,n){e||(e=this),C++;try{return e.runCount++,e.zone.runTask(e,t,n)}finally{1==C&&g(),C--}},Object.defineProperty(t.prototype,"zone",{get:function(){return this._zone},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"state",{get:function(){return this._state},enumerable:!0,configurable:!0}),t.prototype.cancelScheduleRequest=function(){this._transitionTo(m,k)},t.prototype._transitionTo=function(e,t,n){if(this._state!==t&&this._state!==n)throw new Error(this.type+" '"+this.source+"': can not transition to '"+e+"', expecting state '"+t+"'"+(n?" or '"+n+"'":"")+", was '"+this._state+"'.");this._state=e,e==m&&(this._zoneDelegates=null)},t.prototype.toString=function(){return this.data&&void 0!==this.data.handleId?this.data.handleId.toString():Object.prototype.toString.call(this)},t.prototype.toJSON=function(){return{type:this.type,state:this.state,source:this.source,zone:this.zone.name,runCount:this.runCount}},t}(),u=I("setTimeout"),f=I("Promise"),p=I("then"),h=[],d=!1;function v(t){if(0===C&&0===h.length)if(a||e[f]&&(a=e[f].resolve(0)),a){var n=a[p];n||(n=a.then),n.call(a,g)}else e[u](g,0);t&&h.push(t)}function g(){if(!d){for(d=!0;h.length;){var e=h;h=[];for(var t=0;t=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}}};Zone.__load_patch("ZoneAwarePromise",function(t,n,r){var o=Object.getOwnPropertyDescriptor,a=Object.defineProperty,i=r.symbol,s=[],c=i("Promise"),l=i("then"),u="__creationTrace__";r.onUnhandledError=function(e){if(r.showUncaughtError()){var t=e&&e.rejection;t?console.error("Unhandled Promise rejection:",t instanceof Error?t.message:t,"; Zone:",e.zone.name,"; Task:",e.task&&e.task.source,"; Value:",t,t instanceof Error?t.stack:void 0):console.error(e)}},r.microtaskDrainDone=function(){for(;s.length;)for(var e=function(){var e=s.shift();try{e.zone.runGuarded(function(){throw e})}catch(t){p(t)}};s.length;)e()};var f=i("unhandledPromiseRejectionHandler");function p(e){r.onUnhandledError(e);try{var t=n[f];t&&"function"==typeof t&&t.call(this,e)}catch(o){}}function h(e){return e&&e.then}function d(e){return e}function v(e){return M.reject(e)}var g=i("state"),y=i("value"),m=i("finally"),k=i("parentPromiseValue"),_=i("parentPromiseState"),b="Promise.then",T=null,w=!0,E=!1,S=0;function D(e,t){return function(n){try{P(e,t,n)}catch(r){P(e,!1,r)}}}var Z=function(){var e=!1;return function(t){return function(){e||(e=!0,t.apply(null,arguments))}}},z="Promise resolved with itself",O=i("currentTaskTrace");function P(e,t,o){var i,c=Z();if(e===o)throw new TypeError(z);if(e[g]===T){var l=null;try{"object"!=typeof o&&"function"!=typeof o||(l=o&&o.then)}catch(v){return c(function(){P(e,!1,v)})(),e}if(t!==E&&o instanceof M&&o.hasOwnProperty(g)&&o.hasOwnProperty(y)&&o[g]!==T)j(o),P(e,o[g],o[y]);else if(t!==E&&"function"==typeof l)try{l.call(o,c(D(e,t)),c(D(e,!1)))}catch(v){c(function(){P(e,!1,v)})()}else{e[g]=t;var f=e[y];if(e[y]=o,e[m]===m&&t===w&&(e[g]=e[_],e[y]=e[k]),t===E&&o instanceof Error){var p=n.currentTask&&n.currentTask.data&&n.currentTask.data[u];p&&a(o,O,{configurable:!0,enumerable:!1,writable:!0,value:p})}for(var h=0;h1?c[1]:null,h=p&&p.signal;return new Promise(function(p,d){var v=t.current.scheduleMacroTask("fetch",f,c,function(){var s,l=t.current;try{l[i]=!0,s=r.apply(e,c)}catch(f){return void d(f)}finally{l[i]=!1}if(!(s instanceof o)){var u=s.constructor;u[a]||n.patchThen(u)}s.then(function(e){"notScheduled"!==v.state&&v.invoke(),p(e)},function(e){"notScheduled"!==v.state&&v.invoke(),d(e)})},function(){if(l)if(h&&h.abortController&&!h.aborted&&"function"==typeof h.abortController.abort&&u)try{t.current[s]=!0,u.call(h.abortController)}finally{t.current[s]=!1}else d("cancel fetch need a AbortController.signal");else d("No AbortController supported, can not cancel fetch")});h&&h.abortController&&(h.abortController.task=v)})}}});var t=Object.getOwnPropertyDescriptor,n=Object.defineProperty,r=Object.getPrototypeOf,o=Object.create,a=Array.prototype.slice,i="addEventListener",s="removeEventListener",c=Zone.__symbol__(i),l=Zone.__symbol__(s),u="true",f="false",p="__zone_symbol__";function h(e,t){return Zone.current.wrap(e,t)}function d(e,t,n,r,o){return Zone.current.scheduleMacroTask(e,t,n,r,o)}var v=Zone.__symbol__,g="undefined"!=typeof window,y=g?window:void 0,m=g&&y||"object"==typeof self&&self||global,k="removeAttribute",_=[null];function b(e,t){for(var n=e.length-1;n>=0;n--)"function"==typeof e[n]&&(e[n]=h(e[n],t+"_"+n));return e}function T(e){return!e||!1!==e.writable&&!("function"==typeof e.get&&void 0===e.set)}var w="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,E=!("nw"in m)&&void 0!==m.process&&"[object process]"==={}.toString.call(m.process),S=!E&&!w&&!(!g||!y.HTMLElement),D=void 0!==m.process&&"[object process]"==={}.toString.call(m.process)&&!w&&!(!g||!y.HTMLElement),Z={},z=function(e){if(e=e||m.event){var t=Z[e.type];t||(t=Z[e.type]=v("ON_PROPERTY"+e.type));var n,r=this||e.target||m,o=r[t];return S&&r===y&&"error"===e.type?!0===(n=o&&o.call(this,e.message,e.filename,e.lineno,e.colno,e.error))&&e.preventDefault():null==(n=o&&o.apply(this,arguments))||n||e.preventDefault(),n}};function O(e,r,o){var a=t(e,r);if(!a&&o&&t(o,r)&&(a={enumerable:!0,configurable:!0}),a&&a.configurable){var i=v("on"+r+"patched");if(!e.hasOwnProperty(i)||!e[i]){delete a.writable,delete a.value;var s=a.get,c=a.set,l=r.substr(2),u=Z[l];u||(u=Z[l]=v("ON_PROPERTY"+l)),a.set=function(t){var n=this;n||e!==m||(n=m),n&&(n[u]&&n.removeEventListener(l,z),c&&c.apply(n,_),"function"==typeof t?(n[u]=t,n.addEventListener(l,z,!1)):n[u]=null)},a.get=function(){var t=this;if(t||e!==m||(t=m),!t)return null;var n=t[u];if(n)return n;if(s){var o=s&&s.call(this);if(o)return a.set.call(this,o),"function"==typeof t[k]&&t.removeAttribute(r),o}return null},n(e,r,a),e[i]=!0}}}function P(e,t,n){if(t)for(var r=0;r1?new r(e,n):new r(e),f=t(u,"onmessage");return f&&!1===f.configurable?(c=o(u),l=u,[i,s,"send","close"].forEach(function(e){c[e]=function(){var t=a.call(arguments);if(e===i||e===s){var n=t.length>0?t[0]:void 0;if(n){var r=Zone.__symbol__("ON_PROPERTY"+n);u[r]=c[r]}}return u[e].apply(u,t)}})):c=u,P(c,["close","error","message","open"],l),c};var c=n.WebSocket;for(var l in r)c[l]=r[l]}(0,c)}}var ge=v("unbound");function ye(e,n,r,o){var a=Zone.__symbol__(r);if(!e[a]){var i=e[a]=e[r];e[r]=function(a,s,c){return s&&s.prototype&&o.forEach(function(e){var o,a,i,c,l=n+"."+r+"::"+e,u=s.prototype;if(u.hasOwnProperty(e)){var f=t(u,e);f&&f.value?(f.value=h(f.value,l),c=(i=f).configurable,re(o=s.prototype,a=e,i=ne(o,a,i),c)):u[e]&&(u[e]=h(u[e],l))}else u[e]&&(u[e]=h(u[e],l))}),i.call(e,a,s,c)},L(e[r],i)}}Zone.__load_patch("util",function(e,t,n){n.patchOnProperties=P,n.patchMethod=M,n.bindArguments=b}),Zone.__load_patch("timers",function(e){K(e,"set","clear","Timeout"),K(e,"set","clear","Interval"),K(e,"set","clear","Immediate")}),Zone.__load_patch("requestAnimationFrame",function(e){K(e,"request","cancel","AnimationFrame"),K(e,"mozRequest","mozCancel","AnimationFrame"),K(e,"webkitRequest","webkitCancel","AnimationFrame")}),Zone.__load_patch("blocking",function(e,t){for(var n=["alert","prompt","confirm"],r=0;r=0&&"function"==typeof n[r.cbIdx]?d(r.name,n[r.cbIdx],r,a):e.apply(t,n)}})}()}),Zone.__load_patch("XHR",function(e,t){!function(u){var f=XMLHttpRequest.prototype,p=f[c],h=f[l];if(!p){var g=e.XMLHttpRequestEventTarget;if(g){var y=g.prototype;p=y[c],h=y[l]}}var m="readystatechange",k="scheduled";function _(e){var t=e.data,r=t.target;r[a]=!1,r[s]=!1;var i=r[o];p||(p=r[c],h=r[l]),i&&h.call(r,m,i);var u=r[o]=function(){if(r.readyState===r.DONE)if(!t.aborted&&r[a]&&e.state===k){var n=r.__zone_symbol__loadfalse;if(n&&n.length>0){var o=e.invoke;e.invoke=function(){for(var n=r.__zone_symbol__loadfalse,a=0;a 0 ? this.CycleTime : 30; 263 | 264 | if (this.ID === 101 || this.ID === 102 || this.ID == 104) { 265 | // Special case - Count number of farms using silos 266 | for (var i = 0; i < island.Factories.length; i++) { 267 | if (island.Factories[i].UseSilo) { 268 | amountRequiredPerMinute += island.Factories[i].BuiltCount; 269 | } 270 | } 271 | } 272 | else if (this.ID == 103) { 273 | // Special case - Count number of farms using tractor barns 274 | for (var i = 0; i < island.Factories.length; i++) { 275 | if (island.Factories[i].UseTractorBarn) { 276 | amountRequiredPerMinute += island.Factories[i].BuiltCount; 277 | } 278 | } 279 | } 280 | else { 281 | // Default case - Calculate from population needs 282 | for (var i = 0; i < island.PopulationLevels.length; i++) { 283 | amountRequiredPerMinute += island.PopulationLevels[i].GetProductRequirement(outputProductID); 284 | } 285 | 286 | // Population requirements appear to be in tons per second, so multiply by 60 287 | amountRequiredPerMinute *= 60; 288 | } 289 | 290 | let requiredFactoriesFromParent = 0; 291 | if (this.ParentFactory) { 292 | let parentInput = this.ParentFactory.Inputs.filter(i => i.ProductID === outputProductID)[0]; 293 | if (parentInput) { 294 | const parentRequiredCountUnmodified = this.ParentFactory.GetRequiredCount(island) - this.ParentFactory.TradeBalance; 295 | const parentRequiredNormalized100Productivity = parentRequiredCountUnmodified * this.ParentFactory.Productivity / 100; 296 | const parentCycleTime = this.ParentFactory.CycleTime > 0 ? this.ParentFactory.CycleTime : 30; 297 | const childParentFactoryRatio = parentInput.Amount / this.Outputs[0].Amount * cycleTime / parentCycleTime; 298 | requiredFactoriesFromParent = parentRequiredNormalized100Productivity * childParentFactoryRatio; 299 | } 300 | } 301 | 302 | let producedPerMinute = this.Outputs[0].Amount * 60 / cycleTime; 303 | 304 | return Math.max(Math.round((amountRequiredPerMinute / producedPerMinute + requiredFactoriesFromParent) * 100 * 100 / this.Productivity) / 100, 0); 305 | } 306 | 307 | GetSatisfactionClass(island: Island): string { 308 | let result = 'factory'; 309 | 310 | if (this.Outputs[0].Amount === 0) { 311 | result = result + ' publicService'; 312 | 313 | if (this.Enabled) { 314 | result = result + ' satisfied'; 315 | } 316 | 317 | return result; 318 | } 319 | 320 | if (!this.Enabled) { 321 | return result; 322 | } 323 | 324 | let required = this.GetRequiredCount(island); 325 | let satisfaction = this.BuiltCount + this.TradeBalance; 326 | 327 | if(satisfaction >= required) { 328 | result = result + ' satisfied'; 329 | } 330 | else if (required - satisfaction < 0.5) { 331 | result = result + ' slightlyUnsatisfied'; 332 | } 333 | else { 334 | result = result + ' unsatisfied'; 335 | } 336 | 337 | return result; 338 | } 339 | 340 | ToggleSilo() { 341 | if (!this.Enabled) { 342 | return; 343 | } 344 | 345 | this.UseSilo = !this.UseSilo; 346 | 347 | if (this.UseSilo) { 348 | // +100%, plus an additional product every 3rd cycle = 200 * 4 / 3 = 266.67 349 | this.Productivity = 266.67; 350 | } 351 | else { 352 | this.Productivity = 100; 353 | } 354 | } 355 | 356 | ToggleTractorBarn() { 357 | if (!this.Enabled) { 358 | return; 359 | } 360 | 361 | this.UseTractorBarn = !this.UseTractorBarn; 362 | 363 | if (this.UseTractorBarn) { 364 | // +200%, plus an additional product every 3rd cycle = 300 * 4 / 3 = 400 365 | this.Productivity = 400; 366 | } 367 | else { 368 | this.Productivity = 100; 369 | } 370 | } 371 | 372 | IsInUse(island: Island) { 373 | let relevantFactory = this as Factory; 374 | while (relevantFactory.ParentFactory) { 375 | relevantFactory = relevantFactory.ParentFactory; 376 | } 377 | 378 | // Does the population demand it? 379 | if ( 380 | island.PopulationLevels.filter(p => 381 | p.Inputs.filter(input => input.ProductID === relevantFactory.Outputs[0].ProductID).length > 0 382 | && (p.HouseCount > 0 || p.ShowUnused) 383 | ).length > 0 384 | ) { 385 | return true; 386 | } 387 | 388 | // Are any required? 389 | if (this.GetRequiredCount(island)) { 390 | return true; 391 | } 392 | 393 | return false; 394 | } 395 | 396 | Save(): FactorySaveInfo { 397 | return { 398 | FactoryID: this.ID, 399 | ParentFactoryID: this.ParentFactory ? this.ParentFactory.ID : null, 400 | Enabled: this.Enabled, 401 | BuiltCount: this.BuiltCount, 402 | Productivity: this.Productivity, 403 | TradeBalance: this.TradeBalance, 404 | UseSilo: this.UseSilo, 405 | UseTractorBarn: this.UseTractorBarn, 406 | }; 407 | } 408 | } 409 | 410 | export class FactoryIngredient { 411 | ProductID: number 412 | Amount: number 413 | } 414 | 415 | export class FactorySaveInfo { 416 | FactoryID: number; 417 | ParentFactoryID?: number; 418 | Enabled: boolean; 419 | BuiltCount: number; 420 | Productivity: number; 421 | TradeBalance: number; 422 | UseSilo: boolean; 423 | UseTractorBarn: boolean; 424 | } --------------------------------------------------------------------------------