├── icon.dds ├── hotspot.dds ├── FS22_Tardis.zip ├── .gitignore ├── New-ModZip.ps1 ├── TardisEvents.lua ├── RegisterSpecialization.lua ├── README.md ├── modDesc.xml └── Tardis.lua /icon.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sperrgebiet/FS22_Tardis/HEAD/icon.dds -------------------------------------------------------------------------------- /hotspot.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sperrgebiet/FS22_Tardis/HEAD/hotspot.dds -------------------------------------------------------------------------------- /FS22_Tardis.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sperrgebiet/FS22_Tardis/HEAD/FS22_Tardis.zip -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | /debug 43 | /DEBUG 44 | -------------------------------------------------------------------------------- /New-ModZip.ps1: -------------------------------------------------------------------------------- 1 | $srcPath = $PSScriptRoot 2 | $dstPath = $PSScriptRoot 3 | $dstFilename = "FS22_Tardis.zip" 4 | 5 | $tmpPath = Join-Path $env:TMP "TmpZip" 6 | 7 | $ignorelist = get-content (Join-Path $srcPath ".gitignore") 8 | $ignorelist += "/New-ModZip.ps1" 9 | $ignorelist += "/textures/png" 10 | $ignorelist += "/screenshots" 11 | $ignoreList += "/README.md" 12 | $ignoreList += "/DEBUG" 13 | 14 | $ignoreList = ($ignorelist | ?{ -not [string]::IsNullOrEmpty($_) }) 15 | 16 | if($srcPath.Length -gt 0 -and $dstPath.Length -gt 0) 17 | { 18 | Remove-Item (Join-Path $dstPath $dstFilename) -Force 19 | 20 | [array]$allFiles = Get-ChildItem -Path $srcPath -Exclude {.gitignore} -Recurse -Attributes !Directory 21 | [array]$allFolders = Get-ChildItem -Path $srcPath -Exclude {.gitignore} -Recurse -Attributes Directory 22 | 23 | 24 | New-Item -ItemType Directory -Path $tmpPath -Force 25 | 26 | foreach($f in $allFolders) 27 | { 28 | if( -not $ignorelist.contains($f.FullName.ToString().Replace("$srcPath", "").Replace("\","/")) ) 29 | { 30 | New-Item -ItemType Directory -Path $f.FullName.ToString().Replace($srcPath, $tmpPath) 31 | } 32 | } 33 | 34 | 35 | foreach($f in $allFiles) 36 | { 37 | if( -not $ignorelist.contains($f.DirectoryName.ToString().Replace("$srcPath", "").Replace("\","/")) ) 38 | { 39 | if( -not $ignorelist.contains($f.FullName.ToString().Replace("$srcPath", "").Replace("\","/")) ) 40 | { 41 | #$F.FullName 42 | #$f.FullName.ToString().Replace("$srcPath", "").Replace("\","/") 43 | Copy-Item $f.FullName -Destination $f.FullName.ToString().Replace($srcPath, $tmpPath) -Force 44 | } 45 | } 46 | } 47 | 48 | ##Compress-Archive -Path (Join-Path $tmpPath "\*") -DestinationPath (Join-Path $dstPath $dstFilename) 49 | # For some reason neither PS nor the .NET libraries create an archive suitable for FS19. the translations folder is included, but not used by the Giants engine 50 | # Switching to a quick and dirty Winrar solution for now 51 | $binary = "C:\Program Files\winrar\WinRAR.exe" 52 | $folder = Join-Path $tmpPath "\*" 53 | $file = Join-Path $dstPath $dstFilename 54 | $rarargs = @("a", "-afzip -ep1 -r", "`"$file`"", "`"$($folder)`"" ) 55 | Start-Process -FilePath $binary -ArgumentList $rarargs -Wait 56 | 57 | Remove-Item -Path $tmpPath -Recurse -Force 58 | 59 | ## MP? 60 | Copy-Item -Path $dstFilename -Destination "..\..\mods-mp\" -Force 61 | Copy-Item -Path $dstFilename -Destination "M:\TEMP3\FS22\" -Force 62 | } -------------------------------------------------------------------------------- /TardisEvents.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- MP Stuff 3 | 4 | -- Teleport 5 | 6 | TardisTeleportEvent = {} 7 | TardisTeleportEvent_mt = Class(TardisTeleportEvent, Event) 8 | 9 | InitEventClass(TardisTeleportEvent, "TardisTeleportEvent") 10 | 11 | function TardisTeleportEvent.emptyNew() 12 | local self = Event.new(TardisTeleportEvent_mt) 13 | self.className = "TardisTeleportEvent" 14 | return self 15 | end 16 | 17 | function TardisTeleportEvent.new(x, z, vehicle, isReset, isHotspot) 18 | local self = TardisTeleportEvent:emptyNew() 19 | self.x = x 20 | self.z = z 21 | self.vehicle = vehicle 22 | self.isReset = isReset 23 | self.isHotspot = isHotspot 24 | return self 25 | end 26 | 27 | function TardisTeleportEvent:writeStream(streamId, connection) 28 | streamWriteFloat32(streamId, self.x) 29 | streamWriteFloat32(streamId, self.z) 30 | NetworkUtil.writeNodeObject(streamId, self.vehicle) 31 | streamWriteBool(streamId, self.isReset) 32 | streamWriteBool(streamId, self.isHotspot) 33 | end 34 | 35 | function TardisTeleportEvent:readStream(streamId, connection) 36 | self.x = streamReadFloat32(streamId) 37 | self.z = streamReadFloat32(streamId) 38 | self.vehicle = NetworkUtil.readNodeObject(streamId) 39 | self.isReset = streamReadBool(streamId) 40 | self.isHotspot = streamReadBool(streamId) 41 | self:run(connection) 42 | end 43 | 44 | function TardisTeleportEvent:run(connection) 45 | Tardis:teleportToLocation(self.x, self.z, self.vehicle, self.isReset, self.isHotspot) 46 | end 47 | 48 | function TardisTeleportEvent.sendEvent(x, z, vehicle, isReset, isHotspot, noEventSend) 49 | if isReset == nil then 50 | isReset = false 51 | end 52 | 53 | if isHotspot == nil then 54 | isHotspot = false 55 | end 56 | 57 | if noEventSend == nil or noEventSend == false then 58 | if g_server ~= nil then 59 | g_server:broadcastEvent(TardisTeleportEvent:new(x, z, vehicle, isReset, isHotspot), nil, nil, self) 60 | else 61 | g_client:getServerConnection():sendEvent(TardisTeleportEvent:new(x, z, vehicle, isReset, isHotspot)) 62 | end 63 | end 64 | end 65 | 66 | -- Create Hotspot 67 | 68 | TardisCreateHotspotEvent = {} 69 | TardisCreateHotspotEvent_mt = Class(TardisCreateHotspotEvent, Event) 70 | 71 | InitEventClass(TardisCreateHotspotEvent, "TardisCreateHotspotEvent") 72 | 73 | function TardisCreateHotspotEvent.emptyNew() 74 | local self = Event.new(TardisCreateHotspotEvent_mt) 75 | self.className = "TardisCreateHotspotEvent" 76 | return self 77 | end 78 | 79 | function TardisCreateHotspotEvent.new(hotspotId, x, z) 80 | local self = TardisCreateHotspotEvent:emptyNew() 81 | self.hotspotId = hotspotId 82 | self.x = x 83 | self.z = z 84 | return self 85 | end 86 | 87 | function TardisCreateHotspotEvent:writeStream(streamId, connection) 88 | streamWriteInt8(streamId, self.hotspotId) 89 | streamWriteFloat32(streamId, self.x) 90 | streamWriteFloat32(streamId, self.z) 91 | end 92 | 93 | function TardisCreateHotspotEvent:readStream(streamId, connection) 94 | self.hotspotId = streamReadInt8(streamId) 95 | self.x = streamReadFloat32(streamId) 96 | self.z = streamReadFloat32(streamId) 97 | self:run(connection) 98 | end 99 | 100 | function TardisCreateHotspotEvent:run(connection) 101 | Tardis:createMapHotspot(self.hotspotId , self.x, self.z) 102 | end 103 | 104 | function TardisCreateHotspotEvent.sendEvent(hotspotId, x, y, noEventSend) 105 | if noEventSend == nil or noEventSend == false then 106 | if g_server ~= nil then 107 | g_server:broadcastEvent(TardisCreateHotspotEvent:new(hotspotId, x, y), nil, nil, self) 108 | else 109 | g_client:getServerConnection():sendEvent(TardisCreateHotspotEvent:new(hotspotId, x, y)) 110 | end 111 | end 112 | end 113 | 114 | -- Remove Hotspot 115 | 116 | TardisRemoveHotspotEvent = {} 117 | TardisRemoveHotspotEvent_mt = Class(TardisRemoveHotspotEvent, Event) 118 | 119 | InitEventClass(TardisRemoveHotspotEvent, "TardisRemoveHotspotEvent") 120 | 121 | function TardisRemoveHotspotEvent.emptyNew() 122 | local self = Event.new(TardisRemoveHotspotEvent_mt) 123 | self.className = "TardisRemoveHotspotEvent" 124 | return self 125 | end 126 | 127 | function TardisRemoveHotspotEvent.new(hotspotId) 128 | local self = TardisRemoveHotspotEvent:emptyNew() 129 | self.hotspotId = hotspotId 130 | return self 131 | end 132 | 133 | function TardisRemoveHotspotEvent:writeStream(streamId, connection) 134 | streamWriteInt8(streamId, self.hotspotId) 135 | end 136 | 137 | function TardisRemoveHotspotEvent:readStream(streamId, connection) 138 | self.hotspotId = streamReadInt8(streamId) 139 | self:run(connection) 140 | end 141 | 142 | function TardisRemoveHotspotEvent:run(connection) 143 | Tardis:removeMapHotspot(self.hotspotId) 144 | end 145 | 146 | function TardisRemoveHotspotEvent.sendEvent(hotspotId, noEventSend) 147 | if noEventSend == nil or noEventSend == false then 148 | if g_server ~= nil then 149 | g_server:broadcastEvent(TardisRemoveHotspotEvent:new(hotspotId), nil, nil, self) 150 | else 151 | g_client:getServerConnection():sendEvent(TardisRemoveHotspotEvent:new(hotspotId)) 152 | end 153 | end 154 | end -------------------------------------------------------------------------------- /RegisterSpecialization.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | RegisterSpecialization 3 | 4 | Author: Ifko[nator] 5 | Date: 21.04.2022 6 | Version: 2.6 7 | 8 | Changelog: v1.0 @02.01.2019 - initial implementation in FS 19 9 | --------------------------------------------------- 10 | v2.0 @18.11.2021 - convert to FS 22 11 | --------------------------------------------------- 12 | v2.5 @01.04.2022 - changed loading logic 13 | --------------------------------------------------- 14 | v2.6 @21.04.2022 - fix for patch 1.4 and higher 15 | ]] 16 | 17 | RegisterSpecialization = {}; 18 | RegisterSpecialization.currentModDirectory = g_currentModDirectory; 19 | 20 | local modDesc = loadXMLFile("modDesc", RegisterSpecialization.currentModDirectory .. "modDesc.xml"); 21 | 22 | RegisterSpecialization.debugPriority = Utils.getNoNil(getXMLInt(modDesc, "modDesc.registerSpecializations#debugPriority"), 0); 23 | 24 | local function printError(errorMessage, isWarning, isInfo) 25 | local prefix = "::ERROR:: "; 26 | 27 | if isWarning then 28 | prefix = "::WARNING:: "; 29 | elseif isInfo then 30 | prefix = "::INFO:: "; 31 | end; 32 | 33 | print(prefix .. "from the RegisterSpecialization.lua: " .. tostring(errorMessage)); 34 | end; 35 | 36 | local function printDebug(debugMessage, priority, addString) 37 | if RegisterSpecialization.debugPriority >= priority then 38 | local prefix = ""; 39 | 40 | if addString then 41 | prefix = "::DEBUG:: from the RegisterSpecialization.lua: "; 42 | end; 43 | 44 | print(prefix .. tostring(debugMessage)); 45 | end; 46 | end; 47 | 48 | function RegisterSpecialization:addSpecializations() 49 | local specializationNumber = 0; 50 | 51 | while true do 52 | local specializationKey = string.format("modDesc.registerSpecializations.registerSpecialization(%d)", specializationNumber); 53 | 54 | if not hasXMLProperty(modDesc, specializationKey) then 55 | break; 56 | end; 57 | 58 | local specializationName = Utils.getNoNil(getXMLString(modDesc, specializationKey .. "#name"), ""); 59 | local specializationClassName = Utils.getNoNil(getXMLString(modDesc, specializationKey .. "#className"), ""); 60 | local specializationFilename = Utils.getNoNil(Utils.getFilename(getXMLString(modDesc, specializationKey .. "#filename"), RegisterSpecialization.currentModDirectory), ""); 61 | local searchedSpecializations = string.split(Utils.getNoNil(getXMLString(modDesc, specializationKey .. "#searchedSpecializations"), ""), " "); 62 | 63 | local searchedSpecializationsString = ""; 64 | 65 | local function getSearchedSpecializations(searchedSpecializations) 66 | for _, searchedSpecialization in pairs(searchedSpecializations) do 67 | if searchedSpecializationsString ~= "" then 68 | searchedSpecialization = ", " .. searchedSpecialization; 69 | end; 70 | 71 | searchedSpecializationsString = searchedSpecializationsString .. searchedSpecialization; 72 | end; 73 | 74 | return searchedSpecializationsString; 75 | end; 76 | 77 | printDebug("specializationName = " .. specializationName .. " specializationClassName " .. specializationClassName .. " specializationFilename = " .. specializationFilename .. " searchedSpecializations = " .. getSearchedSpecializations(searchedSpecializations), 1, true); 78 | 79 | if specializationName ~= "" 80 | and specializationClassName ~= "" 81 | and specializationFilename ~= "" and fileExists(specializationFilename) 82 | and searchedSpecializations ~= "" 83 | then 84 | if g_specializationManager:getSpecializationByName(specializationName) == nil then 85 | g_specializationManager:addSpecialization(specializationName, specializationClassName, specializationFilename, nil); 86 | end; 87 | 88 | for vehicleType, vehicle in pairs(g_vehicleTypeManager.types) do 89 | if vehicle ~= nil then 90 | for name in pairs(vehicle.specializationsByName) do 91 | for _, searchedSpecialization in pairs(searchedSpecializations) do 92 | if string.lower(name) == string.lower(searchedSpecialization) then 93 | local specializationObject = g_specializationManager:getSpecializationObjectByName(specializationName); 94 | 95 | if vehicle.specializationsByName[specializationName] == nil then 96 | vehicle.specializationsByName[specializationName] = specializationObject; 97 | table.insert(vehicle.specializationNames, specializationName); 98 | table.insert(vehicle.specializations, specializationObject); 99 | 100 | printDebug("Added Specialization '" .. specializationName .. "' succsessfully to vehicle type '" .. vehicleType .. "'.", 1, true); 101 | end; 102 | end; 103 | end; 104 | end; 105 | end; 106 | end; 107 | else 108 | if specializationName == nil then 109 | printError("Missing specialization name! Skipping this specialization now!", false, false); 110 | elseif specializationClassName == nil then 111 | printError("Missing specialization class name! Skipping specialization '" .. specializationName .. "' now!", false, false); 112 | elseif specializationFilename == nil then 113 | printError("Missing specialization filename! Skipping specialization '" .. specializationName .. "' now!", false, false); 114 | elseif searchedSpecializations == nil then 115 | printError("Missing searched specialization names! Skipping specialization '" .. specializationName .. "' now!", false, false); 116 | end; 117 | end; 118 | 119 | specializationNumber = specializationNumber + 1; 120 | end; 121 | end; 122 | 123 | TypeManager.finalizeTypes = Utils.prependedFunction(TypeManager.finalizeTypes, RegisterSpecialization.addSpecializations) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tardis for FS22 2 | **This is a revamp of the good old Tardis from FS17** 3 | 4 | For beginners: With Tardis you can teleport yourself or your vehicles to any location on the map. 5 | Besides that it has a couple of additional functionalities. See below. 6 | 7 | Feedback, this readme and additional information incl. source code can be find at: https://github.com/sperrgebiet/FS22_Tardis 8 | 9 | **Please download the latest version directly from GitHub** 10 | [Latest version](https://github.com/sperrgebiet/FS22_Tardis/blob/main/FS22_Tardis.zip?raw=true) 11 | 12 | ### Features 13 | * Teleport yourself (the player) to any location on the map 14 | * Enter a vehicle to teleport yourself AND the vehicle (incl. implements, trailers) to any location on the map 15 | * Reset a crashed/turned over vehicle. Reset in this case means to turn it over again 16 | * Set up to 5 map hotspots to easily teleport yourself and/or vehicles to one of those spots. Those hotspots obviously get saved & restored. 17 | * Integration with Vehicle Explorer (https://github.com/sperrgebiet/FS22_VehicleExplorer) 18 | 19 | 20 | ### Known issues 21 | * You can't teleport goods like pallets on a trailer. Filllevels work though 22 | 23 | 24 | ### Incompatible Mods 25 | * No known ones 26 | 27 | ## Default Keybinding 28 | |Key Combo|Action| 29 | |:---:|---| 30 | |LAlt + t|Activate Tardis| 31 | |LAlt + Backspace|Reset| 32 | |LAlt + LShift + KeyPad 1 - 5|Set or visit the map hotspot| 33 | |LAlt + LShift + Backspace|Delete a map hotspot in a 25m range| 34 | |LAlt + LShift + R|Camera reset - Workaround for a bug with AD| 35 | |Mouse Left|Teleport| 36 | 37 | 38 | ## Note that the current version does NOT support multiplayer! 39 | Quite frankly, I've no idea about the MP code needed and also no possibility to test it. Actually I think it shouldn't be a big deal, and maybe it already works by just changing 40 | the MP setting in the moddesc.xml from false to true. I assume just the parking possibility has an impact to MP. 41 | 42 | ## Credits 43 | TyKonKet, fcelsa for Tardis on FS17. That brought the idea and the initial code base. Although the majority was altered for this FS19 version. 44 | Also Kudos to the guys and gals from CoursePlay, VehicleInspector, VehicleFruitHud, EnhancedVehicle and many more for some inspiration and ideas. 45 | Additionally Ifko[nator] for the RegisterSpecialization script. 46 | 47 | 48 | ## Latest Version 49 | 0.1.0.0 - I wouldn't call it beta anymore, but I won't make it a v1 before MP is working. 50 | 51 | ----- 52 | 53 | 54 | # Tardis für LS22 55 | **Dies ist eine Reinkarnation von Tardis aus LS17** 56 | 57 | Für Neueinsteiger: Mit Tardis kannst du dich selbst oder eines deiner Fahrzeuge zu jeder Lokation teleportieren. 58 | 59 | Feedback, dieses ReadMe und weitere Informationen sowie der Quelltext findet sich unter: https://github.com/sperrgebiet/FS22_Tardis 60 | 61 | **Bitte lade die letzte Version direkt von bei GitHub herunter** 62 | [Letzte Version](https://github.com/sperrgebiet/FS22_Tardis/blob/main/FS22_Tardis.zip?raw=true) 63 | 64 | ### Funktionen 65 | * Teleportiere dich (den Spieler) an jeden Ort auf der Karte 66 | * Besteige ein Fahrzeug um dich UND das Fahrzeug (inkl. angehängter Geräte, Anhänger) an jeden Ort zu teleportieren 67 | * Wieder aufrichten von umgekippten Fahrzeugen 68 | * Setze bis zu 5 Hotspots auf der Karte um dich und/oder Fahrzeuge an diese Orte zu teleportieren. Diese werden natürlich gespeichert und geladen. 69 | * Integration mit VehicleExplorer (https://github.com/sperrgebiet/FS22_VehicleExplorer) 70 | 71 | 72 | ### Bekannte Probleme 73 | * Man kann keine Güter wie Paletten teleportieren. Inhalte von Anhängern (Filllevels) funktioniert jedoch. 74 | 75 | ### Inkompatible Mods 76 | * Keine bekannt 77 | 78 | ## Standard Tastenbelegung 79 | |Key Kombi|Aktion| 80 | |:---:|---| 81 | |LAlt + t|Tardis aktivieren (funktioniert nur auf der MiniMap)| 82 | |LAlt + Rücktaste|Fahrzeug aufrichten| 83 | |LAlt + LUmschalt + NumPad 1 - 5|Setzen/besuchen der Hotspots| 84 | |LAlt + LUmschalt + Rücktaste|Löschen eines Mapspots in der Nähe (25m)| 85 | |LAlt + LUmschalt + R|Zurücksetzen der Kameras - Workaround zu nem Bug mit AD| 86 | |Linke Maustaste|An diese Position teleportieren| 87 | 88 | 89 | ## Beachte, dass die jetzige Version kein Multiplayer unterstützt! 90 | Ehrlich gesagt habe ich keine Ahnung was für MP Code notwendig wäre und auch keine Möglichkeit es zu testen. Vielleicht funktioniert es einfach nur in de moddesc.xml von false auf true zu wechseln. 91 | Ich glaube nur das parken von Fahrzeugen sollte einen Einfluss auf MP haben. 92 | 93 | ## Credits 94 | TyKonKet, fcelsa für Tardis in LS17. Initiale Idee und Logik, wenn auch der Grossteil des Codes mittlerweile geändert wurde. 95 | Auch Kudos an die Jungs und Mädls von CoursePlay, VehicleInspector, VehicleFruitHud, EnhancedVehicle und vielen anderen für Inspirationen und Ideen. 96 | Des weiteren noch Ifko[nator] für das RegisterSpecialization Skript. 97 | 98 | ## Letzte Version 99 | 0.9.2.1 - Ich nenn es nicht mehr Beta, da es bei mir sehr gut funktioniert. Jedoch werde ich erst eine v1. draus machen wenn ich mal Zeit für MP hab. 100 | 101 | 102 | # Screenshots 103 | YouTube Vorstellung in Deutsch: https://www.youtube.com/watch?v=w2LY9rmA4-g&t=7s 104 | 105 | ![FS19_Tardis_1](https://user-images.githubusercontent.com/20586786/53123931-7bb06f80-355a-11e9-87c5-3373f0e49519.png) 106 | ![FS19_Tardis_2](https://user-images.githubusercontent.com/20586786/53123933-7bb06f80-355a-11e9-9416-7ff49150004b.png) 107 | ![FS19_Tardis_3](https://user-images.githubusercontent.com/20586786/53123934-7bb06f80-355a-11e9-8965-6c13951ac5fb.png) 108 | ![FS19_Tardis_4](https://user-images.githubusercontent.com/20586786/53123935-7bb06f80-355a-11e9-951e-6eb95f6ad5cf.png) 109 | ![FS19_Tardis_5](https://user-images.githubusercontent.com/20586786/53123936-7c490600-355a-11e9-93e2-fdda079be63d.png) 110 | -------------------------------------------------------------------------------- /modDesc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | sperrgebiet 4 | 0.1.0.1 5 | Tardis Teleport 6 | 7 | 17 | 18 | 26 | 27 | 28 | icon.dds 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | Tardis Teleport 100 | Tardis Teleport 101 | 102 | 103 | Tardis: Auf Minimap aktivieren 104 | Tardis: Activate on Minimap 105 | 106 | 107 | Tardis: Fahrzeug auf derzeitiger Position aufrichten 108 | Tardis: Raise up vehicle on current position 109 | 110 | 111 | Einsamer Landwirt 112 | Lonely Farmer 113 | 114 | 115 | Ein Drescher sollte leer und zugeklappt sein, da es ansonsten Probleme geben kann. 116 | A combine should be folded and empty, otherwise there can be issues. 117 | 118 | 119 | Ein Zug passt leider nicht in eine Telefonzelle. Tardis funktioniert hier nicht. 120 | A train doesn't fit a phone box. Tardis won't work here. 121 | 122 | 123 | Ein Kran passt leider nicht in eine Telefonzelle. Tardis funktioniert hier nicht. 124 | A crane doesn't fit a phone box. Tardis won't work here. 125 | 126 | 127 | Tardis Hotspot 128 | Tardis Hotspot 129 | 130 | 131 | Tardis: Ort 1 132 | Tardis: Spot 1 133 | 134 | 135 | Tardis: Ort 2 136 | Tardis: Spot 2 137 | 138 | 139 | Tardis: Ort 3 140 | Tardis: Spot 3 141 | 142 | 143 | Tardis: Ort 4 144 | Tardis: Spot 4 145 | 146 | 147 | Tardis: Ort 5 148 | Tardis: Spot 5 149 | 150 | 151 | Tardis: Ort 6 152 | Tardis: Spot 6 153 | 154 | 155 | Tardis: Ort 7 156 | Tardis: Spot 7 157 | 158 | 159 | Tardis: Ort 8 160 | Tardis: Spot 8 161 | 162 | 163 | Tardis: Ort 9 164 | Tardis: Spot 9 165 | 166 | 167 | Tardis: Lösche Hotspot im Umkreis 168 | Tardis: Delete Hotspot in range 169 | 170 | 171 | Tardis: Kameras zurücksetzen 172 | Tardis: Reset cameras 173 | 174 | 175 | Keine Tardis Hotspots im Umkreis gefunden 176 | No Tardis Hotspots found in this area 177 | 178 | 179 | erstellt 180 | created 181 | 182 | 183 | gelöscht 184 | deleted 185 | 186 | 187 | Tardis: Kameras zurücksetzen 188 | Tardis: Reset cameras 189 | 190 | 191 | Kameras wurden zurückgesetzt 192 | Camera reset successful 193 | 194 | 195 | -------------------------------------------------------------------------------- /Tardis.lua: -------------------------------------------------------------------------------- 1 | -- Tardis.lua for FS19 2 | -- Author: sperrgebiet 3 | -- Please see https://github.com/sperrgebiet/FS19_Tardis for additional information, credits, issues and everything else 4 | 5 | Tardis = {}; 6 | Tardis.eventName = {}; 7 | 8 | -- It's great that Giants gets rid of functions as part of an update. Now we can do things more complicated than before 9 | --Tardis.ModName = g_currentModName 10 | --Tardis.ModDirectory = g_currentModDirectory 11 | Tardis.ModName = "FS22_Tardis" 12 | Tardis.ModDirectory = g_modManager.nameToMod.FS22_Tardis.modDir 13 | Tardis.Version = "0.9.2.1"; 14 | 15 | -- Integration environment for VehicleExplorer 16 | envVeEx = nil; 17 | 18 | Tardis.camBackup = {}; 19 | Tardis.hotspots = {}; 20 | 21 | Tardis.debug = fileExists(Tardis.ModDirectory ..'debug'); 22 | 23 | -- Load MP source files 24 | source(Tardis.ModDirectory .. "TardisEvents.lua"); 25 | 26 | print(string.format('Tardis v%s - DebugMode %s)', Tardis.Version, tostring(Tardis.debug))); 27 | 28 | addModEventListener(Tardis); 29 | 30 | function Tardis:dp(val, fun, msg) -- debug mode, write to log 31 | if not Tardis.debug then 32 | return; 33 | end 34 | if msg == nil then 35 | msg = ' '; 36 | else 37 | msg = string.format(' msg = [%s] ', tostring(msg)); 38 | end 39 | local pre = 'Tardis DEBUG:'; 40 | if type(val) == 'table' then 41 | if #val > 0 then 42 | print(string.format('%s BEGIN Printing table data: (%s)%s(function = [%s()])', pre, tostring(val), msg, tostring(fun))); 43 | DebugUtil.printTableRecursively(val, '.', 0, 3); 44 | print(string.format('%s END Printing table data: (%s)%s(function = [%s()])', pre, tostring(val), msg, tostring(fun))); 45 | else 46 | print(string.format('%s Table is empty: (%s)%s(function = [%s()])', pre, tostring(val), msg, tostring(fun))); 47 | end 48 | else 49 | print(string.format('%s [%s]%s(function = [%s()])', pre, tostring(val), msg, tostring(fun))); 50 | end 51 | end 52 | 53 | 54 | function Tardis:prerequisitesPresent(specializations) 55 | return true; 56 | end 57 | 58 | function Tardis:loadMap(name) 59 | print("--- loading Tardis V".. Tardis.Version .. " | ModName " .. Tardis.ModName .. " ---"); 60 | FSBaseMission.registerActionEvents = Utils.appendedFunction(FSBaseMission.registerActionEvents, Tardis.registerActionEvents); 61 | Player.registerActionEvents = Utils.appendedFunction(Player.registerActionEvents, Tardis.registerActionEventsPlayer); 62 | 63 | Tardis.TardisActive = false; 64 | Tardis.mousePos = {0.5, 0.5}; 65 | Tardis.worldXpos = 0; 66 | Tardis.worldZpos = 0; 67 | Tardis.fieldNumber = 1; 68 | 69 | -- Integration with Vehicle Explorer 70 | local VeExName = "FS19_VehicleExplorer"; 71 | 72 | if g_modIsLoaded[VeExName] then 73 | envVeEx = getfenv(0)[VeExName]; 74 | print("Tardis: VehicleExplorer integration available"); 75 | end 76 | end 77 | 78 | -- Global action events 79 | function Tardis:registerActionEvents(isSelected, isOnActiveVehicle) 80 | local actions = { 81 | "tardis_showTardisCursor", 82 | "tardis_useHotspot1", 83 | "tardis_useHotspot2", 84 | "tardis_useHotspot3", 85 | "tardis_useHotspot4", 86 | "tardis_useHotspot5", 87 | "tardis_useHotspot6", 88 | "tardis_useHotspot7", 89 | "tardis_useHotspot8", 90 | "tardis_useHotspot9", 91 | "tardis_deleteHotspot", 92 | "tardis_resetCamera" 93 | }; 94 | 95 | for _, action in pairs(actions) do 96 | local actionMethod = string.format("action_%s", action); 97 | local result, eventName = InputBinding.registerActionEvent(g_inputBinding, action, self, Tardis[actionMethod], false, true, false, true) 98 | if result then 99 | table.insert(Tardis.eventName, eventName); 100 | if envVeEx ~= nil and VehicleSort.config[13][2] then 101 | g_inputBinding.events[eventName].displayIsVisible = true; 102 | else 103 | g_inputBinding.events[eventName].displayIsVisible = false; 104 | end 105 | end 106 | end 107 | 108 | end 109 | 110 | function Tardis:registerActionEventsPlayer() 111 | end 112 | 113 | function Tardis.registerEventListeners(vehicleType) 114 | local functionNames = { "onRegisterActionEvents", }; 115 | 116 | for _, functionName in ipairs(functionNames) do 117 | SpecializationUtil.registerEventListener(vehicleType, functionName, Tardis); 118 | end 119 | end 120 | 121 | --Vehicle functions 122 | function Tardis:onRegisterActionEvents(isSelected, isOnActiveVehicle) 123 | 124 | local result, eventName = InputBinding.registerActionEvent(g_inputBinding, 'tardis_resetVehicle',self, Tardis.action_tardis_resetVehicle ,false ,true ,false ,true) 125 | if result then 126 | table.insert(Tardis.eventName, eventName); 127 | if envVeEx ~= nil and VehicleSort.config[13][2] then 128 | g_inputBinding.events[eventName].displayIsVisible = true; 129 | else 130 | g_inputBinding.events[eventName].displayIsVisible = false; 131 | end 132 | end 133 | 134 | end 135 | 136 | function Tardis:keyEvent(unicode, sym, modifier, isDown) 137 | end 138 | 139 | function Tardis:mouseEvent(posX, posY, isDown, isUp, button) 140 | --Tardis:dp(string.format('posX {%s) posY {%s}', posX, posY)); 141 | if Tardis.isActionAllowed() then 142 | local mOX = g_currentMission.hud.ingameMap.layout.mapPosX; 143 | local mOY = g_currentMission.hud.ingameMap.layout.mapPosY; 144 | if posX >= mOX and posX <= mOX + g_currentMission.hud.ingameMap.layout.mapSizeX then 145 | Tardis.worldXpos = (posX - mOX) / g_currentMission.hud.ingameMap.layout.mapSizeX; 146 | end; 147 | if posY >= mOY and posY <= mOY + g_currentMission.hud.ingameMap.layout.mapSizeY then 148 | Tardis.worldZpos = 1 - (posY - mOY) / g_currentMission.hud.ingameMap.layout.mapSizeY; 149 | end; 150 | if isDown and button == Input.MOUSE_BUTTON_LEFT then 151 | Tardis:dp(string.format('posX {%s} posY {%s} - mOX {%s} mOY {%s} - worldXpos {%s} worldZpos {%s}', posX, posY, mOX, mOY, Tardis.worldXpos, Tardis.worldZpos)); 152 | 153 | local posX = Tardis.worldXpos * g_currentMission.terrainSize; 154 | local posZ = Tardis.worldZpos * g_currentMission.terrainSize; 155 | 156 | if g_currentMission.controlledVehicle then 157 | local veh = g_currentMission.controlledVehicle; 158 | Tardis:teleportToLocation(posX, posZ, veh, false, false, false) 159 | TardisTeleportEvent.sendEvent(posX, posZ, veh, false, false, false) 160 | else 161 | --Tardis:dp(string.format('telePort param1 {%s} - param2 {%s}', Tardis.worldXpos * g_currentMission.terrainSize, Tardis.worldZpos * g_currentMission.terrainSize)); 162 | Tardis:teleportToLocation(posX, posZ); 163 | TardisTeleportEvent.sendEvent(posX, posZ, nil, false, false, false) 164 | end 165 | Tardis.TardisActive = false; 166 | g_inputBinding:setShowMouseCursor(false); 167 | end; 168 | Tardis.mousePos[1] = posX; 169 | Tardis.mousePos[2] = posY; 170 | end 171 | end 172 | 173 | function Tardis:draw() 174 | if Tardis.TardisActive then 175 | local ovrlX = g_currentMission.hud.ingameMap.layout.mapPosX + getTextWidth(g_currentMission.hud.fillLevelsDisplay.fillLevelTextSize, "DummyText"); 176 | local ovrlY = g_currentMission.hud.ingameMap.layout.mapPosY + g_currentMission.hud.ingameMap.layout.mapSizeY; 177 | local px = 0.01; 178 | local py = 0.005; 179 | local name; 180 | local veh; 181 | local drawImage = false; --There are so many cases where we don't want to draw a image, so easier to just set it to true in case it's the currently controlled vehicle 182 | 183 | if envVeEx ~= nil and envVeEx.VehicleSort.showVehicles and envVeEx.VehicleSort.config[22][2] then 184 | local realVeh = g_currentMission.vehicles[envVeEx.VehicleSort.Sorted[envVeEx.VehicleSort.selectedIndex]]; 185 | if realVeh ~= nil then 186 | veh = realVeh; 187 | end 188 | elseif g_currentMission.controlledVehicle ~= nil then 189 | veh = g_currentMission.controlledVehicle; 190 | drawImage = true; 191 | end 192 | 193 | if veh ~= nil then 194 | --Get image size 195 | local storeImgX, storeImgY = getNormalizedScreenValues(128, 128) 196 | 197 | if drawImage then 198 | Tardis:DrawImage(veh, ovrlX, ovrlY) 199 | end 200 | 201 | name = veh:getName(); 202 | 203 | if veh.getAttachedImplements ~= nil then 204 | local allAttached = {} 205 | local function addAllAttached(vehicle) 206 | for _, implA in pairs(vehicle:getAttachedImplements()) do 207 | addAllAttached(implA.object); 208 | table.insert(allAttached, {vehicle = vehicle, object = implA.object, jointDescIndex = implA.jointDescIndex, inputAttacherJointDescIndex = implA.object.inputAttacherJointDescIndex}); 209 | end 210 | end 211 | 212 | addAllAttached(veh); 213 | 214 | for i = table.getn(allAttached), 1, -1 do 215 | if drawImage then 216 | Tardis:DrawImage(allAttached[i].object, ovrlX + storeImgX * i, ovrlY) 217 | end 218 | 219 | name = name .. " + " .. allAttached[i].object:getName(); 220 | end 221 | end 222 | end 223 | 224 | if veh and Tardis:isTrain(veh) then 225 | g_currentMission:showBlinkingWarning(g_i18n.modEnvironments[Tardis.ModName].texts.warning_train, 2000); 226 | name = veh:getName(); 227 | end 228 | 229 | if veh and Tardis:isCrane(veh) then 230 | g_currentMission:showBlinkingWarning(g_i18n.modEnvironments[Tardis.ModName].texts.warning_crane, 2000); 231 | name = veh:getName(); 232 | end 233 | 234 | if name == nil or string.len(name) == 0 then 235 | name = string.format('%s %s', g_i18n.modEnvironments[Tardis.ModName].texts.lonelyFarmer, g_currentMission.playerNickname); 236 | end 237 | 238 | if veh and veh.spec_combine ~= nil and veh.getFillLevelInformation ~= nil then 239 | local fillLevelTable = {}; 240 | veh:getFillLevelInformation(fillLevelTable); 241 | 242 | for _,fillLevelVehicle in pairs(fillLevelTable) do 243 | fillLevel = fillLevelVehicle.fillLevel; 244 | end 245 | 246 | if fillLevel ~= nil and fillLevel > 0 then 247 | g_currentMission:showBlinkingWarning(g_i18n.modEnvironments[Tardis.ModName].texts.warning_combine, 2000); 248 | end 249 | end 250 | 251 | if Tardis.mousePos[1] > ovrlX then 252 | --px = -(string.len(name) * 0.005) - 0.03; 253 | end 254 | 255 | if Tardis.mousePos[2] > ovrlY then 256 | py = -0.04; 257 | end 258 | 259 | renderText(Tardis.mousePos[1] + px, Tardis.mousePos[2] + py, getCorrectTextSize(0.016), name); 260 | setTextAlignment(RenderText.ALIGN_RIGHT) 261 | setTextBold(false) 262 | setTextColor(0, 1, 0.4, 1) 263 | renderText(g_currentMission.inGameMenu.hud.ingameMap.layout.mapPosX + g_currentMission.inGameMenu.hud.ingameMap.layout.mapSizeX - g_currentMission.inGameMenu.hud.ingameMap.layout.coordOffsetX, g_currentMission.inGameMenu.hud.ingameMap.layout.mapPosY + g_currentMission.inGameMenu.hud.ingameMap.layout.coordOffsetY + 0.010, g_currentMission.inGameMenu.hud.ingameMap.layout.coordinateFontSize, string.format(" [%04d", Tardis.worldXpos * g_currentMission.terrainSize) .. string.format(",%04d]", Tardis.worldZpos * g_currentMission.terrainSize)); 264 | setTextColor(1, 1, 1, 1) 265 | setTextAlignment(RenderText.ALIGN_LEFT) 266 | 267 | end 268 | end 269 | 270 | -- Functions for actionEvents/inputBindings 271 | 272 | function Tardis:action_tardis_showTardisCursor(actionName, keyStatus, arg3, arg4, arg5) 273 | Tardis:dp(string.format('%s fires', actionName)); 274 | Tardis:showTardis(); 275 | end 276 | 277 | function Tardis:action_tardis_resetVehicle(actionName, keyStatus, arg3, arg4, arg5) 278 | Tardis:dp(string.format('%s fires', actionName)); 279 | 280 | if g_currentMission.controlledVehicle then 281 | -- We can provide dummy values, as we'll do the actual stuff in the teleport function 282 | Tardis:teleportToLocation(0, 0, nil, true); 283 | --TardisTeleportEvent.sendEvent(0, 0, nil, true, false, false) 284 | end 285 | end 286 | 287 | function Tardis:action_tardis_useHotspot1(actionName, keyStatus, arg3, arg4, arg5) 288 | Tardis:dp(string.format('%s fires', actionName)); 289 | Tardis:useOrSetHotspot(1); 290 | end 291 | 292 | function Tardis:action_tardis_useHotspot2(actionName, keyStatus, arg3, arg4, arg5) 293 | Tardis:dp(string.format('%s fires', actionName)); 294 | Tardis:useOrSetHotspot(2); 295 | end 296 | 297 | function Tardis:action_tardis_useHotspot3(actionName, keyStatus, arg3, arg4, arg5) 298 | Tardis:dp(string.format('%s fires', actionName)); 299 | Tardis:useOrSetHotspot(3); 300 | end 301 | 302 | function Tardis:action_tardis_useHotspot4(actionName, keyStatus, arg3, arg4, arg5) 303 | Tardis:dp(string.format('%s fires', actionName)); 304 | Tardis:useOrSetHotspot(4); 305 | end 306 | 307 | function Tardis:action_tardis_useHotspot5(actionName, keyStatus, arg3, arg4, arg5) 308 | Tardis:dp(string.format('%s fires', actionName)); 309 | Tardis:useOrSetHotspot(5); 310 | end 311 | 312 | function Tardis:action_tardis_useHotspot6(actionName, keyStatus, arg3, arg4, arg5) 313 | Tardis:dp(string.format('%s fires', actionName)); 314 | Tardis:useOrSetHotspot(6); 315 | end 316 | 317 | function Tardis:action_tardis_useHotspot7(actionName, keyStatus, arg3, arg4, arg5) 318 | Tardis:dp(string.format('%s fires', actionName)); 319 | Tardis:useOrSetHotspot(7); 320 | end 321 | 322 | function Tardis:action_tardis_useHotspot8(actionName, keyStatus, arg3, arg4, arg5) 323 | Tardis:dp(string.format('%s fires', actionName)); 324 | Tardis:useOrSetHotspot(8); 325 | end 326 | 327 | function Tardis:action_tardis_useHotspot9(actionName, keyStatus, arg3, arg4, arg5) 328 | Tardis:dp(string.format('%s fires', actionName)); 329 | Tardis:useOrSetHotspot(9); 330 | end 331 | 332 | function Tardis:action_tardis_deleteHotspot(actionName, keyStatus, arg3, arg4, arg5) 333 | Tardis:dp(string.format('%s fires', actionName)); 334 | local hotspotId = Tardis:hotspotNearby(); 335 | if hotspotId > 0 then 336 | Tardis:dp(string.format('Found hotspot {%d}. Going to delete it.', hotspotId), 'action_deleteHotspot'); 337 | Tardis:removeMapHotspot(hotspotId); 338 | --TardisRemoveHotspotEvent.sendEvent(hotspotId, false); 339 | else 340 | Tardis:dp('No hotspots nearby', 'action_deleteHotspot'); 341 | Tardis:showBlinking(nil, 3); 342 | end 343 | end 344 | 345 | function Tardis:action_tardis_resetCamera(actionName, keyStatus, arg3, arg4, arg5) 346 | Tardis:dp(string.format('%s fires', actionName)); 347 | 348 | local veh = g_currentMission.controlledVehicle 349 | for i, _ in ipairs(veh.spec_enterable.cameras) do 350 | veh.spec_enterable.cameras[i].isRotatable = true 351 | veh.spec_enterable.cameras[i].storedIsRotatable = true 352 | end 353 | 354 | text = g_i18n.modEnvironments[Tardis.ModName].texts.resetCameraText; 355 | g_currentMission:showBlinkingWarning(text, 2000); 356 | 357 | 358 | end 359 | 360 | -- 361 | -- Tardis specific functions 362 | -- 363 | 364 | function Tardis:showTardis() 365 | 366 | if (g_currentMission.hud.ingameMap.isVisible and g_currentMission.hud.ingameMap.state == IngameMap.STATE_MAP) then 367 | Tardis.TardisActive = not Tardis.TardisActive; 368 | if Tardis.TardisActive then 369 | g_inputBinding:setShowMouseCursor(true); 370 | Tardis:Freeze(true); 371 | 372 | --It's getting confusing when we want to use Tardis and VehicleExplorer at the same but, although the integration was disabled 373 | --So better to close the vehicle list from VeEx in that case 374 | if envVeEx ~= nil and not envVeEx.VehicleSort.config[22][2] and envVeEx.VehicleSort.showVehicles then 375 | envVeEx.VehicleSort.showVehicles = false; 376 | end 377 | else 378 | Tardis.TardisActive = false; 379 | g_inputBinding:setShowMouseCursor(false); 380 | Tardis:Freeze(false); 381 | end 382 | elseif Tardis.TardisActive then 383 | Tardis.TardisActive = false; 384 | g_inputBinding:setShowMouseCursor(false); 385 | end 386 | end 387 | 388 | function Tardis:teleportToLocation(x, z, veh, isReset, isHotspot) 389 | if g_client ~= nil then 390 | x = tonumber(x); 391 | z = tonumber(z); 392 | if x == nil then 393 | return; 394 | end; 395 | 396 | if envVeEx ~= nil and veh == nil then 397 | if envVeEx.VehicleSort.showVehicles and envVeEx.VehicleSort.config[22][2] then 398 | local realVeh = g_currentMission.vehicles[envVeEx.VehicleSort.Sorted[envVeEx.VehicleSort.selectedIndex]]; 399 | if realVeh ~= nil then 400 | veh = realVeh; 401 | if veh ~= g_currentMission.controlledVehicle then 402 | envVeEx.VehicleSort.wasTeleportAction = true; 403 | end 404 | end 405 | end 406 | end 407 | 408 | if veh == nil then 409 | veh = g_currentMission.controlledVehicle; 410 | end 411 | 412 | -- We don't want to teleport cranes or trains 413 | if veh ~= nil and (Tardis:isTrain(veh) or Tardis:isCrane(veh)) then 414 | Tardis:Freeze(false); 415 | return false; 416 | end 417 | 418 | local targetX, targetY, targetZ = 0, 0, 0; 419 | 420 | if not isReset and not isHotspot then 421 | local worldSizeX = g_currentMission.hud.ingameMap.worldSizeX; 422 | local worldSizeZ = g_currentMission.hud.ingameMap.worldSizeZ; 423 | targetX = MathUtil.clamp(x, 0, worldSizeX) - worldSizeX * 0.5; 424 | targetZ = MathUtil.clamp(z, 0, worldSizeZ) - worldSizeZ * 0.5; 425 | elseif isHotspot then 426 | targetX = x; 427 | targetZ = z; 428 | else 429 | targetX, targetY, targetZ = getWorldTranslation(g_currentMission.controlledVehicle.rootNode); 430 | end 431 | 432 | Tardis:dp(string.format('targetX {%s} - targetZ {%s}', tostring(targetX), tostring(targetZ)), 'teleportToLocation'); 433 | 434 | if veh == nil and not isReset then 435 | g_currentMission.player:moveTo(targetX, 0.5, targetZ, false, false); 436 | Tardis:Freeze(false); 437 | else 438 | local vehicleCombos = {}; 439 | local vehicles = {}; 440 | 441 | local function addVehiclePositions(vehicle) 442 | local x, y, z = getWorldTranslation(vehicle.rootNode); 443 | table.insert(vehicles, {vehicle = vehicle, offset = {worldToLocal(veh.rootNode, x, y, z)}}); 444 | 445 | if not Tardis:isHorse(veh) then 446 | if #vehicle:getAttachedImplements() > 0 then 447 | for _, impl in pairs(vehicle:getAttachedImplements()) do 448 | addVehiclePositions(impl.object); 449 | table.insert(vehicleCombos, {vehicle = vehicle, object = impl.object, jointDescIndex = impl.jointDescIndex, inputAttacherJointDescIndex = impl.object.spec_attachable.inputAttacherJointDescIndex}); 450 | end 451 | 452 | for i = table.getn(vehicle:getAttachedImplements()), 1, -1 do 453 | vehicle:detachImplement(1, true); 454 | end 455 | end 456 | end 457 | 458 | vehicle:removeFromPhysics(); 459 | end 460 | 461 | addVehiclePositions(veh); 462 | 463 | for k, data in pairs(vehicles) do 464 | local x, y, z = targetX, targetY, targetZ; 465 | if k > 1 then 466 | x, _, z = localToWorld(veh.rootNode, unpack(data.offset)); 467 | end; 468 | local _, ry, _ = getWorldRotation(data.vehicle.rootNode); 469 | data.vehicle:setRelativePosition(x, 0.5, z, ry, true); 470 | data.vehicle:addToPhysics(); 471 | end 472 | 473 | if #vehicleCombos > 0 then 474 | for _, combo in pairs(vehicleCombos) do 475 | combo.vehicle:attachImplement(combo.object, combo.inputAttacherJointDescIndex, combo.jointDescIndex, true, nil, nil, false); 476 | end 477 | end 478 | 479 | Tardis:Freeze(false); 480 | end 481 | end 482 | 483 | end 484 | 485 | function Tardis:DrawImage(obj, imgX, imgY) 486 | local imgFileName = Tardis:getStoreImageByConf(obj.configFileName); 487 | 488 | local storeImage = createImageOverlay(imgFileName); 489 | if storeImage > 0 then 490 | local storeImgX, storeImgY = getNormalizedScreenValues(128, 128) 491 | renderOverlay(storeImage, imgX, imgY, storeImgX, storeImgY) 492 | end 493 | end 494 | 495 | function Tardis:getStoreImageByConf(confFile) 496 | local storeItem = g_storeManager.xmlFilenameToItem[string.lower(confFile)]; 497 | if storeItem ~= nil then 498 | local imgFileName = storeItem.imageFilename; 499 | if string.find(imgFileName, 'locomotive') then 500 | imgFileName = "data/store/store_empty.png"; 501 | end 502 | return imgFileName; 503 | end 504 | end 505 | 506 | function Tardis:isCrane(obj) 507 | return obj['typeName'] == 'crane'; 508 | end 509 | 510 | function Tardis:isTrain(obj) 511 | return obj['typeName'] == 'locomotive'; 512 | end 513 | 514 | function Tardis:isHorse(obj) 515 | return obj['typeName'] == 'horse'; 516 | end 517 | 518 | function Tardis:Freeze(setFreeze) 519 | local veh = g_currentMission.controlledVehicle; 520 | 521 | if setFreeze then 522 | if veh ~= nil then 523 | local veh = g_currentMission.controlledVehicle; 524 | -- We just want to mess with the cameras when we can ensure that we can do a backup first 525 | if Tardis.camBackup[veh.id] == nil then 526 | Tardis.camBackup[veh.id] = {}; 527 | for i, camera in pairs(veh.spec_enterable.cameras) do 528 | local camSettings = {}; 529 | camSettings['camId'] = i; 530 | camSettings['isRotatable'] = camera.isRotatable; 531 | table.insert(Tardis.camBackup[veh.id], camSettings); 532 | camSettings = nil; 533 | camera.storedIsRotatable = camera.isRotatable; 534 | camera.isRotatable = false; 535 | end 536 | end 537 | else 538 | g_currentMission.isPlayerFrozen = true; 539 | end 540 | else 541 | if veh ~= nil and veh.id ~= nil then 542 | if Tardis.camBackup[veh.id] ~= nil then 543 | for _, v in pairs(Tardis.camBackup[veh.id]) do 544 | veh.spec_enterable.cameras[v['camId']]['isRotatable'] = v['isRotatable']; 545 | veh.spec_enterable.cameras[v['camId']]['storedIsRotatable'] = v['isRotatable']; 546 | end 547 | Tardis.camBackup[veh.id] = nil; 548 | end 549 | end 550 | --Always unfreeze player 551 | g_currentMission.isPlayerFrozen = false; 552 | end 553 | 554 | end 555 | 556 | function Tardis:useOrSetHotspot(hotspotId) 557 | Tardis:dp(string.format('hotspotId: {%d}', hotspotId), 'useOrSetHotspot'); 558 | if Tardis.hotspots[hotspotId] ~= nil then 559 | 560 | local x = Tardis.hotspots[hotspotId]['worldX']; 561 | local z = Tardis.hotspots[hotspotId]['worldZ']; 562 | Tardis:dp(string.format('Hotspot {%d} exists. Teleporting now to: x {%s}, z {%s}', hotspotId, tostring(x), tostring(z)), 'createMapHotspot'); 563 | Tardis:teleportToLocation(x, z, nil, false, true); 564 | --TardisTeleportEvent.sendEvent(x, z, nil, false, true, false) 565 | else 566 | Tardis:createMapHotspot(hotspotId); 567 | end 568 | end 569 | 570 | function Tardis:createMapHotspot(hotspotId, paramX, paramZ) 571 | local x = paramX 572 | local z = paramZ 573 | local y = nil 574 | 575 | 576 | local name = string.format('%s %s', g_i18n.modEnvironments[Tardis.ModName].texts.hotspot, hotspotId); 577 | 578 | local hotspot = PlaceableHotspot.new() 579 | 580 | local width, height = getNormalizedScreenValues(48, 48) 581 | local file = Utils.getFilename("hotspot.dds", self.ModDirectory) 582 | hotspot.icon = Overlay.new(file, 0, 0, width, height) 583 | 584 | hotspot.placeableType = PlaceableHotspot.TYPE.EXCLAMATION_MARK 585 | hotspot:setName(name) 586 | 587 | if x == nil and z == nil then 588 | x, y, z = getWorldTranslation(g_currentMission.player.rootNode) 589 | end 590 | 591 | if y == nil then 592 | y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z) 593 | end 594 | 595 | Tardis:dp(string.format('Hotspot position x: {%d} / z: {%d}', x, z), 'createMapHotspot'); 596 | hotspot:setWorldPosition(x, z) 597 | 598 | hotspot:setTeleportWorldPosition(x, y, z) 599 | Tardis.hotspots[hotspotId] = hotspot 600 | 601 | g_currentMission:addMapHotspot(Tardis.hotspots[hotspotId]) 602 | 603 | -- if there is a paramX and paramZ it means we got it from a savegame or MP, so no need for a blinking warning 604 | if paramX == nil and paramZ == nil then 605 | Tardis:showBlinking(hotspotId, 1) 606 | end 607 | end 608 | 609 | function Tardis:removeMapHotspot(hotspotId) 610 | g_currentMission:removeMapHotspot(Tardis.hotspots[hotspotId]); 611 | Tardis.hotspots[hotspotId] = nil; 612 | Tardis:showBlinking(hotspotId, 2); 613 | end 614 | 615 | function Tardis:saveHotspots(missionInfo) 616 | if #Tardis.hotspots > 0 then 617 | 618 | if missionInfo.isValid and missionInfo.xmlKey ~= nil then 619 | local tardisKey = missionInfo.xmlKey .. ".TardisHotspots"; 620 | 621 | for k, v in pairs(Tardis.hotspots) do 622 | setXMLFloat(missionInfo.xmlFile, tardisKey .. '.hotspot' .. k .. '#worldX' , v.worldX); 623 | setXMLFloat(missionInfo.xmlFile, tardisKey .. '.hotspot' .. k .. '#worldZ' , v.worldZ); 624 | end 625 | end 626 | end 627 | end 628 | 629 | function Tardis:loadHotspots() 630 | if g_currentMission == nil or not g_currentMission:getIsServer() then return end 631 | 632 | if g_currentMission.missionInfo.savegameDirectory ~= nil then 633 | local xmlFile = Utils.getFilename("careerSavegame.xml", g_currentMission.missionInfo.savegameDirectory.."/"); 634 | local savegame = loadXMLFile('careerSavegameXML', xmlFile); 635 | local tardisKey = g_currentMission.missionInfo.xmlKey .. ".TardisHotspots"; 636 | 637 | Tardis:dp(string.format('Going to load {%s} from {%s}', tardisKey, xmlFile), 'loadHotspots'); 638 | 639 | if hasXMLProperty(savegame, tardisKey) then 640 | Tardis:dp(string.format('{%s} exists.', tardisKey), 'loadHotspots'); 641 | 642 | for i=1, 9 do 643 | local hotspotKey = tardisKey .. '.hotspot' .. i; 644 | if hasXMLProperty(savegame, hotspotKey) then 645 | local worldX = getXMLFloat(savegame, hotspotKey .. "#worldX"); 646 | local worldZ = getXMLFloat(savegame, hotspotKey .. "#worldZ"); 647 | Tardis:dp(string.format('Loaded MapHotSpot {%d} from savegame. worldX {%s}, worldZ {%s}', i, tostring(worldX), tostring(worldZ)), 'loadHotspots'); 648 | Tardis:createMapHotspot(i, worldX, worldZ); 649 | TardisCreateHotspotEvent.sendEvent(hotspotId, worldX, worldZ, true); 650 | end 651 | end 652 | end 653 | end 654 | 655 | end 656 | 657 | function Tardis.loadedMission() 658 | Tardis:loadHotspots(); 659 | end 660 | 661 | function Tardis.saveToXMLFile(missionInfo) 662 | Tardis:saveHotspots(missionInfo); 663 | end 664 | 665 | -- it would be nicer to do that with triggers if possible. But it should do the job for now 666 | function Tardis:hotspotNearby() 667 | local range = 25; 668 | 669 | local playerX, _, playerZ = getWorldTranslation(g_currentMission.player.rootNode); 670 | local hotspotNearby = false; 671 | 672 | for k, v in pairs(Tardis.hotspots) do 673 | local hsX = v.worldX; 674 | local hsZ = v.worldZ; 675 | if (playerX >= (hsX - range) and playerX <= (hsX + range)) and (playerZ >= (hsZ - range) and playerZ <= (hsZ + range)) then 676 | Tardis:dp(string.format('Hotspot {%d} nearby', k), 'hotspotNearby'); 677 | return k; 678 | end 679 | end 680 | 681 | return 0; 682 | end 683 | 684 | function Tardis:showBlinking(hotspotId, action) 685 | if g_client ~= nil then 686 | --action: 1 created, 2 deleted, 3 nohotspots 687 | local text = ''; 688 | if action == 1 then 689 | text = string.format('%s %d %s', g_i18n.modEnvironments[Tardis.ModName].texts.hotspot, hotspotId, g_i18n.modEnvironments[Tardis.ModName].texts.warning_created); 690 | elseif action == 2 then 691 | text = string.format('%s %d %s', g_i18n.modEnvironments[Tardis.ModName].texts.hotspot, hotspotId, g_i18n.modEnvironments[Tardis.ModName].texts.warning_deleted); 692 | elseif action == 3 then 693 | text = g_i18n.modEnvironments[Tardis.ModName].texts.warning_nohotspot; 694 | end 695 | g_currentMission:showBlinkingWarning(text, 2000); 696 | end 697 | end 698 | 699 | function Tardis:isActionAllowed() 700 | -- We don't want to accidently switch vehicle when the vehicle list is opened and we change to a menu 701 | if string.len(g_gui.currentGuiName) > 0 or #g_gui.dialogs > 0 then 702 | return false; 703 | elseif Tardis.TardisActive then 704 | return true; 705 | end 706 | end 707 | 708 | Mission00.loadMission00Finished = Utils.appendedFunction(Mission00.loadMission00Finished, Tardis.loadedMission) 709 | FSCareerMissionInfo.saveToXMLFile = Utils.appendedFunction(FSCareerMissionInfo.saveToXMLFile, Tardis.saveToXMLFile) --------------------------------------------------------------------------------