= Принцип работы с наборами тайлов (тайлсетами) = Версия №1. '''В процессе проектирования'''. Более старые версии: * нет. Данный текст описывает только проект, т.е. относительно подробное, и в то же время достаточно абстрактное описание того как все должно быть устроено и как должно работать. ---- == Организация файлов тайлсетов == Все тайлсеты расположены в одном каталоге. Внутри этого каталога - по подкаталогу на каждый тайлсет, имя каталога является именем тайлсета. Внутри каждого каталога тайлсета содержатся следующие файлы: * !TileSet.ini - настройки тайлсета. Формат файла описан [wiki:TileSetIniFormat_v1 тут]. * Если набор не сжатый - то куча *.bmp-файлов с тайлами с именем вида {!TileName}.bmp, где {!TileName}=T_{Layer}_{Terrains}_{!VarNumber}, где {Layer} - двузначное число, номер слоя (любое число от единицы до 99, ибо номер слоя 00 зарезервирован); {!VarNumber} - двузначное число, номер варианта данного тайла; {Terrains}={Terr1}_{Terr2}_{Terr3}_{Terr4}, где {Terr1}-{Terr4} соотв названия террейнов в углах тайла - по строчкам, справа-налево, сверху-вниз, вот так: ||Terr1||Terr2|| ||Terr3||Terr4|| * Если набор сжатый - куча *.7z-файлов с bmp-хами с тайлами с именем вида {!TileName}.bmp, где {!TileName} - то же самое, что описано выше. * Пустые файлы с именами вида Deny_{Terr1}_{Terr2} где Terr1 и Terr2 - имена террейнов, между которыми запрещены переходы. Именем террейна может быть зарезервированное имя All что означает все террейны того же слоя что и второй террейн. По-умолчанию все переходы разрешены. Наличие такого файла запрещает переход. Порядок следования террейнов в имени значения не имеет. Смысловые дубли не должны мешать правильной работе реализации, но в то же время реализация должна стараться не допускать появления таких дублей, а также отслеживать их и при обнаружении вычищать. Имена Deny имеют более высокий приоритет над разрешением всех переходов по умолчанию и более низкий к именам Allow, поэтому можно сначала запретить все переходы для какого-либо террейна, а затем уже разрешать определенные переходы через Allow. Писать Deny_All_All нельзя, такой файл если он появится - должен быть удален, ибо настройки переходов в реализации должны идти для каждого террейна отдельно. * Пустые файлы с именами вида Allow_{Terr1}_{Terr2} где Terr1 и Terr2 - имена террейнов, между которыми разрешены переходы. Использование имени All в данном случае бессмысленно, ибо по умолчанию все переходы уже разрешены. Отношение к дублям - такое же как для Deny. ---- == Принцип реализации == Работа с тайлами реализуется несколькими взаимозависимыми классами, связанными через их элементы-ссылки. Основным классом, связывающим все остальные является '''TTileSet''', который реализует работу с содержимым одного набора тайлов. Есть более высокий уровень - '''TTileSetsCollection''' - фактически контейнер, содержащий ссылки на объекты типа TTileSet для всех доступных программе тайлсетов. '''TTileSetsCollection содержит''': * Строка - имя набора тайлсетов (возм. по имени каталога - это чисто для юзверя). * Массив ссылок на все объекты тайлсетов. * Путь по которому располагаются тайлсеты. * Путь к временному каталогу для временных файлов при работе с тайлсетами. * Ссылка на !CallBack-метод, для синхронизации при многопоточной работе, с учетом того что !CallBack будет иметь доступ к инфе объекта и всей вложенной инфе. * Булево значение - если true то !CallBack используется. Также содержит методы: * Удалений, добавлений тайлсетов. * Метод, осуществляющие собственно чтение набора тайлсетов. * Метод, возвращающий true если в каком-либо из тайлсетов есть "неправильные" (с нестандартной структурой) тайлы и false если таких нет. '''TTileSet содержит''': * Обратную ссылку на объект TTileSetsCollection. * Ссылку на объект, реализующий работу с файлом !TileSet.ini для этого тайлсета (TTileSetIni) - описание формата смотри [wiki:TileSetIniFormat_v1 тут]. * Строка - имя тайлсета. * Строка - полный путь к тайлсету. * Строка - полный путь к временной директории тайлсета (используется если он сжатый). * Булево значение - используется ли svn. * Число - номер ревизии в случае если используется svn. * Массив ссылок на все тайлы, принадлежащие тайлсету - точнее на объекты класса TTile, реализующего работу с тайлом. * Массив ссылок на все тайлы, файлы которых реально существуют. * Массив ссылок на все тайлы, конфигурация которых неправильна (т.е. является одним из вариантов поворота для одной из стандартных конфигураций, а значит такой тайл требуется перевернуть в стандартную конфигурацию при нулевом повороте и переименовать). * Массив ссылок на все террейны, принадлежащие тайлсету - т.е. на объекты класса TTerrain, реализующего работу с террейном. * Массив ссылок на объекты класса TLayer, который по-сути является контейнером ссылок на объекты TTerrain и содержит ссылки на все террейны своего слоя. * Ссылка на объект класса TMasksSet - описание класса смотри на [wiki:WorkingWithMasks странице] с описанием работы масок. * Список строк Allow и Deny для информации из имен файлов разрешений-запретов переходов. * Дополнительная информация о тайлсете, например номер ревизии в случае если используется svn. * Объект класса TFlistFormat для списка тайлов в сжатом тайлсете. * Объект класса TFlistFormat для списка тайлов в временном каталоге с тайлами тайлсета. Последние 2 элемента - используются только если работаем со сжатым тайлсетом. * Список строк - имена всех террейнов тайлсета, вне выполнения методов должен быть в алфавитном порядке и соответствовать приоритету террейнов. * Булево значение - true если есть "неправильные" тайлы и false если таковых нет. * Булево значение - true если есть тайлы без реального файла с картинкой и false если таковых нет. Также тут есть методы для: * Удалений, добавлений тайлов (по имени, влияющая на все три массива ссылок на тайлы). * Удалений, добавлений террейнов (по имени). * Удалений, добавлений слоев (по номеру слоя, с удалением принадлежащий слою террейнов). * Удалений, добавлений Allow и Deny, влияющее и на соответствующие объекты террейнов. * Метод для синхронизации временного распакованного варианта тайлсета со сжатым реальным тайлсетом. * Метод для обратной синхронизации содержимого временного каталога со сжатым реальным тайлсетом. * Метод, собственно выполняющий чтение информации о тайлсете. * Метод, ищущий неправильные тайлы, добавляющий их в соотв. списки, помечающий и выставляющий соотв. булево значение о их наличии в данном объекте. * Метод, генерящий на основе информации о слое и переходах террейна конфигурации всех возможных для террейна тайлов, проверяющий наличие каждого такого тайла, если его нету - добавляющий "несуществующий" (т.е. для него отсутствует файл с картинкой) тайл в систему и все нужные списки, помечающий правильным образом этот тайл и ставящий в нужную метку соотв. булево значение этого объекта о наличии "несуществующих" тайлов. * Метод, исправляющий тайлы с неправильной структурой (поворачивает и дает правильное имя). * Метод, осуществляющий генерацию конкретного переходного тайла (в аргументе линка на объект тайла). * Метод, осуществляющий генерацию всех переходных тайлов, для которых еще нету картинок. Удаление тайлов, разрешений\запретов переходов, если оно связано с реальным файлом набора - автоматически приводит к удалению соответствующих файлов из набора. Юзверь этой системы классов должен сам обеспечить предупреждение пользователя проги о возможном безвозвратном удалении файлов до вызова таких методов. Кроме этого - следует организовать выделение и освобождение памяти так, чтобы памятью всех слоев, террейнов и тайлов заведовал именно класс TTileSet. Другие классы также могут иметь подобные ссылки, и даже выделять память под новые объекты - но в любом случае такой объект должен попасть в массив объекта TTileSet и в дальнейшем уничтожаться должен под управлением именно TTileSet, если же вложенные в него объекты сами новых объектов не создают, то использоваться может только копирование ссылки на уже созданный объект. Таким образом, в других классах может использоваться new, но delete всегда выполняется только классом TTileSet, причем гарантированно для всего, что могло быть создано по любому такому new. '''Класс TLayer''' весьма прост и '''содержит''' только номер слоя (в виде числового значения), массив ссылок на террейны и обратную ссылку на тайлсет, которому принадлежит слой. '''Класс TTileSetIni''' по сути также более чем прост, типичен для любого класса, работающего с конфигами в ini, '''содержит''' в себе структуры и переменные с данными секций !TileSet.ini, а также методы Load, Save, Clean для загрузки, чтения информации в\из файла и очистки (простановке значений по умолчанию) соответственно. Метод Load должен проверять соответствие номера версии формата в файле текущей реализации - если в файле версия новее - давать ошибку, если старее - конвертировать файл в новую версию (без записи на диск), если тот же - просто читать. На уровне террейна работа обеспечивается объектами класса '''TTerrain, который содержит''': * Имя террейна. * Число - приоритет террейна. Если приоритет неизвестен то значение = 0. * Ссылку на объект класса TLayer, которому принадлежит террейн. Если имеются проблемы с идентификацией единственного номера слоя - присваивается ссылка на слой с индексом 00 (ноль). * Массив линков со всеми найденными для этого террейна значениями слоя. * Массивы ссылок Allow и Deny на объекты террейнов, на которые, соответственно, разрешен или запрещен переход. * Массив ссылок на все тайлы террейна. * Обратная ссылка на тайлсет, которому принадлежит террейн. Также тут есть методы для: * Удаления\добавления тайлов. Работают по имени или индексу. Убеждаются что такой тайл имеется в террейне, затем удаляется на уровне тайлсета. * Удаления\добавления элементов Allow и Deny - с синхронизацией с вовлеченными террейнами и списками реальных имен файлов в объекте тайлсета. * Смена слоя. Работает независимо от того, есть ли проблемы со слоями, меняет слой, переименовывает файлы тех тайлов, от которых есть какой-то смысл, остальные тайлы (т.е. те переходы, которые больше не нужны) либо удаляются, либо переименовываются как будто бы в них был смысл (в зависимости от аргументов метода) - второй вариант может использоваться при смене слоя сразу для нескольких террейнов без потери перехода между ними. На уровне тайла вся работа обеспечивается классом '''TTile, который содержит''': * Ссылку на свой тайлсет. * Ссылку на свой слой. * Путь к bmp-файлу, с именем файла (строка). * Путь к файлу в тайлсете, с именем файла (может не совпадать с bmp если оно например 7z). * Ссылка на объект типа TTileTerrs - содержит информацию о структуре тайла (территории) в удобном для быстрой программной обработки виде. * Номер варианта тайла с такой структурой. * Булевое значение Exists - true если тайл реально существует в тайлсете а не только должен наличествовать в теории исходя из структуры террейнов на слоях и возможных переходов. * Булевое значение !IsValid - true если структура тайла соответствует одной из стандартных, если соответствия нет - тайл является одним из вариантов поворота для одной из стандартных конфигураций, а значит такой тайл требуется перевернуть в стандартную конфигурацию при нулевом повороте и переименовать. Также тут есть методы для: * Загрузки информации о тайле по его имени (смотрит свойства тайлсета и ищет нужные файлы). * Метод, возвращающий строку с именем тайла. * Метод, возвращающий повернутую нужное количество раз bmp-ху тайла. * Метод, возвращающий bmp-ху тайла без поворотов (вызывает предыдущий метод с нулевым поворотом). '''Класс TTileTerrs''' был бы простой структурой если бы не имел методы сравнения двух объектов этого типа, поэтому он класс. '''TTileTerrs Содержит''': * Аналогично ссылки на террейны (вместо имен) в соотв. углах (справа налево, по строкам сверху вниз, A соответствует {Terr1} а D соответствует {Terr4} в имени тайла). Вот табличка расположения углов: ||A||B|| ||C||D|| * Ссылка на родной тайл. Также содержит методы: * Для чтения террейнов в углах (т.е. ABCD) из имени-строки тайла (и записи в инфу объекта). * Для генерации имени тайла по ABCD без той части где указывается вариант тайла. * Метод, возвращающий integer - если аргумент не совпадает по структуре с тайлом (аргумент также типа TTileTerrs) - вернет -1, иначе - вернет количество поворотов которое нужно применить к аргументу чтобы структура тайла совпадала со структурой аргумента. Таким образом если возвращает 0 значит структура тайла и аргумента метода идентичны. * Перегрузка оператора == - при сравнении двух объектов этого типа этим оператором - соотв. возвращает true если объекты идентичны и false если чем-либо различаются. ---- == Как определяется правильна ли структура тайла == Для начала определимся с приоритетом террейнов. Приоритет определяется по имени террейна, в алфавитном порядке (не только для первой, для всех букв имени террейна). Чем выше в списке по алфавиту идет имя террейна тем выше его приоритет. В дальнейшем буду считать что террейн с наивысшим приоритетом имеет номер 1, террейн с приоритетом ниже номер 2, ниже номер 3 и так далее. На основе этого допущения определим стандартные структуры террейнов: * Из двух террейнов-1: ||2||2|| ||1||2|| * Из двух террейнов-2: ||1||1|| ||2||1|| * Из двух террейнов-3: ||1||1|| ||2||2|| * Из трех террейнов-1: ||1||1|| ||2||3|| * Из трех террейнов-2: ||1||1|| ||3||2|| * Из трех террейнов-3: ||1||2|| ||3||1|| * Из трех террейнов-4: ||2||2|| ||1||3|| * Из трех террейнов-5: ||2||2|| ||3||1|| * Из трех террейнов-6: ||3||3|| ||1||2|| * Из трех террейнов-7: ||3||3|| ||2||1|| * Из четырех террейнов-1: ||1||2|| ||3||4|| * Из четырех террейнов-2: ||1||2|| ||4||3|| * Из четырех террейнов-3: ||1||3|| ||2||4|| * Из четырех террейнов-4: ||1||3|| ||4||2|| * Из четырех террейнов-5: ||1||4|| ||2||3|| * Из четырех террейнов-6: ||1||4|| ||3||2|| ---- == Как это все по идее должно работать == В начале программа создает верхний уровень - объект класса '''TTileSetsCollection'''. Все остальные уровни создаются конструкторами и прочими методами этого объекта и его вложенными объектами. Освобождение памяти во избежание утечек - там же, деструкторами классов и их методами. Программа должна позаботиться только о правильном удалении объекта '''TTileSetsCollection''' когда он станет не нужен. После создания объекта класса '''TTileSetsCollection''' - программа пробивает в его опции все необходимые пути, если нужно - !CallBack-метод, а затем вызывает нужный метод чтения информации тайлсетов. Данный метод - читает имена тайлсетов, их количество - и создает нужное количество подобъектов класса '''TTileSet'''. Обращаю внимание, что все вложенные в '''TTileSetsCollection''' объекты имеют обратные ссылки на "родительский" объект, т.е. тот объект в который они вложены - что позволяет этим объектам использовать информацию своего владельца и даже своих соседей и вложенных в соседей объектов. Для каждого объекта '''TTileSet''' вызывается метод чтения информации тайлсета. Причем перед запуском чтения тайлсета при необходимости вызывается !CallBack что позволяет индицировать процесс загрузки, например, в окошко с прогрессбаром. Метод чтения информации тайлсетов - для начала читает свой конфиг, если используется svn - читает информацию о ревизии. Если тайлсет сжат - первым делом выполняет синхронизацию с временной, несжатой версией тайлсета. Затем - производит поиск всех файлов с тайлами, подсчитывает их количество, после чего (либо одновременно - смотря как будет реализовано и как оптимальнее работает) выделяет нужное количество объектов в массив с тайлами и загружает в эти объекты информацию по всем найденным тайлам. Обновременно со вбивкой информации в основной массив - создаются объекты террейнов, слоев, в них вбивается соответствующая информация, в т.ч. присваиваются ссылки на объекты тайлов. По завершению всего этого процесса - все объекты террейнов и слоев уже созданы, в них есть массивы со ссылками на тайлы. После этого становится возможным составление списка террейнов по алфавиту и установка приоритета для них. После того как приоритеты расставлены - становится возможным понять - какой тайл имеет правильную конфигурацию, а какой, увы, нет. Сканируется список тайлов, обнаруженные неправильные тайлы добавляются в соответствующие списки и помечаются. Автоматического исправления таких тайлов не происходит, это делает отдельный метод, вызываемый программой, например по команде пользователя. Затем осуществляется генерация всех возможных тайлов-переходов - если такой переходный тайл уже наличествует, хотя-бы в количестве 1 (адЫн) штука - ничего не происходит, если же ни одного такого тайла найдено не было - создается объект тайла, с пометкой что файла картинки у него нет, ссылка на него добавляется во все соответствующие массивы соответствующих объектов. Собсно это все что касается механизма чтения информации о тайлсете. После его завершения - в объекте класса '''TTileSetsCollection''' содержится вся необходимая информация о всех имеющихся тайлсетов, более чем достаточная для вывода любой информации в интерфейс программы, в т.ч. дерева террейнов и тайлов. Кроме этого у всех объектов системы имеются дополнительные методы, которые не вызываются при чтении тайлсетов, и предназначены для непосредственного их вызова программой - например методы удаления\добавления тайлов, генерации переходных тайлов, исправления "неправильных" тайлов и т.д. Подробно расписывать эти методы смысла нет, ибо механизм их действия понятен из простого их описания, а описания же для них наличествуют выше в данном тексте, в списках методов классов. Спасибо за то что прочитали весь этот бред :).