예제 #1
0
        public void TsMapOverlayItem825(int startOffset)
        {
            var fileOffset = startOffset + 0x34; // Set position at start of flags

            ZoomLevelVisibility = MemoryHelper.ReadUint8(Sector.Stream, fileOffset);
            var dlcGuardCount = (Sector.Mapper.IsEts2) ? Common.Ets2DlcGuardCount : Common.AtsDlcGuardCount;

            Hidden = MemoryHelper.ReadInt8(Sector.Stream, fileOffset + 0x01) > dlcGuardCount || ZoomLevelVisibility == 255;

            var type         = MemoryHelper.ReadUint8(Sector.Stream, fileOffset + 0x02);
            var overlayToken = MemoryHelper.ReadUInt64(Sector.Stream, fileOffset += 0x05);

            if (type == 1 && overlayToken == 0)
            {
                overlayToken = ScsHash.StringToToken("parking_ico");                                 // parking
            }
            Overlay     = Sector.Mapper.LookupOverlay(overlayToken);
            OverlayName = ScsHash.TokenToString(overlayToken);
            if (Overlay == null)
            {
                Valid = false;
                if (overlayToken != 0)
                {
                    Log.Msg($"Could not find Overlay: '{OverlayName}'({ScsHash.StringToToken(OverlayName):X}), in {Path.GetFileName(Sector.FilePath)} @ {fileOffset}");
                }
            }
            fileOffset += 0x08 + 0x08; // 0x08(overlayId) + 0x08(nodeUid)
            BlockSize   = fileOffset - startOffset;
        }
예제 #2
0
        public void TsPrefabItem855(int startOffset)
        {
            var fileOffset    = startOffset + 0x34; // Set position at start of flags
            var dlcGuardCount = (Sector.Mapper.IsEts2) ? Common.Ets2DlcGuardCount : Common.AtsDlcGuardCount;

            Hidden = MemoryHelper.ReadInt8(Sector.Stream, fileOffset + 0x01) > dlcGuardCount || (MemoryHelper.ReadUint8(Sector.Stream, fileOffset + 0x02) & 0x02) != 0;

            var prefabId = MemoryHelper.ReadUInt64(Sector.Stream, fileOffset += 0x05); // 0x05(flags)

            Prefab = Sector.Mapper.LookupPrefab(prefabId);
            if (Prefab == null)
            {
                Valid = false;
                Log.Msg($"Could not find Prefab: '{ScsHash.TokenToString(prefabId)}'({MemoryHelper.ReadUInt64(Sector.Stream, fileOffset):X}), " +
                        $"in {Path.GetFileName(Sector.FilePath)} @ {fileOffset} (item uid: 0x{Uid:X})");
            }
            var additionalPartsCount = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x08 + 0x08);                          // 0x08(prefabId) + 0x08(m_variant)
            var nodeCount            = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x04 + (additionalPartsCount * 0x08)); // 0x04(addPartsCount) + additionalParts

            fileOffset += 0x04;                                                                                                   // set cursor after nodeCount
            for (var i = 0; i < nodeCount; i++)
            {
                Nodes.Add(MemoryHelper.ReadUInt64(Sector.Stream, fileOffset));
                fileOffset += 0x08;
            }
            var connectedItemCount = MemoryHelper.ReadInt32(Sector.Stream, fileOffset);

            Origin      = MemoryHelper.ReadUint8(Sector.Stream, fileOffset += 0x04 + (0x08 * connectedItemCount) + 0x08); // 0x04(connItemCount) + connItemUids + 0x08(m_some_uid)
            fileOffset += 0x02 + nodeCount * 0x0C + 0x08;                                                                 // 0x02(origin & padding) + nodeLooks + 0x08(padding2)

            BlockSize = fileOffset - startOffset;
        }
예제 #3
0
        public void TsCompanyItem858(int startOffset)
        {
            var fileOffset    = startOffset + 0x34; // Set position at start of flags
            var dlcGuardCount = (Sector.Mapper.IsEts2) ? Common.Ets2DlcGuardCount : Common.AtsDlcGuardCount;

            Hidden = MemoryHelper.ReadInt8(Sector.Stream, fileOffset + 0x01) > dlcGuardCount;

            OverlayToken = MemoryHelper.ReadUInt64(Sector.Stream, fileOffset += 0x05); // 0x05(flags)

            Overlay = Sector.Mapper.LookupOverlay(OverlayToken);
            if (Overlay == null)
            {
                Valid = false;
                if (OverlayToken != 0)
                {
                    Log.Msg(
                        $"Could not find Company Overlay: '{ScsHash.TokenToString(OverlayToken)}'({OverlayToken:X}), in {Path.GetFileName(Sector.FilePath)} @ {fileOffset}");
                }
            }

            Nodes.Add(MemoryHelper.ReadUInt64(Sector.Stream, fileOffset += 0x08 + 0x08));             // (prefab uid) | 0x08(OverlayToken) + 0x08(uid[0])

            var count = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x08 + 0x08);             // count | 0x08 (uid[1] & uid[2])

            count       = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x04 + (0x08 * count)); // count2
            count       = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x04 + (0x08 * count)); // count3
            count       = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x04 + (0x08 * count)); // count4
            count       = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x04 + (0x08 * count)); // count5
            count       = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x04 + (0x08 * count)); // count6
            fileOffset += 0x04 + (0x08 * count);
            BlockSize   = fileOffset - startOffset;
        }
예제 #4
0
        public TsCity(TsMapper mapper, string path)
        {
            _mapper = mapper;
            var file = _mapper.Rfs.GetFileEntry(path);

            if (file == null)
            {
                return;
            }

            var fileContent = file.Entry.Read();

            var lines = Encoding.UTF8.GetString(fileContent).Split('\n');

            foreach (var line in lines)
            {
                if (line.Contains("city_data"))
                {
                    Token = ScsHash.StringToToken(line.Split('.')[1].Trim());
                }
                else if (line.Contains("city_name") && !line.Contains("uppercase"))
                {
                    Name = line.Split('"')[1];
                }
                else if (line.Contains("country"))
                {
                    Country = line.Split(':')[1].Trim();
                }
            }
        }
예제 #5
0
        private void ParsePrefabFiles()
        {
            var worldDirectory = Rfs.GetDirectory("def/world");

            if (worldDirectory == null)
            {
                Log.Msg("Could not read 'def/world' dir");
                return;
            }

            var prefabFiles = worldDirectory.GetFiles("prefab");

            if (prefabFiles == null)
            {
                Log.Msg("Could not read prefab files");
                return;
            }

            foreach (var prefabFile in prefabFiles)
            {
                var data  = prefabFile.Entry.Read();
                var lines = Encoding.UTF8.GetString(data).Split('\n');

                var token    = 0UL;
                var path     = "";
                var category = "";

                foreach (var line in lines)
                {
                    if (line.Contains("prefab_model"))
                    {
                        token = ScsHash.StringToToken(line.Split('.')[1].Trim());
                    }
                    else if (line.Contains("prefab_desc"))
                    {
                        path = Helper.GetFilePath(line.Split('"')[1]);
                    }
                    else if (line.Contains("category"))
                    {
                        category = line.Split('"')[1];
                    }

                    if (line.Contains("}") && token != 0 && path != "")
                    {
                        var prefab = new TsPrefab(this, path, token, category);
                        if (prefab.Token != 0 && !_prefabLookup.ContainsKey(prefab.Token))
                        {
                            _prefabLookup.Add(prefab.Token, prefab);
                        }

                        token    = 0;
                        path     = "";
                        category = "";
                    }
                }
            }
        }
예제 #6
0
        public void TsTriggerItem875(int startOffset)
        {
            var fileOffset    = startOffset + 0x34; // Set position at start of flags
            var dlcGuardCount = (Sector.Mapper.IsEts2) ? Common.Ets2DlcGuardCount : Common.AtsDlcGuardCount;

            Hidden = MemoryHelper.ReadInt8(Sector.Stream, fileOffset + 0x01) > dlcGuardCount;
            var tagCount  = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x05);                               // 0x05(flags)
            var nodeCount = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x04 + (0x08 * tagCount));           // 0x04(nodeCount) + tags

            var triggerActionCount = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x04 + (0x08 * nodeCount)); // 0x04(nodeCount) + nodeUids

            fileOffset += 0x04;                                                                                      // cursor after triggerActionCount

            for (var i = 0; i < triggerActionCount; i++)
            {
                var action = MemoryHelper.ReadUInt64(Sector.Stream, fileOffset);
                if (action == ScsHash.StringToToken("hud_parking"))
                {
                    OverlayName = "parking_ico";
                    Overlay     = Sector.Mapper.LookupOverlay(ScsHash.StringToToken(OverlayName));
                    if (Overlay == null)
                    {
                        Console.WriteLine("Could not find parking overlay");
                        Valid = false;
                    }
                }

                var hasOverride = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x08); // 0x08(action)
                fileOffset += 0x04;                                                          // set cursor after hasOverride
                if (hasOverride < 0)
                {
                    continue;
                }
                fileOffset += 0x04 * hasOverride; // set cursor after override values

                var parameterCount = MemoryHelper.ReadInt32(Sector.Stream, fileOffset);
                fileOffset += 0x04; // set cursor after parameterCount

                for (var j = 0; j < parameterCount; j++)
                {
                    var paramLength = MemoryHelper.ReadInt32(Sector.Stream, fileOffset);
                    fileOffset += 0x04 + 0x04 + paramLength; // 0x04(paramLength) + 0x04(padding) + (param)
                }
                var targetTagCount = MemoryHelper.ReadInt32(Sector.Stream, fileOffset);

                fileOffset += 0x04 + targetTagCount * 0x08 + 0x08; // 0x04(targetTagCount) + targetTags + 0x04(m_range & m_type)
            }

            if (nodeCount == 1)
            {
                fileOffset += 0x04;                 // 0x04(m_radius)
            }
            BlockSize = fileOffset - startOffset;
        }
