diff --git a/.github/workflows/OpenTTE.yml b/.github/workflows/OpenTTE.yml new file mode 100644 index 0000000..056d916 --- /dev/null +++ b/.github/workflows/OpenTTE.yml @@ -0,0 +1,37 @@ +name: OpenTTE + +on: [push, pull_request] + +jobs: + Build: + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Dependencies + run: python3 --version && pip3 install setuptools pip && pip3 install nml + - name: Build + run: python3 build.py --compile OpenTTE + - name: Artifact + uses: actions/upload-artifact@v2 + with: + name: OpenTTE + path: build/OpenTTE.* + if-no-files-found: error + - name: Release Create + id: release_create + uses: actions/create-release@v1 + with: + tag_name: development.${{ github.run_id }} + release_name: PreRelease development.${{ github.run_id }} + prerelease: true + - name: Release Upload + id: release_upload + uses: actions/upload-release-asset@v1 + with: + upload_url: ${{ steps.release_create.outputs.upload_url }} + asset_path: build/OpenTTE.grf + asset_name: OpenTTE-development.grf + asset_content_type: application/grf diff --git a/.gitignore b/.gitignore index 9ef03f9..c26b2a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,12 @@ # Build files -build/ -build.json -conf.ps1 -*.grf -*.tar +build/** # NML cache -.nmlcache/ -*.cache -*.cacheindex +.nmlcache/** # Test files *test* -# No clue -master-gfx/ -list_ids.py +# Development and IDE +.vscode/** +.idea/** diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 76a2e1f..0000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - - // List of extensions which should be recommended for users of this workspace. - "recommendations": [ - "pnda.nml-language" - - ], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. - "unwantedRecommendations": [ - - ] -} \ No newline at end of file diff --git a/README.md b/README.md index d15f1d3..cecefa2 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,21 @@ # OpenTTE - The Thomas The Tank Engine set for OpenTTD +![Thomas and Friends](banner.png) OpenTTE is a train set for OpenTTD, including vehicles from the Thomas The Tank Engine universe -Drawn by DanMacK and others, coded by Audigex, and based on the MUTS (Modern UK Train Set) build scripts and some code stolen (but not really) from BRTrains +Drawn by DanMacK and others, coded by Audigex and Bazek, and based on the MUTS (Modern UK Train Set) build scripts and some code stolen (but not really) from BRTrains ### Train List - - - +![Thomas and Friends](vehicles.png) ### Installation Grab the latest release from the in-game content downloader. -Alternatively get it from the releases page and copy it into your `OpenTTD/newGRF` folder. +Alternatively get it from the ![Releases](../../releases/) page and copy it into your `OpenTTD/newGRF` folder. ### Building from Source Building from the source should be mostly automated using the `build.py` script, but it has a few requirements: - - Python3.8 (may work on earlier versions but untested) + - Python3.* (Tested on Python3.6, Python3.7, Python3.8, Python3.9) - `nml` Python package (available through `pip`) To build the grf completely, just run the following command in your terminal: @@ -38,14 +37,17 @@ This will also perform the --compile function, and will not start the game if an #### Developers - Audigex +- Bazek #### Artists - DanMacK (Original art) - Audigex (Modifications for release) +- Bazek ### Contributing -Fork the project, make your changes, submit a pull request. +- Fork the project, make your changes, submit a pull request +- Or just send me sprite sheet with your new vehicles and I will add it (use ![Template](gfx/Template/) Directory) ### License This project is licensed under the GPLv2 license diff --git a/banner.png b/banner.png index 93c23fb..49aefb2 100644 Binary files a/banner.png and b/banner.png differ diff --git a/build.bat b/build.bat deleted file mode 100644 index d5a4f92..0000000 --- a/build.bat +++ /dev/null @@ -1,2 +0,0 @@ -python build.py --compile OpenTTE -pause \ No newline at end of file diff --git a/build.ps1 b/build.ps1 deleted file mode 100644 index 5b4b371..0000000 --- a/build.ps1 +++ /dev/null @@ -1,241 +0,0 @@ -<# -.SYNOPSIS - Builds the grf file. - -.DESCRIPTION - Compiles the project into a grf file, optionally starting the game. - -.PARAMETER ProjectName - The name of the file to create when compiling the grf. - -.PARAMETER StartGame - This switch will, if already running close openttd, copy over the compiled grf file, - and restart the game again. - -.PARAMETER SkipChecks - Skip project folder/file validations. - -.PARAMETER srcDirectory - The relative path pointing to the folder containing the logic code. - -.PARAMETER gfxDirectory - The relative path pointing to the folder containing all the graphic files. - -.PARAMETER langDirectory - The relative path pointing to the folder containing the language files. - -.PARAMETER buildDirectory - The relative path pointing to the folder where the build .nml and .grf files will be created. - -.EXAMPLE - PS C:\> .\build.ps1 -ProjectName "test" -StartGame - - This will compile a grf to the default path .\build\test.grf and start the openttd client. - -.INPUTS - None - -.OUTPUTS - None - -.NOTES - All of the parameters allow for default values so that you don't have to type out the parameters manually. - Edit this file to configure these defaults. - -#> -[CmdletBinding()] -param ( - - [Parameter()] - [string] - $ProjectName = "mukts", - - [Parameter()] - [switch] - $StartGame, - - [Parameter()] - [switch] - $SkipChecks, - - [Parameter()] - [string] - $srcDirectory = "src", - - [Parameter()] - [string] - $gfxDirectory = "gfx", - - [Parameter()] - [string] - $langDirectory = "lang", - - [Parameter()] - [string] - $buildDirectory = "build" - -) - -function Test-ProjectStructure { - - [CmdletBinding()] - param ( - - [Parameter()] - [string] - $srcDirectory, - - [Parameter()] - [string] - $gfxDirectory, - - [Parameter()] - [string] - $langDirectory - - ) - - # Ensure that the filepaths provided are valid - Write-Verbose "Checking required folders exist." - - if ((Test-Path -Path $srcDirectory) -eq $false) { - - Write-Error "The source directory: $srcDirectory could not be found." - throw - - } - if ((Test-Path -Path $gfxDirectory) -eq $false) { - - Write-Error "The graphics directory: $gfxDirectory could not be found." - throw - - } - if ((Test-Path -Path $langDirectory) -eq $false) { - - Write-Error "The language directory: $langDirectory could not be found." - throw - - } - - # Check that some required files are present - Write-Verbose "Checking necessary files are present." - - if ((Test-Path -Path (Join-Path -Path $srcDirectory -ChildPath "grf.pnml")) -eq $false) { - - Write-Error "The main grf.pnml file could not be found in $srcDirectory. This file must be present." - throw - - } - if ((Test-Path -Path (Join-Path -Path $srcDirectory -ChildPath "railtypes.pnml")) -eq $false) { - - Write-Error "The definition railtypes.pnml file could not be found in the $srcDirectory. This file must be present." - throw - - } - if ((Test-Path -Path (Join-Path -Path $srcDirectory -ChildPath "templates.pnml")) -eq $false) { - - Write-Warning "The definition templates.pnml file was not found. It is highly recommended to put template definitions into this file." - - } - - Write-Verbose "The project structure is correct." - - -} - -# ==Main function== - -# Check that there's a config file. -$configFile = Join-Path -Path $PSScriptRoot -ChildPath "conf.ps1" -if ((Test-Path -Path $configFile) -eq $false) { - - $confString = "`# Point this to the openttd executable`n`$config_OpenttdExecutablePath = `"`"`n`# Point this to the openttd user folder`n`$config_OpenttdUserDataPath = `"`"" - Out-File -InputObject $confString -FilePath $configFile -Force - - Write-Error "First time running script. Please fill in the configuration file located at $configFile" - return - -} - -# Load the config file in -. $configFile - -# Resolve the paths from relative to absolute -$srcDirectory = Join-Path -Path $PSScriptRoot -ChildPath $srcDirectory -$gfxDirectory = Join-Path -Path $PSScriptRoot -ChildPath $gfxDirectory -$langDirectory = Join-Path -Path $PSScriptRoot -ChildPath $langDirectory -$buildDirectory = Join-Path -Path $PSScriptRoot -ChildPath $buildDirectory - -# Run validation on project structure -if ($SkipChecks -eq $false) { - try { - - Test-ProjectStructure -srcDirectory $srcDirectory -gfxDirectory $gfxDirectory -langDirectory $langDirectory - - }catch { - - return - - } -} - -# Get list of all .pnml files aside from the special ones -$pnmlFiles = Get-ChildItem -Path $PSScriptRoot -Recurse -Include "*.pnml" -Exclude "grf.pnml","railtypes.pnml","templates.pnml" -Write-Verbose "Found $($pnmlFiles.Count) .pnml files." -$pnmlFiles | Write-Verbose - -# First take the contents of special .pnml files and put them into the master file -Write-Verbose "Merging together special .pnml files." -$nmlContent += Get-Content -Path (Join-Path -Path $srcDirectory -ChildPath "grf.pnml") -$nmlContent += Get-Content -Path (Join-Path -Path $srcDirectory -ChildPath "railtypes.pnml") -if ((Test-Path -Path (Join-Path -Path $srcDirectory -ChildPath "templates.pnml")) -eq $true) { - - $nmlContent += Get-Content -Path (Join-Path -Path $srcDirectory -ChildPath "templates.pnml") - -} - -# Now take the contents of the rest .pnml files and put them into the master file -Write-Verbose "Merging together all other .pnml files." -$pnmlFiles | ForEach-Object { - $nmlContent += Get-Content -Path $_.FullName -} - -# Write the master nml file to disk, overwrite any existing file -Write-Verbose "Writing master nml file to: $buildDirectory" -Out-File -InputObject $nmlContent -FilePath (Join-Path -Path $buildDirectory -ChildPath "$ProjectName.nml") -Force - -# Compile the nml to a grf -Write-Verbose "Compiling nml to grf file." -try { - - Start-Process nmlc -ArgumentList "--lang", $langDirectory, "--grf", (Join-Path -Path $buildDirectory -ChildPath "$ProjectName.grf"),` - (Join-Path -Path $buildDirectory -ChildPath "$ProjectName.nml") -Wait -NoNewWindow - -}catch [System.InvalidOperationException]{ - - Write-Error "The nml binary could not be located.`nCheck to make sure it is in PATH or alternatively install it by running: pip install nml" - return - -}catch { - - Write-Error "There was an issue in compiling the .nml to a .grf file." - return - -} - -# Optionally, (re)start the game -if ($StartGame -eq $true) { - Write-Verbose "Copying over grf file and restarting the game." - - # Kill existing openttd window - Stop-Process -Name "OpenTTD*" -Force -ErrorAction SilentlyContinue - - # Copy over the grf to the openttd user folder - Copy-Item -Path (Join-Path -Path $buildDirectory -ChildPath "$ProjectName.grf") -Destination ` - (Join-Path -Path $config_OpenttdUserDataPath -ChildPath "newgrf") -Force - - # Start openttd again - Start-Process -FilePath $config_OpenttdExecutablePath - -} - diff --git a/build.py b/build.py index 5920924..6f4e7cc 100644 --- a/build.py +++ b/build.py @@ -1,329 +1,258 @@ -from pathlib import Path -from sys import argv -from argparse import ArgumentParser -from importlib import util - +import json +import os +import sys -def check_project_structure(src_directory: Path, gfx_directory: Path, - lang_directory: Path): +from argparse import ArgumentParser +from pathlib import Path +from shutil import copy +from subprocess import Popen - has_lang_dir = True +def check_project_structure(src_directory: Path, lang_directory: Path): # Check that the project is properly structured if not src_directory.exists(): - print("\"src\" directory not found. Aborting") - return (False, -1) - if not gfx_directory.exists(): - print("\"gfx\" directory not found. Aborting") - return (False, -1) + print("%s directory not found." % src_directory) + return False if not lang_directory.exists(): - print( - "\"lang\" directory not found. Assuming hard-coded strings (this is not best practice)" - ) - has_lang_dir = False + print("%s directory not found. Assuming hard-coded strings (this is not best practice)" % lang_directory) # Find the grf, railtypes, and templates files if not src_directory.joinpath("grf.pnml").exists(): - print( - "\"grf.pnml\" not found. It should be in \"src\" and contain the grf block" - ) - return (False, -1) + print("%s/grf.pnml not found. It should contain the grf block" % src_directory) + return False if not src_directory.joinpath("railtypes.pnml").exists(): - print( - "\"railtypes.pnml\" not found. It should be in \"src\" and contain the railtypetable block" - ) - return (False, -1) + print("%s/railtypes.pnml not found. It should contain the railtypetable block" % src_directory) + return False if not src_directory.joinpath("templates.pnml").exists(): - print( - "\"templates.pnml\" not found. Assuming no templates are required" - ) + print("%s/templates.pnml not found. Assuming no templates are required" % src_directory) print("Project structure is correct\n") - return (has_lang_dir, 0) - - -def copy_file(filepath: Path, nml_file: str): - # If the pnml filepath doesn't exist, exit - if not filepath.exists(): - print("The file <%s> does not exist" % str(filepath)) - return -1 - - # Read the pnml file into the internal nml - with open(str(filepath), "r") as file: - nml_file += "// " + filepath.stem + filepath.suffix + "\n" - for line in file: - nml_file += line - nml_file += "\n\n" - return nml_file - - -def find_pnml_files(src_directory: Path): - print("Finding pnml files in %s" % src_directory) - file_list = dict() - # Iterate through all files in src_directory recursively, finding any that end in .pnml - for path in src_directory.rglob("*.pnml"): - # Don't add the special ones - if path.stem in ["railtypes", "grf", "templates"]: - continue - - if path.parent.stem not in file_list.keys(): - file_list[path.parent.stem] = list() - file_list[path.parent.stem].append(path) - - # List found files - for directory in file_list.keys(): - print("Found in directory [%s]:" % directory) - print([str(file.stem + file.suffix) for file in file_list[directory]]) - - return file_list - - -def write_file(filename: str, nml_file: str): - from os import makedirs - # Generate the filepath and check if it exists - filepath = Path("build/" + filename + ".nml") - build_dir = Path("build/") - if not build_dir.exists(): - makedirs("build") - - if filepath.exists(): - print("'%s.nml' already exists. Overwriting" % filename) - - # Write the internal nml to the file - with open(filepath, "w") as file_writer: - for line in nml_file: - file_writer.write(line) - - print("Written all files to '%s.nml' file\n" % filename) - return 0 + return True + + +class PnmlCompiler: + + def __init__(self, src_directory: Path, build_directory: Path): + self.src_directory = src_directory + self.build_directory = build_directory + self.SPECIAL_FILES = [ + self.src_directory.joinpath("grf.pnml"), + self.src_directory.joinpath("railtypes.pnml"), + self.src_directory.joinpath("templates.pnml"), + ] + + @staticmethod + def _copy_file(source_file: Path, destination_file: Path): + # If the source_file doesn't exist, exit + if not source_file.exists(): + print("The file %s does not exist" % source_file) + return + + # Read the source file and append it to destination file + print("Reading file %s" % source_file) + with destination_file.open("a") as dst_file: + with source_file.open("r") as src_file: + dst_file.write("// %s\n\n" % source_file) + dst_file.write(src_file.read()) + dst_file.write("\n\n") + + def _find_pnml_files(self): + file_dict = {} + # Iterate through all files in src_directory recursively, finding any that end in .pnml + for path in self.src_directory.rglob("*.pnml"): + # Don't add the special ones + if path not in self.SPECIAL_FILES: + file_dict.setdefault(path.parent.name, []) + file_dict[path.parent.name].append(path) + return file_dict + + def compile(self, nml_file: Path): + print("Compiling nml file %s" % nml_file) + if nml_file.exists(): + nml_file.unlink() # In Python 3.6 first param is missing + + # Compile the special files first + for special_file in self.SPECIAL_FILES: + self._copy_file(special_file, nml_file) + + # Get a list of all the pnml files in src + file_list = self._find_pnml_files() + pnml_files = [] + # Read all the files in folders that begin with "_" into the final nml + for directory in file_list: + if directory.startswith("_"): + for file in file_list[directory]: + file = Path(file) + self._copy_file(file, nml_file) + else: + pnml_files += file_list[directory] + + # Read the regular files + for file in sorted(pnml_files): + file = Path(file) + self._copy_file(file, nml_file) + + print("Compiled all *.pnml files into %s\n" % nml_file) -def compile_grf(has_lang_dir, grf_name, lang_dir): - # Check if we have the nml package - found_nml = util.find_spec("nml") - if found_nml is not None: - # Import nml's main module - import nml.main - parameters = [] - if has_lang_dir: - # If we have a lang directory, add it to the parameters - parameters = ["--lang", str(lang_dir), "build/" + grf_name + ".nml"] - else: - # If not, just the nml name - parameters = ["build/" + grf_name + ".nml"] +class GrfCompiler: + + @staticmethod + def compile(nml_file: Path, grf_file: Path, lang_directory: Path): try: + # Check if we have the nml package + import nml.main + # setup parameters for nml + parameters = [str(nml_file), "--grf", str(grf_file)] + if lang_directory.exists(): + # If we have a lang directory, add it to the parameters + parameters += ["--lang", str(lang_directory)] # Try to compile the nml file + print("nml parameters: %s" % parameters) nml.main.main(parameters) + except ImportError: + # nml isn't installed + print("nml is not installed. You can get it using 'pip install nml'") + return False except SystemExit: # nml uses sys.exit(), so catch this to stop the program exiting - print("nml tried to exit but was stopped") - print("Finished compiling grf file\n") - return 1 - else: - # nml isn't installed - print("nml is not installed. You can get it using 'pip install nml'") - return -2 - - -def run_game(grf_name): - from sys import platform - - print("Detecting platform") - - # Change default paths depending on whether we use Linux or Windows (sorry OSX) - if platform.startswith("linux"): - newgrf_dir = Path.home().joinpath(".openttd", "newgrf") - executable_path = "/usr/bin/openttd" - kill_cmd = ["killall","openttd"] - print("Detected as Linux") - elif platform.startswith("win32"): - newgrf_dir = Path.home().joinpath("Documents", "OpenTTD", "newgrf") - executable_path = "C:/Program Files/OpenTTD/openttd.exe" - kill_cmd = ["taskkill.exe" "/IM" "OpenTTD.exe"] - print("Detected as Windows") - else: - print("Detected as Other. Cannot run game.") - return -3 - - print("Attempting to read config") - json_read_ok = False - # Check that the config file exists - if Path("build/build.json").exists(): - from json import load, decoder - with open("build/build.json") as json_data: - # Try to read the config file - try: - data = load(json_data) - # Errors if the file in invalid - except decoder.JSONDecodeError: - print("The config file is invalid") - json_read_ok = False - else: - # Read successfully - # Try to read the keys from the json file + print("Finished compiling grf file: %s\n" % grf_file) + return True + + +class Game: + + @staticmethod + def _detect_platform_settings(platform_settings_file: Path): + print("Detecting platform settings") + platform_settings = {} + + if platform_settings_file.exists(): + print("Found existing platform settings file %s" % platform_settings_file) + with platform_settings_file.open("r") as file: try: - newgrf_dir = data["newgrf_dir"] - executable_path = data["executable"] - # Errors if not all keys are found - except KeyError: - print("The config json file is invalid") - json_read_ok = False - # Read successfully, set read_ok to true - else: - json_read_ok = True - print("Read config successfully") - - # If reading the json didn't work - if not json_read_ok: - from json import dump - from os import access, X_OK - - print("No config, require user input") - - # Prompt the user for the "newgrf" directory until we get something like it - while not Path(newgrf_dir).exists(): - newgrf_dir = input("Enter the newgrf directory: ") - if len(newgrf_dir) > 6: - newgrf_dir = "~/.openttd/newgrf" - continue - if newgrf_dir[-6:] != "newgrf": - newgrf_dir = "~/.openttd/newgrf" - - # Prompt the user for the executable path until we get an executable - while not Path(executable_path).exists(): - executable_path = input("Enter the OpenTTD executable path: ") - if not (access(executable_path, X_OK)): - executable_path = "/usr/bin/openttd" - - # Dump the two paths to a json file for next time - with open("build/build.json", "w") as json_data: - data = { - "newgrf_dir": str(newgrf_dir), - "executable": str(executable_path) + platform_settings = json.loads(file.read()) + except json.decoder.JSONDecodeError: + print("Settings in file was invalid json. Ignoring.") + + elif sys.platform.startswith("linux"): + platform_settings = { + "newgrf_directory": Path.home().joinpath(".openttd", "newgrf"), + "executable_path": "/usr/bin/openttd", + "executable_params": ["-t", "1920", "-g"], + "kill_cmd": ["killall", "openttd"], + } + + elif sys.platform.startswith("win32"): + platform_settings = { + "newgrf_directory": str(Path.home().joinpath("Documents", "OpenTTD", "newgrf")), + "executable_path": "C:/Program Files/OpenTTD/openttd.exe", + "executable_params": ["-t", "1920", "-g"], + "kill_cmd": ["taskkill.exe", "/IM", "openttd.exe"], } - dump(data, json_data) - - from shutil import copy - from subprocess import Popen - from os import devnull - - # Kill existing processes - print("Killing existing processes") - try: - kill_process = Popen(kill_cmd) - kill_process.wait() - except: - print("Something went wrong when trying to kill processes") - - # Copy grf - print("Copying grf") - copy("build/" + grf_name + ".grf", Path(newgrf_dir)) - - # Run the game in it's root directory - print("Running game\n") - # Redirect stdout and stderr - null = open(devnull, "w") - Popen([executable_path, "-t", "2050", "-g"], cwd=Path(executable_path).parent, stdout=null, stderr=null) - null - return 2 - - -def main(grf_name, src_dir, lang_dir, gfx_dir, b_compile_grf, b_run_game): - src_directory = Path("src") - lang_directory = Path("lang") - gfx_directory = Path("gfx") - - nml_file = "" - has_lang_dir = False - - # Check if the project is set up properly and we have a lang directory - (has_lang_dir, - error_code) = check_project_structure(src_directory, gfx_directory, - lang_directory) - if error_code != 0: - return -1 - - # Add the special files to the internal nml file - nml_file = copy_file(src_directory.joinpath("grf.pnml"), nml_file) - nml_file = copy_file(src_directory.joinpath("railtypes.pnml"), nml_file) - nml_file = copy_file(src_directory.joinpath("templates.pnml"), nml_file) - - # Get a list of all the pnml files in src - file_list = find_pnml_files(src_directory) - print("Finished finding pnml files\n") - pnml_files = list() - # Read all the files in folders that begin with "_" into the internal nml - for directory in file_list: - if directory.startswith("_"): - for file in file_list[directory]: - print("Reading '%s'" % (file.stem + file.suffix)) - nml_file = copy_file(file, nml_file) - else: - pnml_files += file_list[directory] - - # Read the regular files - for file in sorted(pnml_files): - print("Reading '%s'" % (file.stem + file.suffix)) - nml_file = copy_file(file, nml_file) - - print("Copied all files to internal buffer\n") - - # Try to write the internal nml to a file - if write_file(grf_name, nml_file) != 0: - print("The nml file failed to compile") - return -1 - - # If we're compiling or running the game - if b_compile_grf or b_run_game: + + platform_settings["platform"] = sys.platform + print("Detected platform settings: %s" % platform_settings) + + for setting in ("newgrf_directory", "executable_path"): + path = Path(platform_settings.get(setting, "/dev/non-existent-path")) + while not path.exists(): + platform_settings[setting] = input("Setting %s: %s is not valid, enter valid path: " % (setting, path)) + path = Path(platform_settings.get(setting)) + + for setting in ["executable_params", "kill_cmd"]: + while not platform_settings.get(setting): + platform_settings[setting] = input("Enter %s: " % setting).split(" ") + + print("File %s - Saving platform settings: %s" % (platform_settings_file, platform_settings)) + with platform_settings_file.open("w") as file: + file.write(json.dumps(platform_settings, indent=4)) + + return platform_settings + + @staticmethod + def _kill(platform_settings: dict): + print("Killing existing processes using kill_cmd: %s" % platform_settings["kill_cmd"]) + try: + kill_process = Popen(platform_settings["kill_cmd"]) + kill_process.wait() + except Exception as e: + print("Something went wrong when trying to kill processes: %s" % e) + + @staticmethod + def _copy_grf(grf_file: Path, platform_settings: dict): + print("Copying %s to %s" % (grf_file, platform_settings["newgrf_directory"])) + copy(grf_file, Path(platform_settings["newgrf_directory"])) + + @staticmethod + def _run(platform_settings: dict): + # Run the game in it's root directory + print("Running game: %s" % platform_settings["executable_path"]) + # Redirect stdout and stderr + with open(os.devnull, "w") as null: + subprocess = Popen( + [platform_settings["executable_path"], *platform_settings["executable_params"]], + cwd=Path(platform_settings["executable_path"]).parent, + stdout=null, + stderr=null, + ) + subprocess.wait() + return 0 + + @classmethod + def run(cls, grf_file: Path): + platform_settings = cls._detect_platform_settings(grf_file.parent.joinpath("platform_settings.json")) + cls._kill(platform_settings) + cls._copy_grf(grf_file, platform_settings) + return cls._run(platform_settings) + + +# Main section + + +def parse_args(): + # Parse arguments + parser = ArgumentParser(description="Compile pnml files into one nml/grf file") + parser.add_argument("name") + parser.add_argument("--src", default="src", help="Source files directory") + parser.add_argument("--lang", default="lang", help="Language files directory") + parser.add_argument("--builddir", default="build", help="Build files directory") + parser.add_argument("--compile", action="store_true", help="Compile the nml file with nml lib") + parser.add_argument("--run", action="store_true", help="Run the game after compilation") + return parser.parse_args() + + +def main(): + args = parse_args() + src_directory = Path(args.src) + lang_directory = Path(args.lang) + build_directory = Path(args.builddir) + + # Check if the project is set up properly + if not check_project_structure(src_directory, lang_directory): + return 1 + + # Build directory and compiled files + if not build_directory.exists(): + build_directory.mkdir(parents=True) + nml_file = build_directory.joinpath("%s.nml" % args.name) + grf_file = build_directory.joinpath("%s.grf" % args.name) + + # Compile all pnml files into one nml + pnm_compiler = PnmlCompiler(src_directory, build_directory) + pnm_compiler.compile(nml_file) + + # If we're compiling grf or running the game + if args.compile or args.run: # Try to compile the GRF - error = compile_grf(has_lang_dir, grf_name, lang_dir) - if error == -2: - return -2 - elif b_run_game == False: + if not GrfCompiler.compile(nml_file, grf_file, lang_directory): return 1 - - # Optionally run the game - if b_run_game: - return run_game(grf_name) + # Optionally run the game + if args.run: + return Game.run(grf_file) return 0 if __name__ == "__main__": - # Parser arguments - parser = ArgumentParser(description="Compile pnml files into one nml file") - parser.add_argument("grf_name") - parser.add_argument("--src", default="src", help="Source files directory") - parser.add_argument("--lang", - default="lang", - help="Language files directory") - parser.add_argument("--gfx", - default="gfx", - help="Graphics files directory") - parser.add_argument("--compile", - action="store_true", - help="Compile the nml file with nmlc") - parser.add_argument( - "--run", - action="store_true", - help="Run the game after compilation (will also compile the file. Also kills existing instances)") - args = parser.parse_args() - - # Reports any errors in the nml file compilation process - error_code = main(args.grf_name, args.src, args.lang, args.gfx, - args.compile, args.run) - if error_code == -1: - print( - "The nml file failed to compile properly. Please consult the log") - elif error_code == -2: - print("The nml file compiled correctly, but nml failed to compile it") - elif error_code == -3: - print( - "The grf file compiled successfully but the game failed to start") - elif error_code == 1: - print("The grf file was compiled successfully") - elif error_code == 2: - print( - "The grf file was compiled successfully, and the game was started") - else: - print("The nml file was compiled successfully (this is the not grf)") + sys.exit(main()) diff --git a/gfx/Edward_James.PNG b/gfx/Edward_James.PNG deleted file mode 100644 index e7601b2..0000000 Binary files a/gfx/Edward_James.PNG and /dev/null differ diff --git a/gfx/Edward_James.png b/gfx/Edward_James.png new file mode 100644 index 0000000..8c8ae85 Binary files /dev/null and b/gfx/Edward_James.png differ diff --git a/gfx/Express_Coaches.PNG b/gfx/Express_Coaches.PNG deleted file mode 100644 index 1439185..0000000 Binary files a/gfx/Express_Coaches.PNG and /dev/null differ diff --git a/gfx/Express_Coaches.png b/gfx/Express_Coaches.png new file mode 100644 index 0000000..0a7462a Binary files /dev/null and b/gfx/Express_Coaches.png differ diff --git a/gfx/Gordon.PNG b/gfx/Gordon.PNG deleted file mode 100644 index d85b64d..0000000 Binary files a/gfx/Gordon.PNG and /dev/null differ diff --git a/gfx/Gordon.png b/gfx/Gordon.png new file mode 100644 index 0000000..f022f17 Binary files /dev/null and b/gfx/Gordon.png differ diff --git a/gfx/Henry.PNG b/gfx/Henry.PNG deleted file mode 100644 index b6e9a95..0000000 Binary files a/gfx/Henry.PNG and /dev/null differ diff --git a/gfx/Henry.png b/gfx/Henry.png new file mode 100644 index 0000000..64be706 Binary files /dev/null and b/gfx/Henry.png differ diff --git a/gfx/Pip_Emma.PNG b/gfx/Pip_Emma.PNG deleted file mode 100644 index 4649422..0000000 Binary files a/gfx/Pip_Emma.PNG and /dev/null differ diff --git a/gfx/Pip_Emma.png b/gfx/Pip_Emma.png new file mode 100644 index 0000000..912431e Binary files /dev/null and b/gfx/Pip_Emma.png differ diff --git a/gfx/Thomas_Percy_AnnieClarabel.PNG b/gfx/Thomas_Percy_AnnieClarabel.PNG deleted file mode 100644 index bac9c89..0000000 Binary files a/gfx/Thomas_Percy_AnnieClarabel.PNG and /dev/null differ diff --git a/gfx/Thomas_Percy_AnnieClarabel.png b/gfx/Thomas_Percy_AnnieClarabel.png new file mode 100644 index 0000000..6bbb8e6 Binary files /dev/null and b/gfx/Thomas_Percy_AnnieClarabel.png differ diff --git a/gfx/Toby.PNG b/gfx/Toby.PNG deleted file mode 100644 index cc54da0..0000000 Binary files a/gfx/Toby.PNG and /dev/null differ diff --git a/gfx/Toby.png b/gfx/Toby.png new file mode 100644 index 0000000..bd12181 Binary files /dev/null and b/gfx/Toby.png differ diff --git a/lang/english.lng b/lang/english.lng index 4d2c20e..c2bf5b5 100644 --- a/lang/english.lng +++ b/lang/english.lng @@ -1,56 +1,60 @@ ##grflangid 0x01 # GRF name and description -STR_GRF_NAME :OpenTTE - A Thomas the Tank Engine Set -STR_GRF_DESC :A Thomas The Tank Engine trainset for OpenTTD.{}{}{WHITE}{COPYRIGHT}2020 {LTBLUE}Audigex{WHITE}{}{WHITE}License: {LTBLUE}GPL v2 +STR_GRF_NAME :OpenTTE - A Thomas The Tank Engine Set +STR_GRF_DESC :A Thomas The Tank Engine trainset for OpenTTD.{}{}{WHITE}{COPYRIGHT}2020 {LTBLUE}Audigex{WHITE}{}{WHITE}License: {LTBLUE}GPL v2 # Parameters -STR_PARAM_NAME_SOUNDS :Use sounds -STR_PARAM_DESC_SOUNDS :Enable or Disable Running Sounds for OpenTTE -STR_PARAM_SOUNDS_OFF :Disabled -STR_PARAM_SOUNDS_ON :Enabled +STR_PARAM_DUPLICITY_NAME :Allow duplicity +STR_PARAM_DUPLICITY_DESC :Allow buying the same engine more than once. You can have more Thomases or more Gordons, etc... +STR_PARAM_DUPLICITY_OFF :Disabled +STR_PARAM_DUPLICITY_ON :Enabled +STR_PARAM_SOUNDS_NAME :Use sounds +STR_PARAM_SOUNDS_DESC :Enable or Disable Running Sounds for OpenTTE +STR_PARAM_SOUNDS_OFF :Disabled +STR_PARAM_SOUNDS_ON :Enabled # Messages -STR_CANNOT_ATTACH :Cannot attach a different class of train +STR_CANNOT_ATTACH :Cannot attach a different class of train # Vehicle types -str_purchase_type_diesel :{RED}Diesel locomotive -str_purchase_type_steam :{GREEN}Steam Locomotive -str_purchase_type_dmu :{RED}Diesel Multiple Unit -str_purchase_type_demu :{RED}Diesel{BLACK}-{BLUE}Electric {PURPLE}Multiple Unit -str_purchase_type_emu :{BLUE}Electric Multiple Unit -str_purchase_type_electric :{BLUE}Electric Locomotive +str_purchase_type_diesel :{RED}Diesel locomotive +str_purchase_type_steam :{GREEN}Steam Locomotive +str_purchase_type_dmu :{RED}Diesel Multiple Unit +str_purchase_type_demu :{RED}Diesel{BLACK}-{BLUE}Electric {PURPLE}Multiple Unit +str_purchase_type_emu :{BLUE}Electric Multiple Unit +str_purchase_type_electric :{BLUE}Electric Locomotive # Vehicle names etc STR_Thomas_Name :Thomas The Tank Engine (#1) STR_Thomas_Desc :An 0-6-0 Tank Engine, and a really useful engine STR_Thomas_Usage :Branch Lines -STR_Edward_Name :Edward the Blue Engine (#2) +STR_Edward_Name :Edward The Blue Engine (#2) STR_Edward_Desc :A 4-4-0 Locomotive STR_Edward_Usage :Local Passenger Services -STR_Henry_Name :Henry the Green Engine (#3) +STR_Henry_Name :Henry The Green Engine (#3) STR_Henry_Desc :4-6-0 Large Locomotive STR_Henry_Usage :Long Distance Passenger and Heavy Freight -STR_Gordon_Name :Gordon the Big Engine (#4) +STR_Gordon_Name :Gordon The Big Engine (#4) STR_Gordon_Desc :4-6-2 Express Locomotive STR_Gordon_Usage :Express Passenger Services -STR_James_Name :James the Red Engine (#5) +STR_James_Name :James The Red Engine (#5) STR_James_Desc :A 2-6-0 Locomotive STR_James_Usage :Mixed Freight and Passengers -STR_Percy_Name :Percy the Small Engine (#6) +STR_Percy_Name :Percy The Small Engine (#6) STR_Percy_Desc :A small 0-4-0 Tank Engine STR_Percy_Usage :Shunting and short distance Freight -STR_Toby_Name :Toby the Tram (#7) +STR_Toby_Name :Toby The Tram (#7) STR_Toby_Desc :An 0-6-0 Tram STR_Toby_USage :Branch Lines, particularly in towns -STR_Duck_Name :Duck the Great Western Engine (#8) +STR_Duck_Name :Duck The Great Western Engine (#8) STR_Duck_Desc :A large 0-6-0 tank engine STR_Duck_Usage :Branch Lines, particularly Freight @@ -58,11 +62,11 @@ STR_DonaldDouglas_Name :Donald and Douglas (#9 & #10) STR_DonaldDouglas_Desc :Light 0-6-0 Locomotives STR_DonaldDouglas_Usage :Freight (and snowplowing) -STR_Oliver_Name :Oliver the Great Western Engine (#11) +STR_Oliver_Name :Oliver The Great Western Engine (#11) STR_Oliver_Desc :An 0-6-0 Tank Engine STR_Oliver_Usage :Branch Lines (Passenger) -STR_Daisy_Name :Daisy the Diesel Railcar (D1) +STR_Daisy_Name :Daisy The Diesel Railcar (D1) STR_Daisy_Desc :A Diesel Railcar (BR 101 Protype) STR_Daisy_Usage :Short Distance Passenger @@ -74,14 +78,15 @@ STR_Bear_Name :Bear (D3) STR_Bear_Desc :A Diesel-Hydrualic Locomotive (BR 35) STR_Bear_Usage :Local Distance Freight or Passenger -STR_PipEmma_Name :Pip And Emma -STR_PipEmma_Desc :Diesl-Electric High Speed Locomotives (BR43/IC125) +STR_PipEmma_Name :Pip and Emma +STR_PipEmma_Desc :Diesel-Electric High Speed Locomotives (BR43/IC125) STR_PipEmma_Usage :High Speed Passenger Express -STR_AnnieClarabel_Name :Annie and Clarabel (Coaches) -STR_AnnieClarabel_Desc :Passenger Coaches +STR_Annie_Name :Annie (Coach) +STR_Clarabel_Name :Clarabel (Coach) +STR_AnnieClarabel_Desc :Passenger Coache STR_AnnieClarabel_Usage :Local/Branchline passenger services -STR_AnnieClarabel_liveries :Liveries: Annie, Clarabel +STR_AnnieClarabel_Liveries :Liveries: Annie, Clarabel STR_Henrietta_Name :Henrietta (Coach) STR_Henrietta_Desc :A passenger coach, and Toby's good friend @@ -89,12 +94,18 @@ STR_Henrietta_Usage :Coupled to Toby, for branch line services STR_Elsie_Name :Elsie (Brake Van) STR_Elsie_Desc :A brake van, and Toby's friend -STR_Elsie_Usage : Coupled to Toby and Henrietta +STR_Elsie_Usage :Coupled to Toby and Henrietta -STR_ExpressCoach_Name :Express Coach -STR_ExpressCoach_Desc :Fast coaches for the express +STR_ExpressCoach_RWS_Name :Express RWS (Coach) +STR_ExpressCoach_Second_Name :Express Second Class (Coach) +STR_ExpressCoach_First_Name :Express First Class (Coach) +STR_ExpressCoach_Desc :Fast coach for the express STR_ExpressCoach_Usage :Express Passenger -STR_ExpressCoach_liveries :Liveries: RWS, First Class, Second Class +STR_ExpressCoach_Liveries :Liveries: RWS, First Class, Second Class + +STR_PipEmma_Coach_Name :Express Inter City 125 (Coach) +STR_PipEmma_Coach_Desc :Inter City 125 Coach for Pip and Emma +STR_PipEmma_Coach_Usage :Coupled to Pip and Emma, for branch line services STR_Bertie_Name :Bertie The Bus STR_Bertie_Desc :A single deck bus (AEC Regal) @@ -104,7 +115,7 @@ STR_Bulgie_Name :Bulgie The Bus STR_Bulgie_Desc :A Double Decker Bus (AEC Bridgemaster) STR_Bulgie_Usage :Short Distance Passenger Services -STR_Harold_Name :Harold the Helicopter +STR_Harold_Name :Harold The Helicopter STR_Harold_Desc :A Helicopter STR_Harold_Usage :Just flying around @@ -112,5 +123,3 @@ STR_Harold_Usage :Just flying around STR_Livery_ExpressCoach_RWS :RWS STR_Livery_ExpressCoach_First :First Class STR_Livery_ExpressCoach_Second :Second Class -STR_Livery_Annie :Annie -STR_Livery_Clarabel :Clarabel \ No newline at end of file diff --git a/src/grf.pnml b/src/grf.pnml index c3a7a2b..8e2783e 100644 --- a/src/grf.pnml +++ b/src/grf.pnml @@ -1,22 +1,38 @@ // GRF definition block grf { - grfid: "otte"; // OpenTTE, a Thomas The Tank Engine train set for OpenTTD - name: string(STR_GRF_NAME); - desc: string(STR_GRF_DESC); - version: 0; - min_compatible_version: 0; //bump up this number to version when breaking compatibility - param 0 { - param_sounds{ - name: string(STR_PARAM_NAME_SOUNDS); - desc: string(STR_PARAM_DESC_SOUNDS); - min_value: 0; - max_value: 1; - def_value: 1; - names: { - 0: string(STR_PARAM_SOUNDS_OFF); - 1: string(STR_PARAM_SOUNDS_ON); - }; - } - } + grfid: "otte"; // OpenTTE, a Thomas The Tank Engine train set for OpenTTD + name: string(STR_GRF_NAME); + desc: string(STR_GRF_DESC); + version: 1; + min_compatible_version: 1; //bump up this number to version when breaking compatibility + param { + param_duplicity { + name: string(STR_PARAM_DUPLICITY_NAME); + desc: string(STR_PARAM_DUPLICITY_DESC); + min_value: 0; + max_value: 1; + def_value: 0; + names: { + 0: string(STR_PARAM_DUPLICITY_OFF); + 1: string(STR_PARAM_DUPLICITY_ON); + }; + } + } + param { + param_sounds { + name: string(STR_PARAM_SOUNDS_NAME); + desc: string(STR_PARAM_SOUNDS_DESC); + min_value: 0; + max_value: 1; + def_value: 1; + names: { + 0: string(STR_PARAM_SOUNDS_OFF); + 1: string(STR_PARAM_SOUNDS_ON); + }; + } + } } -disable_item(FEAT_TRAINS); \ No newline at end of file + +// Disable default engines +disable_item(FEAT_TRAINS, 0, 26); +disable_item(FEAT_TRAINS, 54, 115); diff --git a/src/sortpurchase.pnml b/src/sortpurchase.pnml index df730a3..693267c 100644 --- a/src/sortpurchase.pnml +++ b/src/sortpurchase.pnml @@ -1,12 +1,18 @@ sort(FEAT_TRAINS, [ - item_Thomas, - item_Edward, - item_Henry, - item_Gordon, - item_James, - item_Percy, - item_Toby, - item_PipEmma, // IC125/Class 253 - item_AnnieClarabel, - item_ExpressCoach - ]); \ No newline at end of file + item_Thomas, + item_Edward, + item_Henry, + item_Gordon, + item_James, + item_Percy, + item_Toby, + item_PipEmma, // IC125/Class 253 + item_Annie, + item_Clarabel, + item_ExpressCoach_RWS, + item_ExpressCoach_Second, + item_ExpressCoach_First, + item_PipEmma_Coach, + item_Henrietta, + item_Elsie + ]); diff --git a/src/templates.pnml b/src/templates.pnml index 0fdc8f9..1500354 100644 --- a/src/templates.pnml +++ b/src/templates.pnml @@ -1,128 +1,141 @@ -template tmpl_purchase(x,y){ +template tmpl_purchase(x, y) { //xpos, ypos, width, height, xoffset, yoffset, Flags - [x, y, 87, 12, -25,- 6, NOWHITE] + [x, y, 68, 12, -25, -6, NOWHITE] } +template tmpl_purchase_dual_head(x, y, w) { + //xpos, ypos, width, height, xoffset, yoffset, Flags + [x, y, 1, 1, 0, 0, NOWHITE] + [x, y, 1, 1, 0, 0, NOWHITE] + [x+w, y, 68-w, 12, -11, -6, NOWHITE] + [x, y, 1, 1, 0, 0, NOWHITE] + [x, y, 1, 1, 0, 0, NOWHITE] + [x, y, 1, 1, 0, 0, NOWHITE] + [x, y, w, 12, -11, -6, NOWHITE] + [x, y, 1, 1, 0, 0, NOWHITE] +} + + // Used for Gordon and Henry template tmpl_loco_29(x,y) { //leftx, topy, width, height, offsetx, offsety - [x, y, 8, 22, -2, -14] // N - [x+9, y, 18, 15, -9, -8] // NE - [x+28, y, 29, 12, 4, -6] // E - [x+58, y, 18, 15, 4, -1] // SE - [x+77, y, 8, 22, -3, -2] // S - [x+86, y, 18, 15, -19, -2] // SW - [x+105, y, 29, 12, -12, -8] // W - [x+135, y, 18, 15, -10, -8] // NW + [x, y, 8, 22, -3, -14] // N + [x+9, y, 18, 15, -8, -8] // NE + [x+28, y, 29, 12, -2, -8] // E + [x+58, y, 18, 15, 5, -3] // SE + [x+77, y, 8, 22, -3, -3] // S + [x+86, y, 18, 15, -23, -1] // SW + [x+105, y, 29, 12, -28, -8] // W + [x+135, y, 18, 14, -11, -9] // NW } template tmpl_James(x,y) { //leftx, topy, width, height, offsetx, offsety - [x, y, 8, 18, -2, -14] // N - [x+9, y, 14, 13, -8, -7] // NE - [x+24, y, 21, 11, 4, -5] // E - [x+46, y, 15, 13, 4, -1] // SE - [x+62, y, 8, 16, -3, -2] // S - [x+71, y, 15, 13, -15, -3] // SW - [x+87, y, 21, 11, -4, -8] // W - [x+109, y, 14, 13, -6, -7] // NW + [x, y, 8, 18, -3, -14] // N + [x+9, y, 14, 13, -7, -8] // NE + [x+24, y, 21, 11, -3, -7] // E + [x+46, y, 15, 13, 4, -3] // SE + [x+62, y, 8, 16, -3, -2] // S + [x+71, y, 15, 13, -20, -1] // SW + [x+87, y, 21, 11, -19, -7] // W + [x+109, y, 14, 13, -8, -9] // NW } template tmpl_Edward(x,y) { //leftx, topy, width, height, offsetx, offsety - [x, y, 8, 19, -2, -14] // N - [x+9, y, 16, 14, -8, -8] // NE - [x+26, y, 25, 11, -8, -8] // E - [x+52, y, 16, 14, 5, 0] // SE - [x+69, y, 8, 18, -3, -2] // S - [x+78, y, 16, 14, -16, -3] // SW - [x+95, y, 25, 11, -4, -8] // W - [x+121, y, 16, 14, -8, -8] // NW + [x, y, 8, 19, -3, -11] // N + [x+9, y, 16, 14, -8, -8] // NE + [x+26, y, 25, 11, -6, -7] // E + [x+52, y, 16, 14, 2, -4] // SE + [x+69, y, 8, 18, -3, -7] // S + [x+78, y, 16, 14, -19, -3] // SW + [x+95, y, 25, 11, -20, -7] // W + [x+121, y, 16, 14, -7, -7] // NW } // Tenders for Gordon, Henry, James, Edward template tmpl_tender_16(x, y) { //leftx, topy, width, height, offsetx, offsety - [x, y, 8, 14, -2, -10] // N - [x+9, y, 12, 10, -6, -5] // NE - [x+22, y, 16, 10, -3, -7] // E - [x+39, y, 12, 10, 6, 2] // SE - [x+52, y, 8, 14, -3, 0] // S - [x+61, y, 12, 10, -15, 0] // SW - [x+74, y, 16, 10, -4, -6] // W - [x+91, y, 12, 10, -6, -6] // NW + [x, y, 8, 14, -3, -10] // N + [x+9, y, 12, 10, -6, -5] // NE + [x+22, y, 16, 10, 2, -6] // E + [x+39, y, 12, 10, 5, -1] // SE + [x+52, y, 8, 14, -3, -1] // S + [x+61, y, 12, 10, -18, 0] // SW + [x+74, y, 16, 10, -19, -6] // W + [x+91, y, 12, 10, -6, -6] // NW } template tmpl_thomas(x, y) { //leftx, topy, width, height, offsetx, offsety - [x, y, 8, 17, -3, -10] // N - [x+9, y, 14, 13, -9, -7] // NE - [x+24, y, 21, 12, 1, -7] // E - [x+46, y, 15, 13, 2, -2] // SE - [x+62, y, 8, 16, -3, -6] // S - [x+71, y, 14, 13, -15, 0] // SW - [x+86, y, 21, 11, 0, -8] // W - [x+108, y, 14, 13, -5, -7] // NW + [x, y, 8, 17, -3, -10] // N + [x+9, y, 14, 13, -9, -6] // NE + [x+24, y, 21, 12, -6, -8] // E + [x+46, y, 15, 13, 2, -4] // SE + [x+62, y, 8, 16, -3, -6] // S + [x+71, y, 14, 13, -17, -3] // SW + [x+86, y, 21, 11, -16, -7] // W + [x+108, y, 14, 13, -5, -7] // NW } template tmpl_percy(x, y) { //leftx, topy, width, height, offsetx, offsety - [x, y, 8, 15, -3, -7] // N - [x+9, y, 13, 12, -9, -6] // NE - [x+23, y, 17, 12, -8, -7] // E - [x+41, y, 12, 12, 2, -3] // SE - [x+54, y, 8, 14, -3, -5] // S - [x+63, y, 12, 12, -13, -3] // SW - [x+76, y, 17, 12, -1, -8] // W - [x+94, y, 13, 12, -3, -6] // NW + [x, y, 8, 15, -3, -7] // N + [x+9, y, 13, 12, -9, -5] // NE + [x+23, y, 17, 12, -6, -8] // E + [x+41, y, 12, 12, 2, -4] // SE + [x+54, y, 8, 14, -3, -5] // S + [x+63, y, 12, 12, -15, -2] // SW + [x+76, y, 17, 12, -12, -8] // W + [x+94, y, 13, 12, -6, -7] // NW } template tmpl_toby(x, y) { //leftx, topy, width, height, offsetx, offsety - [x, y, 8, 14, -3, -10] // N - [x+9, y, 14, 12, -7, -7] // NE - [x+24, y, 17, 11, -4, -7] // E - [x+42, y, 14, 12, 2, -2] // SE - [x+57, y, 8, 14, -3, 0] // S - [x+66, y, 14, 12, -19, 0] // SW - [x+81, y, 17, 11, -12, -7] // W - [x+99, y, 14, 12, -8, -6] // NW + [x, y, 8, 14, -3, -10] // N + [x+9, y, 14, 12, -7, -7] // NE + [x+24, y, 17, 11, -6, -6] // E + [x+42, y, 14, 12, 3, -4] // SE + [x+57, y, 8, 14, -3, 0] // S + [x+66, y, 14, 12, -18, -2] // SW + [x+81, y, 17, 11, -12, -6] // W + [x+99, y, 14, 12, -6, -6] // NW } template tmpl_coach_21(x, y) { //leftx, topy, width, height, offsetx, offsety - [x, y, 8, 16, -3, -8] // N - [x+9, y, 14, 12, -6, -7] // NE - [x+24, y, 21, 10, -4, -6] // E - [x+46, y, 14, 12, 2, -2] // SE - [x+61, y, 8, 16, -3, 0] // S - [x+70, y, 14, 12, -19, 0] // SW - [x+85, y, 21, 10, -16, -6] // W - [x+107, y, 14, 12, -8, -7] // NW + [x, y, 8, 16, -3, -8] // N + [x+9, y, 14, 12, -7, -7] // NE + [x+24, y, 21, 10, -6, -5] // E + [x+46, y, 14, 12, 3, -3] // SE + [x+61, y, 8, 16, -3, 0] // S + [x+70, y, 14, 12, -17, -2] // SW + [x+85, y, 21, 10, -16, -4] // W + [x+107, y, 14, 12, -6, -6] // NW } template tmpl_brake_van(x, y) { //leftx, topy, width, height, offsetx, offsety - [x, y, 8, 12, -3, -8] // N - [x+9, y, 10, 10, -0, -8] // NE - [x+20, y, 13, 10, 8, -6] // E - [x+34, y, 10, 10, 4, -4] // SE - [x+45, y, 8, 12, -3, 0] // S - [x+54, y, 10, 10, -21, 3] // SW - [x+65, y, 13, 10, -7, -6] // W - [x+79, y, 10, 10, -6, -6] // NW + [x, y, 8, 12, -3, -8] // N + [x+9, y, 10, 10, -4, -5] // NE + [x+20, y, 13, 10, 2, -5] // E + [x+34, y, 10, 10, 5, -2] // SE + [x+45, y, 8, 12, -3, 0] // S + [x+54, y, 10, 10, -16, 0] // SW + [x+65, y, 13, 10, -16, -5] // W + [x+79, y, 10, 10, -5, -6] // NW } template tmpl_coach_33(x, y) { //leftx, topy, width, height, offsetx, offsety - [x, y, 8, 24, -3, -8] // N - [x+9, y, 20, 16, -15, -7] // NE - [x+30, y, 33, 11, -23, -8] // E - [x+64, y, 20, 16, -7, -7] // SE + [x, y, 8, 24, -3, -8] // N + [x+9, y, 20, 16, -16, -6] // NE + [x+30, y, 33, 11, -18, -6] // E + [x+64, y, 20, 16, -5, -8] // SE [x+85, y, 8, 24, -3, -13] // S - [x+94, y, 20, 16, -15, -6] // SW - [x+115, y, 33, 11, 0, -9] // W - [x+149, y, 20, 16, -4, -7] // NW + [x+94, y, 20, 16, -16, -6] // SW + [x+115, y, 33, 11, -16, -6] // W + [x+149, y, 20, 16, -4, -7] // NW } @@ -131,7 +144,6 @@ template tmpl_coach_33(x, y) { - switch (FEAT_TRAINS, SELF, diesel_create_visual_effect, [STORE_TEMP(create_effect(EFFECT_SPRITE_DIESEL, 0, 0, 10), 0x100)]) { return 1; } diff --git a/src/trains/253_PipEmma.pnml b/src/trains/253_PipEmma.pnml index a2c90f4..9c40f21 100644 --- a/src/trains/253_PipEmma.pnml +++ b/src/trains/253_PipEmma.pnml @@ -1,67 +1,112 @@ spriteset(spriteset_PipEmma_Purchase, "gfx/Pip_Emma.png") { - tmpl_coach_33(0, 13) + tmpl_purchase_dual_head(0, 0, 28) } -spriteset(spriteset_PipEmma, "gfx/Pip_Emma.png") { - tmpl_coach_33(0, 13) +spriteset(spriteset_PipEmma_Coach_Purchase, "gfx/Pip_Emma.png") { + tmpl_purchase(89, 0) } -switch(FEAT_TRAINS,SELF, sw_BRPipEmma,cargo_subtype){ - 0: spriteset_PipEmma; + +spriteset(spriteset_PipEmma, "gfx/Pip_Emma.png") { + //leftx, topy, width, height, offsetx, offsety + [0, 13, 8, 20, -3, -4] // N + [9, 13, 18, 15, -16, -5] // NE + [28, 13, 28, 12, -18, -6] // E + [57, 13, 18, 15, -5, -8] // SE + [76, 13, 8, 20, -3, -13] // S + [85, 13, 18, 15, -14, -6] // SW + [104, 13, 28, 12, -11, -6] // W + [133, 13, 18, 15, -2, -6] // NW } -switch(FEAT_TRAINS,SELF,sw_BRPipEmma_cargo_subtype_text,cargo_subtype){ - 0: return string(STR_PipEmma_Name); - return CB_RESULT_NO_TEXT; + +spriteset(spriteset_PipEmma_Coach, "gfx/Pip_Emma.png") { + tmpl_coach_33(0, 34) } -switch(FEAT_TRAINS,SELF,sw_BRPipEmma_sound_type,cargo_subtype){ - 0: return sw_hst_sound; - return sw_hst_sound; + + +switch(FEAT_TRAINS, SELF, sw_BRPip_sound_type, cargo_subtype) { + 0: return sw_hst_sound; + return sw_hst_sound; } + item (FEAT_TRAINS, item_PipEmma, 2531) { - property { - name: string(STR_PipEmma_Name); - climates_available: ALL_CLIMATES; - introduction_date: date(1975,4,21); - model_life: 12; - retire_early: 1; - vehicle_life: 50; - reliability_decay: 7; - refittable_cargo_classes: CC_PASSENGERS; - cargo_allow_refit: [PASS, TOUR]; - loading_speed: 3; - cost_factor: 43; - running_cost_factor: 52; - sprite_id: SPRITE_ID_NEW_TRAIN; - speed: 125 mph; - misc_flags: TRAIN_FLAG_MU; - refit_cost: 0; - track_type: RAIL; - ai_special_flag: AI_FLAG_CARGO; - power: 4500 hp; - running_cost_base: RUNNING_COST_DIESEL; - dual_headed: 1; - default_cargo_type: PASS; - cargo_capacity: 1; - weight: 214 ton; - engine_class: ENGINE_CLASS_DIESEL; - tractive_effort_coefficient: 0.3; - air_drag_coefficient: 0.1; - length: 8; - effect_spawn_model_and_powered: EFFECT_SPAWN_MODEL_DIESEL; - extra_weight_per_wagon: 0; - bitmask_vehicle_info: 0; - } - graphics { - additional_text: return(string(str_purchase_type_diesel,string(str_route_5),string(str_BRPipEmma_usage),string(str_BRPipEmma_eos),string(str_BRPipEmma_liveries))); - can_attach_wagon: CB_RESULT_ATTACH_ALLOW; - cargo_capacity: return 0; - cargo_subtype_text: sw_BRPipEmma_cargo_subtype_text; - default: sw_BRPipEmma; - purchase: spriteset_PipEmma_Purchase; - colour_mapping: return PALETTE_CC_FIRST; - create_effect: diesel_create_visual_effect; - sound_effect: sw_BRPipEmma_sound_type; + property { + name: string(STR_PipEmma_Name); + climates_available: ALL_CLIMATES; + introduction_date: date(1920,1,1); + model_life: VEHICLE_NEVER_EXPIRES; + vehicle_life: 18; + reliability_decay: 7; + refittable_cargo_classes: CC_PASSENGERS; + cargo_allow_refit: [PASS, TOUR]; + loading_speed: 10; + cost_factor: 180; + running_cost_factor: 200; + sprite_id: SPRITE_ID_NEW_TRAIN; + speed: 100 mph; + misc_flags: TRAIN_FLAG_MU; + refit_cost: 0; + track_type: RAIL; + ai_special_flag: AI_FLAG_CARGO; + power: 3200 hp; + running_cost_base: RUNNING_COST_DIESEL; + dual_headed: 1; + default_cargo_type: PASS; + cargo_capacity: 4; + weight: 200 ton; + engine_class: ENGINE_CLASS_DIESEL; + tractive_effort_coefficient: 0.3; + air_drag_coefficient: 0.1; + length: 8; + effect_spawn_model_and_powered: EFFECT_SPAWN_MODEL_DIESEL; + extra_weight_per_wagon: 0; + bitmask_vehicle_info: 0; + } + graphics { + additional_text: return(string(str_purchase_type_diesel,string(str_route_5),string(str_BRPip_usage),string(str_BRPip_eos),string(str_BRPip_liveries))); + can_attach_wagon: CB_RESULT_ATTACH_ALLOW; + cargo_capacity: return 0; + cargo_subtype_text: return(string(STR_PipEmma_Name)); + default: spriteset_PipEmma; + purchase: spriteset_PipEmma_Purchase; + colour_mapping: return PALETTE_CC_FIRST; + create_effect: diesel_create_visual_effect; + sound_effect: sw_BRPip_sound_type; + } +} + + +item (FEAT_TRAINS, item_PipEmma_Coach, 2532) { + property { + name: string(STR_PipEmma_Coach_Name); + climates_available: ALL_CLIMATES; + introduction_date: date(1920,1,1); + model_life: VEHICLE_NEVER_EXPIRES; + vehicle_life: 20; + cargo_allow_refit: [PASS, TOUR]; + loading_speed: 10; + cost_factor: 140; + running_cost_factor: 120; + speed: 100 mph; + power: 0; + sprite_id: SPRITE_ID_NEW_TRAIN; + refit_cost: 0; + running_cost_base: RUNNING_COST_STEAM; + default_cargo_type: PASS; + cargo_capacity: 168; + weight: 40 ton; + length: 8; + bitmask_vehicle_info: 0; + cargo_age_period: 185; + } + graphics { + additional_text: return(string(STR_PipEmma_Coach_Desc)); + can_attach_wagon: CB_RESULT_ATTACH_ALLOW; + cargo_subtype_text: return(string(STR_PipEmma_Coach_Name)); + default: spriteset_PipEmma_Coach; + purchase: spriteset_PipEmma_Coach_Purchase; + colour_mapping: return PALETTE_CC_FIRST; + } } -} \ No newline at end of file diff --git a/src/trains/AnnieClarabel_RWS.pnml b/src/trains/AnnieClarabel_RWS.pnml index da74cc6..dbffe84 100644 --- a/src/trains/AnnieClarabel_RWS.pnml +++ b/src/trains/AnnieClarabel_RWS.pnml @@ -1,25 +1,20 @@ -spriteset(spriteset_AnnieClarabel_Purchase, "gfx/Thomas_Percy_AnnieClarabel.png") { - tmpl_purchase(178, 0) +spriteset(spriteset_Annie_Purchase, "gfx/Thomas_Percy_AnnieClarabel.png") { + tmpl_purchase(178, 0) } +spriteset(spriteset_Clarabel_Purchase, "gfx/Thomas_Percy_AnnieClarabel.png") { + tmpl_purchase(267, 0) +} + + spriteset(spriteset_Annie, "gfx/Thomas_Percy_AnnieClarabel.png") { - tmpl_coach_33(0, 66) + tmpl_coach_33(0, 66) } spriteset(spriteset_Clarabel, "gfx/Thomas_Percy_AnnieClarabel.png") { - tmpl_coach_33(0, 91) + tmpl_coach_33(0, 91) } -switch(FEAT_TRAINS,SELF, sw_AnnieClarabel,cargo_subtype){ - 0: spriteset_Annie; - 1: spriteset_Clarabel; -} - -switch(FEAT_TRAINS,SELF,sw_AnnieClarabel_cargo_subtype_text,cargo_subtype){ - 0: return string(STR_Livery_Annie); - 1: return string(STR_Livery_Clarabel); - return CB_RESULT_NO_TEXT; -} item (FEAT_TRAINS, item_Annie, 10012) { property { diff --git a/src/trains/Edward.pnml b/src/trains/Edward.pnml index 88d1239..5c1393c 100644 --- a/src/trains/Edward.pnml +++ b/src/trains/Edward.pnml @@ -32,18 +32,17 @@ item (FEAT_TRAINS, item_Edward, 201) { property { name: string(STR_Edward_Name); climates_available: ALL_CLIMATES; - introduction_date: date(1925,1,1); - model_life: 100; - retire_early: 1; - vehicle_life: 100; + introduction_date: date(1920,1,1); + model_life: VEHICLE_NEVER_EXPIRES; + vehicle_life: 18; reliability_decay: 7; refittable_cargo_classes: 0; cargo_allow_refit: [GOOD]; loading_speed: 3; - cost_factor: 27; - running_cost_factor: 32; + cost_factor: 60; + running_cost_factor: 70; sprite_id: SPRITE_ID_NEW_TRAIN; - speed: 108 mph; + speed: 72 mph; misc_flags: TRAIN_FLAG_NO_BREAKDOWN_SMOKE; refit_cost: 0; track_type: RAIL; diff --git a/src/trains/Gordon.pnml b/src/trains/Gordon.pnml index 75a06fa..d4ca05a 100644 --- a/src/trains/Gordon.pnml +++ b/src/trains/Gordon.pnml @@ -32,28 +32,27 @@ item (FEAT_TRAINS, item_Gordon, 401) { property { name: string(STR_Gordon_Name); climates_available: ALL_CLIMATES; - introduction_date: date(1925,1,1); - model_life: 100; - retire_early: 1; - vehicle_life: 100; + introduction_date: date(1920,1,1); + model_life: VEHICLE_NEVER_EXPIRES; + vehicle_life: 18; reliability_decay: 7; refittable_cargo_classes: 0; cargo_allow_refit: [GOOD]; loading_speed: 3; - cost_factor: 27; - running_cost_factor: 32; + cost_factor: 80; + running_cost_factor: 100; sprite_id: SPRITE_ID_NEW_TRAIN; - speed: 108 mph; + speed: 92 mph; misc_flags: TRAIN_FLAG_NO_BREAKDOWN_SMOKE; refit_cost: 0; track_type: RAIL; ai_special_flag: AI_FLAG_CARGO; - power: 1536 hp; + power: 1600 hp; running_cost_base: RUNNING_COST_STEAM; dual_headed: 0; default_cargo_type: GOOD; cargo_capacity: 1; - weight: 103 ton; + weight: 100 ton; engine_class: ENGINE_CLASS_STEAM; tractive_effort_coefficient: 0.3; air_drag_coefficient: 0.1; diff --git a/src/trains/Henry.pnml b/src/trains/Henry.pnml index 7a86ccc..b789349 100644 --- a/src/trains/Henry.pnml +++ b/src/trains/Henry.pnml @@ -32,18 +32,17 @@ item (FEAT_TRAINS, item_Henry, 301) { property { name: string(STR_Henry_Name); climates_available: ALL_CLIMATES; - introduction_date: date(1925,1,1); - model_life: 100; - retire_early: 1; - vehicle_life: 100; + introduction_date: date(1920,1,1); + model_life: VEHICLE_NEVER_EXPIRES; + vehicle_life: 18; reliability_decay: 7; refittable_cargo_classes: 0; cargo_allow_refit: [GOOD]; loading_speed: 3; - cost_factor: 27; - running_cost_factor: 32; + cost_factor: 75; + running_cost_factor: 90; sprite_id: SPRITE_ID_NEW_TRAIN; - speed: 100 mph; + speed: 80 mph; misc_flags: TRAIN_FLAG_NO_BREAKDOWN_SMOKE; refit_cost: 0; track_type: RAIL; @@ -53,7 +52,7 @@ item (FEAT_TRAINS, item_Henry, 301) { dual_headed: 0; default_cargo_type: GOOD; cargo_capacity: 1; - weight: 91 ton; + weight: 90 ton; engine_class: ENGINE_CLASS_STEAM; tractive_effort_coefficient: 0.5; air_drag_coefficient: 0.1; diff --git a/src/trains/James.pnml b/src/trains/James.pnml index bd6c058..79a49cc 100644 --- a/src/trains/James.pnml +++ b/src/trains/James.pnml @@ -32,18 +32,17 @@ item (FEAT_TRAINS, item_James, 501) { property { name: string(STR_James_Name); climates_available: ALL_CLIMATES; - introduction_date: date(1925,1,1); - model_life: 100; - retire_early: 1; - vehicle_life: 100; + introduction_date: date(1920,1,1); + model_life: VEHICLE_NEVER_EXPIRES; + vehicle_life: 18; reliability_decay: 7; refittable_cargo_classes: 0; cargo_allow_refit: [GOOD]; loading_speed: 3; - cost_factor: 27; - running_cost_factor: 32; + cost_factor: 60; + running_cost_factor: 70; sprite_id: SPRITE_ID_NEW_TRAIN; - speed: 108 mph; + speed: 72 mph; misc_flags: TRAIN_FLAG_NO_BREAKDOWN_SMOKE; refit_cost: 0; track_type: RAIL; diff --git a/src/trains/Percy.pnml b/src/trains/Percy.pnml index 4321c4d..c7c2e65 100644 --- a/src/trains/Percy.pnml +++ b/src/trains/Percy.pnml @@ -23,18 +23,17 @@ item (FEAT_TRAINS, item_Percy, 601) { property { name: string(STR_Percy_Name); climates_available: ALL_CLIMATES; - introduction_date: date(1925,1,1); - model_life: 100; - retire_early: 1; - vehicle_life: 100; + introduction_date: date(1920,1,1); + model_life: VEHICLE_NEVER_EXPIRES; + vehicle_life: 18; reliability_decay: 7; refittable_cargo_classes: 0; cargo_allow_refit: [GOOD]; loading_speed: 3; - cost_factor: 27; - running_cost_factor: 32; + cost_factor: 40; + running_cost_factor: 50; sprite_id: SPRITE_ID_NEW_TRAIN; - speed: 70 mph; + speed: 60 mph; misc_flags: TRAIN_FLAG_NO_BREAKDOWN_SMOKE; refit_cost: 0; track_type: RAIL; diff --git a/vehicles.png b/vehicles.png new file mode 100644 index 0000000..4f9ac2a Binary files /dev/null and b/vehicles.png differ