Книга: главы ножа и Могильной крысы; плакат ножа, грифельная доска, дверь-шкаф; чертоги 25x8x25; плата силовой ножа 20/40
- Книга некроманта: главы «Нож некроманта» и «Могильная крыса» - лист-чертёж сверху (Textures/, три вида из Blender, подписи на эсперанто), управление и лор; окно книги непрозрачное, текст с контуром - «Дух крысы» переименован в «Могильную крысу» на 13 языках (ключи прежние) - Блоки (Dev): плакат-синька ножа (necroPosterKnife, 3:2 под полотно рамки), грифельная доска necroChalkboard (надписи - TEFeatureSignable, писать только в редакторе/творческом; шрифт Neucha OFL, свой шейдер текста с тестом глубины), дверь-книжный шкаф necroBookDoor (своя модель с аниматором, хитбокс створки через RootTransformRefParent) - Чертоги: префаб 25x8x25 (промежуточный); открываются только межкомнатные двери; надписи на досках по дням игры: «Отомсти архимагу!», с 5-го дня «Берегись излучения красной луны» - Нож: силовая атака стоит 20 здоровья, с «Мёртвой бурей» 40; не хватает - itembreak, «Недостаточно здоровья» и обычный удар; описания на 13 языках - Консольная команда necromodel - устройство модели блока в лог Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e94e967b8e
commit
4e14a0562b
+25
-11
File diff suppressed because one or more lines are too long
@@ -122,14 +122,16 @@
|
||||
чертогах (BlockNecroMemoryBook.Open) и консольная команда necrotome. -->
|
||||
<append xpath="/windows">
|
||||
<window name="necroTome" anchor="Center" pos="-650,420" width="1300" height="840" cursor_area="true" depth="1001" lock_navigation="true" controller="NecromancerTome.NecroTome, NecromancerHarmony">
|
||||
<sprite depth="0" name="bg" sprite="menu_empty" color="14,10,10,250" type="sliced" />
|
||||
<sprite depth="1" name="frame" sprite="menu_empty2px" color="120,18,14,255" type="sliced" fillcenter="false" />
|
||||
<sprite depth="0" name="bg" sprite="menu_empty" color="14,10,10,250" type="sliced" globalopacitymod="0" />
|
||||
<sprite depth="1" name="frame" sprite="menu_empty2px" color="120,18,14,255" type="sliced" fillcenter="false" globalopacitymod="0" />
|
||||
<label depth="2" name="lblTitle" pos="0,-22" width="1300" height="50" justify="center" font_size="38" color="205,40,30,255" effect="outline" upper_case="false" />
|
||||
<sprite depth="1" name="rule" pos="200,-82" width="900" height="2" sprite="menu_empty" color="120,18,14,255" />
|
||||
<label depth="2" name="lblText" pos="60,-104" width="1180" height="640" justify="left" font_size="27" color="215,205,190,255" overflow="shrinkcontent" upper_case="false" />
|
||||
<sprite depth="1" name="rule" pos="200,-82" width="900" height="2" sprite="menu_empty" color="120,18,14,255" globalopacitymod="0" />
|
||||
<label depth="2" name="lblText" pos="60,-104" width="1180" height="640" justify="left" font_size="27" color="215,205,190,255" overflow="shrinkcontent" upper_case="false" effect="outline" effect_color="0,0,0,255" />
|
||||
<!-- Иллюстрация страницы справа (Chapter.Image / Chapter.Video в NecroTome.cs); при ней
|
||||
текст сужается до левой половины. Видео - тот же XUiV_Video, что фон главного меню. -->
|
||||
<texture depth="2" name="imgPage" pos="680,-104" width="560" height="640" visible="false" />
|
||||
<!-- globalopacitymod="0" (09.10): иначе фон и иллюстрация берут настройку прозрачности интерфейса и
|
||||
просвечивают обоями чертогов - красные заголовки сливались, картинка была полупрозрачной. -->
|
||||
<texture depth="2" name="imgPage" pos="680,-104" width="560" height="640" visible="false" globalopacitymod="0" />
|
||||
<video depth="2" name="vidPage" pos="680,-104" width="560" height="640" videoaspect="FitInside" loop="true" visible="false" />
|
||||
<simplebutton name="btnCh1" depth="3" pos="350,-130" width="600" height="48" font_size="30" visible="false" />
|
||||
<simplebutton name="btnCh2" depth="3" pos="350,-190" width="600" height="48" font_size="30" visible="false" />
|
||||
|
||||
@@ -283,6 +283,57 @@
|
||||
necroposters (на каждый плакат свой префаб с тем же багетом). Исходник рамки:
|
||||
BlenderWork/necroSchemeFramePrefab/build_frame.py. -->
|
||||
<append xpath="/blocks">
|
||||
<block name="necroBookDoor">
|
||||
<!-- Дверь-книжный шкаф 1x2 (09.10.2026): «замаскирована под книжную полку, закрыта, откроется только по событию».
|
||||
Механика, звуки и прочность - ванильной межкомнатной двери; модель своя (бандл necroposters, собирает
|
||||
ItemsUnityProject Assets/Editor/NecroBookDoor.cs): створка 0.98x1.98x0.12 с полками на обеих сторонах, петля
|
||||
слева, Animator IsOpen/OpenTrigger, состояния Open/Close (как ждёт TEFeatureDoor). Чертоги её не открывают
|
||||
(MindPalace.OpenDoors трогает только exteriorHouseDoorWhite). -->
|
||||
<property name="Extends" value="interiorHouseDoorWhite" param1="TintColor,CustomIconTint,MeshDamage"/>
|
||||
<!-- MeshDamage не наследуем: он ищет в модели Door/DMG0..DMG3 (у ванильной двери они есть, у нашей нет) - тест 09.10:
|
||||
NullReferenceException в BlockShapeModelEntity.UpdateDamageState при постановке. -->
|
||||
<property name="CreativeMode" value="Dev"/>
|
||||
<property name="CustomIcon" value="necroBookDoor"/>
|
||||
<property name="Model" value="#@modfolder(NecromancerTome):Resources/necroposters?necroBookDoorPrefab.prefab"/>
|
||||
<property name="ModelOffset" value="0,0,0"/>
|
||||
<property name="MultiBlockDim" value="1,2,1"/>
|
||||
</block>
|
||||
<block name="necroChalkboard">
|
||||
<!-- Грифельная доска (09.10.2026): «как рамка, только тёмная винтажная доска; вписаться в 2x2; надписи как
|
||||
на ящиках - для сюжета». Модель - бандл necroposters (собирает ItemsUnityProject Assets/Editor/NecroChalkboard.cs):
|
||||
рама 1.90x1.40 м, полочка для мела, дочерний TextMesh (шрифт Neucha, OFL). Надпись - TEFeatureSignable,
|
||||
писать можно только в редакторе префабов / творческом режиме / отладке (ChalkboardPatch.cs).
|
||||
Размер букв: em = FontSize x 0.01 / 10 м; 110 -> ~9 строк по ~30 знаков. -->
|
||||
<property name="Extends" value="pictureFrame_01a"/>
|
||||
<property name="CreativeMode" value="Dev"/>
|
||||
<property name="CustomIcon" value="necroChalkboard"/>
|
||||
<property name="Material" value="Mwood_weak"/>
|
||||
<property name="Class" value="CompositeTileEntity"/>
|
||||
<property class="CompositeFeatures">
|
||||
<property class="TEFeatureSignable">
|
||||
<property name="FontSize" value="110"/>
|
||||
<property name="LineCount" value="9"/>
|
||||
<property name="LineWidth" value="1.68"/>
|
||||
<property name="LineSpacing" value="1"/>
|
||||
</property>
|
||||
</property>
|
||||
<property name="Model" value="#@modfolder(NecromancerTome):Resources/necroposters?necroPosterChalkboardPrefab.prefab"/>
|
||||
<property name="ModelOffset" value="0,0,-0.5"/>
|
||||
<property name="MultiBlockDim" value="2,2,1"/>
|
||||
<property name="OnlySimpleRotations" value="true"/>
|
||||
<property name="CanPickup" value="false"/>
|
||||
</block>
|
||||
<block name="necroPosterKnife">
|
||||
<!-- Схема ножа некроманта (09.10.2026): синька в стиле ванильного чертежа пистолета, три вида из Blender
|
||||
(BlenderWork/necroTomePets/make_knife.py poster). Тот же багет; пользователь повесит сам в чертогах. -->
|
||||
<property name="Extends" value="pictureFrame_01a"/>
|
||||
<property name="CreativeMode" value="Dev"/>
|
||||
<property name="CustomIcon" value="necroPosterKnife"/>
|
||||
<property name="Model" value="#@modfolder(NecromancerTome):Resources/necroposters?necroPosterKnifePrefab.prefab"/>
|
||||
<property name="ModelOffset" value="0,0,-0.5"/>
|
||||
<property name="MultiBlockDim" value="2,1,1"/>
|
||||
<property name="CanPickup" value="false"/>
|
||||
</block>
|
||||
<block name="necroPosterAnatomy">
|
||||
<property name="Extends" value="pictureFrame_01a"/>
|
||||
<property name="CreativeMode" value="Dev"/>
|
||||
|
||||
@@ -274,8 +274,9 @@
|
||||
<!-- Вдвое дороже по стамине, только силовая. -->
|
||||
<passive_effect name="StaminaLoss" operation="perc_add" value="1" tags="secondary"/>
|
||||
|
||||
<!-- Вторая половина платы: базовые 5 + эти 5 = 10 HP. -->
|
||||
<triggered_effect trigger="onSelfSecondaryActionRayHit" action="ModifyStats" stat="Health" operation="subtract" value="5">
|
||||
<!-- Вторая половина платы: базовые 20 + эти 20 = 40 HP (было 5 + 5; указание 09.10). Не хватает 40 -
|
||||
KnifePowerCostPatch.cs меняет силовую на обычный удар. -->
|
||||
<triggered_effect trigger="onSelfSecondaryActionRayHit" action="ModifyStats" stat="Health" operation="subtract" value="20">
|
||||
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
|
||||
<!-- "Health GT 0" на цели - тот же гейт на трупы, что у самого ножа, чтобы
|
||||
силовой удар по трупу не брал 5 HP впустую; подробный разбор в items.xml
|
||||
|
||||
+4
-2
@@ -1543,10 +1543,12 @@
|
||||
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
|
||||
<requirement name="StatCompareCurrent" target="other" stat="Health" operation="GT" value="0"/>
|
||||
</triggered_effect>
|
||||
<triggered_effect trigger="onSelfSecondaryActionRayHit" action="ModifyStats" stat="Health" operation="subtract" value="5">
|
||||
<!-- Плата 20 (было 5; указание 09.10: лечение удара - убийства/20, прежние 5 окупались одним ударом).
|
||||
Не хватает здоровья - KnifePowerCostPatch.cs меняет силовую на обычный удар ещё до замаха. -->
|
||||
<triggered_effect trigger="onSelfSecondaryActionRayHit" action="ModifyStats" stat="Health" operation="subtract" value="20">
|
||||
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
|
||||
<requirement name="StatCompareCurrent" target="other" stat="Health" operation="GT" value="0"/>
|
||||
<requirement name="StatCompareCurrent" stat="Health" operation="GT" value="5"/>
|
||||
<requirement name="StatCompareCurrent" stat="Health" operation="GT" value="20"/>
|
||||
</triggered_effect>
|
||||
</effect_group>
|
||||
</item>
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NecromancerTome
|
||||
{
|
||||
/// <summary>
|
||||
/// Грифельная доска (necroChalkboard, 09.10.2026): надписи мелом - как на табличках (TEFeatureSignable), но
|
||||
/// «игрок этого делать не будет, а нам для сюжета пригодится». Поэтому команда «написать» у доски есть
|
||||
/// только в редакторе префабов, в творческом режиме и в режиме отладки (так мы и подписываем её - текст
|
||||
/// сохраняется в префабе локации, IFeatureSavedInPrefab). В обычной игре доску только читают: без команды
|
||||
/// нет и подсказки «E - использовать».
|
||||
/// </summary>
|
||||
public static class Chalkboard
|
||||
{
|
||||
public const string BlockName = "necroChalkboard";
|
||||
|
||||
public static bool Is(TEFeatureSignable _sign)
|
||||
{
|
||||
return _sign?.Parent?.TeData?.Block != null && _sign.Parent.TeData.Block.GetBlockName() == BlockName;
|
||||
}
|
||||
|
||||
/// <summary>Кто может писать: редактор префабов, творческий режим, отладка.</summary>
|
||||
public static bool CanWrite()
|
||||
{
|
||||
return GameManager.Instance != null && GameManager.Instance.IsEditMode() ||
|
||||
GameStats.GetBool(EnumGameStats.IsCreativeMenuEnabled) ||
|
||||
GamePrefs.GetBool(EnumGamePrefs.DebugMenuEnabled);
|
||||
}
|
||||
|
||||
/// <summary>Материал надписи - из бандла, свой шейдер NecroTome/ChalkText с ZTest LEqual (тест 09.10: надпись
|
||||
/// просвечивала сквозь стены - встроенный GUI/Text Shader берёт ZTest из глобального unity_GUIZTestMode, который
|
||||
/// интерфейс держит «Always», и переопределение в материале не помогло). Шрифт динамический: его текстура
|
||||
/// растёт и пересоздаётся при новых буквах - держим её в материале надписи (один материал на все доски).</summary>
|
||||
static Material _mat;
|
||||
static Font _font;
|
||||
static bool _hooked;
|
||||
|
||||
public static void BindText(TextMesh _tm, MeshRenderer _r)
|
||||
{
|
||||
_mat = _r.sharedMaterial;
|
||||
_font = _tm.font;
|
||||
if (_mat == null || _font == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_mat.mainTexture = _font.material.mainTexture;
|
||||
if (!_hooked)
|
||||
{
|
||||
_hooked = true;
|
||||
Font.textureRebuilt += _f =>
|
||||
{
|
||||
if (_mat != null && _f == _font)
|
||||
{
|
||||
_mat.mainTexture = _f.material.mainTexture;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(TEFeatureSignable), nameof(TEFeatureSignable.SetBlockEntityData))]
|
||||
public static class Patch_TEFeatureSignable_SetBlockEntityData_Chalkboard
|
||||
{
|
||||
public static void Postfix(TEFeatureSignable __instance, BlockEntityData _blockEntityData)
|
||||
{
|
||||
if (_blockEntityData == null || !_blockEntityData.bHasTransform || !Chalkboard.Is(__instance))
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (TextMesh tm in _blockEntityData.transform.GetComponentsInChildren<TextMesh>())
|
||||
{
|
||||
MeshRenderer r = tm.GetComponent<MeshRenderer>();
|
||||
if (r != null && tm.font != null)
|
||||
{
|
||||
Chalkboard.BindText(tm, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(TEFeatureSignable), nameof(TEFeatureSignable.AllowBlockActivationCommand))]
|
||||
public static class Patch_TEFeatureSignable_AllowBlockActivationCommand_Chalkboard
|
||||
{
|
||||
public static void Postfix(TEFeatureSignable __instance, ITileEntityFeature _module, ReadOnlySpan<char> _commandName, ref bool __result)
|
||||
{
|
||||
if (__result && __instance.Equals(_module) && _commandName.SequenceEqual("edit".AsSpan()) &&
|
||||
Chalkboard.Is(__instance) && !Chalkboard.CanWrite())
|
||||
{
|
||||
__result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(TEFeatureSignable), nameof(TEFeatureSignable.GetActivationText))]
|
||||
public static class Patch_TEFeatureSignable_GetActivationText_Chalkboard
|
||||
{
|
||||
public static void Postfix(TEFeatureSignable __instance, ref string __result)
|
||||
{
|
||||
if (Chalkboard.Is(__instance) && !Chalkboard.CanWrite())
|
||||
{
|
||||
__result = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace NecromancerTome
|
||||
|
||||
static bool Ours(string _name)
|
||||
{
|
||||
return _name == "necroBookMindPrefab" || _name == "necroKnifeDecorPrefab" || _name == "necroMortarPrefab" || _name.StartsWith("necroBloodRose") || _name.StartsWith("necroPoster");
|
||||
return _name == "necroBookMindPrefab" || _name == "necroKnifeDecorPrefab" || _name == "necroMortarPrefab" || _name.StartsWith("necroBloodRose") || _name.StartsWith("necroPoster") || _name == "necroBookDoorPrefab";
|
||||
}
|
||||
|
||||
public static void Postfix(Transform __result)
|
||||
@@ -37,6 +37,21 @@ namespace NecromancerTome
|
||||
}
|
||||
__result.tag = vanilla.tag;
|
||||
__result.gameObject.layer = vanilla.gameObject.layer;
|
||||
// Дверь-шкаф (09.10): коллайдер на створке (ходит с петлёй), а не на корне - тег и ей.
|
||||
foreach (Collider c in __result.GetComponentsInChildren<Collider>(true))
|
||||
{
|
||||
c.gameObject.tag = vanilla.tag;
|
||||
c.gameObject.layer = vanilla.gameObject.layer;
|
||||
}
|
||||
// По попаданию в дочерний коллайдер игра ищет блок через RootTransformRefParent.FindRoot (Voxel ->
|
||||
// GameUtils.FindMasterBlockForEntityModelBlock): без него луч по створке «не находил» блок - двери не было
|
||||
// хитбокса (тест 09.10). Как у ванильной двери - компонент на корне со ссылкой на корень; при копировании
|
||||
// модели Unity переводит ссылку на корень копии.
|
||||
if (__result.GetComponentInChildren<Collider>(true) != null && __result.GetComponent<Collider>() == null)
|
||||
{
|
||||
RootTransformRefParent rr = __result.GetComponent<RootTransformRefParent>() ?? __result.gameObject.AddComponent<RootTransformRefParent>();
|
||||
rr.RootTransform = __result;
|
||||
}
|
||||
Debug.Log("[NecromancerTome] DecorCollider: '" + __result.name + "' тег " + vanilla.tag + ", слой " + vanilla.gameObject.layer +
|
||||
", коллайдер " + (__result.GetComponent<Collider>() != null ? "есть" : "НЕТ"));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NecromancerTome
|
||||
{
|
||||
/// <summary>
|
||||
/// Плата здоровьем за силовую атаку Ножа некроманта (указание 09.10.2026: «обычная силовая - 20 хп, с модификацией
|
||||
/// 40; если здоровья на штраф не хватает - звук сломавшегося инвентаря, а силовая заменится обычной»).
|
||||
/// Саму плату снимает XML (items.xml - 20 при попадании силовой, item_modifiers.xml «Мёртвая буря» - ещё 20).
|
||||
/// Здесь - только проверка до удара: ItemClass.ExecuteAction запускает и обычный удар (действие 0), и силовую
|
||||
/// (действие 1); не хватает здоровья - номер действия меняется на 0 (тот же удар с той же анимацией обычного),
|
||||
/// а при нажатии звучит ванильный itembreak и надпись «Недостаточно здоровья».
|
||||
/// Почему был нужен штраф побольше: лечение обычным ударом - убийства/20, и уже со 100 убийств один удар
|
||||
/// окупал прежнюю плату в 5 здоровья.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.ExecuteAction))]
|
||||
public static class Patch_ItemClass_ExecuteAction_KnifePowerCost
|
||||
{
|
||||
public const string KnifeName = "necroWpnBladeNecroKnife";
|
||||
public const string StormMod = "necroModKnifeDeadStorm";
|
||||
/// <summary>Должно совпадать с items.xml и item_modifiers.xml.</summary>
|
||||
public const int Cost = 20, StormCost = 40;
|
||||
|
||||
public static void Prefix(ItemClass __instance, ref int _actionIdx, ItemInventoryData _data, bool _bReleased)
|
||||
{
|
||||
if (_actionIdx != 1 || __instance.Name != KnifeName || !(_data?.holdingEntity is EntityPlayerLocal player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
int cost = HasStorm(_data.itemValue) ? StormCost : Cost;
|
||||
if (player.Health > cost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_actionIdx = 0;
|
||||
if (!_bReleased)
|
||||
{
|
||||
Audio.Manager.PlayInsidePlayerHead("itembreak");
|
||||
// Надпись (указание 09.10) - сразу, мимо очереди подсказок, чтобы при частых нажатиях не копилась.
|
||||
GameManager.ShowTooltip(player, Localization.Get("necroKnifeNoHealth"), true);
|
||||
Debug.Log("[NecromancerTome] KnifePowerCost: здоровья " + player.Health + ", плата " + cost + " - вместо силовой обычный удар");
|
||||
}
|
||||
}
|
||||
|
||||
static bool HasStorm(ItemValue _iv)
|
||||
{
|
||||
if (_iv?.modifications == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
foreach (ItemValue m in _iv.modifications)
|
||||
{
|
||||
if (m != null && !m.IsEmpty() && m.ItemClass?.Name == StormMod)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,9 @@ namespace NecromancerTome
|
||||
/// (woodShapes:cube) -> terrBedrock (в меню творчества его нет, рисовали деревом),
|
||||
/// одиночная книга на столе -> necroMemoryBook, на стол с реактивами - нож-декорация,
|
||||
/// кровать (SleepingBag) -> necroPalaceBed: иначе игра делает её точкой возрождения игрока.
|
||||
/// Построены ли - по книге: нет её - ставим заново (самопочинка).
|
||||
/// Построены ли - сверка с префабом каждой клетки (Mismatches: тип, плотность, покраска) при каждом входе:
|
||||
/// расхождение - ставим заново (самопочинка; заодно новый, более широкий префаб сам перестраивает старые
|
||||
/// чертоги в сейвах). Надписи на досках из префаба НЕ переносятся (указание 09.10: «текст будет добавлять мод по событиям»).
|
||||
///
|
||||
/// ОХРАНА. Внутри чертогов игра считает место «зоной торговца» (патч IsWithinTraderArea):
|
||||
/// блоки не ломаются ни ударом, ни взрывом, строить нельзя. Чужие сущности в коробке чертогов
|
||||
@@ -45,6 +47,8 @@ namespace NecromancerTome
|
||||
public const string BedBlock = "necroPalaceBed";
|
||||
public const string ShellFrom = "woodShapes:cube";
|
||||
public const string ShellTo = "terrBedrock";
|
||||
/// <summary>Межкомнатные двери - их чертоги открывают сами при входе (кроме входной).</summary>
|
||||
public const string PassageDoor = "exteriorHouseDoorWhite";
|
||||
/// <summary>Одиночная книга на обеденном столе (cntBookPile09 в префабе).</summary>
|
||||
public static readonly Vector3i BookAt = new Vector3i(12, 2, 9);
|
||||
/// <summary>Место ножа на столе с реактивами (указание пользователя).</summary>
|
||||
@@ -408,8 +412,9 @@ namespace NecromancerTome
|
||||
return Mismatches(_world, _o) == 0;
|
||||
}
|
||||
|
||||
/// <summary>Все двери чертогов - открыты (в редакторе сохранились закрытыми, а дух дверей не
|
||||
/// открывает). Состояние двери - в её TEFeatureDoor, не в блоке.</summary>
|
||||
/// <summary>Межкомнатные двери чертогов (PassageDoor) - открыты (в редакторе сохранились закрытыми, а дух
|
||||
/// дверей не открывает), входная - закрыта. Прочие двери (шкаф-тайник, стальная - указание 09.10: «закрыты
|
||||
/// и откроются только по событию») не трогаем: их открывает событие. Состояние двери - в её TEFeatureDoor.</summary>
|
||||
public static void OpenDoors(World _world, Vector3i _o)
|
||||
{
|
||||
Vector3i s = Size;
|
||||
@@ -421,6 +426,10 @@ namespace NecromancerTome
|
||||
if (_world.GetTileEntity(_o + new Vector3i(x, y, z)) is TileEntityComposite tec)
|
||||
{
|
||||
TEFeatureDoor door = tec.GetFeature<TEFeatureDoor>();
|
||||
if (door == null || _world.GetBlock(_o + new Vector3i(x, y, z)).Block.GetBlockName() != PassageDoor)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Входная - закрыть (могла остаться открытой от прежних входов), остальные - открыть.
|
||||
bool wantOpen = new Vector3i(x, y, z) != EntranceDoorAt;
|
||||
if (door != null && door.IsOpen() != wantOpen)
|
||||
@@ -436,6 +445,54 @@ namespace NecromancerTome
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Надписи на досках чертогов (указание 09.10). Лор: некроманта бросили в этот мир умирать - раздели,
|
||||
/// лишили артефактов (и вживлённых тоже) и ментальной магией стёрли память, «закрыв знания, опасные для общества».
|
||||
/// Доска - то, что он ещё помнит или успел себе написать; строки прибавляются по событиям игры. Пока событие -
|
||||
/// только день: (с какого дня, ключ локализации). Остальные строки - позднее.</summary>
|
||||
public static readonly (int day, string key)[] BoardLines =
|
||||
{
|
||||
(1, "necroPalaceBoardRevenge"),
|
||||
(5, "necroPalaceBoardRedMoon"),
|
||||
};
|
||||
|
||||
/// <summary>При каждом входе: на каждой доске (necroChalkboard) чертогов - все наступившие строки, на языке игрока.
|
||||
/// Текст из префаба в чертоги не идёт (Stamp ставит только блоки) - пишет только мод.</summary>
|
||||
public static void UpdateBoards(World _world, Vector3i _o)
|
||||
{
|
||||
int day = GameUtils.WorldTimeToDays(_world.worldTime);
|
||||
var lines = new List<string>();
|
||||
foreach (var l in BoardLines)
|
||||
{
|
||||
if (day >= l.day)
|
||||
{
|
||||
lines.Add(Localization.Get(l.key));
|
||||
}
|
||||
}
|
||||
string text = string.Join("\n", lines);
|
||||
Vector3i s = Size;
|
||||
int n = 0;
|
||||
for (int y = 0; y < s.y; y++)
|
||||
for (int z = 0; z < s.z; z++)
|
||||
for (int x = 0; x < s.x; x++)
|
||||
{
|
||||
Vector3i wp = _o + new Vector3i(x, y, z);
|
||||
if (_world.GetBlock(wp).Block?.GetBlockName() != Chalkboard.BlockName ||
|
||||
!(_world.GetTileEntity(wp) is TileEntityComposite tec) || !tec.TryGetSelfOrFeature(out TEFeatureSignable sign))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ((sign.GetAuthoredText()?.Text ?? "") != text)
|
||||
{
|
||||
sign.SetText(text, true);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (n > 0)
|
||||
{
|
||||
Debug.Log("[NecromancerTome] MindPalace: надпись на досках (день " + day + ", строк " + lines.Count + "), досок " + n);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Stamp(World _world, Vector3i _o)
|
||||
{
|
||||
Prefab p = LoadPrefab();
|
||||
@@ -707,6 +764,7 @@ namespace NecromancerTome
|
||||
Debug.Log("[NecromancerTome] MindPalace: коллизии " + (world.IsChunkAreaCollidersLoaded(spawn) ? "готовы" : "НЕ готовы, пускаем по таймеру"));
|
||||
_stage = Stage.Ready;
|
||||
OpenDoors(world, _origin);
|
||||
UpdateBoards(world, _origin);
|
||||
LogCell(world, _origin + KnifeAt, "нож");
|
||||
LogCell(world, _origin + BookAt, "книга");
|
||||
MindControl.Begin(player, spawn, SpawnYaw);
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace NecromancerTome
|
||||
{
|
||||
/// <summary>
|
||||
/// necromodel <блок> - выписать в лог устройство модели блока (09.10.2026, для своей двери-книжного
|
||||
/// шкафа: где у ванильной двери петля, куда она открывается, какие коллайдеры). Иерархия с местом, поворотом,
|
||||
/// масштабом, мешами и коллайдерами; у аниматоров - параметры и клипы, а створка в состояниях «Open» и
|
||||
/// «Close» - пробой клипа на копии префаба. Копия создаётся и тут же уничтожается.
|
||||
/// </summary>
|
||||
[Preserve]
|
||||
public class ConsoleCmdNecroModel : ConsoleCmdAbstract
|
||||
{
|
||||
public override string[] getCommands() => new[] { "necromodel" };
|
||||
public override string getDescription() => "NecromancerTome: устройство модели блока в лог (necromodel <имя блока>)";
|
||||
|
||||
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo)
|
||||
{
|
||||
if (_params.Count < 1)
|
||||
{
|
||||
SdtdConsole.Instance.Output("necromodel <имя блока>");
|
||||
return;
|
||||
}
|
||||
Block block = Block.GetBlockByName(_params[0]);
|
||||
string model = block?.Properties.GetString("Model");
|
||||
if (string.IsNullOrEmpty(model))
|
||||
{
|
||||
SdtdConsole.Instance.Output("Нет блока или у него нет Model: " + _params[0]);
|
||||
return;
|
||||
}
|
||||
if (!model.StartsWith("@") && !model.StartsWith("#"))
|
||||
{
|
||||
model = "@:" + model;
|
||||
}
|
||||
GameObject prefab = DataLoader.LoadAsset<GameObject>(model);
|
||||
if (prefab == null)
|
||||
{
|
||||
SdtdConsole.Instance.Output("Модель не загрузилась: " + model);
|
||||
return;
|
||||
}
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("[NecromancerTome] necromodel " + _params[0] + " = " + model + ", ModelOffset " +
|
||||
block.Properties.GetString("ModelOffset") + ", MultiBlockDim " + block.Properties.GetString("MultiBlockDim"));
|
||||
Dump(prefab.transform, 0, sb);
|
||||
GameObject copy = Object.Instantiate(prefab);
|
||||
try
|
||||
{
|
||||
foreach (Animator a in copy.GetComponentsInChildren<Animator>(true))
|
||||
{
|
||||
RuntimeAnimatorController rc = a.runtimeAnimatorController;
|
||||
sb.AppendLine("Animator на " + a.name + ": " + (rc != null ? rc.name : "нет контроллера"));
|
||||
foreach (AnimatorControllerParameter p in a.parameters)
|
||||
{
|
||||
sb.AppendLine(" параметр " + p.name + " " + p.type);
|
||||
}
|
||||
if (rc == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (AnimationClip clip in rc.animationClips)
|
||||
{
|
||||
sb.AppendLine(" клип " + clip.name + ", " + clip.length.ToString("F2") + " с");
|
||||
clip.SampleAnimation(copy, clip.length);
|
||||
foreach (Transform t in copy.GetComponentsInChildren<Transform>(true))
|
||||
{
|
||||
sb.AppendLine(" в конце: " + Path(t, copy.transform) + " pos " + t.localPosition.ToString("F3") +
|
||||
" rot " + t.localEulerAngles.ToString("F1"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Object.Destroy(copy);
|
||||
}
|
||||
Debug.Log(sb.ToString());
|
||||
SdtdConsole.Instance.Output("necromodel: выписано в лог");
|
||||
}
|
||||
|
||||
static void Dump(Transform _t, int _depth, StringBuilder _sb)
|
||||
{
|
||||
var line = new StringBuilder(new string(' ', _depth * 2));
|
||||
line.Append(_t.name).Append(" pos ").Append(_t.localPosition.ToString("F3")).Append(" rot ")
|
||||
.Append(_t.localEulerAngles.ToString("F1")).Append(" scale ").Append(_t.localScale.ToString("F2"))
|
||||
.Append(" tag ").Append(_t.tag).Append(" layer ").Append(_t.gameObject.layer);
|
||||
MeshFilter mf = _t.GetComponent<MeshFilter>();
|
||||
if (mf != null && mf.sharedMesh != null)
|
||||
{
|
||||
line.Append(" mesh ").Append(mf.sharedMesh.name).Append(" bounds c").Append(mf.sharedMesh.bounds.center.ToString("F3"))
|
||||
.Append(" size").Append(mf.sharedMesh.bounds.size.ToString("F3"));
|
||||
}
|
||||
foreach (Collider c in _t.GetComponents<Collider>())
|
||||
{
|
||||
line.Append(" | ").Append(c.GetType().Name).Append(c.isTrigger ? "(trigger)" : "");
|
||||
if (c is BoxCollider b)
|
||||
{
|
||||
line.Append(" c").Append(b.center.ToString("F3")).Append(" s").Append(b.size.ToString("F3"));
|
||||
}
|
||||
}
|
||||
foreach (Component comp in _t.GetComponents<Component>())
|
||||
{
|
||||
if (!(comp is Transform) && !(comp is MeshFilter) && !(comp is MeshRenderer) && !(comp is Collider))
|
||||
{
|
||||
line.Append(" [").Append(comp != null ? comp.GetType().Name : "null").Append("]");
|
||||
}
|
||||
}
|
||||
_sb.AppendLine(line.ToString());
|
||||
foreach (Transform ch in _t)
|
||||
{
|
||||
Dump(ch, _depth + 1, _sb);
|
||||
}
|
||||
}
|
||||
|
||||
static string Path(Transform _t, Transform _root)
|
||||
{
|
||||
string p = _t.name;
|
||||
while (_t.parent != null && _t != _root)
|
||||
{
|
||||
_t = _t.parent;
|
||||
p = _t.name + "/" + p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
-4
@@ -29,6 +29,9 @@ namespace NecromancerTome
|
||||
/// "@modfolder(NecromancerTome):Textures/x.png") или зацикленное видео без звука. Ни того,
|
||||
/// ни другого - текст на всю ширину.</summary>
|
||||
public string Image, Video;
|
||||
/// <summary>Картинка полосой сверху во всю ширину, текст под ней (главы питомцев, указание 09.10:
|
||||
/// «под картинкой - управление схематично, под ним - описание питомца»).</summary>
|
||||
public bool ImageTop;
|
||||
}
|
||||
|
||||
/// <summary>cvar «письмо архимага прочитано» - ставит записка noteDuke01 (Config/items.xml).</summary>
|
||||
@@ -54,6 +57,26 @@ namespace NecromancerTome
|
||||
PageKeys = new[] { "necroTomeCh1P1", "necroTomeCh1P2", "necroTomeCh1P3", "necroTomeCh1P4" },
|
||||
Unlocked = _p => true
|
||||
},
|
||||
// Нож некроманта (указание 09.10): лист-чертёж ножа (тот же рецепт, что у крысы), управление ножом и духом
|
||||
// змеи, описание. Перед питомцами - «основной инструмент», с него всё начинается.
|
||||
new Chapter
|
||||
{
|
||||
TitleKey = "necroTomeKnifeTitle",
|
||||
PageKeys = new[] { "necroTomeKnifeP1", "necroTomeKnifeP2", "necroTomeKnifeP3" },
|
||||
Image = "@modfolder(NecromancerTome):Textures/necroTomeKnife.png",
|
||||
ImageTop = true,
|
||||
Unlocked = _p => true
|
||||
},
|
||||
// Главы питомцев (указание 09.10): лист-чертёж модели (три вида из Blender, подписи на эсперанто) сверху, на первой
|
||||
// странице - управление, дальше - описание. Крыса - учебная модель, глава открыта сразу.
|
||||
new Chapter
|
||||
{
|
||||
TitleKey = "necroTomeRatTitle",
|
||||
PageKeys = new[] { "necroTomeRatP1", "necroTomeRatP2", "necroTomeRatP3" },
|
||||
Image = "@modfolder(NecromancerTome):Textures/necroTomeRat.png",
|
||||
ImageTop = true,
|
||||
Unlocked = _p => true
|
||||
},
|
||||
};
|
||||
|
||||
public static void Open(EntityPlayerLocal _player)
|
||||
@@ -73,6 +96,11 @@ namespace NecromancerTome
|
||||
XUiV_Video _vid;
|
||||
/// <summary>Ширина текста без иллюстрации и с ней (windows.xml: lblText 1180, картинка с x 680).</summary>
|
||||
const int TextWidthFull = 1180, TextWidthHalf = 590;
|
||||
/// <summary>Раскладка «картинка сверху»: лист-чертёж 1180x400 (как Textures/necroTomeRat.png), текст ниже.</summary>
|
||||
static readonly Vector2i ImgSidePos = new Vector2i(680, -104), ImgSideSize = new Vector2i(560, 640);
|
||||
static readonly Vector2i ImgTopPos = new Vector2i(60, -104), ImgTopSize = new Vector2i(1180, 400);
|
||||
static readonly Vector2i TextPos = new Vector2i(60, -104), TextTopPos = new Vector2i(60, -518);
|
||||
const int TextHeightFull = 640, TextHeightTop = 245;
|
||||
XUiC_SimpleButton _toc, _prev, _next, _close;
|
||||
readonly XUiC_SimpleButton[] _chapterButtons = new XUiC_SimpleButton[MaxButtons];
|
||||
/// <summary>Глава по кнопке оглавления (только открытые главы, по порядку).</summary>
|
||||
@@ -164,7 +192,7 @@ namespace NecromancerTome
|
||||
SetLabel(_title, Localization.Get(ch.TitleKey));
|
||||
SetLabel(_text, Localization.Get(ch.PageKeys[_pageIndex]));
|
||||
SetLabel(_page, (_pageIndex + 1) + " / " + ch.PageKeys.Length);
|
||||
SetMedia(ch.Image, ch.Video);
|
||||
SetMedia(ch.Image, ch.Video, ch.ImageTop);
|
||||
SetVisible(_toc, true);
|
||||
SetVisible(_prev, true);
|
||||
SetVisible(_next, true);
|
||||
@@ -181,12 +209,15 @@ namespace NecromancerTome
|
||||
/// <summary>Показать картинку или видео справа (или убрать обе). Видео задаётся только здесь,
|
||||
/// а не в вёрстке: XUiV_Video сам запускает ролик при открытии окна, и в оглавлении он играл
|
||||
/// бы невидимым. Пустой uri - XUiV_Video.startVideo сам освобождает свою текстуру.</summary>
|
||||
void SetMedia(string _image, string _video)
|
||||
void SetMedia(string _image, string _video, bool _top = false)
|
||||
{
|
||||
bool img = !string.IsNullOrEmpty(_image), vid = !img && !string.IsNullOrEmpty(_video);
|
||||
if (_img != null)
|
||||
{
|
||||
if (img && _img.Path != _image)
|
||||
// Без чтения _img.Path: у пустой картинки (в вёрстке texture не задан) его геттер берёт
|
||||
// textureUris[0] из пустого массива - IndexOutOfRange (тест 09.10). Сеттер сам сравнивает
|
||||
// с текущим путём и не перезагружает тот же файл.
|
||||
if (img)
|
||||
{
|
||||
_img.Path = _image;
|
||||
}
|
||||
@@ -212,9 +243,16 @@ namespace NecromancerTome
|
||||
}
|
||||
_vid.IsVisible = vid;
|
||||
}
|
||||
if (_img != null)
|
||||
{
|
||||
_img.Position = _top ? ImgTopPos : ImgSidePos;
|
||||
_img.Size = _top ? ImgTopSize : ImgSideSize;
|
||||
}
|
||||
if (_text != null)
|
||||
{
|
||||
_text.Size = new Vector2i(img || vid ? TextWidthHalf : TextWidthFull, _text.Size.y);
|
||||
bool top = _top && img;
|
||||
_text.Position = top ? TextTopPos : TextPos;
|
||||
_text.Size = new Vector2i(top || !(img || vid) ? TextWidthFull : TextWidthHalf, top ? TextHeightTop : TextHeightFull);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+12
-13
@@ -8,20 +8,19 @@
|
||||
<property name="DifficultyTier" value="0" />
|
||||
<property name="ShowQuestClearCount" value="1" />
|
||||
<property name="TraderArea" value="False" />
|
||||
<property name="PrefabSize" value="17, 8, 17" />
|
||||
<property name="PrefabSize" value="25, 8, 25" />
|
||||
<property name="RotationToFaceNorth" value="2" />
|
||||
<property name="Zoning" value="none" />
|
||||
<property class="IndexedBlockOffsets">
|
||||
<property class="POILight">
|
||||
<property name="0" value="11, 3, 9" />
|
||||
<property name="1" value="12, 3, 9" />
|
||||
<property name="2" value="8, 4, 10" />
|
||||
<property name="3" value="12, 4, 10" />
|
||||
<property name="4" value="12, 4, 12" />
|
||||
<property name="5" value="9, 3, 13" />
|
||||
<property name="2" value="9, 3, 13" />
|
||||
<property name="3" value="8, 4, 10" />
|
||||
<property name="4" value="11, 3, 10" />
|
||||
</property>
|
||||
<property class="challenge_sinks">
|
||||
<property name="0" value="11, 1, 3" />
|
||||
<property name="0" value="13, 1, 3" />
|
||||
<property name="1" value="13, 1, 4" />
|
||||
</property>
|
||||
<property class="TraderOnOff">
|
||||
@@ -36,16 +35,16 @@
|
||||
</property>
|
||||
</property>
|
||||
<property class="Stats">
|
||||
<property name="LightsVolume" value="5516.637" />
|
||||
<property name="TotalVertices" value="91813" />
|
||||
<property name="TotalTriangles" value="61958" />
|
||||
<property name="LightsVolume" value="9206.96" />
|
||||
<property name="TotalVertices" value="108077" />
|
||||
<property name="TotalTriangles" value="69295" />
|
||||
<property class="BlockEntities">
|
||||
<property name="Vertices" value="73400" />
|
||||
<property name="Triangles" value="52089" />
|
||||
<property name="Vertices" value="76900" />
|
||||
<property name="Triangles" value="53018" />
|
||||
</property>
|
||||
<property class="ChunkMeshes0">
|
||||
<property name="Vertices" value="18121" />
|
||||
<property name="Triangles" value="9645" />
|
||||
<property name="Vertices" value="30885" />
|
||||
<property name="Triangles" value="16053" />
|
||||
</property>
|
||||
<property class="ChunkMeshes1">
|
||||
<property name="Vertices" value="0" />
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
Copyright (c) 2008-2010 by Jovanny Lemonad (http://www.jovanny.ru)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 624 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 638 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Reference in New Issue
Block a user