예제 #7
0
        public TsCountry(TsMapper mapper, string path)
        {
            _mapper = mapper;
            var file = _mapper.Rfs.GetFileEntry(path);

            if (file == null)
            {
                return;
            }
            LocalizedNames = new Dictionary <string, string>();
            var fileContent = file.Entry.Read();

            var lines = Encoding.UTF8.GetString(fileContent).Split('\n');

            foreach (var line in lines)
            {
                var(validLine, key, value) = SiiHelper.ParseLine(line);
                if (!validLine)
                {
                    continue;
                }

                if (key == "country_data")
                {
                    Token = ScsHash.StringToToken(SiiHelper.Trim(value.Split('.')[2]));
                }
                else if (key == "country_id")
                {
                    CountryId = int.Parse(value);
                }
                else if (key == "name")
                {
                    Name = value.Split('"')[1];
                }
                else if (key == "name_localized")
                {
                    LocalizationToken = value.Split('"')[1];
                    LocalizationToken = LocalizationToken.Replace("@", "");
                }
                else if (key == "country_code")
                {
                    CountryCode = value.Split('"')[1];
                }
                else if (key == "pos")
                {
                    var vector = value.Split('(')[1].Split(')')[0];
                    var values = vector.Split(',');
                    X = float.Parse(values[0], CultureInfo.InvariantCulture);
                    Y = float.Parse(values[2], CultureInfo.InvariantCulture);
                }
            }
        }
예제 #8
0
        public void TsTriggerItem829(int startOffset)
        {
            var fileOffset    = startOffset + 0x34; // Set position at start of flags
            var dlcGuardCount = (Sector.Mapper.IsEts2) ? Common.Ets2DlcGuardCount : Common.AtsDlcGuardCount;

            Hidden = MemoryHelper.ReadInt8(Sector.Stream, fileOffset + 0x01) > dlcGuardCount;
            var tagCount  = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x05);                               // 0x05(flags)
            var nodeCount = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x04 + (0x08 * tagCount));           // 0x04(nodeCount) + tags

            var triggerActionCount = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x04 + (0x08 * nodeCount)); // 0x04(nodeCount) + nodeUids

            fileOffset += 0x04;                                                                                      // cursor after triggerActionCount

            for (var i = 0; i < triggerActionCount; i++)
            {
                var action = MemoryHelper.ReadUInt64(Sector.Stream, fileOffset);
                if (action == ScsHash.StringToToken("hud_parking"))
                {
                    OverlayName = "parking_ico";
                    Overlay     = Sector.Mapper.LookupOverlay(ScsHash.StringToToken(OverlayName));
                    if (Overlay == null)
                    {
                        Console.WriteLine("Could not find parking overlay");
                        Valid = false;
                    }
                }

                var hasOverride = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x08); // 0x08(action)
                if (hasOverride > 0)
                {
                    fileOffset += 0x04 * hasOverride;
                }

                var hasParameters = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x04); // 0x04(hasOverride)
                fileOffset += 0x04;                                                            // set cursor after hasParameters
                if (hasParameters == 1)
                {
                    var parametersLength = MemoryHelper.ReadInt32(Sector.Stream, fileOffset);
                    fileOffset += 0x04 + 0x04 + parametersLength; // 0x04(parametersLength) + 0x04(padding) + text(parametersLength * 0x01)
                }
                else if (hasParameters == 3)
                {
                    fileOffset += 0x08;                                                         // 0x08 (m_some_uid)
                }
                var targetTagCount = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x08); // 0x08(unk/padding)
                fileOffset += 0x04 + targetTagCount * 0x08;                                     // 0x04(targetTagCount) + targetTags
            }

            fileOffset += 0x18; // 0x18(range & reset_delay & reset_distance & min_speed & max_speed & flags2)
            BlockSize   = fileOffset - startOffset;
        }
예제 #9
0
        public void TsFeryItem825(int startOffset)
        {
            var fileOffset = startOffset + 0x34; // Set position at start of flags

            Train = MemoryHelper.ReadUint8(Sector.Stream, fileOffset) != 0;
            if (Train)
            {
                Overlay = Sector.Mapper.LookupOverlay(ScsHash.StringToToken("train_ico"));
            }
            else
            {
                Overlay = Sector.Mapper.LookupOverlay(ScsHash.StringToToken("port_overlay"));
            }

            FerryPortId = MemoryHelper.ReadUInt64(Sector.Stream, fileOffset += 0x05);
            Sector.Mapper.AddFerryPortLocation(FerryPortId, X, Z);
            fileOffset += 0x08 + 0x1C; // 0x08(ferryPorId) + 0x1C(prefab_uid & node_uid & unloadoffset)
            BlockSize   = fileOffset - startOffset;
        }
예제 #10
0
파일: TsItem.cs 프로젝트: edoaxyz/ts-map
        public TsFerryItem(TsSector sector, int startOffset) : base(sector, startOffset)
        {
            Valid = true;
            var fileOffset = startOffset + 0x34; // Set position at start of flags

            Train = (Sector.Stream[fileOffset] != 0);
            if (Train)
            {
                Overlay = Sector.Mapper.LookupOverlay(ScsHash.StringToToken("train_ico"));
            }
            else
            {
                Overlay = Sector.Mapper.LookupOverlay(ScsHash.StringToToken("port_overlay"));
            }

            FerryPortId = BitConverter.ToUInt64(Sector.Stream, fileOffset += 0x05);
            sector.Mapper.AddFerryPortLocation(FerryPortId, X, Z);
            fileOffset += 0x08 + 0x1C;
            BlockSize   = fileOffset - startOffset;
        }
예제 #11
0
        public void TsCityItem825(int startOffset)
        {
            var fileOffset = startOffset + 0x34; // Set position at start of flags

            Hidden = (MemoryHelper.ReadUint8(Sector.Stream, fileOffset) & 0x01) != 0;
            var cityId = MemoryHelper.ReadUInt64(Sector.Stream, fileOffset + 0x05);

            City = Sector.Mapper.LookupCity(cityId);
            if (City == null)
            {
                Valid = false;
                Log.Msg($"Could not find City: '{ScsHash.TokenToString(cityId)}'({cityId:X}), " +
                        $"in {Path.GetFileName(Sector.FilePath)} @ {fileOffset}");
            }

            Width       = MemoryHelper.ReadSingle(Sector.Stream, fileOffset += 0x05 + 0x08); // 0x05(flags) + 0x08(cityId)
            Height      = MemoryHelper.ReadSingle(Sector.Stream, fileOffset += 0x04);        // 0x08(Width)
            NodeUid     = MemoryHelper.ReadUInt64(Sector.Stream, fileOffset += 0x04);        // 0x08(height)
            fileOffset += 0x08;                                                              // nodeUid
            BlockSize   = fileOffset - startOffset;
        }
예제 #12
0
        public void TsPrefabItem825(int startOffset)
        {
            var fileOffset    = startOffset + 0x34; // Set position at start of flags
            var dlcGuardCount = (Sector.Mapper.IsEts2) ? Common.Ets2DlcGuardCount : Common.AtsDlcGuardCount;

            Hidden = MemoryHelper.ReadInt8(Sector.Stream, fileOffset + 0x01) > dlcGuardCount || (MemoryHelper.ReadUint8(Sector.Stream, fileOffset + 0x02) & 0x02) != 0;

            var prefabId = MemoryHelper.ReadUInt64(Sector.Stream, fileOffset += 0x05); // 0x05(flags)

            Prefab = Sector.Mapper.LookupPrefab(prefabId);
            if (Prefab == null)
            {
                Valid = false;
                Log.Msg($"Could not find Prefab: '{ScsHash.TokenToString(prefabId)}'({MemoryHelper.ReadUInt64(Sector.Stream, fileOffset):X}), " +
                        $"in {Path.GetFileName(Sector.FilePath)} @ {fileOffset} (item uid: 0x{Uid:X})");
            }
            var nodeCount = MemoryHelper.ReadInt32(Sector.Stream, fileOffset += 0x18); // 0x18(id & look & variant)

            fileOffset += 0x04;                                                        // set cursor after nodeCount
            for (var i = 0; i < nodeCount; i++)
            {
                Nodes.Add(MemoryHelper.ReadUInt64(Sector.Stream, fileOffset));
                fileOffset += 0x08;
            }

            var connectedItemCount = MemoryHelper.ReadInt32(Sector.Stream, fileOffset);

            Origin = MemoryHelper.ReadUint8(Sector.Stream, fileOffset += 0x04 + (0x08 * connectedItemCount) + 0x08);                            // 0x04(connItemCount) + connItemUids + 0x08(m_some_uid)
            var prefabVegetationCount = MemoryHelper.ReadInt32(Sector.Stream,
                                                               fileOffset += 0x01 + 0x01 + (NodeLookBlockSize825 * nodeCount));                 // 0x01(origin) + 0x01(padding) + nodeLooks
            var vegetationSphereCount = MemoryHelper.ReadInt32(Sector.Stream,
                                                               fileOffset += 0x04 + (PrefabVegetaionBlockSize * prefabVegetationCount) + 0x04); // 0x04(prefabVegCount) + prefabVegs + 0x04(padding2)

            fileOffset += 0x04 + (VegetationSphereBlockSize825 * vegetationSphereCount);                                                        // 0x04(vegSphereCount) + vegSpheres


            BlockSize = fileOffset - startOffset;
        }
