Добавлен Hares,
опубликован
Работа с РО в промышленных масштабах - Введение
Содержание:
Итак, мы научились описывать различные структуры. Теперь перейдём к написанию инициализатора.
План работы на сегодня
Итак, план:
- (*) Научиться извлекать файлы из Mpq-архивов.
- Написать инициализатор типа "Показатель", который:
- принимает 2 аргумента (тип атрибута и является ли он основным).
- читал бы из архивов Варкрафта соответствующие поля и записывал их значения.
- Написать инициализатор типа "Получение опыта", который:
- принимает аргумент - id героя Варкрафта.
- возвращает алгоритм, соответствующий именно этому герою.
- при этом читает архив вц3, и достаёт оттуда всю нужную инфу.
- и после этого обрабатывает её (считает).
- Автоматически прогнать все алгоритмы.
- Конвертировать сии структуры в xml-код старкрафта.
- Забить всё это дело в архив карты.
Что ж, приступим...
Часть 2, Археологическая и рабоче-крестьянская
Почему археологическая? Мы ведь залезем в архивы варкрафта. =)
Шаг 3.
Вообще на этом и следующим за ним шагах мы должны найти нужные нам источники и использовать их. То есть, если бы делали конвертер эффектов урона из строковой формулы типа "15 + 3D12" (15 базово + 3 случайных значения от 1 до 12) или "2*8 + 5" (2 атаки по 8 ед., и после этого ещё одна на 5), то на выходе мы должны были получить массив объектов типа Эффект разных подтипов (урон, периодический и т.д.).
А в нашем случае - это пункты №1 и №2 из нашего плана.
Для начала мы создаём инициализатор-полупустышку. Понимаю, звучит странно (страшно?), но попытаюсь объяснить, что это за зверь. Мы создаём инициализатор-пустышку, и начинаем присваивать значения так же, как и в предположительно конечном варианте. А поля присвоения оставляем пустыми, кое-где оставляя комменты. Некоторые поля можно сразу же заполнить константными или легко вычисляемыми значениями.
Образец:
Для большей наглядности взят пример из способа исполнения с помощью наследования.
/// <summary>
/// Создаст 1 из 3 алгоритмов основных параметров героя.
/// </summary>
/// <param name="Par">
/// Индекс атрибута. Введите:
/// 0 - Сила;
/// 1 - Ловкость;
/// 2 - Разум.
/// </param>
/// <param name="Main">Является ли параметр основным.</param>
public Attribute(int Id, bool Main)
{
Attribute Result = new Attribute();
Result.Type = "CBehaviorAttribute";
Result.MaxPoints = 999999999;
Result.Permanent = true;
Result.AbilityorEffectType = "Unit";
Result.IsMain = Main;
Result.Modification = new Modifications();
switch (Id)
{
#region Strength
case 0:
Result.Id = "Strength";
Result.NameValue;
Result.Modification.VitalBonus;
Result.Modification.VitalRegenBonus;
break;
#endregion
#region Agility
case 1:
Result.Id = "Agility";
Result.NameValue;
Result.Modification.MovementSpeedBonus;
Result.Modification.AttackSpeedBonus;
Result.Modification.VitalArmorBonus;
break;
#endregion
#region Intellect
case 2:
Result.Id = "Intellect";
Result.NameValue;
Result.Modification.EnergyBonus;
Result.Modification.EnergyRegenBonus;
break;
#endregion
}
Result.Name = "Behavior/HeroAttribute/" + Result.Id;
if (Main)
{
Result.Modification.AttackBonus;
Result.Id += "Main";
EditorSuffix = "Behavior/HeroAttribute/Main/Suffix";
}
EditorPrefix = "Behavior/HeroAttribute/Prefix";
this = Result;
}
Затем мы переходим к заполнению этих полей.
Шаг 4.
Здесь мы закончим заполнение полей инициализатора.
Мы расставляем все формулы, юзаем функции итд итп.
Как я делал атрибуты:
- Поиск полей.
- Запустил World Editor.
- Зашёл в игровые константы.
- Нашёл раздел "Характеристики героев".
- Нажал Ctrl+D, тем самым показав названия переменных.
- Записал их.
- Поиск того, где они лежат.
- Запустил браузер Mpq-архивов (я использовал W3ME).
- Открыл "war3patch.mpq".
- Открыл "(listfile)".
- Вбил в поисковик по файлу "misc" - фрагмент названия файла констант в карте.
- Нашёл какой-то файл. Это оно. ("Units\MiscGame.txt")
- Написал функцию, которая:
- Открыла mpq.
- Открыла этот файл.
- Построчно его прочитала.
- Нашла нужное значение.
- Вернула его.
- Присвоил полям эту функцию с выписанными ранее аргументами.
- Повторил то же самое с геройскими левелами. Только там лазал по РО и slk-шкам.
Итого, мы получили 2 полноценных инициализатора.
По идее. Но вот если вы использовали только один тип - алгоритм - и поле XmlCode, то у вас непременно должен был возникнуть вопрос: куда вписывать значения?
По идее. Но вот если вы использовали только один тип - алгоритм - и поле XmlCode, то у вас непременно должен был возникнуть вопрос: куда вписывать значения?
Их надо писать по такому принципу:
Result.XmlCode[0] = " <Modification>";
Result.XmlCode[1] = " <VitalMaxArray index=\"Life\" value=\""
+ Mpq.File.ReadFromConstants("StrHitPointBonus") + "\"/>";
Да, получается, вам придётся так вот вбивать всю xml-структуру целиком. Хотя нет, не всю. Только ту, что содержится в параметрах.
Итого, мы получили 2 полноценных инициализатора.
Код - способ 1:
Здесь показан пример без использования форматной строки. Посмотрев и сравнив его со следующим, вы сразу её полюбите. =)
/// <summary>
/// Создаст 1 из 3 алгоритмов основных параметров героя.
/// </summary>
/// <param name="Par">
/// Индекс атрибута. Введите:
/// 0 - Сила;
/// 1 - Ловкость;
/// 2 - Разум.
/// </param>
/// <param name="Main">Является ли параметр основным.</param>
public Behavior(int Id, bool Main)
{
Behavior Result = new Behavior();
Result.Type = "CBehaviorAttribute";
Result.MaxPoints = 999999999;
Result.Permanent = true;
Result.AbilityorEffectType = "Unit";
switch (Id)
{
#region Strength
case 0:
Result.Id = "Strength";
Result.Name = Interface.FindInStringList("STRENGTH");
Result.Icon = "";
#region Xml
Result.XmlCode = new string[4 + Convert.ToInt16(Main) * 3];
Result.XmlCode[0] = " <Modification>";
Result.XmlCode[1] = " <VitalMaxArray index=\"Life\" value=\""
+ Mpq.File.ReadFromConstants("StrHitPointBonus") + "\"/>";
Result.XmlCode[2] = " <VitalRegenArray index=\"Life\" value=\""
+ Mpq.File.ReadFromConstants("StrRegenBonus") + "\"/>";
if (Main)
{
Result.XmlCode[3] = " <DamageDealtScaled index=\"Melee\" value=\""
+ Mpq.File.ReadFromConstants("StrAttackBonus") + "\"/>";
Result.XmlCode[4] = " <DamageDealtScaled index=\"Ranged\" value=\""
+ Mpq.File.ReadFromConstants("StrAttackBonus") + "\"/>";
}
Result.XmlCode[3 + Convert.ToInt16(Main) * 2] = " </Modification>";
if (Main)
{
Result.XmlCode[6] = " <Name value=\"" + "Behavior/Name/" + Result.Id + "\"/>";
Result.Id += "Main";
}
#endregion
break;
#endregion
#region Agility
case 1:
Result.Id = "Agility";
Result.Name = Interface.FindInStringList("Agility");
Result.Icon = "";
#region Xml
Result.XmlCode = new string[5 + Convert.ToInt16(Main) * 3];
Result.XmlCode[0] = " <Modification>";
Result.XmlCode[1] = " <MoveSpeedBonus value=\""
+ Mpq.File.ReadFromConstants("AgiMoveBonus") + "\"/>";
Result.XmlCode[2] = " <AttackSpeedMultiplier value=\""
+ (Convert.ToDouble(Mpq.File.ReadFromConstants("AgiAttackSpeedBonus")) + 1).ToString() + "\"/>";
Result.XmlCode[3] = " <LifeArmorBonus value=\""
+ Mpq.File.ReadFromConstants("AgiDefenseBonus") + "\"/>";
if (Main)
{
Result.XmlCode[4] = " <DamageDealtScaled index=\"Melee\" value=\""
+ Mpq.File.ReadFromConstants("StrAttackBonus") + "\"/>";
Result.XmlCode[5] = " <DamageDealtScaled index=\"Ranged\" value=\""
+ Mpq.File.ReadFromConstants("StrAttackBonus") + "\"/>";
}
Result.XmlCode[4 + Convert.ToInt16(Main) * 2] = " </Modification>";
if (Main)
{
Result.XmlCode[7] = " <Name value=\"" + "Behavior/Name/" + Result.Id + "\"/>";
Result.Id += "Main";
}
#endregion
break;
#endregion
#region Intellect
case 2:
Result.Id = "Intellect";
Result.Name = Interface.FindInStringList("Intellect");
Result.Icon = "";
#region Xml
Result.XmlCode = new string[4 + Convert.ToInt16(Main) * 3];
Result.XmlCode[0] = " <Modification>";
Result.XmlCode[1] = " <VitalMaxArray index=\"Energy\" value=\""
+ Mpq.File.ReadFromConstants("IntManaBonus") + "\"/>";
Result.XmlCode[2] = " <VitalRegenArray index=\"Energy\" value=\""
+ Mpq.File.ReadFromConstants("IntRegenBonus") + "\"/>";
if (Main)
{
Result.XmlCode[3] = " <DamageDealtScaled index=\"Melee\" value=\""
+ Mpq.File.ReadFromConstants("StrAttackBonus") + "\"/>";
Result.XmlCode[4] = " <DamageDealtScaled index=\"Ranged\" value=\""
+ Mpq.File.ReadFromConstants("StrAttackBonus") + "\"/>";
}
Result.XmlCode[3 + Convert.ToInt16(Main) * 2] = " </Modification>";
if (Main)
{
Result.XmlCode[6] = " <Name value=\"" + "Behavior/Name/" + Result.Id + "\"/>";
Result.Id += "Main";
}
#endregion
break;
#endregion
}
if (Main)
EditorSuffix = " - Основной";
EditorPrefix = "Параметр героя - ";
this = Result;
}
И второй инициализатор:
/// <summary>
/// Создаёт алгоритм типа "Получение опыта". Это для прокачки героев.
/// </summary>
/// <param name="HeroWarId">Id героя в варкрафте.</param>
public Behavior(string HeroWarId)
{
Behavior Result = new Behavior();
Warcraft3.RaceConstructor.Unit u = Warcraft3.RaceConstructor.GetUnitById(HeroWarId); // Эта функция возвращает загруженного заранее юнита по его Id.
Result.Type = "CBehaviorVeterancy";
Result.Id = u.StarId; // Это поле возвращает хитроумно вычисляемый Id юнита для старкрафта.
Result.Permanent = true;
Result.MaxPoints = 255; //По умолчанию.
Result.AbilityorEffectType = "Units";
Result.EditorSuffix = " - Уровни";
Result.Race = u.Race;
#region Xml
Result.XmlCode = new string[0];
string[] GrantXPBaseList = Mpq.File.ReadFromConstants("GrantHeroXP").Split(','); // Эта функция возвращает игровую константу.
double[] GrantHeroXP = new double[GrantXPBaseList.Length + 1];
for (int i = 0; i < GrantXPBaseList.Length; i++)
GrantHeroXP[i + 1] = Convert.ToDouble(GrantXPBaseList[i]);
double GrantHeroXPFormulaA = Convert.ToDouble(Mpq.File.ReadFromConstants("GrantHeroXPFormulaA"));
double GrantHeroXPFormulaB = Convert.ToDouble(Mpq.File.ReadFromConstants("GrantHeroXPFormulaB"));
double GrantHeroXPFormulaC = Convert.ToDouble(Mpq.File.ReadFromConstants("GrantHeroXPFormulaC"));
// Это данные формулы. Формула приводится в статье "Осваиваем WorldEdit" (или как-то так).
int MaxHeroLevel = Convert.ToInt32(Mpq.File.ReadFromConstants("MaxHeroLevel"));
double Last = 0;
int Agi = Convert.ToInt32(u.GetValue("Agi")); // Эта функция возвращает какое-то из значений юнита по id или названию этого поля.
int Str = Convert.ToInt32(u.GetValue("Str"));
int Int = Convert.ToInt32(u.GetValue("Int"));
double AgiPlus = Convert.ToDouble(u.GetValue("AgiPlus"));
double StrPlus = Convert.ToDouble(u.GetValue("StrPlus"));
double IntPlus = Convert.ToDouble(u.GetValue("IntPlus"));
string Strength = "Strength";
string Agility = "Agility";
string Intellect = "Intellect";
string Main = u.GetValue("Primary").ToLower();
switch (Main)
{
case "str":
Strength += "Main";
break;
case "agi":
Agility += "Main";
break;
case "int":
Intellect += "Main";
break;
}
for (int i = 0; i < MaxHeroLevel; i++)
{
Array.Resize(ref Result.XmlCode, Result.XmlCode.Length + 1);
if (i == 0)
{
Result.XmlCode[Result.XmlCode.Length - 1] = " <VeterancyLevelArray>";
Result.XmlCode[Result.XmlCode.Length - 1] =
String.Format(" <VeterancyLevelArray>", 0);
Array.Resize(ref Result.XmlCode, Result.XmlCode.Length + 1);
Result.XmlCode[Result.XmlCode.Length - 1] = " <Modification>";
Array.Resize(ref Result.XmlCode, Result.XmlCode.Length + 3);
Result.XmlCode[Result.XmlCode.Length - 3] =
String.Format(" <BehaviorLinkEnableArray value=\"{0}\"/>",
Strength);
Result.XmlCode[Result.XmlCode.Length - 2] =
String.Format(" <BehaviorLinkEnableArray value=\"{0}\"/>",
Agility);
Result.XmlCode[Result.XmlCode.Length - 1] =
String.Format(" <BehaviorLinkEnableArray value=\"{0}\"/>",
Intellect);
Array.Resize(ref Result.XmlCode, Result.XmlCode.Length + 3);
Result.XmlCode[Result.XmlCode.Length - 3] =
String.Format(" <AttributeChangeArray Attribute=\"{0}\" Points=\"{1}\"/>",
Strength, Convert.ToInt32(Str + StrPlus));
Result.XmlCode[Result.XmlCode.Length - 2] =
String.Format(" <AttributeChangeArray Attribute=\"{0}\" Points=\"{1}\"/>",
Agility, Convert.ToInt32(Agi + AgiPlus));
Result.XmlCode[Result.XmlCode.Length - 1] =
String.Format(" <AttributeChangeArray Attribute=\"{0}\" Points=\"{1}\"/>",
Intellect, Convert.ToInt32(Int + IntPlus));
}
else
{
if (i < GrantHeroXP.Length)
Last = GrantHeroXP[i];
else
Last = Last * GrantHeroXPFormulaA + GrantHeroXPFormulaB * (i + 1) + GrantHeroXPFormulaC;
Result.XmlCode[Result.XmlCode.Length - 1] =
String.Format(" <VeterancyLevelArray MinVeterancyXP=\"{0}\">",
Last);
Array.Resize(ref Result.XmlCode, Result.XmlCode.Length + 1);
Result.XmlCode[Result.XmlCode.Length - 1] = " <Modification>";
Array.Resize(ref Result.XmlCode, Result.XmlCode.Length + 3);
Result.XmlCode[Result.XmlCode.Length - 3] =
String.Format(" <AttributeChangeArray Attribute=\"{0}\" Points=\"{1}\"/>",
Strength,
Convert.ToInt32(StrPlus * (i + 1)) - Convert.ToInt32(StrPlus * i));
Result.XmlCode[Result.XmlCode.Length - 2] =
String.Format(" <AttributeChangeArray Attribute=\"{0}\" Points=\"{1}\"/>",
Agility,
Convert.ToInt32(AgiPlus * (i + 1)) - Convert.ToInt32(AgiPlus * i));
Result.XmlCode[Result.XmlCode.Length - 1] =
String.Format(" <AttributeChangeArray Attribute=\"{0}\" Points=\"{1}\"/>",
Intellect,
Convert.ToInt32(IntPlus * (i + 1)) - Convert.ToInt32(IntPlus * i));
}
Array.Resize(ref Result.XmlCode, Result.XmlCode.Length + 2);
Result.XmlCode[Result.XmlCode.Length - 2] = " </Modification>";
Result.XmlCode[Result.XmlCode.Length - 1] = " </VeterancyLevelArray>";
}
#endregion
this = Result;
}
И для наследования:
Описания действий функций смотреть в предыдущем блоке.
/// <summary>
/// Создаст 1 из 3 алгоритмов основных параметров героя.
/// </summary>
/// <param name="Par">
/// Индекс атрибута. Введите:
/// 0 - Сила;
/// 1 - Ловкость;
/// 2 - Разум.
/// </param>
/// <param name="Main">Является ли параметр основным.</param>
public Attribute(int Id, bool Main)
{
this.Type = "CBehaviorAttribute";
this.MaxPoints = 999999999;
this.Permanent = true;
this.AbilityorEffectType = "Unit";
this.IsMain = Main;
this.Modification = new Modifications();
switch (Id)
{
#region Strength
case 0:
this.Id = "Strength";
this.NameValue = Interface.FindInStringList("Strength");
this.Modification.VitalBonus = Convert.ToDouble(Mpq.File.ReadFromConstants("StrHitPointBonus"));
this.Modification.VitalRegenBonus = Convert.ToDouble(Mpq.File.ReadFromConstants("StrRegenBonus"));
break;
#endregion
#region Agility
case 1:
this.Id = "Agility";
this.Name = Interface.FindInStringList("Agility");
this.Modification.MovementSpeedBonus = Convert.ToDouble(Mpq.File.ReadFromConstants("AgiMoveBonus")) / 100;
this.Modification.AttackSpeedBonus = Convert.ToDouble(Mpq.File.ReadFromConstants("AttackSpeedMultiplier")) + 1;
this.Modification.VitalArmorBonus = Convert.ToDouble(Mpq.File.ReadFromConstants("AgiDefenseBonus"));
break;
#endregion
#region Intellect
case 2:
this.Id = "Intellect";
this.Name = Interface.FindInStringList("Intellect");
this.Modification.EnergyBonus = Convert.ToDouble(Mpq.File.ReadFromConstants("IntManaBonus"));
this.Modification.EnergyRegenBonus = Convert.ToDouble(Mpq.File.ReadFromConstants("IntRegenBonus"));
break;
#endregion
}
this.Name = "Behavior/HeroAttribute/" + this.Id;
if (Main)
{
this.Modification.AttackBonus = Convert.ToDouble(Mpq.File.ReadFromConstants("StrAttackBonus"));
this.Id += "Main";
EditorSuffix = "Behavior/HeroAttribute/Main/Suffix";
}
EditorPrefix = "Behavior/HeroAttribute/Prefix";
}
И для Получения Опыта:
/// <summary>
/// Тип алгоритма "Получение опыта". Характеризует левелы героя.
/// </summary>
/// <param name="HeroWarId">Id героя из варкрафта, для которомого мы всё это делаем.</param>
public Veterancy(string HeroWarId)
{
Warcraft3.RaceConstructor.Unit u = Warcraft3.RaceConstructor.GetUnitById(HeroWarId);
this.Type = "CBehaviorVeterancy";
this.Id = u.StarId;
this.Permanent = true;
this.AbilityorEffectType = "Units";
this.EditorSuffix = " - Уровни";
this.Race = u.Race;
#region Values
string[] GrantXPBaseList = Mpq.File.ReadFromConstants("GrantHeroXP").Split(',');
double[] GrantHeroXP = new double[GrantXPBaseList.Length + 1];
for (int i = 0; i < GrantXPBaseList.Length; i++)
GrantHeroXP[i + 1] = Convert.ToDouble(GrantXPBaseList[i]);
double GrantHeroXPFormulaA = Convert.ToDouble(Mpq.File.ReadFromConstants("GrantHeroXPFormulaA"));
double GrantHeroXPFormulaB = Convert.ToDouble(Mpq.File.ReadFromConstants("GrantHeroXPFormulaB"));
double GrantHeroXPFormulaC = Convert.ToDouble(Mpq.File.ReadFromConstants("GrantHeroXPFormulaC"));
int MaxHeroLevel = Convert.ToInt32(Mpq.File.ReadFromConstants("MaxHeroLevel"));
this.LevelModification = new Modifications[MaxHeroLevel];
this.LevelMinXp = new ulong[MaxHeroLevel];
double Last = 0;
int Agi = Convert.ToInt32(u.GetValue("Agi"));
int Str = Convert.ToInt32(u.GetValue("Str"));
int Int = Convert.ToInt32(u.GetValue("Int"));
double AgiPlus = Convert.ToDouble(u.GetValue("AgiPlus"));
double StrPlus = Convert.ToDouble(u.GetValue("StrPlus"));
double IntPlus = Convert.ToDouble(u.GetValue("IntPlus"));
string Strength = "Strength";
string Agility = "Agility";
string Intellect = "Intellect";
string Main = u.GetValue("Primary").ToLower();
switch (Main)
{
case "str":
Strength += "Main";
break;
case "agi":
Agility += "Main";
break;
case "int":
Intellect += "Main";
break;
}
for (int i = 0; i < MaxHeroLevel; i++)
if (i == 0)
{
Array.Resize(ref this.LevelModification[i].BehaviorsActivided, 3);
Array.Resize(ref this.LevelModification[i].BehaviorChanged, 3);
Array.Resize(ref this.LevelModification[i].BehaviorChangedToValue, 3);
this.LevelModification[i].BehaviorsActivided[0] = Strength;
this.LevelModification[i].BehaviorsActivided[1] = Agility;
this.LevelModification[i].BehaviorsActivided[2] = Intellect;
this.LevelModification[i].BehaviorChanged[0] = Strength;
this.LevelModification[i].BehaviorChangedToValue[0] = Convert.ToInt32(Str + StrPlus);
this.LevelModification[i].BehaviorChanged[1] = Agility;
this.LevelModification[i].BehaviorChangedToValue[1] = Convert.ToInt32(Agi + AgiPlus);
this.LevelModification[i].BehaviorChanged[2] = Intellect;
this.LevelModification[i].BehaviorChangedToValue[2] = Convert.ToInt32(Int + IntPlus);
}
else
{
if (i < GrantHeroXP.Length)
Last = GrantHeroXP[i];
else
Last = Last * GrantHeroXPFormulaA + GrantHeroXPFormulaB * (i + 1) + GrantHeroXPFormulaC;
this.LevelMinXp[i] = (ulong)Last;
Array.Resize(ref this.LevelModification[i].BehaviorChanged, 3);
Array.Resize(ref this.LevelModification[i].BehaviorChangedToValue, 3);
this.LevelModification[i].BehaviorChanged[0] = Strength;
this.LevelModification[i].BehaviorChangedToValue[0] = Convert.ToInt32(Str + StrPlus);
this.LevelModification[i].BehaviorChanged[1] = Agility;
this.LevelModification[i].BehaviorChangedToValue[1] = Convert.ToInt32(Agi + AgiPlus);
this.LevelModification[i].BehaviorChanged[2] = Intellect;
this.LevelModification[i].BehaviorChangedToValue[2] = Convert.ToInt32(Int + IntPlus);
}
#endregion
}
Как мы видим, способ №2 гораздо лучше способа №1. Однако преимущества способа №1 ощущаются лишь в следующем шаге. В принципе есть ещё и способ №3 - делать всё только с xml. Но этот способ - поистине сатанисский.
Шаг 5.
Теперь мы имеем нормальные алгоритмы. НО! Как мы будем вставлять их в стар? Пока это невозможно. Пока. Пришло время написания метода .ToXml().
А вот здесь те, кто мучились в предыдущие фазы, те, кто избрал частичную работу с xml, будут пировать. Им не надо писать это повторно. Чего не скажешь о тех, кто описывал все эти поля.
Наша задача такая написать этот метод для каждого из типов данных (а их у нас 3!), если Вы работали с наследованием, и один простой общий для тех, кто поленился и страдал:
public string[] ToXml(int TabCount)
{
// ...
}
// TabCount - это количество табов перед текстом. Параметр сугубо добровольный.
Но сначала расскажу поподробнее о старовском XML'е.
Из Википедии
Из Википедии
XML (англ. eXtensible Markup Language — расширяемый язык разметки; произносится [экс-эм-эл]) — рекомендованный Консорциумом Всемирной паутины язык разметки, фактически представляющий собой свод общих синтаксических правил. XML — текстовый формат, предназначенный для хранения структурированных данных (взамен существующих файлов баз данных), для обмена информацией между программами, а также для создания на его основе более специализированных языков разметки (например, XHTML). XML является упрощённым подмножеством языка SGML.
Все данные редактора разбиты на типы данных. Всего их куча - называть их все смысла не имеет. Каждый из предоставлен отдельный файликом в папке "Base.SC2Data", как правило в подкаталоге "GameData". Нас интересует "Base.SC2Data\GameData\BehaviorData.xml". Начиная с третьей строчки, там идут подряд (относительно подряд - после конца предыдущего) все алгоритмы (или что Вам надо) с указанием типа. Последняя строчка - тоже служебная. Мы должны написать программу, которая всё это и сгнерирует.
В принципе, существуют различные библиотеки для работы с xml. Нам же куда проще работать с xml напрямую - через строчки.
В принципе, существуют различные библиотеки для работы с xml. Нам же куда проще работать с xml напрямую - через строчки.
В этом методе мы должны заботиться и об экономии места на диске - не будем писать в код те значения, которые мы не использовали в этой структуре.
Покажу только один пример - тип "Modifications".
Код:
/// <summary>
/// Конвертирует данную структуру в понятный старкрафту xml.
/// </summary>
/// <param name="TabCount">Количество символов табуляции, отложенных от начала строки. Для красоты.</param>
/// <returns>Возвращает массив строк с xml-кодом.</returns>
public string[] ToXml(int TabCount)
{
string[] Result = new string[1];
Result[0] =
String.Format("{0}" + "<Modification>",
"".PadLeft(4 * TabCount, ' '));
for (int i = 0; i < this.BehaviorChanged.Length; i++)
{
Array.Resize(ref Result, Result.Length + 1);
Result[Result.Length - 1] =
String.Format("{0}" + "<AttributeChangeArray Attribute=\"{1}\" Points=\"{2}\"/>",
"".PadLeft(4 * (TabCount + 1), ' '),
this.BehaviorChanged[i],
this.BehaviorChangedToValue[i]);
}
for (int i = 0; i < this.BehaviorsActivided.Length; i++)
{
Array.Resize(ref Result, Result.Length + 1);
Result[Result.Length - 1] =
String.Format("{0}" + "<BehaviorLinkEnableArray value=\"{1}\"/>",
"".PadLeft(4 * (TabCount + 1), ' '),
this.BehaviorsActivided[i]);
}
if (this.AttackBonus != 0)
{
Array.Resize(ref Result, Result.Length + 2);
Result[Result.Length - 2] =
String.Format("{0}" + "<DamageDealtScaled index=\"Melee\" value=\"{1}\"/>",
"".PadLeft(4 * (TabCount + 1), ' '),
this.AttackBonus);
Result[Result.Length - 1] =
String.Format("{0}" + "<DamageDealtScaled index=\"Melee\" value=\"{1}\"/>",
"".PadLeft(4 * (TabCount + 1), ' '),
this.AttackBonus);
}
if (this.AttackSpeedBonus != 1)
{
Array.Resize(ref Result, Result.Length + 1);
Result[Result.Length - 1] =
String.Format("{0}" + "<AttackSpeedMultiplier value=\"{1}\"/>",
"".PadLeft(4 * (TabCount + 1), ' '),
this.AttackSpeedBonus);
}
if (this.MovementSpeedBonus != 0)
{
Array.Resize(ref Result, Result.Length + 1);
Result[Result.Length - 1] =
String.Format("{0}" + "<MoveSpeedBonus value=\"{1}\"/>",
"".PadLeft(4 * (TabCount + 1), ' '),
this.MovementSpeedBonus);
}
if (this.VitalArmorBonus != 0)
{
Array.Resize(ref Result, Result.Length + 1);
Result[Result.Length - 1] =
String.Format("{0}" + "<LifeArmorBonus value=\"{1}\"/>",
"".PadLeft(4 * (TabCount + 1), ' '),
this.VitalArmorBonus);
}
if (this.VitalBonus != 0)
{
Array.Resize(ref Result, Result.Length + 1);
Result[Result.Length - 1] =
String.Format("{0}" + "<VitalMaxArray index=\"Life\" value=\"{1}\"/>",
"".PadLeft(4 * (TabCount + 1), ' '),
this.VitalBonus);
}
if (this.VitalRegenBonus != 0)
{
Array.Resize(ref Result, Result.Length + 1);
Result[Result.Length - 1] =
String.Format("{0}" + "<VitalRegenArray index=\"Life\" value=\"{1}\"/>",
"".PadLeft(4 * (TabCount + 1), ' '),
this.VitalRegenBonus);
}
if (this.EnergyBonus != 0)
{
Array.Resize(ref Result, Result.Length + 1);
Result[Result.Length - 1] =
String.Format("{0}" + "<VitalMaxArray index=\"Energy\" value=\"{1}\"/>",
"".PadLeft(4 * (TabCount + 1), ' '),
this.EnergyBonus);
}
if (this.EnergyRegenBonus != 0)
{
Array.Resize(ref Result, Result.Length + 1);
Result[Result.Length - 1] =
String.Format("{0}" + "<VitalRegenArray index=\"Energy\" value=\"{1}\"/>",
"".PadLeft(4 * (TabCount + 1), ' '),
this.EnergyRegenBonus);
}
return Result;
}
Остальные доделайте сами (если хотите, конечно же!), это несложно (особенно когда есть такой наглядный пример!).
Шаг 6.
Что ж, можете себя поздравить! Вы дошли до самого конца, и теперь осталось самое простое - затолкать всё в карту. Скажу честно, я так и не научился пользоваться функцией импорта файлов в mpq, поэтому загонял по старинке. Быть может, Вам повезёт?..
План:
- Написать автоматический инициализатор всех алгоритмов. (Юниты у нас уже из варкрафта загружены.)
- Конвертировать все их в текст.
- Переписать файл внутри архива карты (модификации).
Все пункты настолько просты, что я даже не буду тратить на них внимание.
Подведём итоги.
Теперь Вы поняли принцип работ промышленных масштабов со StarCraft II Editor'ом. Поздравляю Вас!
Итак, что мы получили...
BehaviorData.xml - алгоритмы с уровнями всех героев стандартного WarCraft III. Также содержит стандартные характеристики.
((кат P.S.
Я хотел выложить конечный код, но ужаснулся его размеру, да и так кто-то будет иметь шанс написать конвертер раньше меня. А мне это не улыбается.
))));
))));
break;
#endregion
#region Intellect
case 2:
this.Id =
#endregion
#region Intellect
case 2:
this.Id =
Содержание
`
ОЖИДАНИЕ РЕКЛАМЫ...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
Надеюсь мне это никогда не понадобится.
Или что все эти процессы будут оптимизированы в 1.5 или Сварме.