예제 #13
0
        public void TsRoadItem854(int startOffset)
        {
            var fileOffset    = startOffset + 0x34; // Set position at start of flags
            var dlcGuardCount = (Sector.Mapper.IsEts2) ? Common.Ets2DlcGuardCount : Common.AtsDlcGuardCount;

            Hidden = MemoryHelper.ReadInt8(Sector.Stream, fileOffset + 0x06) > dlcGuardCount || (MemoryHelper.ReadUint8(Sector.Stream, fileOffset + 0x03) & 0x02) != 0;
            var roadLookId = MemoryHelper.ReadUInt64(Sector.Stream, fileOffset += 0x09);

            RoadLook = Sector.Mapper.LookupRoadLook(roadLookId);

            if (RoadLook == null)
            {
                Valid = false;
                Log.Msg($"Could not find RoadLook: '{ScsHash.TokenToString(roadLookId)}'({MemoryHelper.ReadUInt64(Sector.Stream, fileOffset):X}), " +
                        $"in {Path.GetFileName(Sector.FilePath)} @ {fileOffset}");
            }

            StartNodeUid = MemoryHelper.ReadUInt64(Sector.Stream, fileOffset += 0x08 + 0xA4); // 0x08(RoadLook) + 0xA4(sets cursor before node_uid[])
            EndNodeUid   = MemoryHelper.ReadUInt64(Sector.Stream, fileOffset += 0x08);        // 0x08(startNodeUid)
            fileOffset  += 0x08 + 0x04;                                                       // 0x08(EndNodeUid) + 0x04(m_unk)

            BlockSize = fileOffset - startOffset;
        }
예제 #14
0
파일: TsMapper.cs 프로젝트: shardick/ts-map
        /// <summary>
        /// Creates a json file with the positions and names (w/ localizations) of all cities
        /// </summary>
        public void ExportCities(ExportFlags exportFlags, string path)
        {
            if (!Directory.Exists(path))
            {
                return;
            }
            var citiesJArr = new JArray();

            foreach (var city in Cities)
            {
                if (city.Hidden)
                {
                    continue;
                }
                var cityJObj = JObject.FromObject(city.City);
                cityJObj["X"] = city.X;
                cityJObj["Y"] = city.Z;
                if (_countriesLookup.ContainsKey(ScsHash.StringToToken(city.City.Country)))
                {
                    var country = _countriesLookup[ScsHash.StringToToken(city.City.Country)];
                    cityJObj["CountryId"] = country.CountryId;
                }
                else
                {
                    Log.Msg($"Could not find country for {city.City.Name}");
                }

                if (exportFlags.IsActive(ExportFlags.CityLocalizedNames))
                {
                    cityJObj["LocalizedNames"] = JObject.FromObject(city.City.LocalizedNames);
                }

                citiesJArr.Add(cityJObj);
            }
            File.WriteAllText(Path.Combine(path, "Cities.json"), citiesJArr.ToString(Formatting.Indented));
        }
예제 #15
0
파일: TsCity.cs 프로젝트: iuzar/ts-map
        public TsCity(TsMapper mapper, string path)
        {
            _mapper = mapper;
            var file = _mapper.Rfs.GetFileEntry(path);

            if (file == null)
            {
                return;
            }

            var fileContent = file.Entry.Read();

            var lines       = Encoding.UTF8.GetString(fileContent).Split('\n');
            var offsetCount = 0;

            XOffsets = new List <int>();
            YOffsets = new List <int>();

            foreach (var line in lines)
            {
                if (line.Contains("city_data"))
                {
                    Token = ScsHash.StringToToken(line.Split('.')[1].Trim());
                }
                else if (line.Contains("city_name") && !line.Contains("uppercase") && !line.Contains("short") && !line.Contains("localized"))
                {
                    Name = line.Split('"')[1];
                }
                else if (line.Contains("city_name_localized"))
                {
                    NameLocalized = line.Split('"')[1];
                    NameLocalized = NameLocalized.Substring(2, NameLocalized.Length - 4);
                }
                else if (line.Contains("country"))
                {
                    Country = line.Split(':')[1].Trim();
                }
                else if (line.Contains("map_x_offsets[]"))
                {
                    if (++offsetCount > 4)
                    {
                        var offset = 0;
                        if (int.TryParse(line.Split(':')[1].Trim(), out offset))
                        {
                            XOffsets.Add(offset);
                        }
                    }
                    if (offsetCount == 8)
                    {
                        offsetCount = 0;
                    }
                }
                else if (line.Contains("map_y_offsets[]"))
                {
                    if (++offsetCount > 4)
                    {
                        var offset = 0;
                        if (int.TryParse(line.Split(':')[1].Trim(), out offset))
                        {
                            YOffsets.Add(offset);
                        }
                    }
                }
            }
        }
예제 #16
0
        public TsCity(TsMapper mapper, string path)
        {
            _mapper = mapper;
            var file = _mapper.Rfs.GetFileEntry(path);

            if (file == null)
            {
                return;
            }
            LocalizedNames = new Dictionary <string, string>();
            var fileContent = file.Entry.Read();

            var lines       = Encoding.UTF8.GetString(fileContent).Split('\n');
            var offsetCount = 0;

            XOffsets = new List <int>();
            YOffsets = new List <int>();

            foreach (var line in lines)
            {
                var(validLine, key, value) = SiiHelper.ParseLine(line);
                if (!validLine)
                {
                    continue;
                }

                if (key == "city_data")
                {
                    Token = ScsHash.StringToToken(SiiHelper.Trim(value.Split('.')[1]));
                }
                else if (key == "city_name")
                {
                    Name = line.Split('"')[1];
                }
                else if (key == "city_name_localized")
                {
                    LocalizationToken = value.Split('"')[1];
                    LocalizationToken = LocalizationToken.Replace("@", "");
                }
                else if (key == "country")
                {
                    Country = value;
                }
                else if (key.Contains("map_x_offsets[]"))
                {
                    if (++offsetCount > 4)
                    {
                        if (int.TryParse(value, out var offset))
                        {
                            XOffsets.Add(offset);
                        }
                    }
                    if (offsetCount == 8)
                    {
                        offsetCount = 0;
                    }
                }
                else if (key.Contains("map_y_offsets[]"))
                {
                    if (++offsetCount > 4)
                    {
                        if (int.TryParse(value, out var offset))
                        {
                            YOffsets.Add(offset);
                        }
                    }
                }
            }
        }
예제 #17
0
파일: TsMapper.cs 프로젝트: shardick/ts-map
        private void ParseFerryConnections()
        {
            var connectionDirectory = Rfs.GetDirectory("def/ferry/connection");

            if (connectionDirectory == null)
            {
                Log.Msg("Could not read 'def/ferry/connection' dir");
                return;
            }

            var ferryConnectionFiles = connectionDirectory.GetFiles("sii");

            if (ferryConnectionFiles == null)
            {
                Log.Msg("Could not read ferry connection files files");
                return;
            }

            foreach (var ferryConnectionFile in ferryConnectionFiles)
            {
                var data  = ferryConnectionFile.Entry.Read();
                var lines = Encoding.UTF8.GetString(data).Split('\n');

                TsFerryConnection conn = null;

                foreach (var line in lines)
                {
                    var(validLine, key, value) = SiiHelper.ParseLine(line);
                    if (validLine)
                    {
                        if (conn != null)
                        {
                            if (key.Contains("connection_positions"))
                            {
                                var index  = int.Parse(key.Split('[')[1].Split(']')[0]);
                                var vector = value.Split('(')[1].Split(')')[0];
                                var values = vector.Split(',');
                                var x      = float.Parse(values[0], CultureInfo.InvariantCulture);
                                var z      = float.Parse(values[2], CultureInfo.InvariantCulture);
                                conn.AddConnectionPosition(index, x, z);
                            }
                            else if (key.Contains("connection_directions"))
                            {
                                var index  = int.Parse(key.Split('[')[1].Split(']')[0]);
                                var vector = value.Split('(')[1].Split(')')[0];
                                var values = vector.Split(',');
                                var x      = float.Parse(values[0], CultureInfo.InvariantCulture);
                                var z      = float.Parse(values[2], CultureInfo.InvariantCulture);
                                conn.AddRotation(index, Math.Atan2(z, x));
                            }
                        }

                        if (key == "ferry_connection")
                        {
                            var portIds = value.Split('.');
                            conn = new TsFerryConnection
                            {
                                StartPortToken = ScsHash.StringToToken(portIds[1]),
                                EndPortToken   = ScsHash.StringToToken(portIds[2].TrimEnd('{').Trim())
                            };
                        }
                    }

                    if (!line.Contains("}") || conn == null)
                    {
                        continue;
                    }
                    ;

                    var existingItem = _ferryConnectionLookup.FirstOrDefault(item =>
                                                                             (item.StartPortToken == conn.StartPortToken && item.EndPortToken == conn.EndPortToken) ||
                                                                             (item.StartPortToken == conn.EndPortToken && item.EndPortToken == conn.StartPortToken)); // Check if connection already exists
                    if (existingItem == null)
                    {
                        _ferryConnectionLookup.Add(conn);
                    }
                    conn = null;
                }
            }
        }
예제 #18
0
파일: TsMapper.cs 프로젝트: shardick/ts-map
        public TsCountry GetCountryByTokenName(string name)
        {
            var token = ScsHash.StringToToken(name);

            return(_countriesLookup.ContainsKey(token) ? _countriesLookup[token] : null);
        }
예제 #19
0
        private void ParseOverlays()
        {
            var uiMapDirectory = Rfs.GetDirectory("material/ui/map");

            if (uiMapDirectory == null)
            {
                Log.Msg("Could not read 'material/ui/map' dir");
                return;
            }

            var matFiles = uiMapDirectory.GetFiles(".mat");

            if (matFiles == null)
            {
                Log.Msg("Could not read .mat files");
                return;
            }

            var uiMapRoadDirectory = Rfs.GetDirectory("material/ui/map/road");

            if (uiMapRoadDirectory != null)
            {
                var data = uiMapRoadDirectory.GetFiles(".mat");
                if (data != null)
                {
                    matFiles.AddRange(data);
                }
            }
            else
            {
                Log.Msg("Could not read 'material/ui/map/road' dir");
            }

            var uiCompanyDirectory = Rfs.GetDirectory("material/ui/company/small");

            if (uiCompanyDirectory != null)
            {
                var data = uiCompanyDirectory.GetFiles(".mat");
                if (data != null)
                {
                    matFiles.AddRange(data);
                }
            }
            else
            {
                Log.Msg("Could not read 'material/ui/company/small' dir");
            }

            foreach (var matFile in matFiles)
            {
                var data  = matFile.Entry.Read();
                var lines = Encoding.UTF8.GetString(data).Split('\n');

                foreach (var line in lines)
                {
                    if (line.Contains("texture") && !line.Contains("_name"))
                    {
                        var tobjPath = Helper.CombinePath(matFile.GetLocalPath(), line.Split('"')[1]);

                        var tobjData = Rfs.GetFileEntry(tobjPath)?.Entry?.Read();

                        if (tobjData == null)
                        {
                            break;
                        }

                        var path = Helper.GetFilePath(Encoding.UTF8.GetString(tobjData, 0x30, tobjData.Length - 0x30));

                        var name = matFile.GetFileName();
                        if (name.StartsWith("map"))
                        {
                            continue;
                        }
                        if (name.StartsWith("road_"))
                        {
                            name = name.Substring(5);
                        }

                        var token = ScsHash.StringToToken(name);
                        if (!_overlayLookup.ContainsKey(token))
                        {
                            _overlayLookup.Add(token, new TsMapOverlay(this, path));
                        }
                    }
                }
            }
        }
예제 #20
0
        private void ParseRoadLookFiles()
        {
            var worldDirectory = Rfs.GetDirectory("def/world");

            if (worldDirectory == null)
            {
                Log.Msg("Could not read 'def/world' dir");
                return;
            }

            var roadLookFiles = worldDirectory.GetFiles("road_look");

            if (roadLookFiles == null)
            {
                Log.Msg("Could not read road look files");
                return;
            }

            foreach (var roadLookFile in roadLookFiles)
            {
                var        data     = roadLookFile.Entry.Read();
                var        lines    = Encoding.UTF8.GetString(data).Split('\n');
                TsRoadLook roadLook = null;

                foreach (var line in lines)
                {
                    if (line.Contains(":") && roadLook != null)
                    {
                        var value = line.Substring(line.IndexOf(':') + 1).Trim();
                        var key   = line.Substring(0, line.IndexOf(':')).Trim();
                        switch (key)
                        {
                        case "lanes_left[]":
                            roadLook.LanesLeft.Add(value);
                            break;

                        case "lanes_right[]":
                            roadLook.LanesRight.Add(value);
                            break;

                        case "road_offset":
                            float.TryParse(value.Replace('.', ','), out roadLook.Offset);
                            break;
                        }
                    }

                    if (line.Contains("road_look"))
                    {
                        roadLook = new TsRoadLook(ScsHash.StringToToken(line.Split('.')[1].Trim('{').Trim()));
                    }

                    if (line.Contains("}") && roadLook != null)
                    {
                        if (roadLook.Token != 0 && !_roadLookup.ContainsKey(roadLook.Token))
                        {
                            _roadLookup.Add(roadLook.Token, roadLook);
                            roadLook = null;
                        }
                    }
                }
            }
        }
예제 #21
0
파일: TsMapper.cs 프로젝트: shardick/ts-map
        /// <summary>
        /// Saves all overlays as .png images.
        /// Creates a json file with all positions of said overlays
        /// </summary>
        /// <remarks>
        /// ZoomLevelVisibility flags: Multiple can be selected at the same time,
        /// eg. if value is 3 then 0 and 1 are both selected
        /// Selected = hidden (0-7 => numbers in game editor)
        /// 1 = (Nav map, 3D view, zoom 0) (0)
        /// 2 = (Nav map, 3D view, zoom 1) (1)
        /// 4 = (Nav map, 2D view, zoom 0) (2)
        /// 8 = (Nav map, 2D view, zoom 1) (3)
        /// 16 = (World map, zoom 0) (4)
        /// 32 = (World map, zoom 1) (5)
        /// 64 = (World map, zoom 2) (6)
        /// 128 = (World map, zoom 3) (7)
        /// </remarks>
        /// <param name="path"></param>
        public void ExportOverlays(ExportFlags exportFlags, string path)
        {
            if (!Directory.Exists(path))
            {
                return;
            }

            var saveAsPNG = exportFlags.IsActive(ExportFlags.OverlayPNGs);

            var overlayPath = Path.Combine(path, "Overlays");

            if (saveAsPNG)
            {
                Directory.CreateDirectory(overlayPath);
            }

            var overlaysJArr = new JArray();

            foreach (var overlay in MapOverlays)
            {
                if (overlay.Hidden)
                {
                    continue;
                }
                var overlayName = overlay.OverlayName;
                var b           = overlay.Overlay?.GetBitmap();
                if (b == null)
                {
                    continue;
                }
                var overlayJObj = new JObject
                {
                    ["X"] = overlay.X,
                    ["Y"] = overlay.Z,
                    ["ZoomLevelVisibility"] = overlay.ZoomLevelVisibility,
                    ["Name"]   = overlayName,
                    ["Type"]   = "Overlay",
                    ["Width"]  = b.Width,
                    ["Height"] = b.Height,
                };
                overlaysJArr.Add(overlayJObj);
                if (saveAsPNG && !File.Exists(Path.Combine(overlayPath, $"{overlayName}.png")))
                {
                    b.Save(Path.Combine(overlayPath, $"{overlayName}.png"));
                }
            }
            foreach (var company in Companies)
            {
                if (company.Hidden)
                {
                    continue;
                }
                var overlayName = ScsHash.TokenToString(company.OverlayToken);
                var point       = new PointF(company.X, company.Z);
                if (company.Nodes.Count > 0)
                {
                    var prefab = Prefabs.FirstOrDefault(x => x.Uid == company.Nodes[0]);
                    if (prefab != null)
                    {
                        var originNode = GetNodeByUid(prefab.Nodes[0]);
                        if (prefab.Prefab.PrefabNodes == null)
                        {
                            continue;
                        }
                        var mapPointOrigin = prefab.Prefab.PrefabNodes[prefab.Origin];

                        var rot = (float)(originNode.Rotation - Math.PI -
                                          Math.Atan2(mapPointOrigin.RotZ, mapPointOrigin.RotX) + Math.PI / 2);

                        var prefabstartX = originNode.X - mapPointOrigin.X;
                        var prefabStartZ = originNode.Z - mapPointOrigin.Z;
                        var companyPos   = prefab.Prefab.SpawnPoints.FirstOrDefault(x => x.Type == TsSpawnPointType.CompanyPos);
                        if (companyPos != null)
                        {
                            point = RenderHelper.RotatePoint(prefabstartX + companyPos.X, prefabStartZ + companyPos.Z,
                                                             rot,
                                                             originNode.X, originNode.Z);
                        }
                    }
                }
                var b = company.Overlay?.GetBitmap();
                if (b == null)
                {
                    continue;
                }
                var overlayJObj = new JObject
                {
                    ["X"]      = point.X,
                    ["Y"]      = point.Y,
                    ["Name"]   = overlayName,
                    ["Type"]   = "Company",
                    ["Width"]  = b.Width,
                    ["Height"] = b.Height,
                };
                overlaysJArr.Add(overlayJObj);
                if (saveAsPNG && !File.Exists(Path.Combine(overlayPath, $"{overlayName}.png")))
                {
                    b.Save(Path.Combine(overlayPath, $"{overlayName}.png"));
                }
            }
            foreach (var trigger in Triggers)
            {
                if (trigger.Hidden)
                {
                    continue;
                }
                var overlayName = trigger.OverlayName;
                var b           = trigger.Overlay?.GetBitmap();
                if (b == null)
                {
                    continue;
                }
                var overlayJObj = new JObject
                {
                    ["X"]      = trigger.X,
                    ["Y"]      = trigger.Z,
                    ["Name"]   = overlayName,
                    ["Type"]   = "Parking",
                    ["Width"]  = b.Width,
                    ["Height"] = b.Height,
                };
                overlaysJArr.Add(overlayJObj);
                if (saveAsPNG && !File.Exists(Path.Combine(overlayPath, $"{overlayName}.png")))
                {
                    b.Save(Path.Combine(overlayPath, $"{overlayName}.png"));
                }
            }
            foreach (var ferry in FerryConnections)
            {
                if (ferry.Hidden)
                {
                    continue;
                }
                var overlayName = ScsHash.TokenToString(ferry.OverlayToken);
                var b           = ferry.Overlay?.GetBitmap();
                if (b == null)
                {
                    continue;
                }
                var overlayJObj = new JObject
                {
                    ["X"]      = ferry.X,
                    ["Y"]      = ferry.Z,
                    ["Name"]   = overlayName,
                    ["Type"]   = (ferry.Train) ? "Train" : "Ferry",
                    ["Width"]  = b.Width,
                    ["Height"] = b.Height,
                };
                overlaysJArr.Add(overlayJObj);
                if (saveAsPNG && !File.Exists(Path.Combine(overlayPath, $"{overlayName}.png")))
                {
                    b.Save(Path.Combine(overlayPath, $"{overlayName}.png"));
                }
            }

            foreach (var prefab in Prefabs)
            {
                if (prefab.Hidden)
                {
                    continue;
                }
                var originNode = GetNodeByUid(prefab.Nodes[0]);
                if (prefab.Prefab.PrefabNodes == null)
                {
                    continue;
                }
                var mapPointOrigin = prefab.Prefab.PrefabNodes[prefab.Origin];

                var rot = (float)(originNode.Rotation - Math.PI -
                                  Math.Atan2(mapPointOrigin.RotZ, mapPointOrigin.RotX) + Math.PI / 2);

                var prefabStartX = originNode.X - mapPointOrigin.X;
                var prefabStartZ = originNode.Z - mapPointOrigin.Z;
                foreach (var spawnPoint in prefab.Prefab.SpawnPoints)
                {
                    var newPoint = RenderHelper.RotatePoint(prefabStartX + spawnPoint.X, prefabStartZ + spawnPoint.Z, rot,
                                                            originNode.X, originNode.Z);

                    var overlayJObj = new JObject
                    {
                        ["X"] = newPoint.X,
                        ["Y"] = newPoint.Y,
                    };

                    string overlayName;

                    switch (spawnPoint.Type)
                    {
                    case TsSpawnPointType.GasPos:
                    {
                        overlayName         = "gas_ico";
                        overlayJObj["Type"] = "Fuel";
                        break;
                    }

                    case TsSpawnPointType.ServicePos:
                    {
                        overlayName         = "service_ico";
                        overlayJObj["Type"] = "Service";
                        break;
                    }

                    case TsSpawnPointType.WeightStationPos:
                    {
                        overlayName         = "weigh_station_ico";
                        overlayJObj["Type"] = "WeightStation";
                        break;
                    }

                    case TsSpawnPointType.TruckDealerPos:
                    {
                        overlayName         = "dealer_ico";
                        overlayJObj["Type"] = "TruckDealer";
                        break;
                    }

                    case TsSpawnPointType.BuyPos:
                    {
                        overlayName         = "garage_large_ico";
                        overlayJObj["Type"] = "Garage";
                        break;
                    }

                    case TsSpawnPointType.RecruitmentPos:
                    {
                        overlayName         = "recruitment_ico";
                        overlayJObj["Type"] = "Recruitment";
                        break;
                    }

                    default:
                        continue;
                    }

                    overlayJObj["Name"] = overlayName;
                    var overlay = LookupOverlay(ScsHash.StringToToken(overlayName));
                    var b       = overlay.GetBitmap();
                    if (b == null)
                    {
                        continue;
                    }
                    overlayJObj["Width"]  = b.Width;
                    overlayJObj["Height"] = b.Height;
                    overlaysJArr.Add(overlayJObj);
                    if (saveAsPNG && !File.Exists(Path.Combine(overlayPath, $"{overlayName}.png")))
                    {
                        b.Save(Path.Combine(overlayPath, $"{overlayName}.png"));
                    }
                }

                var lastId = -1;
                foreach (var triggerPoint in prefab.Prefab.TriggerPoints)
                {
                    var newPoint = RenderHelper.RotatePoint(prefabStartX + triggerPoint.X, prefabStartZ + triggerPoint.Z, rot,
                                                            originNode.X, originNode.Z);

                    if (triggerPoint.TriggerId == lastId)
                    {
                        continue;
                    }
                    lastId = (int)triggerPoint.TriggerId;
                    var overlayJObj = new JObject
                    {
                        ["X"]    = newPoint.X,
                        ["Y"]    = newPoint.Y,
                        ["Name"] = "parking_ico",
                        ["Type"] = "Parking",
                    };

                    if (triggerPoint.TriggerActionToken != ScsHash.StringToToken("hud_parking"))
                    {
                        continue;
                    }

                    const string overlayName = "parking_ico";
                    var          overlay     = LookupOverlay(ScsHash.StringToToken(overlayName));
                    var          b           = overlay.GetBitmap();
                    if (b == null)
                    {
                        continue;
                    }
                    overlayJObj["Width"]  = b.Width;
                    overlayJObj["Height"] = b.Height;
                    overlaysJArr.Add(overlayJObj);
                    if (saveAsPNG && !File.Exists(Path.Combine(overlayPath, $"{overlayName}.png")))
                    {
                        b.Save(Path.Combine(overlayPath, $"{overlayName}.png"));
                    }
                }
            }
            File.WriteAllText(Path.Combine(path, "Overlays.json"), overlaysJArr.ToString(Formatting.Indented));
        }
예제 #22
0
        public void Render(Graphics g, Rectangle clip, float scale, PointF startPoint, MapPalette palette, RenderFlags renderFlags = RenderFlags.All)
        {
            var startTime = DateTime.Now.Ticks;

            g.ResetTransform();
            g.FillRectangle(palette.Background, new Rectangle(0, 0, clip.Width, clip.Height));

            g.ScaleTransform(scale, scale);
            g.TranslateTransform(-startPoint.X, -startPoint.Y);
            g.InterpolationMode = InterpolationMode.NearestNeighbor;
            g.PixelOffsetMode   = PixelOffsetMode.None;
            g.SmoothingMode     = SmoothingMode.AntiAlias;

            if (_mapper == null)
            {
                g.DrawString("Map object not initialized", _defaultFont, palette.Error, 5, 5);
                return;
            }

            var zoomIndex = RenderHelper.GetZoomIndex(clip, scale);

            var endPoint = new PointF(startPoint.X + clip.Width / scale, startPoint.Y + clip.Height / scale);

            var ferryStartTime = DateTime.Now.Ticks;

            if (renderFlags.IsActive(RenderFlags.FerryConnections))
            {
                var ferryConnections = _mapper.FerryConnections.Where(item => !item.Hidden)
                                       .ToList();

                var ferryPen = new Pen(palette.FerryLines, 50)
                {
                    DashPattern = new[] { 10f, 10f }
                };

                foreach (var ferryConnection in ferryConnections)
                {
                    var connections = _mapper.LookupFerryConnection(ferryConnection.FerryPortId);

                    foreach (var conn in connections)
                    {
                        if (conn.Connections.Count == 0) // no extra nodes -> straight line
                        {
                            g.DrawLine(ferryPen, conn.StartPortLocation, conn.EndPortLocation);
                            continue;
                        }

                        var startYaw = Math.Atan2(conn.Connections[0].Z - conn.StartPortLocation.Y, // get angle of the start port to the first node
                                                  conn.Connections[0].X - conn.StartPortLocation.X);
                        var bezierNodes = RenderHelper.GetBezierControlNodes(conn.StartPortLocation.X,
                                                                             conn.StartPortLocation.Y, startYaw, conn.Connections[0].X, conn.Connections[0].Z,
                                                                             conn.Connections[0].Rotation);

                        var bezierPoints = new List <PointF>
                        {
                            new PointF(conn.StartPortLocation.X, conn.StartPortLocation.Y),                                             // start
                            new PointF(conn.StartPortLocation.X + bezierNodes.Item1.X, conn.StartPortLocation.Y + bezierNodes.Item1.Y), // control1
                            new PointF(conn.Connections[0].X - bezierNodes.Item2.X, conn.Connections[0].Z - bezierNodes.Item2.Y),       // control2
                            new PointF(conn.Connections[0].X, conn.Connections[0].Z)
                        };

                        for (var i = 0; i < conn.Connections.Count - 1; i++) // loop all extra nodes
                        {
                            var ferryPoint     = conn.Connections[i];
                            var nextFerryPoint = conn.Connections[i + 1];

                            bezierNodes = RenderHelper.GetBezierControlNodes(ferryPoint.X, ferryPoint.Z, ferryPoint.Rotation,
                                                                             nextFerryPoint.X, nextFerryPoint.Z, nextFerryPoint.Rotation);

                            bezierPoints.Add(new PointF(ferryPoint.X + bezierNodes.Item1.X, ferryPoint.Z + bezierNodes.Item1.Y));         // control1
                            bezierPoints.Add(new PointF(nextFerryPoint.X - bezierNodes.Item2.X, nextFerryPoint.Z - bezierNodes.Item2.Y)); // control2
                            bezierPoints.Add(new PointF(nextFerryPoint.X, nextFerryPoint.Z));                                             // end
                        }

                        var lastFerryPoint = conn.Connections[conn.Connections.Count - 1];
                        var endYaw         = Math.Atan2(conn.EndPortLocation.Y - lastFerryPoint.Z, // get angle of the last node to the end port
                                                        conn.EndPortLocation.X - lastFerryPoint.X);

                        bezierNodes = RenderHelper.GetBezierControlNodes(lastFerryPoint.X,
                                                                         lastFerryPoint.Z, lastFerryPoint.Rotation, conn.EndPortLocation.X, conn.EndPortLocation.Y,
                                                                         endYaw);

                        bezierPoints.Add(new PointF(lastFerryPoint.X + bezierNodes.Item1.X, lastFerryPoint.Z + bezierNodes.Item1.Y));             // control1
                        bezierPoints.Add(new PointF(conn.EndPortLocation.X - bezierNodes.Item2.X, conn.EndPortLocation.Y - bezierNodes.Item2.Y)); // control2
                        bezierPoints.Add(new PointF(conn.EndPortLocation.X, conn.EndPortLocation.Y));                                             // end

                        g.DrawBeziers(ferryPen, bezierPoints.ToArray());
                    }
                }
                ferryPen.Dispose();
            }
            var ferryTime = DateTime.Now.Ticks - ferryStartTime;

            var mapAreaStartTime = DateTime.Now.Ticks;

            if (renderFlags.IsActive(RenderFlags.MapAreas))
            {
                var mapAreas = _mapper.MapAreas.Where(item =>
                                                      item.X >= startPoint.X - itemDrawMargin && item.X <= endPoint.X + itemDrawMargin && item.Z >= startPoint.Y - itemDrawMargin &&
                                                      item.Z <= endPoint.Y + itemDrawMargin && !item.Hidden)
                               .ToList();


                foreach (var mapArea in mapAreas.OrderBy(x => x.DrawOver))
                {
                    var points = new List <PointF>();

                    foreach (var mapAreaNode in mapArea.NodeUids)
                    {
                        var node = _mapper.GetNodeByUid(mapAreaNode);
                        if (node == null)
                        {
                            continue;
                        }
                        points.Add(new PointF(node.X, node.Z));
                    }

                    Brush fillColor = palette.PrefabLight;
                    if ((mapArea.ColorIndex & 0x01) != 0)
                    {
                        fillColor = palette.PrefabLight;
                    }
                    else if ((mapArea.ColorIndex & 0x02) != 0)
                    {
                        fillColor = palette.PrefabDark;
                    }
                    else if ((mapArea.ColorIndex & 0x03) != 0)
                    {
                        fillColor = palette.PrefabGreen;
                    }

                    g.FillPolygon(fillColor, points.ToArray());
                }
            }
            var mapAreaTime = DateTime.Now.Ticks - mapAreaStartTime;

            var prefabStartTime = DateTime.Now.Ticks;
            var prefabs         = _mapper.Prefabs.Where(item =>
                                                        item.X >= startPoint.X - itemDrawMargin && item.X <= endPoint.X + itemDrawMargin && item.Z >= startPoint.Y - itemDrawMargin &&
                                                        item.Z <= endPoint.Y + itemDrawMargin && !item.Hidden)
                                  .ToList();

            if (renderFlags.IsActive(RenderFlags.Prefabs))
            {
                List <TsPrefabLook> drawingQueue = new List <TsPrefabLook>();

                foreach (var prefabItem in prefabs)
                {
                    var originNode = _mapper.GetNodeByUid(prefabItem.Nodes[0]);
                    if (prefabItem.Prefab.PrefabNodes == null)
                    {
                        continue;
                    }

                    if (!prefabItem.HasLooks())
                    {
                        var mapPointOrigin = prefabItem.Prefab.PrefabNodes[prefabItem.Origin];

                        var rot = (float)(originNode.Rotation - Math.PI -
                                          Math.Atan2(mapPointOrigin.RotZ, mapPointOrigin.RotX) + Math.PI / 2);

                        var prefabstartX = originNode.X - mapPointOrigin.X;
                        var prefabStartZ = originNode.Z - mapPointOrigin.Z;

                        List <int> pointsDrawn = new List <int>();

                        for (var i = 0; i < prefabItem.Prefab.MapPoints.Count; i++)
                        {
                            var mapPoint = prefabItem.Prefab.MapPoints[i];
                            pointsDrawn.Add(i);

                            if (mapPoint.LaneCount == -1) // non-road Prefab
                            {
                                Dictionary <int, PointF> polyPoints = new Dictionary <int, PointF>();
                                var nextPoint = i;
                                do
                                {
                                    if (prefabItem.Prefab.MapPoints[nextPoint].Neighbours.Count == 0)
                                    {
                                        break;
                                    }

                                    foreach (var neighbour in prefabItem.Prefab.MapPoints[nextPoint].Neighbours)
                                    {
                                        if (!polyPoints.ContainsKey(neighbour)) // New Polygon Neighbour
                                        {
                                            nextPoint = neighbour;
                                            var newPoint = RenderHelper.RotatePoint(
                                                prefabstartX + prefabItem.Prefab.MapPoints[nextPoint].X,
                                                prefabStartZ + prefabItem.Prefab.MapPoints[nextPoint].Z, rot, originNode.X,
                                                originNode.Z);

                                            polyPoints.Add(nextPoint, new PointF(newPoint.X, newPoint.Y));
                                            break;
                                        }
                                        nextPoint = -1;
                                    }
                                } while (nextPoint != -1);

                                if (polyPoints.Count < 2)
                                {
                                    continue;
                                }

                                var colorFlag = prefabItem.Prefab.MapPoints[polyPoints.First().Key].PrefabColorFlags;

                                Brush fillColor = palette.PrefabLight;
                                if ((colorFlag & 0x02) != 0)
                                {
                                    fillColor = palette.PrefabLight;
                                }
                                else if ((colorFlag & 0x04) != 0)
                                {
                                    fillColor = palette.PrefabDark;
                                }
                                else if ((colorFlag & 0x08) != 0)
                                {
                                    fillColor = palette.PrefabGreen;
                                }
                                // else fillColor = _palette.Error; // Unknown

                                var prefabLook = new TsPrefabPolyLook(polyPoints.Values.ToList())
                                {
                                    ZIndex = ((colorFlag & 0x01) != 0) ? 3 : 2,
                                    Color  = fillColor
                                };

                                prefabItem.AddLook(prefabLook);
                                continue;
                            }

                            var mapPointLaneCount = mapPoint.LaneCount;

                            if (mapPointLaneCount == -2 && i < prefabItem.Prefab.PrefabNodes.Count)
                            {
                                if (mapPoint.ControlNodeIndex != -1)
                                {
                                    mapPointLaneCount = prefabItem.Prefab.PrefabNodes[mapPoint.ControlNodeIndex].LaneCount;
                                }
                            }

                            foreach (var neighbourPointIndex in mapPoint.Neighbours) // TODO: Fix connection between road segments
                            {
                                if (pointsDrawn.Contains(neighbourPointIndex))
                                {
                                    continue;
                                }
                                var neighbourPoint = prefabItem.Prefab.MapPoints[neighbourPointIndex];

                                if ((mapPoint.Hidden || neighbourPoint.Hidden) && prefabItem.Prefab.PrefabNodes.Count + 1 <
                                    prefabItem.Prefab.MapPoints.Count)
                                {
                                    continue;
                                }

                                var roadYaw = Math.Atan2(neighbourPoint.Z - mapPoint.Z, neighbourPoint.X - mapPoint.X);

                                var neighbourLaneCount = neighbourPoint.LaneCount;

                                if (neighbourLaneCount == -2 && neighbourPointIndex < prefabItem.Prefab.PrefabNodes.Count)
                                {
                                    if (neighbourPoint.ControlNodeIndex != -1)
                                    {
                                        neighbourLaneCount = prefabItem.Prefab.PrefabNodes[neighbourPoint.ControlNodeIndex].LaneCount;
                                    }
                                }

                                if (mapPointLaneCount == -2 && neighbourLaneCount != -2)
                                {
                                    mapPointLaneCount = neighbourLaneCount;
                                }
                                else if (neighbourLaneCount == -2 && mapPointLaneCount != -2)
                                {
                                    neighbourLaneCount = mapPointLaneCount;
                                }
                                else if (mapPointLaneCount == -2 && neighbourLaneCount == -2)
                                {
                                    Console.WriteLine($"Could not find lane count for ({i}, {neighbourPointIndex}), defaulting to 1 for {prefabItem.Prefab.FilePath}");
                                    mapPointLaneCount = neighbourLaneCount = 1;
                                }

                                var cornerCoords = new List <PointF>();

                                var coords = RenderHelper.GetCornerCoords(prefabstartX + mapPoint.X, prefabStartZ + mapPoint.Z,
                                                                          (Common.LaneWidth * mapPointLaneCount + mapPoint.LaneOffset) / 2f, roadYaw + Math.PI / 2);

                                cornerCoords.Add(RenderHelper.RotatePoint(coords.X, coords.Y, rot, originNode.X, originNode.Z));

                                coords = RenderHelper.GetCornerCoords(prefabstartX + neighbourPoint.X, prefabStartZ + neighbourPoint.Z,
                                                                      (Common.LaneWidth * neighbourLaneCount + neighbourPoint.LaneOffset) / 2f,
                                                                      roadYaw + Math.PI / 2);
                                cornerCoords.Add(RenderHelper.RotatePoint(coords.X, coords.Y, rot, originNode.X, originNode.Z));

                                coords = RenderHelper.GetCornerCoords(prefabstartX + neighbourPoint.X, prefabStartZ + neighbourPoint.Z,
                                                                      (Common.LaneWidth * neighbourLaneCount + mapPoint.LaneOffset) / 2f,
                                                                      roadYaw - Math.PI / 2);
                                cornerCoords.Add(RenderHelper.RotatePoint(coords.X, coords.Y, rot, originNode.X, originNode.Z));

                                coords = RenderHelper.GetCornerCoords(prefabstartX + mapPoint.X, prefabStartZ + mapPoint.Z,
                                                                      (Common.LaneWidth * mapPointLaneCount + mapPoint.LaneOffset) / 2f, roadYaw - Math.PI / 2);
                                cornerCoords.Add(RenderHelper.RotatePoint(coords.X, coords.Y, rot, originNode.X, originNode.Z));

                                TsPrefabLook prefabLook = new TsPrefabPolyLook(cornerCoords)
                                {
                                    Color  = palette.PrefabRoad,
                                    ZIndex = 4,
                                };

                                prefabItem.AddLook(prefabLook);
                            }
                        }
                    }

                    prefabItem.GetLooks().ForEach(x => drawingQueue.Add(x));
                }

                foreach (var prefabLook in drawingQueue.OrderBy(p => p.ZIndex))
                {
                    prefabLook.Draw(g);
                }
            }
            var prefabTime = DateTime.Now.Ticks - prefabStartTime;

            var roadStartTime = DateTime.Now.Ticks;

            if (renderFlags.IsActive(RenderFlags.Roads))
            {
                var roads = _mapper.Roads.Where(item =>
                                                item.X >= startPoint.X - itemDrawMargin && item.X <= endPoint.X + itemDrawMargin && item.Z >= startPoint.Y - itemDrawMargin &&
                                                item.Z <= endPoint.Y + itemDrawMargin && !item.Hidden)
                            .ToList();

                foreach (var road in roads)
                {
                    var startNode = road.GetStartNode();
                    var endNode   = road.GetEndNode();

                    if (!road.HasPoints())
                    {
                        var newPoints = new List <PointF>();

                        var sx = startNode.X;
                        var sz = startNode.Z;
                        var ex = endNode.X;
                        var ez = endNode.Z;

                        var radius = Math.Sqrt(Math.Pow(sx - ex, 2) + Math.Pow(sz - ez, 2));

                        var tanSx = Math.Cos(-(Math.PI * 0.5f - startNode.Rotation)) * radius;
                        var tanEx = Math.Cos(-(Math.PI * 0.5f - endNode.Rotation)) * radius;
                        var tanSz = Math.Sin(-(Math.PI * 0.5f - startNode.Rotation)) * radius;
                        var tanEz = Math.Sin(-(Math.PI * 0.5f - endNode.Rotation)) * radius;

                        for (var i = 0; i < 8; i++)
                        {
                            var s = i / (float)(8 - 1);
                            var x = (float)TsRoadLook.Hermite(s, sx, ex, tanSx, tanEx);
                            var z = (float)TsRoadLook.Hermite(s, sz, ez, tanSz, tanEz);
                            newPoints.Add(new PointF(x, z));
                        }
                        road.AddPoints(newPoints);
                    }

                    var roadWidth = road.RoadLook.GetWidth();

                    var roadPen = new Pen(palette.Road, roadWidth);
                    g.DrawCurve(roadPen, road.GetPoints()?.ToArray());
                    roadPen.Dispose();
                }
            }
            var roadTime = DateTime.Now.Ticks - roadStartTime;

            var mapOverlayStartTime = DateTime.Now.Ticks;

            if (renderFlags.IsActive(RenderFlags.MapOverlays))
            {
                var overlays = _mapper.MapOverlays.Where(item =>
                                                         item.X >= startPoint.X - itemDrawMargin && item.X <= endPoint.X + itemDrawMargin && item.Z >= startPoint.Y - itemDrawMargin &&
                                                         item.Z <= endPoint.Y + itemDrawMargin && !item.Hidden)
                               .ToList();

                foreach (var overlayItem in overlays) // TODO: Scaling
                {
                    Bitmap b = overlayItem.Overlay.GetBitmap();
                    if (b != null)
                    {
                        g.DrawImage(b, overlayItem.X - b.Width, overlayItem.Z - b.Height, b.Width * 2, b.Height * 2);
                    }
                }
            }
            var mapOverlayTime = DateTime.Now.Ticks - mapOverlayStartTime;

            var mapOverlay2StartTime = DateTime.Now.Ticks;

            if (renderFlags.IsActive(RenderFlags.MapOverlays))
            {
                var companies = _mapper.Companies.Where(item =>
                                                        item.X >= startPoint.X - itemDrawMargin && item.X <= endPoint.X + itemDrawMargin && item.Z >= startPoint.Y - itemDrawMargin &&
                                                        item.Z <= endPoint.Y + itemDrawMargin && !item.Hidden)
                                .ToList();

                foreach (var companyItem in companies) // TODO: Scaling
                {
                    var point = new PointF(companyItem.X, companyItem.Z);
                    if (companyItem.Nodes.Count > 0)
                    {
                        var prefab = _mapper.Prefabs.FirstOrDefault(x => x.Uid == companyItem.Nodes[0]);
                        if (prefab != null)
                        {
                            var originNode = _mapper.GetNodeByUid(prefab.Nodes[0]);
                            if (prefab.Prefab.PrefabNodes == null)
                            {
                                continue;
                            }
                            var mapPointOrigin = prefab.Prefab.PrefabNodes[prefab.Origin];

                            var rot = (float)(originNode.Rotation - Math.PI -
                                              Math.Atan2(mapPointOrigin.RotZ, mapPointOrigin.RotX) + Math.PI / 2);

                            var prefabstartX = originNode.X - mapPointOrigin.X;
                            var prefabStartZ = originNode.Z - mapPointOrigin.Z;
                            var companyPos   = prefab.Prefab.SpawnPoints.FirstOrDefault(x => x.Type == TsSpawnPointType.CompanyPos);
                            if (companyPos != null)
                            {
                                point = RenderHelper.RotatePoint(prefabstartX + companyPos.X,
                                                                 prefabStartZ + companyPos.Z, rot,
                                                                 originNode.X, originNode.Z);
                            }
                        }
                    }
                    Bitmap b = companyItem.Overlay?.GetBitmap();
                    if (b != null)
                    {
                        g.DrawImage(b, point.X, point.Y, b.Width, b.Height);
                    }
                }

                foreach (var prefab in prefabs) // Draw all prefab overlays
                {
                    var originNode = _mapper.GetNodeByUid(prefab.Nodes[0]);
                    if (prefab.Prefab.PrefabNodes == null)
                    {
                        continue;
                    }
                    var mapPointOrigin = prefab.Prefab.PrefabNodes[prefab.Origin];

                    var rot = (float)(originNode.Rotation - Math.PI -
                                      Math.Atan2(mapPointOrigin.RotZ, mapPointOrigin.RotX) + Math.PI / 2);

                    var prefabstartX = originNode.X - mapPointOrigin.X;
                    var prefabStartZ = originNode.Z - mapPointOrigin.Z;
                    foreach (var spawnPoint in prefab.Prefab.SpawnPoints)
                    {
                        var newPoint = RenderHelper.RotatePoint(prefabstartX + spawnPoint.X, prefabStartZ + spawnPoint.Z, rot,
                                                                originNode.X, originNode.Z);

                        Bitmap b = null;

                        switch (spawnPoint.Type)
                        {
                        case TsSpawnPointType.GasPos:
                        {
                            var overlay = _mapper.LookupOverlay(ScsHash.StringToToken("gas_ico"));
                            b = overlay?.GetBitmap();
                            break;
                        }

                        case TsSpawnPointType.ServicePos:
                        {
                            var overlay = _mapper.LookupOverlay(ScsHash.StringToToken("service_ico"));
                            b = overlay?.GetBitmap();
                            break;
                        }

                        case TsSpawnPointType.WeightStationPos:
                        {
                            var overlay = _mapper.LookupOverlay(ScsHash.StringToToken("weigh_station_ico"));
                            b = overlay?.GetBitmap();
                            break;
                        }

                        case TsSpawnPointType.TruckDealerPos:
                        {
                            var overlay = _mapper.LookupOverlay(ScsHash.StringToToken("dealer_ico"));
                            b = overlay?.GetBitmap();
                            break;
                        }

                        case TsSpawnPointType.BuyPos:
                        {
                            var overlay = _mapper.LookupOverlay(ScsHash.StringToToken("garage_large_ico"));
                            b = overlay?.GetBitmap();
                            break;
                        }

                        case TsSpawnPointType.RecruitmentPos:
                        {
                            var overlay = _mapper.LookupOverlay(ScsHash.StringToToken("recruitment_ico"));
                            b = overlay?.GetBitmap();
                            break;
                        }
                        }
                        if (b != null)
                        {
                            g.DrawImage(b, newPoint.X - b.Width / 2f, newPoint.Y - b.Height / 2f, b.Width, b.Height);
                        }
                    }

                    var lastId = -1;
                    foreach (var triggerPoint in prefab.Prefab.TriggerPoints) // trigger points in prefabs: garage, hotel, ...
                    {
                        var newPoint = RenderHelper.RotatePoint(prefabstartX + triggerPoint.X, prefabStartZ + triggerPoint.Z, rot,
                                                                originNode.X, originNode.Z);

                        if (triggerPoint.TriggerId == lastId)
                        {
                            continue;
                        }
                        lastId = (int)triggerPoint.TriggerId;

                        if (triggerPoint.TriggerActionToken == ScsHash.StringToToken("hud_parking")) // parking trigger
                        {
                            var    overlay = _mapper.LookupOverlay(ScsHash.StringToToken("parking_ico"));
                            Bitmap b       = overlay?.GetBitmap();

                            if (b != null)
                            {
                                g.DrawImage(b, newPoint.X - b.Width / 2f, newPoint.Y - b.Height / 2f, b.Width, b.Height);
                            }
                        }
                    }
                }

                var triggers = _mapper.Triggers.Where(item =>
                                                      item.X >= startPoint.X - itemDrawMargin && item.X <= endPoint.X + itemDrawMargin && item.Z >= startPoint.Y - itemDrawMargin &&
                                                      item.Z <= endPoint.Y + itemDrawMargin && !item.Hidden)
                               .ToList();

                foreach (var triggerItem in triggers) // TODO: Scaling
                {
                    Bitmap b = triggerItem.Overlay?.GetBitmap();
                    if (b != null)
                    {
                        g.DrawImage(b, triggerItem.X, triggerItem.Z, b.Width, b.Height);
                    }
                }

                var ferryItems = _mapper.FerryConnections.Where(item =>
                                                                item.X >= startPoint.X - itemDrawMargin && item.X <= endPoint.X + itemDrawMargin && item.Z >= startPoint.Y - itemDrawMargin &&
                                                                item.Z <= endPoint.Y + itemDrawMargin)
                                 .ToList();

                foreach (var ferryItem in ferryItems) // TODO: Scaling
                {
                    Bitmap b = ferryItem.Overlay?.GetBitmap();
                    if (b != null)
                    {
                        g.DrawImage(b, ferryItem.X, ferryItem.Z, b.Width, b.Height);
                    }
                }
            }
            var mapOverlay2Time = DateTime.Now.Ticks - mapOverlay2StartTime;

            var cityStartTime = DateTime.Now.Ticks;

            if (renderFlags.IsActive(RenderFlags.CityNames)) // TODO: Fix position and scaling
            {
                var cities = _mapper.Cities.Where(item => !item.Hidden).ToList();

                var cityFont = new Font("Arial", 100 + zoomCaps[zoomIndex] / 100, FontStyle.Bold);

                foreach (var city in cities)
                {
                    var name = city.City.GetLocalizedName(_mapper.SelectedLocalization);

                    var node   = _mapper.GetNodeByUid(city.NodeUid);
                    var coords = (node == null) ? new PointF(city.X, city.Z) : new PointF(node.X, node.Z);
                    if (city.City.XOffsets.Count > zoomIndex && city.City.YOffsets.Count > zoomIndex)
                    {
                        coords.X += city.City.XOffsets[zoomIndex] / (scale * zoomCaps[zoomIndex]);
                        coords.Y += city.City.YOffsets[zoomIndex] / (scale * zoomCaps[zoomIndex]);
                    }

                    var textSize = g.MeasureString(name, cityFont);
                    g.DrawString(name, cityFont, _cityShadowColor, coords.X + 2, coords.Y + 2);
                    g.DrawString(name, cityFont, palette.CityName, coords.X, coords.Y);
                }
                cityFont.Dispose();
            }
            var cityTime = DateTime.Now.Ticks - cityStartTime;

            g.ResetTransform();
            var elapsedTime = DateTime.Now.Ticks - startTime;

            if (renderFlags.IsActive(RenderFlags.TextOverlay))
            {
                g.DrawString(
                    $"DrawTime: {elapsedTime / TimeSpan.TicksPerMillisecond} ms, x: {startPoint.X}, y: {startPoint.Y}, scale: {scale}",
                    _defaultFont, Brushes.WhiteSmoke, 5, 5);

                //g.FillRectangle(new SolidBrush(Color.FromArgb(100, 0, 0, 0)), 5, 20, 150, 150);
                //g.DrawString($"Road: {roadTime / TimeSpan.TicksPerMillisecond}ms", _defaultFont, Brushes.White, 10, 40);
                //g.DrawString($"Prefab: {prefabTime / TimeSpan.TicksPerMillisecond}ms", _defaultFont, Brushes.White, 10, 55);
                //g.DrawString($"Ferry: {ferryTime / TimeSpan.TicksPerMillisecond}ms", _defaultFont, Brushes.White, 10, 70);
                //g.DrawString($"MapOverlay: {mapOverlayTime / TimeSpan.TicksPerMillisecond}ms", _defaultFont, Brushes.White, 10, 85);
                //g.DrawString($"MapOverlay2: {mapOverlay2Time / TimeSpan.TicksPerMillisecond}ms", _defaultFont, Brushes.White, 10, 100);
                //g.DrawString($"MapArea: {mapAreaTime / TimeSpan.TicksPerMillisecond}ms", _defaultFont, Brushes.White, 10, 115);
                //g.DrawString($"City: {cityTime / TimeSpan.TicksPerMillisecond}ms", _defaultFont, Brushes.White, 10, 130);
            }
        }
예제 #23
0
        private void ParseFerryConnections()
        {
            var connectionDirectory = Rfs.GetDirectory("def/ferry/connection");

            if (connectionDirectory == null)
            {
                Log.Msg("Could not read 'def/ferry/connection' dir");
                return;
            }

            var ferryConnectionFiles = connectionDirectory.GetFiles("sii");

            if (ferryConnectionFiles == null)
            {
                Log.Msg("Could not read ferry connection files files");
                return;
            }

            foreach (var ferryConnectionFile in ferryConnectionFiles)
            {
                var data  = ferryConnectionFile.Entry.Read();
                var lines = Encoding.UTF8.GetString(data).Split('\n');

                TsFerryConnection conn = null;

                foreach (var line in lines)
                {
                    if (line.Contains(":"))
                    {
                        var value = line.Split(':')[1].Trim();
                        var key   = line.Split(':')[0].Trim();
                        if (conn != null)
                        {
                            if (key.Contains("connection_positions"))
                            {
                                var vector = value.Split('(')[1].Split(')')[0];
                                var values = vector.Split(',');
                                var x      = float.Parse(values[0].Replace('.', ','));
                                var z      = float.Parse(values[2].Replace('.', ','));
                                conn.AddConnectionPosition(x, z);
                            }
                        }

                        if (line.Contains("ferry_connection"))
                        {
                            var portIds = value.Split('.');
                            conn = new TsFerryConnection
                            {
                                StartPortToken = ScsHash.StringToToken(portIds[1]),
                                EndPortToken   = ScsHash.StringToToken(portIds[2].TrimEnd('{').Trim())
                            };
                        }
                    }

                    if (!line.Contains("}") || conn == null)
                    {
                        continue;
                    }
                    ;

                    var existingItem = _ferryConnectionLookup.FirstOrDefault(item =>
                                                                             (item.StartPortToken == conn.StartPortToken && item.EndPortToken == conn.EndPortToken) ||
                                                                             (item.StartPortToken == conn.EndPortToken && item.EndPortToken == conn.StartPortToken)); // Check if connection already exists
                    if (existingItem == null)
                    {
                        _ferryConnectionLookup.Add(conn);
                    }
                    conn = null;
                }
            }
        }
예제 #24
0
파일: TsMapper.cs 프로젝트: shardick/ts-map
        private void ParseRoadLookFiles()
        {
            var worldDirectory = Rfs.GetDirectory("def/world");

            if (worldDirectory == null)
            {
                Log.Msg("Could not read 'def/world' dir");
                return;
            }

            var roadLookFiles = worldDirectory.GetFiles("road_look");

            if (roadLookFiles == null)
            {
                Log.Msg("Could not read road look files");
                return;
            }

            foreach (var roadLookFile in roadLookFiles)
            {
                if (!roadLookFile.GetFileName().StartsWith("road"))
                {
                    continue;
                }
                var        data     = roadLookFile.Entry.Read();
                var        lines    = Encoding.UTF8.GetString(data).Split('\n');
                TsRoadLook roadLook = null;

                foreach (var line in lines)
                {
                    var(validLine, key, value) = SiiHelper.ParseLine(line);
                    if (validLine)
                    {
                        if (key == "road_look")
                        {
                            roadLook = new TsRoadLook(ScsHash.StringToToken(SiiHelper.Trim(value.Split('.')[1].Trim('{'))));
                        }
                        if (roadLook == null)
                        {
                            continue;
                        }
                        if (key == "lanes_left[]")
                        {
                            roadLook.LanesLeft.Add(value);
                        }
                        else if (key == "lanes_right[]")
                        {
                            roadLook.LanesRight.Add(value);
                        }
                        else if (key == "road_offset")
                        {
                            roadLook.Offset = float.Parse(value, CultureInfo.InvariantCulture);
                        }
                    }

                    if (line.Contains("}") && roadLook != null)
                    {
                        if (roadLook.Token != 0 && !_roadLookup.ContainsKey(roadLook.Token))
                        {
                            _roadLookup.Add(roadLook.Token, roadLook);
                            roadLook = null;
                        }
                    }
                }
            }
        }