Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1180fb9d1b |
@@ -72,7 +72,3 @@ crashlytics-build.properties
|
||||
/[Aa]ssets/[Ss]treamingAssets/aa.meta
|
||||
/[Aa]ssets/[Ss]treamingAssets/aa/*
|
||||
|
||||
# Сборки APK (Tools -> Bogatyri -> Собрать APK) и служебные папки сборки
|
||||
/Builds/
|
||||
/build/
|
||||
/MVP1_BurstDebugInformation_DoNotShip/
|
||||
|
||||
Vendored
+1
-1
@@ -60,7 +60,7 @@
|
||||
"*.asset": "yaml",
|
||||
"*.meta": "yaml",
|
||||
"*.prefab": "yaml",
|
||||
"*.unity": "yaml"
|
||||
"*.unity": "yaml",
|
||||
},
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Android;
|
||||
using UnityEngine;
|
||||
|
||||
// Починка сборки Android на этом компьютере (05.11):
|
||||
// 1. Имя пользователя Windows с русскими буквами (C:\Users\Вальдемар). Gradle хранит свои файлы в
|
||||
// C:\Users\<имя>\.gradle, и одна из его программ (prefab) не понимает такой путь -- сборка падает.
|
||||
// Перед сборкой переносим папку Gradle в C:\gradle (переменная GRADLE_USER_HOME для процесса Unity,
|
||||
// сборщик её наследует). Ничего в Windows менять не нужно.
|
||||
// 2. Два Kotlin в зависимостях (1.8 у Unity и 1.6 у одного из пакетов) -- "Duplicate class kotlin...".
|
||||
// В сгенерированный проект Gradle дописываем правило: все kotlin-stdlib -- одной версии 1.8.22.
|
||||
[InitializeOnLoad]
|
||||
public class AndroidBuildFixes : IPostGenerateGradleAndroidProject
|
||||
{
|
||||
const string GradleHome = @"C:\gradle";
|
||||
const string Marker = "// Bogatyri: kotlin-stdlib одной версии";
|
||||
|
||||
static AndroidBuildFixes() { EnsureAsciiGradleHome(); }
|
||||
|
||||
public static void EnsureAsciiGradleHome()
|
||||
{
|
||||
string current = Environment.GetEnvironmentVariable("GRADLE_USER_HOME");
|
||||
if (!string.IsNullOrEmpty(current) && IsAscii(current)) return;
|
||||
if (IsAscii(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile))) return; // путь и так латиницей
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(GradleHome);
|
||||
Environment.SetEnvironmentVariable("GRADLE_USER_HOME", GradleHome, EnvironmentVariableTarget.Process);
|
||||
Debug.Log("[СБОРКА] Папка Gradle: " + GradleHome + " (в пути пользователя есть русские буквы)");
|
||||
}
|
||||
catch (Exception e) { Debug.LogWarning("[СБОРКА] Не удалось задать GRADLE_USER_HOME: " + e.Message); }
|
||||
}
|
||||
|
||||
static bool IsAscii(string s) => !Regex.IsMatch(s ?? "", @"[^\u0000-\u007F]");
|
||||
|
||||
public int callbackOrder => 999;
|
||||
|
||||
// path -- папка unityLibrary сгенерированного проекта
|
||||
public void OnPostGenerateGradleAndroidProject(string path)
|
||||
{
|
||||
string root = Directory.GetParent(path).FullName;
|
||||
foreach (var file in new[] { Path.Combine(root, "launcher", "build.gradle"), Path.Combine(path, "build.gradle") })
|
||||
{
|
||||
if (!File.Exists(file)) continue;
|
||||
string text = File.ReadAllText(file);
|
||||
if (text.Contains(Marker)) continue;
|
||||
text += "\n" + Marker + "\n" +
|
||||
"configurations.all {\n" +
|
||||
" resolutionStrategy.eachDependency { details ->\n" +
|
||||
" if (details.requested.group == 'org.jetbrains.kotlin' && details.requested.name.startsWith('kotlin-stdlib')) {\n" +
|
||||
" details.useVersion('1.8.22')\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n";
|
||||
File.WriteAllText(file, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 030e644cb052c0f4d959e499015370fd
|
||||
@@ -1,102 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build.Reporting;
|
||||
|
||||
// Сборка APK одной кнопкой (05.11): Tools -> Bogatyri -> Собрать APK.
|
||||
// Файл: C:\MVP1\Builds\Bogatyri_<ветка>_v<версия>_<дата>.apk; сцены -- из списка сборки (первая -- стартовая).
|
||||
// Итог сборки (успех, размер, первые ошибки) пишется в Builds\last_build.txt.
|
||||
// Сборку можно запустить и файлом-запросом Builds\build_request.txt (так её запускает Claude): Unity заметит
|
||||
// его в течение пары секунд, удалит и начнёт сборку.
|
||||
[InitializeOnLoad]
|
||||
public static class ApkBuilder
|
||||
{
|
||||
const string Folder = "Builds";
|
||||
const string RequestFile = "Builds/build_request.txt";
|
||||
const string ResultFile = "Builds/last_build.txt";
|
||||
static double nextCheck;
|
||||
|
||||
static ApkBuilder()
|
||||
{
|
||||
EditorApplication.update += Poll;
|
||||
}
|
||||
|
||||
static void Poll()
|
||||
{
|
||||
if (EditorApplication.timeSinceStartup < nextCheck) return;
|
||||
nextCheck = EditorApplication.timeSinceStartup + 2;
|
||||
if (!File.Exists(RequestFile) || BuildPipeline.isBuildingPlayer || EditorApplication.isCompiling || EditorApplication.isPlaying) return;
|
||||
File.Delete(RequestFile);
|
||||
EditorApplication.delayCall += Build;
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Bogatyri/Собрать APK")]
|
||||
public static void Build()
|
||||
{
|
||||
AndroidBuildFixes.EnsureAsciiGradleHome();
|
||||
Directory.CreateDirectory(Folder);
|
||||
string branch = Git("rev-parse --abbrev-ref HEAD").Replace('/', '-');
|
||||
if (branch.Length == 0) branch = "local";
|
||||
string file = $"{Folder}/Bogatyri_{branch}_v{PlayerSettings.bundleVersion}_{DateTime.Now:yyyyMMdd_HHmm}.apk";
|
||||
var scenes = EditorBuildSettings.scenes.Where(s => s.enabled).Select(s => s.path).ToArray();
|
||||
|
||||
File.WriteAllText(ResultFile, $"Сборка идёт: {DateTime.Now:dd.MM.yyyy HH:mm}\n{file}\n");
|
||||
BuildReport report;
|
||||
try
|
||||
{
|
||||
EditorUserBuildSettings.buildAppBundle = false; // обычный APK
|
||||
report = BuildPipeline.BuildPlayer(new BuildPlayerOptions
|
||||
{
|
||||
scenes = scenes,
|
||||
locationPathName = file,
|
||||
target = BuildTarget.Android,
|
||||
options = BuildOptions.None
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
File.WriteAllText(ResultFile, $"ОШИБКА {DateTime.Now:dd.MM.yyyy HH:mm}\n{e}\n");
|
||||
throw;
|
||||
}
|
||||
|
||||
var s = report.summary;
|
||||
var text = new StringBuilder();
|
||||
text.AppendLine($"{(s.result == BuildResult.Succeeded ? "ГОТОВО" : "ОШИБКА")} {DateTime.Now:dd.MM.yyyy HH:mm} результат: {s.result}");
|
||||
text.AppendLine($"Файл: {Path.GetFullPath(file)}");
|
||||
if (s.result == BuildResult.Succeeded && File.Exists(file)) text.AppendLine($"Размер: {new FileInfo(file).Length / 1048576.0:0.0} МБ");
|
||||
text.AppendLine($"Версия: {PlayerSettings.bundleVersion}, код {PlayerSettings.Android.bundleVersionCode}, ветка {branch} {Git("rev-parse --short HEAD")}");
|
||||
text.AppendLine($"Сцены: {string.Join(", ", scenes)}");
|
||||
text.AppendLine($"Время: {s.totalTime}, ошибок: {s.totalErrors}, предупреждений: {s.totalWarnings}");
|
||||
int shown = 0;
|
||||
foreach (var step in report.steps)
|
||||
foreach (var m in step.messages)
|
||||
if (m.type == UnityEngine.LogType.Error || m.type == UnityEngine.LogType.Exception)
|
||||
{
|
||||
if (shown++ >= 15) break;
|
||||
string msg = m.content.Length > 1500 ? m.content.Substring(0, 1500) + " ..." : m.content;
|
||||
text.AppendLine("--- " + msg);
|
||||
}
|
||||
File.WriteAllText(ResultFile, text.ToString());
|
||||
UnityEngine.Debug.Log("[СБОРКА] " + text.ToString().Split('\n')[0] + " -> " + file);
|
||||
if (s.result == BuildResult.Succeeded) EditorUtility.RevealInFinder(file);
|
||||
}
|
||||
|
||||
static string Git(string args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var p = Process.Start(new ProcessStartInfo("git", args)
|
||||
{
|
||||
RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true,
|
||||
WorkingDirectory = Directory.GetCurrentDirectory()
|
||||
});
|
||||
string o = p.StandardOutput.ReadToEnd().Trim();
|
||||
p.WaitForExit(5000);
|
||||
return o;
|
||||
}
|
||||
catch { return ""; }
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 904233d7647ed024d920bdbbd4058aba
|
||||
@@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.Build.Reporting;
|
||||
|
||||
// Перед каждым билдом записывает в Assets/Resources/BuildInfo.txt: ветку, коммит и дату сборки (05.11).
|
||||
// Игра показывает это в углу экрана рядом с версией (App/GameVersionLabel). После билда файл удаляется,
|
||||
// чтобы не попадать в git.
|
||||
public class BuildInfoWriter : IPreprocessBuildWithReport, IPostprocessBuildWithReport
|
||||
{
|
||||
const string Path = "Assets/Resources/BuildInfo.txt";
|
||||
public int callbackOrder => 0;
|
||||
|
||||
public void OnPreprocessBuild(BuildReport report)
|
||||
{
|
||||
string branch = Or(Git("rev-parse --abbrev-ref HEAD")), commit = Or(Git("rev-parse --short HEAD"));
|
||||
bool dirty = Git("status --porcelain --untracked-files=no").Length > 0;
|
||||
string info = $"{branch} {commit}{(dirty ? "+" : "")} • {DateTime.Now:dd.MM.yyyy HH:mm}";
|
||||
Directory.CreateDirectory("Assets/Resources");
|
||||
File.WriteAllText(Path, info);
|
||||
AssetDatabase.ImportAsset(Path);
|
||||
UnityEngine.Debug.Log("[ВЕРСИЯ] Сборка: v" + PlayerSettings.bundleVersion + " • " + info);
|
||||
}
|
||||
|
||||
public void OnPostprocessBuild(BuildReport report)
|
||||
{
|
||||
AssetDatabase.DeleteAsset(Path);
|
||||
}
|
||||
|
||||
static string Git(string args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var p = Process.Start(new ProcessStartInfo("git", args)
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WorkingDirectory = Directory.GetCurrentDirectory()
|
||||
});
|
||||
string output = p.StandardOutput.ReadToEnd().Trim();
|
||||
p.WaitForExit(5000);
|
||||
return output;
|
||||
}
|
||||
catch { return ""; }
|
||||
}
|
||||
|
||||
static string Or(string s) => string.IsNullOrEmpty(s) ? "?" : s;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1218c67fef1faaf448c6623df897ac12
|
||||
@@ -1,119 +0,0 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
// Создаёт сцену 3D-поселения (прототип, этап I ТЗ «Система строительства поселения» от 04.11.2026):
|
||||
// Tools -> Bogatyri -> 3D-город: создать сцену City3D.
|
||||
//
|
||||
// Что делает:
|
||||
// 1. Добавляет в настройки URP второй рендерер -- обычный 3D (Assets/Settings/Renderer3D.asset).
|
||||
// Старые 2D-сцены продолжают рисоваться 2D-рендерером: он остаётся рендерером по умолчанию.
|
||||
// 2. Создаёт Assets/Scenes/City3D.unity: камера (3D-рендерер, перспектива), солнце с тенями,
|
||||
// объект City3D с City3DController -- всё остальное (земля, деревья, здания) он строит сам при запуске.
|
||||
// 3. Добавляет сцену в Build Settings и открывает её. Дальше -- просто Play.
|
||||
public static class City3DSetup
|
||||
{
|
||||
const string ScenePath = "Assets/Scenes/City3D.unity";
|
||||
const string RendererPath = "Assets/Settings/Renderer3D.asset";
|
||||
|
||||
[MenuItem("Tools/Bogatyri/3D-город: создать сцену City3D")]
|
||||
static void Create()
|
||||
{
|
||||
if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) return;
|
||||
|
||||
int rendererIndex = EnsureRenderer3D();
|
||||
|
||||
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||
|
||||
var camGo = new GameObject("Main Camera") { tag = "MainCamera" };
|
||||
var cam = camGo.AddComponent<Camera>();
|
||||
cam.orthographic = false;
|
||||
cam.fieldOfView = 40f;
|
||||
cam.nearClipPlane = 0.3f;
|
||||
cam.farClipPlane = 300f;
|
||||
cam.clearFlags = CameraClearFlags.SolidColor;
|
||||
cam.backgroundColor = new Color(0.55f, 0.72f, 0.9f);
|
||||
camGo.AddComponent<AudioListener>();
|
||||
var data = camGo.AddComponent<UniversalAdditionalCameraData>();
|
||||
if (rendererIndex >= 0) data.SetRenderer(rendererIndex);
|
||||
camGo.AddComponent<City3DCamera>();
|
||||
|
||||
var sunGo = new GameObject("Sun");
|
||||
var sun = sunGo.AddComponent<Light>();
|
||||
sun.type = LightType.Directional;
|
||||
sun.intensity = 1.2f;
|
||||
sun.shadows = LightShadows.Soft;
|
||||
sunGo.transform.rotation = Quaternion.Euler(50f, -30f, 0f);
|
||||
RenderSettings.ambientMode = AmbientMode.Flat;
|
||||
RenderSettings.ambientLight = new Color(0.55f, 0.58f, 0.62f);
|
||||
|
||||
new GameObject("City3D").AddComponent<City3DController>();
|
||||
|
||||
EditorSceneManager.SaveScene(scene, ScenePath);
|
||||
AddToBuildSettings();
|
||||
Debug.Log($"[3D-ГОРОД] Сцена создана: {ScenePath}. 3D-рендерер: индекс {rendererIndex}. Нажмите Play.");
|
||||
}
|
||||
|
||||
// Второй рендерер URP -- обычный (3D). Добавляется в URP Asset из Graphics Settings и из КАЖДОГО уровня качества
|
||||
// (в этом проекте URP задан только в Quality). Возвращает индекс рендерера в списке (или -1)
|
||||
[MenuItem("Tools/Bogatyri/3D: добавить 3D-рендерер в URP")]
|
||||
static void AddRendererMenu()
|
||||
{
|
||||
int index = EnsureRenderer3D();
|
||||
Debug.Log(index >= 0 ? $"[3D] 3D-рендерер подключён (индекс {index}). Можно запускать карту мира и City3D."
|
||||
: "[3D] URP Asset не найден ни в Graphics, ни в Quality Settings.");
|
||||
}
|
||||
|
||||
static int EnsureRenderer3D()
|
||||
{
|
||||
var pipelines = new System.Collections.Generic.List<UniversalRenderPipelineAsset>();
|
||||
void Add(RenderPipelineAsset a) { if (a is UniversalRenderPipelineAsset u && !pipelines.Contains(u)) pipelines.Add(u); }
|
||||
Add(GraphicsSettings.defaultRenderPipeline);
|
||||
for (int q = 0; q < QualitySettings.names.Length; q++) Add(QualitySettings.GetRenderPipelineAssetAt(q));
|
||||
if (pipelines.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("[3D-ГОРОД] Нет URP Asset ни в Graphics, ни в Quality Settings -- камера будет рисовать рендерером по умолчанию");
|
||||
return -1;
|
||||
}
|
||||
|
||||
var renderer = AssetDatabase.LoadAssetAtPath<UniversalRendererData>(RendererPath);
|
||||
if (renderer == null)
|
||||
{
|
||||
renderer = ScriptableObject.CreateInstance<UniversalRendererData>();
|
||||
AssetDatabase.CreateAsset(renderer, RendererPath);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
int result = -1;
|
||||
foreach (var pipeline in pipelines)
|
||||
{
|
||||
var so = new SerializedObject(pipeline);
|
||||
var list = so.FindProperty("m_RendererDataList");
|
||||
int found = -1;
|
||||
for (int i = 0; i < list.arraySize; i++)
|
||||
if (list.GetArrayElementAtIndex(i).objectReferenceValue == renderer) found = i;
|
||||
if (found < 0)
|
||||
{
|
||||
list.arraySize++;
|
||||
list.GetArrayElementAtIndex(list.arraySize - 1).objectReferenceValue = renderer;
|
||||
so.ApplyModifiedProperties();
|
||||
EditorUtility.SetDirty(pipeline);
|
||||
found = list.arraySize - 1;
|
||||
}
|
||||
if (result < 0) result = found;
|
||||
}
|
||||
AssetDatabase.SaveAssets();
|
||||
return result;
|
||||
}
|
||||
|
||||
static void AddToBuildSettings()
|
||||
{
|
||||
var scenes = new System.Collections.Generic.List<EditorBuildSettingsScene>(EditorBuildSettings.scenes);
|
||||
foreach (var s in scenes) if (s.path == ScenePath) return;
|
||||
scenes.Add(new EditorBuildSettingsScene(ScenePath, true));
|
||||
EditorBuildSettings.scenes = scenes.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37d458150cc84d538a23b4c6612995c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,250 +0,0 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Events;
|
||||
using UnityEditor.SceneManagement;
|
||||
using TMPro;
|
||||
|
||||
// Кладётся в Assets/Editor/.
|
||||
// Tools -> Build HUD Panels:
|
||||
// 1) находит HUD_Canvas,
|
||||
// 2) создаёт для каждой кнопки свою панель (заголовок + крестик + область Content),
|
||||
// 3) вешает на панель её скрипт (QuestsPanel, ShopPanel...) -- он сам рисует содержимое в игре,
|
||||
// 4) вешает UIPanelManager и подключает onClick ВСЕХ кнопок по их именам,
|
||||
// 5) добавляет красные значки (HUDBadge) на кнопки, где бывает "что забрать".
|
||||
// Можно запускать повторно -- старые панели пересоздаются.
|
||||
public class HUDPanelsBuilder
|
||||
{
|
||||
class Entry
|
||||
{
|
||||
public string button, id, title; public Type panel; public bool badge;
|
||||
public Entry(string button, string id, string title, Type panel, bool badge)
|
||||
{ this.button = button; this.id = id; this.title = title; this.panel = panel; this.badge = badge; }
|
||||
}
|
||||
|
||||
// имя кнопки в иерархии -> id панели, заголовок, скрипт панели, нужен ли значок
|
||||
static readonly Entry[] Map =
|
||||
{
|
||||
new Entry("RightBtn_Quests", "Quests", "ЗАДАНИЯ", typeof(QuestsPanel), true),
|
||||
new Entry("RightBtn_Events", "Events", "СОБЫТИЯ", typeof(EventsPanel), true),
|
||||
new Entry("RightBtn_Rewards", "Rewards", "НАГРАДЫ", typeof(RewardsPanel), true),
|
||||
new Entry("NavBtn_Mail", "Mail", "ПОЧТА", typeof(MailPanel), true),
|
||||
new Entry("SettingsButton", "Settings", "НАСТРОЙКИ", typeof(SettingsPanel), false),
|
||||
new Entry("PlayerAvatar", "Profile", "ПРОФИЛЬ", typeof(ProfilePanel), false),
|
||||
new Entry("Nav_Hero", "Hero", "ГЕРОЙ", typeof(HeroPanel), true),
|
||||
new Entry("Nav_Battle", "Battle", "БОЙ", typeof(BattlePanel), false),
|
||||
new Entry("Nav_Arena", "Arena", "АРЕНА", typeof(ArenaPanel), true),
|
||||
new Entry("Nav_Troops", "Troops", "ВОЙСКА", typeof(TroopsPanel), false),
|
||||
new Entry("Nav_Shop", "Shop", "МАГАЗИН", typeof(ShopPanel), true),
|
||||
new Entry("Nav_World", "World", null, null, false), // кнопка "МИР" -> сцена большой карты (UIPanelManager.Open)
|
||||
new Entry("Nav_Home", "Home", null, null, false), // без панели: закрывает всё
|
||||
};
|
||||
|
||||
[MenuItem("Tools/Build HUD Panels")]
|
||||
public static void Build()
|
||||
{
|
||||
GameObject canvasGO = GameObject.Find("HUD_Canvas");
|
||||
if (canvasGO == null) { Debug.LogError("Не найден объект HUD_Canvas на сцене."); return; }
|
||||
|
||||
|
||||
|
||||
|
||||
// менеджер
|
||||
UIPanelManager manager = canvasGO.GetComponent<UIPanelManager>();
|
||||
if (manager == null) manager = canvasGO.AddComponent<UIPanelManager>();
|
||||
manager.panels.Clear();
|
||||
|
||||
// корень для панелей (пересоздаём)
|
||||
Transform old = canvasGO.transform.Find("Panels");
|
||||
if (old != null) UnityEngine.Object.DestroyImmediate(old.gameObject);
|
||||
GameObject root = new GameObject("Panels", typeof(RectTransform));
|
||||
root.transform.SetParent(canvasGO.transform, false);
|
||||
Stretch(root.GetComponent<RectTransform>(), Vector4.zero);
|
||||
|
||||
// Корень сам не блокирует клики (нет Image), поэтому нижняя/верхняя панели HUD остаются живыми.
|
||||
int wired = 0;
|
||||
string missing = "";
|
||||
foreach (var m in Map)
|
||||
{
|
||||
if (m.title != null)
|
||||
{
|
||||
// карте мира нужно больше места -- её окно почти на весь экран между верхней и нижней панелями
|
||||
Vector4 offsets = m.id == "World" ? new Vector4(16, 145, 16, 195) : new Vector4(30, 250, 30, 230);
|
||||
GameObject panel = CreatePanel(root.transform, m.id, m.title, manager, offsets);
|
||||
if (m.panel != null) panel.AddComponent(m.panel);
|
||||
manager.panels.Add(new UIPanelManager.PanelEntry { id = m.id, panel = panel });
|
||||
}
|
||||
|
||||
Button btn = FindButton(canvasGO.transform, m.button);
|
||||
if (btn == null && m.id == "World") btn = CreateWorldButton(canvasGO.transform, root.transform);
|
||||
if (btn == null) { Debug.LogWarning("Кнопка не найдена: " + m.button); missing += " " + m.button; continue; }
|
||||
|
||||
// убираем старые подписки из Inspector и вешаем свою
|
||||
for (int i = btn.onClick.GetPersistentEventCount() - 1; i >= 0; i--)
|
||||
UnityEventTools.RemovePersistentListener(btn.onClick, i);
|
||||
UnityEventTools.AddStringPersistentListener(btn.onClick, manager.Open, m.id);
|
||||
|
||||
// красный значок "есть что забрать"
|
||||
HUDBadge badge = btn.GetComponent<HUDBadge>();
|
||||
if (m.badge)
|
||||
{
|
||||
if (badge == null) badge = btn.gameObject.AddComponent<HUDBadge>();
|
||||
badge.panelId = m.id;
|
||||
}
|
||||
else if (badge != null) UnityEngine.Object.DestroyImmediate(badge);
|
||||
|
||||
wired++;
|
||||
}
|
||||
|
||||
// Кнопка "СТРОЙКА": включает/выключает режим стройки (тап по участку -> выбор здания).
|
||||
// Старое меню стройки прячем: при запуске игры оно больше не открывается.
|
||||
GameObject buildMenu = null;
|
||||
foreach (var t in canvasGO.GetComponentsInChildren<Transform>(true))
|
||||
if (t.name == "BuildMenuPanel") { buildMenu = t.gameObject; break; }
|
||||
if (buildMenu != null) buildMenu.SetActive(false);
|
||||
|
||||
BuildModeController buildMode = canvasGO.GetComponent<BuildModeController>();
|
||||
if (buildMode == null) buildMode = canvasGO.AddComponent<BuildModeController>();
|
||||
Button buildBtn = FindButton(canvasGO.transform, "Nav_Build");
|
||||
if (buildBtn == null)
|
||||
buildBtn = CreateSideButton(canvasGO.transform, root.transform, "Nav_Build", "СТРОЙКА",
|
||||
"Assets/UI/HUD v2/Рюкзак/v1.png", new Vector2(30, 400), new Color(0.55f, 0.4f, 0.2f));
|
||||
for (int i = buildBtn.onClick.GetPersistentEventCount() - 1; i >= 0; i--)
|
||||
UnityEventTools.RemovePersistentListener(buildBtn.onClick, i);
|
||||
UnityEventTools.AddVoidPersistentListener(buildBtn.onClick, buildMode.ToggleMode);
|
||||
buildMode.modeButton = buildBtn;
|
||||
buildMode.legacyBuildMenu = buildMenu;
|
||||
EditorUtility.SetDirty(buildMode);
|
||||
|
||||
// Меню стройки растянуто на весь экран и закрывало все кнопки HUD (ловило клики и белило экран).
|
||||
// Делаем его фон прозрачным и "сквозным" -- кнопки зданий внутри продолжают работать.
|
||||
foreach (string see in new[] { "BuildMenuPanel", "ButtonContainer" })
|
||||
{
|
||||
foreach (var t in canvasGO.GetComponentsInChildren<Transform>(true))
|
||||
{
|
||||
if (t.name != see) continue;
|
||||
var img = t.GetComponent<Image>();
|
||||
if (img == null) continue;
|
||||
var c = img.color; c.a = 0f; img.color = c;
|
||||
img.raycastTarget = false;
|
||||
EditorUtility.SetDirty(img);
|
||||
}
|
||||
}
|
||||
|
||||
EditorUtility.SetDirty(manager);
|
||||
EditorSceneManager.MarkSceneDirty(canvasGO.scene);
|
||||
Debug.Log("Готово: панелей " + manager.panels.Count + ", подключено кнопок " + wired + " из " + Map.Length +
|
||||
(missing.Length > 0 ? ". Не найдены:" + missing : "") + ". Не забудь сохранить сцену (Ctrl+S).");
|
||||
}
|
||||
|
||||
// «Tools/Create Barracks» (префаб и данные Казарм 2D-города) удалён 06.11 вместе с 2D-городом: Казармы -- здание 3D-поселения
|
||||
|
||||
static Sprite LoadSprite(string path)
|
||||
{
|
||||
foreach (var a in AssetDatabase.LoadAllAssetsAtPath(path))
|
||||
if (a is Sprite) return (Sprite)a;
|
||||
return null;
|
||||
}
|
||||
|
||||
// ищем объект по имени среди всех детей (включая выключенные); Button может быть на нём или внутри
|
||||
static Button FindButton(Transform root, string name)
|
||||
{
|
||||
foreach (var t in root.GetComponentsInChildren<Transform>(true))
|
||||
{
|
||||
if (t.name != name) continue;
|
||||
Button b = t.GetComponent<Button>();
|
||||
if (b == null) b = t.GetComponentInChildren<Button>(true);
|
||||
if (b == null) b = t.gameObject.AddComponent<Button>(); // на случай если там просто Image
|
||||
return b;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Кнопка "МИР" слева над нижней панелью (если дизайнер ещё не сделал свою с именем Nav_World)
|
||||
static Button CreateWorldButton(Transform canvas, Transform panelsRoot)
|
||||
{
|
||||
return CreateSideButton(canvas, panelsRoot, "Nav_World", "МИР", "Assets/UI/HUD v2/Мир/v1.png",
|
||||
new Vector2(30, 210), new Color(0.2f, 0.45f, 0.3f));
|
||||
}
|
||||
|
||||
static Button CreateSideButton(Transform canvas, Transform panelsRoot, string objName, string label, string spritePath, Vector2 pos, Color fallback)
|
||||
{
|
||||
GameObject go = new GameObject(objName, typeof(RectTransform), typeof(Image), typeof(Button));
|
||||
go.transform.SetParent(canvas, false);
|
||||
go.transform.SetSiblingIndex(panelsRoot.GetSiblingIndex()); // под панелями, чтобы открытая панель её закрывала
|
||||
RectTransform rt = go.GetComponent<RectTransform>();
|
||||
rt.anchorMin = rt.anchorMax = rt.pivot = new Vector2(0, 0);
|
||||
rt.sizeDelta = new Vector2(150, 150);
|
||||
rt.anchoredPosition = pos;
|
||||
|
||||
var img = go.GetComponent<Image>();
|
||||
Sprite sprite = null;
|
||||
foreach (var a in AssetDatabase.LoadAllAssetsAtPath(spritePath))
|
||||
if (a is Sprite) { sprite = (Sprite)a; break; }
|
||||
if (sprite != null) { img.sprite = sprite; img.preserveAspect = true; }
|
||||
else img.color = fallback;
|
||||
|
||||
GameObject t = new GameObject("Label", typeof(RectTransform), typeof(TextMeshProUGUI));
|
||||
t.transform.SetParent(go.transform, false);
|
||||
var tmp = t.GetComponent<TextMeshProUGUI>();
|
||||
tmp.text = label; tmp.fontSize = 30; tmp.fontStyle = FontStyles.Bold;
|
||||
tmp.alignment = TextAlignmentOptions.Center; tmp.color = Color.white; tmp.raycastTarget = false;
|
||||
tmp.outlineWidth = 0.25f; tmp.outlineColor = Color.black;
|
||||
RectTransform tr = t.GetComponent<RectTransform>();
|
||||
tr.anchorMin = new Vector2(0, 0); tr.anchorMax = new Vector2(1, 0); tr.pivot = new Vector2(0.5f, 1);
|
||||
tr.sizeDelta = new Vector2(0, 40); tr.anchoredPosition = new Vector2(0, 8);
|
||||
return go.GetComponent<Button>();
|
||||
}
|
||||
|
||||
static GameObject CreatePanel(Transform parent, string id, string title, UIPanelManager manager, Vector4 offsets)
|
||||
{
|
||||
// окно оставляет сверху и снизу место под TopPanel и BottomNavBar, чтобы они были кликабельны
|
||||
GameObject go = new GameObject("Panel_" + id, typeof(RectTransform), typeof(Image));
|
||||
go.transform.SetParent(parent, false);
|
||||
go.GetComponent<Image>().color = new Color(0.1f, 0.12f, 0.15f, 0.97f);
|
||||
Stretch(go.GetComponent<RectTransform>(), offsets); // left, top, right, bottom
|
||||
|
||||
// заголовок
|
||||
GameObject t = new GameObject("Title", typeof(RectTransform), typeof(TextMeshProUGUI));
|
||||
t.transform.SetParent(go.transform, false);
|
||||
var tmp = t.GetComponent<TextMeshProUGUI>();
|
||||
tmp.text = title; tmp.fontSize = 52; tmp.fontStyle = FontStyles.Bold;
|
||||
tmp.alignment = TextAlignmentOptions.Center; tmp.color = new Color(0.95f, 0.75f, 0.25f);
|
||||
tmp.raycastTarget = false;
|
||||
RectTransform tr = t.GetComponent<RectTransform>();
|
||||
tr.anchorMin = new Vector2(0, 1); tr.anchorMax = new Vector2(1, 1); tr.pivot = new Vector2(0.5f, 1);
|
||||
tr.sizeDelta = new Vector2(0, 110); tr.anchoredPosition = Vector2.zero;
|
||||
|
||||
// область содержимого: сюда скрипт панели рисует списки, кнопки и т.д.
|
||||
GameObject content = new GameObject("Content", typeof(RectTransform));
|
||||
content.transform.SetParent(go.transform, false);
|
||||
Stretch(content.GetComponent<RectTransform>(), new Vector4(20, 120, 20, 20));
|
||||
|
||||
// крестик
|
||||
GameObject c = new GameObject("CloseButton", typeof(RectTransform), typeof(Image), typeof(Button));
|
||||
c.transform.SetParent(go.transform, false);
|
||||
c.GetComponent<Image>().color = new Color(0.6f, 0.15f, 0.15f);
|
||||
RectTransform cr = c.GetComponent<RectTransform>();
|
||||
cr.anchorMin = cr.anchorMax = cr.pivot = new Vector2(1, 1);
|
||||
cr.sizeDelta = new Vector2(90, 90); cr.anchoredPosition = new Vector2(-12, -10);
|
||||
GameObject x = new GameObject("X", typeof(RectTransform), typeof(TextMeshProUGUI));
|
||||
x.transform.SetParent(c.transform, false);
|
||||
var xt = x.GetComponent<TextMeshProUGUI>();
|
||||
xt.text = "X"; xt.fontSize = 44; xt.alignment = TextAlignmentOptions.Center; xt.color = Color.white;
|
||||
xt.raycastTarget = false;
|
||||
Stretch(x.GetComponent<RectTransform>(), Vector4.zero);
|
||||
UnityEventTools.AddVoidPersistentListener(c.GetComponent<Button>().onClick, manager.CloseAll);
|
||||
|
||||
go.SetActive(false);
|
||||
return go;
|
||||
}
|
||||
|
||||
// left, top, right, bottom -- отступы от краёв родителя
|
||||
static void Stretch(RectTransform rt, Vector4 ltrb)
|
||||
{
|
||||
rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one;
|
||||
rt.offsetMin = new Vector2(ltrb.x, ltrb.w);
|
||||
rt.offsetMax = new Vector2(-ltrb.z, -ltrb.y);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26bcfec97167cdb4b9dee113e5f85abc
|
||||
@@ -1,43 +0,0 @@
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
// Tools -> Сбросить сохранения. Для разработчиков: новая игра одной кнопкой.
|
||||
// - в Play Mode -- тот же сброс, что кнопка в Настройках (все модули + прогресс);
|
||||
// - вне Play Mode -- просто удаляет файлы save.json и progress.json.
|
||||
// Документ: Docs/Modules/Save.md
|
||||
public static class SaveResetMenu
|
||||
{
|
||||
static readonly string[] SaveFiles = { "save.json", "progress.json" };
|
||||
|
||||
[MenuItem("Tools/Сбросить сохранения")]
|
||||
static void ResetSaves()
|
||||
{
|
||||
if (!EditorUtility.DisplayDialog("Сбросить сохранения?",
|
||||
"Ресурсы, здания, Ратуша, войска, походы, задания и почта начнутся заново.\nЭто нельзя отменить.",
|
||||
"Сбросить", "Отмена"))
|
||||
return;
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
GameProgress.Instance.ResetAll();
|
||||
return;
|
||||
}
|
||||
|
||||
int deleted = 0;
|
||||
foreach (var name in SaveFiles)
|
||||
{
|
||||
string path = Path.Combine(Application.persistentDataPath, name);
|
||||
if (!File.Exists(path)) continue;
|
||||
File.Delete(path);
|
||||
deleted++;
|
||||
}
|
||||
Debug.Log($"[СОХРАНЕНИЕ] Удалено файлов сохранения: {deleted} ({Application.persistentDataPath}). Следующий запуск -- новая игра.");
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Открыть папку сохранений")]
|
||||
static void OpenSaveFolder()
|
||||
{
|
||||
EditorUtility.RevealInFinder(Application.persistentDataPath);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 972f2f5a1fa0c38439b03355997f98e9
|
||||
@@ -1,37 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
|
||||
// Следит, чтобы сцена карты мира (WorldMap, от Саши) была в Build Settings --
|
||||
// иначе переход город -> карта не работает. Срабатывает сам после каждой компиляции.
|
||||
// Стартовая сцена -- 3D-поселение City3D (2D-город MVP1 удалён 06.11).
|
||||
[InitializeOnLoad]
|
||||
public static class SceneListFixer
|
||||
{
|
||||
const string WorldScene = "Assets/Scenes/WorldMap.unity";
|
||||
const string City3DScene = "Assets/Scenes/City3D.unity";
|
||||
|
||||
static SceneListFixer() { EditorApplication.delayCall += Ensure; }
|
||||
|
||||
static void Ensure()
|
||||
{
|
||||
var scenes = new List<EditorBuildSettingsScene>(EditorBuildSettings.scenes);
|
||||
bool changed = false;
|
||||
string start = City3DScene;
|
||||
var wanted = new[] { start, WorldScene };
|
||||
foreach (var path in wanted)
|
||||
{
|
||||
if (AssetDatabase.LoadAssetAtPath<SceneAsset>(path) == null) continue;
|
||||
if (scenes.Exists(s => s.path == path)) continue;
|
||||
scenes.Add(new EditorBuildSettingsScene(path, true));
|
||||
changed = true;
|
||||
}
|
||||
// стартовый город должен быть первым (с него стартует игра)
|
||||
int city = scenes.FindIndex(s => s.path == start);
|
||||
if (city > 0) { var c = scenes[city]; scenes.RemoveAt(city); scenes.Insert(0, c); changed = true; }
|
||||
if (changed)
|
||||
{
|
||||
EditorBuildSettings.scenes = scenes.ToArray();
|
||||
UnityEngine.Debug.Log("[СЦЕНЫ] Build Settings: город + карта мира добавлены");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89a48f1e99f1cd4469929f3c418ce55b
|
||||
@@ -1,526 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using TMPro;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
// Tools -> Build World Map Scene
|
||||
// 1. Создаёт сцену Assets/Scenes/WorldMap.unity: камера, EventSystem, UI карты, WorldMapController.
|
||||
// 2. Ставит город (City3D) и WorldMap в Build Settings (иначе SceneManager.LoadScene их не найдёт).
|
||||
// 3. Проверяет, что на кнопке Nav_Battle в городе висит OpenBattleButton.
|
||||
// Можно запускать повторно -- сцена карты пересоздаётся заново.
|
||||
public static class WorldMapSceneBuilder
|
||||
{
|
||||
const string WorldMapScenePath = "Assets/Scenes/WorldMap.unity";
|
||||
const string CityScenePath = "Assets/Scenes/City3D.unity"; // город -- 3D-поселение (MVP1 удалён 06.11)
|
||||
|
||||
[MenuItem("Tools/Build World Map Scene")]
|
||||
public static void Build()
|
||||
{
|
||||
if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) return;
|
||||
|
||||
CreateWorldMapScene();
|
||||
UpdateBuildSettings();
|
||||
HookUpCityBattleButton();
|
||||
|
||||
Debug.Log("[КАРТА] Сцена карты мира создана, кнопка БОЙ в городе подключена. Запускай City3D и жми БОЙ.");
|
||||
}
|
||||
|
||||
// ---------------- СЦЕНА КАРТЫ ----------------
|
||||
|
||||
static void CreateWorldMapScene()
|
||||
{
|
||||
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||
|
||||
var camGO = new GameObject("Main Camera", typeof(Camera), typeof(MapCameraController));
|
||||
camGO.tag = "MainCamera";
|
||||
camGO.transform.position = new Vector3(0, 0, -10);
|
||||
Camera cam = camGO.GetComponent<Camera>();
|
||||
cam.orthographic = true;
|
||||
cam.orthographicSize = 8f;
|
||||
cam.clearFlags = CameraClearFlags.SolidColor;
|
||||
cam.backgroundColor = new Color(0.12f, 0.16f, 0.12f);
|
||||
|
||||
new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule));
|
||||
|
||||
var canvasGO = new GameObject("WorldMap_Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
||||
canvasGO.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
CanvasScaler scaler = canvasGO.GetComponent<CanvasScaler>();
|
||||
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||||
scaler.referenceResolution = new Vector2(1920, 1080);
|
||||
scaler.matchWidthOrHeight = 0.5f;
|
||||
RectTransform canvasRect = canvasGO.GetComponent<RectTransform>();
|
||||
|
||||
var controller = new GameObject("WorldMapController").AddComponent<WorldMapController>();
|
||||
controller.mapCamera = camGO.GetComponent<MapCameraController>();
|
||||
|
||||
// ---- Верхняя панель: назад | войско | мой город ----
|
||||
RectTransform topBar = CreatePanel(canvasRect, "TopBar", new Color(0.08f, 0.08f, 0.1f, 0.85f));
|
||||
AnchorStretch(topBar, top: true, height: 110);
|
||||
|
||||
controller.soldiersText = CreateText(topBar, "SoldiersText", "Войска дома: 30", 40);
|
||||
|
||||
RectTransform back = CreateButton(topBar, "BackButton", "НАЗАД", new Color(0.3f, 0.3f, 0.33f), out _);
|
||||
AnchorSide(back, left: true, new Vector2(220, 80), 20);
|
||||
controller.backButton = back.GetComponent<Button>();
|
||||
|
||||
RectTransform myCity = CreateButton(topBar, "MyCityButton", "МОЙ ГОРОД", new Color(0.25f, 0.45f, 0.8f), out _);
|
||||
AnchorSide(myCity, left: false, new Vector2(280, 80), 20);
|
||||
controller.myCityButton = myCity.GetComponent<Button>();
|
||||
|
||||
// ---- Подсказка (над нижней панелью, правее чата) ----
|
||||
RectTransform hintBar = CreatePanel(canvasRect, "HintBar", new Color(0.08f, 0.08f, 0.1f, 0.75f));
|
||||
SetRect(hintBar, new Vector2(0, 0), new Vector2(1, 0), new Vector2(0.5f, 0), Vector2.zero, Vector2.zero);
|
||||
hintBar.offsetMin = new Vector2(ChatWidth + 24, 172);
|
||||
hintBar.offsetMax = new Vector2(-12, 222);
|
||||
controller.hintText = CreateText(hintBar, "Hint", "", 28);
|
||||
|
||||
// ---- Чат слева внизу ----
|
||||
BuildChat(canvasRect);
|
||||
|
||||
// ---- Нижняя панель: Карта мира | События | Круг | Поселение ----
|
||||
BuildNavBar(canvasRect, controller);
|
||||
|
||||
// ---- Кнопка "Рейтинг" слева ----
|
||||
RectTransform ratingBtn = CreateButton(canvasRect, "RatingButton", "Рейтинг", new Color(0.12f, 0.2f, 0.4f, 0.95f), out _);
|
||||
SetRect(ratingBtn, new Vector2(0, 1), new Vector2(0, 1), new Vector2(0, 1), new Vector2(12, -122), new Vector2(210, 110));
|
||||
controller.ratingButton = ratingBtn.GetComponent<Button>();
|
||||
|
||||
// ---- Панель рейтинга (выше всего, кроме окна атаки и уведомлений) ----
|
||||
controller.ratingPanel = BuildRatingPanel(canvasRect);
|
||||
|
||||
// ---- Окно атаки ----
|
||||
RectTransform attack = CreateCenterPanel(canvasRect, "AttackPanel", new Vector2(900, 640));
|
||||
controller.attackPanel = attack.gameObject;
|
||||
|
||||
RectTransform attackTitle = CreateTextRect(attack, "Title", "Замок", 56);
|
||||
AnchorTop(attackTitle, 110, 20);
|
||||
controller.attackTitleText = attackTitle.GetComponent<TMP_Text>();
|
||||
|
||||
RectTransform attackInfo = CreateTextRect(attack, "Info", "", 32);
|
||||
AnchorTop(attackInfo, 170, 130);
|
||||
controller.attackInfoText = attackInfo.GetComponent<TMP_Text>();
|
||||
|
||||
RectTransform sendCount = CreateTextRect(attack, "SoldiersToSend", "Отправить: 30 из 30", 36);
|
||||
AnchorTop(sendCount, 60, 310);
|
||||
controller.soldiersToSendText = sendCount.GetComponent<TMP_Text>();
|
||||
|
||||
GameObject sliderGO = DefaultControls.CreateSlider(new DefaultControls.Resources());
|
||||
sliderGO.name = "SoldiersSlider";
|
||||
RectTransform sliderRect = sliderGO.GetComponent<RectTransform>();
|
||||
sliderRect.SetParent(attack, false);
|
||||
AnchorTop(sliderRect, 50, 390);
|
||||
sliderRect.offsetMin = new Vector2(80, sliderRect.offsetMin.y);
|
||||
sliderRect.offsetMax = new Vector2(-80, sliderRect.offsetMax.y);
|
||||
StyleSlider(sliderGO);
|
||||
controller.soldiersSlider = sliderGO.GetComponent<Slider>();
|
||||
|
||||
RectTransform send = CreateButton(attack, "SendButton", "В ПОХОД", new Color(0.75f, 0.25f, 0.15f), out _);
|
||||
AnchorBottom(send, new Vector2(320, 100), new Vector2(170, 30));
|
||||
controller.sendButton = send.GetComponent<Button>();
|
||||
|
||||
RectTransform cancel = CreateButton(attack, "CancelButton", "ОТМЕНА", new Color(0.3f, 0.3f, 0.33f), out _);
|
||||
AnchorBottom(cancel, new Vector2(320, 100), new Vector2(-170, 30));
|
||||
controller.cancelButton = cancel.GetComponent<Button>();
|
||||
|
||||
attack.gameObject.SetActive(false);
|
||||
|
||||
// ---- Уведомление ----
|
||||
RectTransform popup = CreateCenterPanel(canvasRect, "PopupPanel", new Vector2(860, 520));
|
||||
controller.popupPanel = popup.gameObject;
|
||||
|
||||
RectTransform popupTitle = CreateTextRect(popup, "Title", "ПОБЕДА", 72);
|
||||
AnchorTop(popupTitle, 120, 20);
|
||||
controller.popupTitleText = popupTitle.GetComponent<TMP_Text>();
|
||||
|
||||
RectTransform popupBody = CreateTextRect(popup, "Body", "", 32);
|
||||
popupBody.offsetMin = new Vector2(40, 140);
|
||||
popupBody.offsetMax = new Vector2(-40, -140);
|
||||
controller.popupBodyText = popupBody.GetComponent<TMP_Text>();
|
||||
|
||||
RectTransform ok = CreateButton(popup, "OkButton", "OK", new Color(0.2f, 0.5f, 0.25f), out _);
|
||||
AnchorBottom(ok, new Vector2(300, 100), new Vector2(0, 30));
|
||||
controller.popupOkButton = ok.GetComponent<Button>();
|
||||
|
||||
popup.gameObject.SetActive(false);
|
||||
|
||||
EditorSceneManager.SaveScene(scene, WorldMapScenePath);
|
||||
}
|
||||
|
||||
// ---------------- ЧАТ ----------------
|
||||
|
||||
const float ChatWidth = 620f;
|
||||
|
||||
static void BuildChat(RectTransform canvasRect)
|
||||
{
|
||||
RectTransform panel = CreatePanel(canvasRect, "ChatPanel", new Color(0.05f, 0.06f, 0.09f, 0.9f));
|
||||
SetRect(panel, Vector2.zero, Vector2.zero, Vector2.zero, new Vector2(12, 12), new Vector2(ChatWidth, 350));
|
||||
ChatPanel chat = panel.gameObject.AddComponent<ChatPanel>();
|
||||
|
||||
RectTransform worldTab = CreateButton(panel, "WorldTab", "Общий мир", new Color(0.16f, 0.35f, 0.7f), out _);
|
||||
SetRect(worldTab, new Vector2(0, 1), new Vector2(0, 1), new Vector2(0, 1), new Vector2(10, -10), new Vector2(220, 54));
|
||||
chat.worldTabButton = worldTab.GetComponent<Button>();
|
||||
|
||||
RectTransform circleTab = CreateButton(panel, "CircleTab", "Круг", new Color(0.12f, 0.13f, 0.17f), out _);
|
||||
SetRect(circleTab, new Vector2(0, 1), new Vector2(0, 1), new Vector2(0, 1), new Vector2(240, -10), new Vector2(220, 54));
|
||||
chat.circleTabButton = circleTab.GetComponent<Button>();
|
||||
|
||||
RectTransform expand = CreateButton(panel, "ExpandButton", "Развернуть", new Color(0.12f, 0.13f, 0.17f), out TMP_Text expandText);
|
||||
SetRect(expand, new Vector2(1, 1), new Vector2(1, 1), new Vector2(1, 1), new Vector2(-10, -10), new Vector2(150, 54));
|
||||
expandText.fontSize = 24;
|
||||
chat.expandButton = expand.GetComponent<Button>();
|
||||
|
||||
// Лента: текст прижат к низу, старые строки уходят вверх и обрезаются маской
|
||||
var messagesGO = new GameObject("Messages", typeof(RectTransform), typeof(RectMask2D));
|
||||
RectTransform messages = messagesGO.GetComponent<RectTransform>();
|
||||
messages.SetParent(panel, false);
|
||||
messages.anchorMin = Vector2.zero;
|
||||
messages.anchorMax = Vector2.one;
|
||||
messages.offsetMin = new Vector2(14, 86);
|
||||
messages.offsetMax = new Vector2(-14, -74);
|
||||
|
||||
RectTransform messagesText = CreateTextRect(messages, "MessagesText", "", 24, TextAlignmentOptions.BottomLeft);
|
||||
TMP_Text tmp = messagesText.GetComponent<TMP_Text>();
|
||||
tmp.overflowMode = TextOverflowModes.Overflow;
|
||||
chat.messagesText = tmp;
|
||||
|
||||
// Поле ввода
|
||||
GameObject inputGO = TMP_DefaultControls.CreateInputField(new TMP_DefaultControls.Resources());
|
||||
inputGO.name = "InputField";
|
||||
RectTransform inputRect = inputGO.GetComponent<RectTransform>();
|
||||
inputRect.SetParent(panel, false);
|
||||
SetRect(inputRect, Vector2.zero, new Vector2(1, 0), new Vector2(0.5f, 0), Vector2.zero, Vector2.zero);
|
||||
inputRect.offsetMin = new Vector2(12, 12);
|
||||
inputRect.offsetMax = new Vector2(-124, 74);
|
||||
inputGO.GetComponent<Image>().color = new Color(0.12f, 0.13f, 0.17f);
|
||||
|
||||
TMP_InputField input = inputGO.GetComponent<TMP_InputField>();
|
||||
input.pointSize = 26;
|
||||
input.textComponent.color = Color.white;
|
||||
((TMP_Text)input.placeholder).text = "Введите сообщение...";
|
||||
((TMP_Text)input.placeholder).color = new Color(1f, 1f, 1f, 0.35f);
|
||||
chat.inputField = input;
|
||||
|
||||
RectTransform send = CreateButton(panel, "SendButton", ">", new Color(0.2f, 0.45f, 0.85f), out _);
|
||||
SetRect(send, new Vector2(1, 0), new Vector2(1, 0), new Vector2(1, 0), new Vector2(-12, 12), new Vector2(100, 62));
|
||||
chat.sendButton = send.GetComponent<Button>();
|
||||
}
|
||||
|
||||
// ---------------- НИЖНЯЯ ПАНЕЛЬ ----------------
|
||||
|
||||
static void BuildNavBar(RectTransform canvasRect, WorldMapController controller)
|
||||
{
|
||||
RectTransform bar = CreatePanel(canvasRect, "BottomNavBar", new Color(0.06f, 0.07f, 0.1f, 0.92f));
|
||||
SetRect(bar, Vector2.zero, new Vector2(1, 0), new Vector2(0.5f, 0), Vector2.zero, Vector2.zero);
|
||||
bar.offsetMin = new Vector2(ChatWidth + 24, 12);
|
||||
bar.offsetMax = new Vector2(-12, 162);
|
||||
|
||||
var layout = bar.gameObject.AddComponent<HorizontalLayoutGroup>();
|
||||
layout.padding = new RectOffset(10, 10, 10, 10);
|
||||
layout.spacing = 10;
|
||||
layout.childForceExpandWidth = true;
|
||||
layout.childForceExpandHeight = true;
|
||||
layout.childControlWidth = true;
|
||||
layout.childControlHeight = true;
|
||||
|
||||
Color navColor = new Color(0.14f, 0.16f, 0.22f);
|
||||
controller.worldMapNavButton = CreateButton(bar, "Nav_WorldMap", "Карта мира", navColor, out _).GetComponent<Button>();
|
||||
controller.eventsNavButton = CreateButton(bar, "Nav_Events", "События", navColor, out _).GetComponent<Button>();
|
||||
controller.circleNavButton = CreateButton(bar, "Nav_Circle", "Круг", navColor, out _).GetComponent<Button>();
|
||||
controller.settlementNavButton = CreateButton(bar, "Nav_Settlement", "Поселение", new Color(0.18f, 0.25f, 0.45f), out _).GetComponent<Button>();
|
||||
}
|
||||
|
||||
// ---------------- РЕЙТИНГ ----------------
|
||||
|
||||
static readonly Color RatingBg = new Color(0.04f, 0.06f, 0.1f, 0.97f);
|
||||
static readonly Color RatingGold = new Color(0.95f, 0.8f, 0.5f);
|
||||
|
||||
const float RatingTableLeft = 340f;
|
||||
const float RatingTableRight = 25f;
|
||||
|
||||
static RatingPanel BuildRatingPanel(RectTransform canvasRect)
|
||||
{
|
||||
RectTransform panel = CreatePanel(canvasRect, "RatingPanel", RatingBg);
|
||||
SetRect(panel, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(1640, 920));
|
||||
RatingPanel rating = panel.gameObject.AddComponent<RatingPanel>();
|
||||
|
||||
// Заголовок-баннер
|
||||
RectTransform banner = CreatePanel(panel, "TitleBanner", new Color(0.12f, 0.25f, 0.55f));
|
||||
SetRect(banner, new Vector2(0.5f, 1), new Vector2(0.5f, 1), new Vector2(0.5f, 1), new Vector2(0, -15), new Vector2(640, 90));
|
||||
CreateText(banner, "Title", "Рейтинг", 60).color = RatingGold;
|
||||
|
||||
RectTransform close = CreateButton(panel, "CloseButton", "X", new Color(0.35f, 0.15f, 0.15f), out _);
|
||||
SetRect(close, new Vector2(1, 1), new Vector2(1, 1), new Vector2(1, 1), new Vector2(-15, -15), new Vector2(80, 80));
|
||||
rating.closeButton = close.GetComponent<Button>();
|
||||
|
||||
// Разделы слева -- порядок совпадает с RatingCategory
|
||||
string[] categories = { "Игроки", "Круги", "Мощь", "PvP", "Развитие", "Сезон" };
|
||||
rating.categoryButtons = new Button[categories.Length];
|
||||
for (int i = 0; i < categories.Length; i++)
|
||||
{
|
||||
RectTransform tab = CreateButton(panel, "Category_" + i, categories[i], new Color(0.1f, 0.12f, 0.17f), out _);
|
||||
SetRect(tab, new Vector2(0, 1), new Vector2(0, 1), new Vector2(0, 1), new Vector2(25, -130 - i * 112), new Vector2(290, 100));
|
||||
rating.categoryButtons[i] = tab.GetComponent<Button>();
|
||||
}
|
||||
|
||||
// Таблица
|
||||
var tableGO = new GameObject("Table", typeof(RectTransform));
|
||||
RectTransform table = tableGO.GetComponent<RectTransform>();
|
||||
table.SetParent(panel, false);
|
||||
table.anchorMin = Vector2.zero;
|
||||
table.anchorMax = Vector2.one;
|
||||
table.offsetMin = new Vector2(RatingTableLeft, 110);
|
||||
table.offsetMax = new Vector2(-RatingTableRight, -130);
|
||||
|
||||
RectTransform header = CreatePanel(table, "Header", new Color(0.08f, 0.1f, 0.15f));
|
||||
SetRect(header, new Vector2(0, 1), new Vector2(1, 1), new Vector2(0.5f, 1), Vector2.zero, new Vector2(0, 50));
|
||||
Color headerColor = new Color(0.8f, 0.82f, 0.88f);
|
||||
PlaceInRow(CreateTextRect(header, "Place", "Место", 26), 0, 140, 50).GetComponent<TMP_Text>().color = headerColor;
|
||||
TMP_Text nameHeader = PlaceInRow(CreateTextRect(header, "Name", "Игрок", 26, TextAlignmentOptions.Left), 225, 330, 50).GetComponent<TMP_Text>();
|
||||
nameHeader.color = headerColor;
|
||||
rating.nameColumnHeader = nameHeader;
|
||||
TMP_Text circleHeader = PlaceInRow(CreateTextRect(header, "Circle", "Круг", 26, TextAlignmentOptions.Left), 625, 320, 50).GetComponent<TMP_Text>();
|
||||
circleHeader.color = headerColor;
|
||||
rating.circleColumnHeader = circleHeader;
|
||||
PlaceInRow(CreateTextRect(header, "Power", "Мощь", 26), 950, 220, 50).GetComponent<TMP_Text>().color = headerColor;
|
||||
|
||||
rating.rows = new RatingRowView[RatingPanel.PageSize];
|
||||
for (int i = 0; i < rating.rows.Length; i++)
|
||||
{
|
||||
rating.rows[i] = BuildRatingRow(table, i);
|
||||
}
|
||||
|
||||
rating.emptyText = CreateText(table, "EmptyText", "Раздел в разработке", 40);
|
||||
rating.emptyText.gameObject.SetActive(false);
|
||||
|
||||
RectTransform status = CreateTextRect(panel, "StatusText", "", 26, TextAlignmentOptions.Left);
|
||||
SetRect(status, Vector2.zero, Vector2.zero, Vector2.zero, new Vector2(RatingTableLeft, 30), new Vector2(520, 60));
|
||||
rating.statusText = status.GetComponent<TMP_Text>();
|
||||
|
||||
// Страницы: < 1 2 3 4 5 > -- по центру под таблицей (а не под всей панелью)
|
||||
float tableCenterX = (RatingTableLeft - RatingTableRight) / 2f;
|
||||
Color pageColor = new Color(0.1f, 0.12f, 0.17f);
|
||||
|
||||
RectTransform prev = CreateButton(panel, "PrevPage", "<", pageColor, out _);
|
||||
SetRect(prev, new Vector2(0.5f, 0), new Vector2(0.5f, 0), new Vector2(0.5f, 0), new Vector2(tableCenterX - 240, 25), new Vector2(64, 64));
|
||||
rating.prevPageButton = prev.GetComponent<Button>();
|
||||
|
||||
rating.pageButtons = new Button[5];
|
||||
for (int i = 0; i < rating.pageButtons.Length; i++)
|
||||
{
|
||||
RectTransform page = CreateButton(panel, "Page_" + i, (i + 1).ToString(), pageColor, out _);
|
||||
SetRect(page, new Vector2(0.5f, 0), new Vector2(0.5f, 0), new Vector2(0.5f, 0), new Vector2(tableCenterX - 150 + i * 75, 25), new Vector2(64, 64));
|
||||
rating.pageButtons[i] = page.GetComponent<Button>();
|
||||
}
|
||||
|
||||
RectTransform next = CreateButton(panel, "NextPage", ">", pageColor, out _);
|
||||
SetRect(next, new Vector2(0.5f, 0), new Vector2(0.5f, 0), new Vector2(0.5f, 0), new Vector2(tableCenterX + 240, 25), new Vector2(64, 64));
|
||||
rating.nextPageButton = next.GetComponent<Button>();
|
||||
|
||||
panel.gameObject.SetActive(false);
|
||||
return rating;
|
||||
}
|
||||
|
||||
static RatingRowView BuildRatingRow(RectTransform table, int index)
|
||||
{
|
||||
const float rowHeight = 54f;
|
||||
var view = new RatingRowView();
|
||||
|
||||
RectTransform row = CreatePanel(table, "Row_" + (index + 1), new Color(0.07f, 0.09f, 0.13f));
|
||||
SetRect(row, new Vector2(0, 1), new Vector2(1, 1), new Vector2(0.5f, 1), new Vector2(0, -56 - index * 58), new Vector2(0, rowHeight));
|
||||
view.root = row.gameObject;
|
||||
view.background = row.GetComponent<Image>();
|
||||
|
||||
RectTransform medal = CreatePanel(row, "Medal", Color.white);
|
||||
PlaceInRow(medal, 45, 50, 50);
|
||||
view.medal = medal.GetComponent<Image>();
|
||||
view.placeText = PlaceInRow(CreateTextRect(row, "Place", "1", 30), 0, 140, rowHeight).GetComponent<TMP_Text>();
|
||||
|
||||
RectTransform avatar = CreatePanel(row, "Avatar", Color.gray);
|
||||
PlaceInRow(avatar, 150, 48, 48);
|
||||
view.avatar = avatar.GetComponent<Image>();
|
||||
view.avatarLetter = CreateText(avatar, "Letter", "В", 28);
|
||||
|
||||
view.nameText = PlaceInRow(CreateTextRect(row, "Name", "Игрок", 30, TextAlignmentOptions.Left), 225, 330, rowHeight).GetComponent<TMP_Text>();
|
||||
|
||||
RectTransform circleBanner = CreatePanel(row, "CircleBanner", Color.blue);
|
||||
PlaceInRow(circleBanner, 570, 36, 46);
|
||||
view.circleBanner = circleBanner.GetComponent<Image>();
|
||||
view.circleText = PlaceInRow(CreateTextRect(row, "Circle", "Круг", 28, TextAlignmentOptions.Left), 625, 320, rowHeight).GetComponent<TMP_Text>();
|
||||
|
||||
TMP_Text power = PlaceInRow(CreateTextRect(row, "Power", "0", 28), 950, 220, rowHeight).GetComponent<TMP_Text>();
|
||||
power.color = RatingGold;
|
||||
view.powerText = power;
|
||||
|
||||
RectTransform inspect = CreateButton(row, "InspectButton", "?", new Color(0.18f, 0.2f, 0.27f), out _);
|
||||
PlaceInRow(inspect, 1190, 56, 44);
|
||||
view.inspectButton = inspect.GetComponent<Button>();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
// Элемент строки: прижат к левому краю строки с отступом x, по вертикали по центру
|
||||
static RectTransform PlaceInRow(RectTransform rt, float x, float width, float height)
|
||||
{
|
||||
SetRect(rt, new Vector2(0, 0.5f), new Vector2(0, 0.5f), new Vector2(0, 0.5f), new Vector2(x, 0), new Vector2(width, height));
|
||||
return rt;
|
||||
}
|
||||
|
||||
static void SetRect(RectTransform rt, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 position, Vector2 size)
|
||||
{
|
||||
rt.anchorMin = anchorMin;
|
||||
rt.anchorMax = anchorMax;
|
||||
rt.pivot = pivot;
|
||||
rt.sizeDelta = size;
|
||||
rt.anchoredPosition = position;
|
||||
}
|
||||
|
||||
// ---------------- BUILD SETTINGS ----------------
|
||||
|
||||
static void UpdateBuildSettings()
|
||||
{
|
||||
var scenes = new List<EditorBuildSettingsScene>
|
||||
{
|
||||
new EditorBuildSettingsScene(CityScenePath, true),
|
||||
new EditorBuildSettingsScene(WorldMapScenePath, true)
|
||||
};
|
||||
|
||||
// Остальные уже добавленные сцены сохраняем (кроме удалённых файлов, например старой Battle)
|
||||
foreach (var existing in EditorBuildSettings.scenes)
|
||||
{
|
||||
if (existing.path == CityScenePath || existing.path == WorldMapScenePath) continue;
|
||||
if (!File.Exists(existing.path)) continue;
|
||||
scenes.Add(existing);
|
||||
}
|
||||
|
||||
EditorBuildSettings.scenes = scenes.ToArray();
|
||||
}
|
||||
|
||||
// ---------------- КНОПКА В ГОРОДЕ ----------------
|
||||
|
||||
static void HookUpCityBattleButton()
|
||||
{
|
||||
var city = EditorSceneManager.OpenScene(CityScenePath, OpenSceneMode.Single);
|
||||
|
||||
GameObject navBattle = GameObject.Find("Nav_Battle");
|
||||
if (navBattle == null)
|
||||
{
|
||||
Debug.LogWarning("[КАРТА] В сцене города не найден объект Nav_Battle -- повесь OpenBattleButton на кнопку БОЙ вручную.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (navBattle.GetComponent<OpenBattleButton>() == null)
|
||||
{
|
||||
Undo.AddComponent<OpenBattleButton>(navBattle); // RequireComponent сам добавит Button, если его нет
|
||||
EditorSceneManager.MarkSceneDirty(city);
|
||||
EditorSceneManager.SaveScene(city);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- HELPERS ----------------
|
||||
|
||||
static RectTransform CreatePanel(RectTransform parent, string name, Color color)
|
||||
{
|
||||
var go = new GameObject(name, typeof(RectTransform), typeof(Image));
|
||||
go.transform.SetParent(parent, false);
|
||||
go.GetComponent<Image>().color = color;
|
||||
return go.GetComponent<RectTransform>();
|
||||
}
|
||||
|
||||
static RectTransform CreateCenterPanel(RectTransform parent, string name, Vector2 size)
|
||||
{
|
||||
RectTransform rt = CreatePanel(parent, name, new Color(0.05f, 0.05f, 0.07f, 0.94f));
|
||||
rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rt.sizeDelta = size;
|
||||
return rt;
|
||||
}
|
||||
|
||||
static RectTransform CreateTextRect(RectTransform parent, string name, string content, int fontSize,
|
||||
TextAlignmentOptions alignment = TextAlignmentOptions.Center)
|
||||
{
|
||||
var go = new GameObject(name, typeof(RectTransform), typeof(TextMeshProUGUI));
|
||||
go.transform.SetParent(parent, false);
|
||||
var tmp = go.GetComponent<TextMeshProUGUI>();
|
||||
tmp.text = content;
|
||||
tmp.fontSize = fontSize;
|
||||
tmp.color = Color.white;
|
||||
tmp.alignment = alignment;
|
||||
tmp.raycastTarget = false;
|
||||
RectTransform rt = go.GetComponent<RectTransform>();
|
||||
rt.anchorMin = Vector2.zero;
|
||||
rt.anchorMax = Vector2.one;
|
||||
rt.offsetMin = Vector2.zero;
|
||||
rt.offsetMax = Vector2.zero;
|
||||
return rt;
|
||||
}
|
||||
|
||||
static TMP_Text CreateText(RectTransform parent, string name, string content, int fontSize)
|
||||
{
|
||||
return CreateTextRect(parent, name, content, fontSize).GetComponent<TMP_Text>();
|
||||
}
|
||||
|
||||
static RectTransform CreateButton(RectTransform parent, string name, string label, Color color, out TMP_Text labelText)
|
||||
{
|
||||
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(Button));
|
||||
go.transform.SetParent(parent, false);
|
||||
go.GetComponent<Image>().color = color;
|
||||
RectTransform rt = go.GetComponent<RectTransform>();
|
||||
labelText = CreateText(rt, "Label", label, 36);
|
||||
return rt;
|
||||
}
|
||||
|
||||
static void StyleSlider(GameObject sliderGO)
|
||||
{
|
||||
Image[] images = sliderGO.GetComponentsInChildren<Image>();
|
||||
foreach (var img in images)
|
||||
{
|
||||
if (img.name == "Background") img.color = new Color(0.25f, 0.25f, 0.28f);
|
||||
else if (img.name == "Fill") img.color = new Color(0.3f, 0.55f, 1f);
|
||||
else if (img.name == "Handle") img.color = Color.white;
|
||||
}
|
||||
// Ручка крупнее -- удобнее тянуть пальцем
|
||||
Transform handle = sliderGO.transform.Find("Handle Slide Area/Handle");
|
||||
if (handle != null) ((RectTransform)handle).sizeDelta = new Vector2(50, 0);
|
||||
}
|
||||
|
||||
static void AnchorStretch(RectTransform rt, bool top, float height)
|
||||
{
|
||||
float y = top ? 1 : 0;
|
||||
rt.anchorMin = new Vector2(0, y);
|
||||
rt.anchorMax = new Vector2(1, y);
|
||||
rt.pivot = new Vector2(0.5f, y);
|
||||
rt.sizeDelta = new Vector2(0, height);
|
||||
rt.anchoredPosition = Vector2.zero;
|
||||
}
|
||||
|
||||
static void AnchorSide(RectTransform rt, bool left, Vector2 size, float offset)
|
||||
{
|
||||
float x = left ? 0 : 1;
|
||||
rt.anchorMin = new Vector2(x, 0.5f);
|
||||
rt.anchorMax = new Vector2(x, 0.5f);
|
||||
rt.pivot = new Vector2(x, 0.5f);
|
||||
rt.sizeDelta = size;
|
||||
rt.anchoredPosition = new Vector2(left ? offset : -offset, 0);
|
||||
}
|
||||
|
||||
// Растянуть по ширине родителя, прижать к верху с отступом
|
||||
static void AnchorTop(RectTransform rt, float height, float topOffset)
|
||||
{
|
||||
rt.anchorMin = new Vector2(0, 1);
|
||||
rt.anchorMax = new Vector2(1, 1);
|
||||
rt.pivot = new Vector2(0.5f, 1);
|
||||
rt.sizeDelta = new Vector2(0, height);
|
||||
rt.anchoredPosition = new Vector2(0, -topOffset);
|
||||
}
|
||||
|
||||
static void AnchorBottom(RectTransform rt, Vector2 size, Vector2 offset)
|
||||
{
|
||||
rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 0);
|
||||
rt.pivot = new Vector2(0.5f, 0);
|
||||
rt.sizeDelta = size;
|
||||
rt.anchoredPosition = offset;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5fea028791c631d4c88cccc887d23df9
|
||||
@@ -482,306 +482,6 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 165797562}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &191012908
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 191012909}
|
||||
- component: {fileID: 191012915}
|
||||
- component: {fileID: 191012914}
|
||||
- component: {fileID: 191012913}
|
||||
- component: {fileID: 191012912}
|
||||
- component: {fileID: 191012911}
|
||||
- component: {fileID: 191012910}
|
||||
m_Layer: 5
|
||||
m_Name: Canvas (1)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &191012909
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 191012908}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0.43890655, y: 0.42053616, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 289613111}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0.64400417, y: -3.4722366}
|
||||
m_SizeDelta: {x: 5.3081, y: 1.2404}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &191012910
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 191012908}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshPro
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text:
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: -1183493901
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 36
|
||||
m_fontSizeBase: 36
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 1
|
||||
m_VerticalAlignment: 256
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_characterHorizontalScale: 1
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_TextWrappingMode: 1
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 0
|
||||
m_ActiveFontFeatures: 6e72656b
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_EmojiFallbackSupport: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 0
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
_SortingLayer: 0
|
||||
_SortingLayerID: 0
|
||||
_SortingOrder: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_renderer: {fileID: 191012911}
|
||||
m_maskType: 0
|
||||
--- !u!23 &191012911
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 191012908}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_ForceMeshLod: -1
|
||||
m_MeshLodSelectionBias: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_GlobalIlluminationMeshLod: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_MaskInteraction: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!114 &191012912
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 191012908}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 67db9e8f0e2ae9c40bc1e2b64352a6b4, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Slider
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_WrapAround: 0
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 1
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 191012910}
|
||||
m_FillRect: {fileID: 0}
|
||||
m_HandleRect: {fileID: 0}
|
||||
m_Direction: 0
|
||||
m_MinValue: 0
|
||||
m_MaxValue: 1
|
||||
m_WholeNumbers: 0
|
||||
m_Value: 0
|
||||
m_OnValueChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!114 &191012913
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 191012908}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.GraphicRaycaster
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &191012914
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 191012908}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.CanvasScaler
|
||||
m_UiScaleMode: 0
|
||||
m_ReferencePixelsPerUnit: 10
|
||||
m_ScaleFactor: 1
|
||||
m_ReferenceResolution: {x: 800, y: 600}
|
||||
m_ScreenMatchMode: 0
|
||||
m_MatchWidthOrHeight: 0
|
||||
m_PhysicalUnit: 3
|
||||
m_FallbackScreenDPI: 96
|
||||
m_DefaultSpriteDPI: 96
|
||||
m_DynamicPixelsPerUnit: 1
|
||||
m_PresetInfoIsWorld: 1
|
||||
--- !u!223 &191012915
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 191012908}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 2
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 0
|
||||
m_UseReflectionProbes: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!1 &204590793
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1830,8 +1530,7 @@ Transform:
|
||||
m_LocalPosition: {x: -14.599976, y: 5.399994, z: 0}
|
||||
m_LocalScale: {x: 2.2783892, y: 2.3779166, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 191012909}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 730896379}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &289613112
|
||||
@@ -1929,15 +1628,15 @@ RectTransform:
|
||||
m_GameObject: {fileID: 290395353}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0.01, y: 0.01, z: 0.01}
|
||||
m_LocalScale: {x: 0.61637074, y: 0.35242996, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1140293331}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 1, y: 1.5}
|
||||
m_SizeDelta: {x: 200, y: 80}
|
||||
m_AnchoredPosition: {x: 0.1278, y: 0.847}
|
||||
m_SizeDelta: {x: 5.3081, y: 1.2404}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &290395355
|
||||
MonoBehaviour:
|
||||
@@ -1959,7 +1658,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: 0/100
|
||||
m_text:
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: baa48fafda29f4247b2910dc9622c97a
|
||||
guid: beae4025d64976c4f93af3b9233183da
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
@@ -1,536 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &325422458062166425
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2603866786527873297}
|
||||
- component: {fileID: 4638465306092719133}
|
||||
- component: {fileID: 8856817474958427356}
|
||||
- component: {fileID: 5954913169102562839}
|
||||
m_Layer: 0
|
||||
m_Name: BuildingButton
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2603866786527873297
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 325422458062166425}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 5360392765729382484}
|
||||
- {fileID: 7148463949653114205}
|
||||
- {fileID: 8193145004195366051}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 628.51, y: -39.77}
|
||||
m_SizeDelta: {x: 181.02, y: 79.54}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4638465306092719133
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 325422458062166425}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &8856817474958427356
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 325422458062166425}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.381132, g: 0.31569237, b: 0.31569237, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Type: 1
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &5954913169102562839
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 325422458062166425}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Button
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_WrapAround: 0
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 1
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 8856817474958427356}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!1 &1339680859893674440
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 8193145004195366051}
|
||||
- component: {fileID: 5650023763325699801}
|
||||
- component: {fileID: 1691647246842355621}
|
||||
m_Layer: 0
|
||||
m_Name: NameText
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &8193145004195366051
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1339680859893674440}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 2603866786527873297}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 2.4907, y: 0}
|
||||
m_SizeDelta: {x: 126.2, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &5650023763325699801
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1339680859893674440}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1691647246842355621
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1339680859893674440}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text:
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: -1183493901
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 36
|
||||
m_fontSizeBase: 36
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 1
|
||||
m_VerticalAlignment: 256
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_characterHorizontalScale: 1
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_TextWrappingMode: 1
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 0
|
||||
m_ActiveFontFeatures: 6e72656b
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_EmojiFallbackSupport: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 1
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_baseMaterial: {fileID: 0}
|
||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||
--- !u!1 &2818511910305682941
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5360392765729382484}
|
||||
- component: {fileID: 3198155674833771624}
|
||||
- component: {fileID: 1435884234621093427}
|
||||
m_Layer: 0
|
||||
m_Name: Text (TMP)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &5360392765729382484
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2818511910305682941}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 2603866786527873297}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 2.4919968, y: 0}
|
||||
m_SizeDelta: {x: -54.8234, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3198155674833771624
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2818511910305682941}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1435884234621093427
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2818511910305682941}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text:
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4281479730
|
||||
m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: -1183493901
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 24
|
||||
m_fontSizeBase: 24
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 2
|
||||
m_VerticalAlignment: 512
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_characterHorizontalScale: 1
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_TextWrappingMode: 1
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 0
|
||||
m_ActiveFontFeatures: 6e72656b
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_EmojiFallbackSupport: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 1
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_baseMaterial: {fileID: 0}
|
||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||
--- !u!1 &3674648620887989720
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7148463949653114205}
|
||||
- component: {fileID: 5562570438576188084}
|
||||
- component: {fileID: 6127251531067287972}
|
||||
m_Layer: 0
|
||||
m_Name: ConstText
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &7148463949653114205
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3674648620887989720}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 2603866786527873297}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 2.4912, y: 0}
|
||||
m_SizeDelta: {x: 126.2, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &5562570438576188084
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3674648620887989720}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &6127251531067287972
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3674648620887989720}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text:
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: -1183493901
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 36
|
||||
m_fontSizeBase: 36
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 1
|
||||
m_VerticalAlignment: 256
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_characterHorizontalScale: 1
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_TextWrappingMode: 1
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 0
|
||||
m_ActiveFontFeatures: 6e72656b
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_EmojiFallbackSupport: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 1
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_baseMaterial: {fileID: 0}
|
||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,52 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 7264ce138a844adc81fa69b77a27c8a6, type: 3}
|
||||
m_Name: BattleConfig
|
||||
m_EditorClassIdentifier:
|
||||
version: 8
|
||||
counterPercent: 25
|
||||
wallsPercent: 20
|
||||
siegeVsWallsPercent: 50
|
||||
siegeInFieldPercent: -25
|
||||
randomPercent: 5
|
||||
famineStrengthPercent: 50
|
||||
fatigueStrengthPercent: 80
|
||||
lossRatioPermille:
|
||||
- 1000
|
||||
- 3000
|
||||
- 5000
|
||||
- 7000
|
||||
- 10000
|
||||
strongLossPermille:
|
||||
- 450
|
||||
- 160
|
||||
- 110
|
||||
- 74
|
||||
- 20
|
||||
weakLossPermille:
|
||||
- 450
|
||||
- 740
|
||||
- 880
|
||||
- 920
|
||||
- 950
|
||||
weakLossMaxPermille: 990
|
||||
deadRatioPermille:
|
||||
- 200
|
||||
- 500
|
||||
- 1000
|
||||
- 3000
|
||||
deadSharePermille:
|
||||
- 700
|
||||
- 450
|
||||
- 300
|
||||
- 200
|
||||
missingSharePermille: 0
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b666973e44a2442b82f2782adc99852f
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,71 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4ddb59400c6d4b08b3afaaadc461ec1e, type: 3}
|
||||
m_Name: EconomyConfig
|
||||
m_EditorClassIdentifier:
|
||||
population:
|
||||
startTotal: 300
|
||||
startWorkers: 120
|
||||
startSoldiers: 30
|
||||
foodPerCivilianPerHour: 0.1
|
||||
foodPerSoldierPerHour: 0.2
|
||||
foodPerTroopTypePerHour:
|
||||
- 0.15
|
||||
- 0.2
|
||||
- 0.15
|
||||
- 0.15
|
||||
growthPerHour: 2.5
|
||||
housingBase: 300
|
||||
housingPerTownHallLevel: 50
|
||||
housingPerHouseLevel: 50
|
||||
maxOfflineHours: 48
|
||||
startResources:
|
||||
wood: 500
|
||||
food: 500
|
||||
stone: 200
|
||||
iron: 100
|
||||
gold: 100
|
||||
gems: 0
|
||||
storage:
|
||||
baseGoods: 1000
|
||||
baseFood: 1500
|
||||
perWarehouseLevel: 1000
|
||||
perGranaryLevel: 1500
|
||||
crystalCap: 50
|
||||
warehouseWood: 200
|
||||
warehouseStone: 100
|
||||
granaryWood: 150
|
||||
granaryStone: 50
|
||||
production:
|
||||
woodcutterPerHour: 2
|
||||
farmerPerHour: 2
|
||||
stonecutterPerHour: 1.5
|
||||
minerIronPerHour: 1
|
||||
minerGoldPerHour: 0.4
|
||||
workerSlotsT1: 30
|
||||
workerSlotsPerLevel: 10
|
||||
efficiencyPerLevel: 0.1
|
||||
upgrade:
|
||||
baseWood: 150
|
||||
baseStone: 100
|
||||
multiplierPerLevel: 1.6
|
||||
maxLevel: 10
|
||||
deposits:
|
||||
poor: 0.6
|
||||
normal: 1
|
||||
rich: 1.5
|
||||
soldier:
|
||||
people: 1
|
||||
food: 15
|
||||
iron: 5
|
||||
gold: 5
|
||||
trainSeconds: 60
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db192d63d5ff4504a4624613292cf3ab
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,26 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: c25bbe993b314a15a6d1a8aaf0017d55, type: 3}
|
||||
m_Name: HospitalConfig
|
||||
m_EditorClassIdentifier:
|
||||
bedsByLevel: 140000001e00000028000000320000003c000000
|
||||
temporaryBeds: 5
|
||||
requiredTownHallLevel: 1
|
||||
buildWood: 250
|
||||
buildStone: 150
|
||||
healGameHours: 04000000040000000600000008000000
|
||||
healFoodPerSoldier: 00000000000000000000000000000000
|
||||
healGoldPerSoldier: 01000000010000000200000003000000
|
||||
missingReturnPercent: 50
|
||||
missingWoundedPercent: 30
|
||||
missingWindowGameHours: 6
|
||||
missingMaxGroups: 4
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d92d3f1bd32b432e9a30ee269f436ca0
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,23 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: c249ad34e2f94460b9f99b49c71760c8, type: 3}
|
||||
m_Name: IntelligenceConfig
|
||||
m_EditorClassIdentifier:
|
||||
settlementRadius: 5
|
||||
armyRadius: 1
|
||||
observationPercent: 0a000000140000001e000000
|
||||
testObservationLevel: 0
|
||||
lastContactMinutes: 10
|
||||
approxPercent: 20
|
||||
identifyPercent: 50
|
||||
sampleStepSeconds: 10
|
||||
hideUnexploredObjects: 0
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4972288700954eda8b97a86b3f7a5492
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,137 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &-7493335797097805923
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 11
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
|
||||
version: 10
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Map3DLit
|
||||
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords: []
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 1
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap:
|
||||
RenderType: Opaque
|
||||
disabledShaderPasses:
|
||||
- MOTIONVECTORS
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BaseMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SpecGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _AddPrecomputedVelocity: 0
|
||||
- _AlphaClip: 0
|
||||
- _AlphaToMask: 0
|
||||
- _Blend: 0
|
||||
- _BlendModePreserveSpecular: 1
|
||||
- _BumpScale: 1
|
||||
- _ClearCoatMask: 0
|
||||
- _ClearCoatSmoothness: 0
|
||||
- _Cull: 2
|
||||
- _Cutoff: 0.5
|
||||
- _DetailAlbedoMapScale: 1
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _DstBlendAlpha: 0
|
||||
- _EnvironmentReflections: 1
|
||||
- _GlossMapScale: 0
|
||||
- _Glossiness: 0
|
||||
- _GlossyReflections: 0
|
||||
- _Metallic: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.005
|
||||
- _QueueOffset: 0
|
||||
- _ReceiveShadows: 1
|
||||
- _Smoothness: 0.12
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _SrcBlendAlpha: 1
|
||||
- _Surface: 0
|
||||
- _WorkflowMode: 1
|
||||
- _XRMotionVectorsPass: 1
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
m_AllowLocking: 1
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ec4d2749e73404eba2facea709d9f24
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,26 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0427eef4612041d2b8a359f9bf358779, type: 3}
|
||||
m_Name: MarchConfig
|
||||
m_EditorClassIdentifier:
|
||||
infantry: 8
|
||||
archers: 9
|
||||
cavalry: 12
|
||||
siege: 4
|
||||
typeSpeedBonusPercent: 00000000000000000000000000000000
|
||||
armySpeedBonusPercent: 0
|
||||
baseMarchSlots: 2
|
||||
patrolsEnabled: 1
|
||||
patrolMaxActive: 3
|
||||
patrolIntervalMinutes: 10
|
||||
patrolSoldiersMin: 10
|
||||
patrolSoldiersMax: 40
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99295aa9eabc4ab6bf8cc687f4d89d96
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1 @@
|
||||
{"TestSuite":"","Date":0,"Player":{"Development":false,"ScreenWidth":0,"ScreenHeight":0,"ScreenRefreshRate":0,"Fullscreen":false,"Vsync":0,"AntiAliasing":0,"Batchmode":false,"RenderThreadingMode":"MultiThreaded","MtRendering":false,"GraphicsJobs":false,"GpuSkinning":false,"Platform":"","ColorSpace":"","AnisotropicFiltering":"","BlendWeights":"","GraphicsApi":"","ScriptingBackend":"IL2CPP","AndroidTargetSdkVersion":"AndroidApiLevelAuto","AndroidBuildSystem":"Gradle","BuildTarget":"Android","StereoRenderingPath":"MultiPass"},"Hardware":{"OperatingSystem":"","DeviceModel":"","DeviceName":"","ProcessorType":"","ProcessorCount":0,"GraphicsDeviceName":"","SystemMemorySizeMB":0},"Editor":{"Version":"6000.5.0f1","Branch":"6000.5/staging","Changeset":"88b47c5e7076","Date":1780938000},"Dependencies":["com.unity.2d.animation@15.1.0","com.unity.2d.aseprite@5.0.3","com.unity.2d.common@14.0.1","com.unity.2d.psdimporter@14.0.3","com.unity.2d.sprite@1.0.0","com.unity.2d.spriteshape@15.0.3","com.unity.2d.tilemap.extras@8.0.3","com.unity.2d.tilemap@1.0.0","com.unity.2d.tooling@3.0.1","com.unity.burst@1.8.29","com.unity.collab-proxy@2.12.4","com.unity.collections@6.5.0","com.unity.ext.nunit@2.1.0","com.unity.ide.rider@3.0.38","com.unity.ide.visualstudio@2.0.26","com.unity.inputsystem@1.19.0","com.unity.mathematics@1.4.0","com.unity.microsoft.gdk.discovery@1.3.0","com.unity.microsoft.gdk.tools@1.7.0","com.unity.microsoft.gdk@1.7.0","com.unity.modules.accessibility@1.0.0","com.unity.modules.adaptiveperformance@1.0.0","com.unity.modules.ai@1.0.0","com.unity.modules.androidjni@1.0.0","com.unity.modules.animation@1.0.0","com.unity.modules.assetbundle@1.0.0","com.unity.modules.audio@1.0.0","com.unity.modules.cloth@1.0.0","com.unity.modules.director@1.0.0","com.unity.modules.hierarchycore@1.0.0","com.unity.modules.imageconversion@1.0.0","com.unity.modules.imgui@1.0.0","com.unity.modules.jsonserialize@1.0.0","com.unity.modules.particlesystem@1.0.0","com.unity.modules.physics@1.0.0","com.unity.modules.physics2d@1.0.0","com.unity.modules.physicscore2d@1.0.0","com.unity.modules.screencapture@1.0.0","com.unity.modules.subsystems@1.0.0","com.unity.modules.terrain@1.0.0","com.unity.modules.terrainphysics@1.0.0","com.unity.modules.tilemap@1.0.0","com.unity.modules.ui@1.0.0","com.unity.modules.uielements@1.0.0","com.unity.modules.umbra@1.0.0","com.unity.modules.unityanalytics@1.0.0","com.unity.modules.unitywebrequest@1.0.0","com.unity.modules.unitywebrequestassetbundle@1.0.0","com.unity.modules.unitywebrequestaudio@1.0.0","com.unity.modules.unitywebrequesttexture@1.0.0","com.unity.modules.unitywebrequestwww@1.0.0","com.unity.modules.vectorgraphics@1.0.0","com.unity.modules.vehicles@1.0.0","com.unity.modules.video@1.0.0","com.unity.modules.wind@1.0.0","com.unity.modules.xr@1.0.0","com.unity.multiplayer.center@1.0.1","com.unity.nuget.mono-cecil@1.11.6","com.unity.nuget.newtonsoft-json@3.2.2","com.unity.purchasing@5.4.2","com.unity.render-pipelines.core@17.5.0","com.unity.render-pipelines.universal@17.5.0","com.unity.render-pipelines.universal-config@17.5.0","com.unity.searcher@4.9.4","com.unity.services.cloud-build@2.0.8","com.unity.services.core@1.18.0","com.unity.shadergraph@17.5.0","com.unity.test-framework.performance@3.5.0","com.unity.test-framework@1.7.0","com.unity.timeline@1.8.12","com.unity.ugui@2.5.0","com.unity.visualscripting@1.9.11"],"Results":[]}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd067b5f8139dbc46b51ef157b0f0613
|
||||
PrefabImporter:
|
||||
guid: 3a842e2b03903434ba52aa026b801445
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -0,0 +1 @@
|
||||
{"MeasurementCount":-1}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee8f2c0bebc1dd648a804386d277d7ea
|
||||
PrefabImporter:
|
||||
guid: 2fec63729866dd44483350cddf3c2b84
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@@ -1,58 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 6ee3759b377140c0b759a4a386327510, type: 3}
|
||||
m_Name: WorldResourceConfig
|
||||
m_EditorClassIdentifier:
|
||||
woodStock:
|
||||
- 200
|
||||
- 600
|
||||
- 1500
|
||||
stoneStock:
|
||||
- 150
|
||||
- 450
|
||||
- 1000
|
||||
metalStock:
|
||||
- 80
|
||||
- 250
|
||||
- 600
|
||||
goldStock:
|
||||
- 30
|
||||
- 100
|
||||
- 250
|
||||
woodPerGameHour: 2
|
||||
stonePerGameHour: 1.5
|
||||
metalPerGameHour: 1
|
||||
goldPerGameHour: 0.5
|
||||
woodCargo: 12
|
||||
stoneCargo: 10
|
||||
metalCargo: 8
|
||||
goldCargo: 5
|
||||
lifetimeRealHours:
|
||||
- 12
|
||||
- 24
|
||||
- 48
|
||||
woodCount:
|
||||
- 4
|
||||
- 3
|
||||
- 1
|
||||
stoneCount:
|
||||
- 4
|
||||
- 2
|
||||
- 1
|
||||
metalCount:
|
||||
- 2
|
||||
- 2
|
||||
- 1
|
||||
goldCount:
|
||||
- 2
|
||||
- 1
|
||||
- 1
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b95775cbe52447a6abff99bfeb488500
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18e162eeff424f8bb3a71543eafba0c8
|
||||
guid: 56f3d86a40782da40b881f23b1905060
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -1,799 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 10
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.55, g: 0.58, b: 0.62, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 3
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 13
|
||||
m_BakeOnSceneLoad: 0
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 0
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 2
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 1
|
||||
m_PVRFilteringGaussRadiusAO: 1
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 3
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
buildHeightMesh: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &110794169
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 110794174}
|
||||
- component: {fileID: 110794173}
|
||||
- component: {fileID: 110794172}
|
||||
- component: {fileID: 110794171}
|
||||
- component: {fileID: 110794170}
|
||||
- component: {fileID: 110794175}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &110794170
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 110794169}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e8645f0028574904969b5802443f100f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::City3DCamera
|
||||
pitch: 52
|
||||
yaw: 45
|
||||
distance: 26
|
||||
minDistance: 8
|
||||
maxDistance: 55
|
||||
target: {x: 0, y: 0, z: 0}
|
||||
bounds:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 48
|
||||
height: 48
|
||||
--- !u!114 &110794171
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 110794169}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalCameraData
|
||||
m_RenderShadows: 1
|
||||
m_RequiresDepthTextureOption: 2
|
||||
m_RequiresOpaqueTextureOption: 2
|
||||
m_CameraType: 0
|
||||
m_Cameras: []
|
||||
m_RendererIndex: -1
|
||||
m_VolumeLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1
|
||||
m_VolumeTrigger: {fileID: 0}
|
||||
m_VolumeFrameworkUpdateModeOption: 2
|
||||
m_RenderPostProcessing: 0
|
||||
m_Antialiasing: 0
|
||||
m_AntialiasingQuality: 2
|
||||
m_StopNaN: 0
|
||||
m_Dithering: 0
|
||||
m_ClearDepth: 1
|
||||
m_AllowXRRendering: 1
|
||||
m_AllowHDROutput: 1
|
||||
m_UseScreenCoordOverride: 0
|
||||
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_RequiresDepthTexture: 0
|
||||
m_RequiresColorTexture: 0
|
||||
m_TaaSettings:
|
||||
m_Quality: 3
|
||||
m_FrameInfluence: 0.1
|
||||
m_JitterScale: 1
|
||||
m_MipBias: 0
|
||||
m_VarianceClampScale: 0.9
|
||||
m_ContrastAdaptiveSharpening: 0
|
||||
m_Version: 2
|
||||
--- !u!81 &110794172
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 110794169}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &110794173
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 110794169}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 2
|
||||
m_BackGroundColor: {r: 0.55, g: 0.72, b: 0.9, a: 1}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_Iso: 200
|
||||
m_ShutterSpeed: 0.005
|
||||
m_Aperture: 16
|
||||
m_FocusDistance: 10
|
||||
m_FocalLength: 50
|
||||
m_BladeCount: 5
|
||||
m_Curvature: {x: 2, y: 11}
|
||||
m_BarrelClipping: 0.25
|
||||
m_Anamorphism: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 300
|
||||
field of view: 40
|
||||
orthographic: 1
|
||||
orthographic size: 5
|
||||
m_Depth: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &110794174
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 110794169}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &110794175
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 110794169}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 772c25e1ad11ec84191ddd62a2559d77, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::CameraController
|
||||
moveSpeed: 5
|
||||
zoomSpeed: 2
|
||||
minOrthoSize: 2
|
||||
maxOrthoSize: 50
|
||||
--- !u!1 &429953705
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 429953708}
|
||||
- component: {fileID: 429953707}
|
||||
- component: {fileID: 429953706}
|
||||
m_Layer: 0
|
||||
m_Name: EventSystem
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &429953706
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 429953705}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.EventSystems.StandaloneInputModule
|
||||
m_SendPointerHoverToParent: 1
|
||||
m_HorizontalAxis: Horizontal
|
||||
m_VerticalAxis: Vertical
|
||||
m_SubmitButton: Submit
|
||||
m_CancelButton: Cancel
|
||||
m_InputActionsPerSecond: 10
|
||||
m_RepeatDelay: 0.5
|
||||
m_ForceModuleActive: 0
|
||||
--- !u!114 &429953707
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 429953705}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.EventSystems.EventSystem
|
||||
m_FirstSelected: {fileID: 0}
|
||||
m_sendNavigationEvents: 1
|
||||
m_DragThreshold: 10
|
||||
--- !u!4 &429953708
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 429953705}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1001 &695231209
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: 390569600188462384, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: -23.000854
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 628544459168404126, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 628544459168404126, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 628544459168404126, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 628544459168404126, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_Pivot.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_Pivot.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchorMin.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2090086157864901368, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3476476116216033849, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3476476116216033849, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3476476116216033849, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3476476116216033849, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3561707029477629692, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3561707029477629692, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3561707029477629692, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4178416176766117917, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4178416176766117917, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5046265208400373706, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_sharedMaterial
|
||||
value:
|
||||
objectReference: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
- target: {fileID: 5449404431136268404, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_Camera
|
||||
value:
|
||||
objectReference: {fileID: 110794173}
|
||||
- target: {fileID: 5795784041573784673, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: HUD_Canvas
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6329222681380648929, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_sharedMaterial
|
||||
value:
|
||||
objectReference: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
- target: {fileID: 7064072091532955446, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: -6.4188232
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: dd067b5f8139dbc46b51ef157b0f0613, type: 3}
|
||||
--- !u!1 &842753593
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 842753595}
|
||||
- component: {fileID: 842753594}
|
||||
m_Layer: 0
|
||||
m_Name: RecourceManager
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &842753594
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 842753593}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5505825aa4d83af49a3a641e48af3ff7, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::ResourceManager
|
||||
startWood: 0
|
||||
startStone: 0
|
||||
startIron: 0
|
||||
startFood: 0
|
||||
startGold: 0
|
||||
startGems: 0
|
||||
startPower: 0
|
||||
startPopulation: 0
|
||||
--- !u!4 &842753595
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 842753593}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 746.5865, y: 295.47598, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1080091918
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1080091921}
|
||||
- component: {fileID: 1080091920}
|
||||
- component: {fileID: 1080091919}
|
||||
m_Layer: 0
|
||||
m_Name: SaveManager
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1080091919
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1080091918}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0f2bb66f2486f084b813736d3a8768d7, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::GameBootstrap
|
||||
--- !u!114 &1080091920
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1080091918}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 749721a5613594941adaed89b39ea5fe, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::SaveManager
|
||||
autoSaveInterval: 30
|
||||
--- !u!4 &1080091921
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1080091918}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -11.03001, y: 0.33877, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1225367365
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1225367367}
|
||||
- component: {fileID: 1225367366}
|
||||
m_Layer: 0
|
||||
m_Name: City3D
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1225367366
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1225367365}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 44d822c903694362bef912c055adda5b, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::City3DController
|
||||
--- !u!4 &1225367367
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1225367365}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1935466178
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1935466180}
|
||||
- component: {fileID: 1935466179}
|
||||
m_Layer: 0
|
||||
m_Name: Sun
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!108 &1935466179
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1935466178}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 13
|
||||
m_Type: 1
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_Intensity: 1.2
|
||||
m_Range: 10
|
||||
m_SpotAngle: 30
|
||||
m_InnerSpotAngle: 21.80208
|
||||
m_CookieSize2D: {x: 0.5, y: 0.5}
|
||||
m_Shadows:
|
||||
m_Type: 2
|
||||
m_Resolution: -1
|
||||
m_CustomResolution: -1
|
||||
m_Strength: 1
|
||||
m_Bias: 0.05
|
||||
m_NormalBias: 0.4
|
||||
m_NearPlane: 0.2
|
||||
m_CullingMatrixOverride:
|
||||
e00: 1
|
||||
e01: 0
|
||||
e02: 0
|
||||
e03: 0
|
||||
e10: 0
|
||||
e11: 1
|
||||
e12: 0
|
||||
e13: 0
|
||||
e20: 0
|
||||
e21: 0
|
||||
e22: 1
|
||||
e23: 0
|
||||
e30: 0
|
||||
e31: 0
|
||||
e32: 0
|
||||
e33: 1
|
||||
m_UseCullingMatrixOverride: 0
|
||||
m_Cookie: {fileID: 0}
|
||||
m_DrawHalo: 0
|
||||
m_Flare: {fileID: 0}
|
||||
m_RenderMode: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingLayerMask: 1
|
||||
m_Lightmapping: 4
|
||||
m_LightShadowCasterMode: 0
|
||||
m_AreaSize: {x: 1, y: 1}
|
||||
m_BounceIntensity: 1
|
||||
m_ColorTemperature: 6570
|
||||
m_UseColorTemperature: 0
|
||||
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_UseBoundingSphereOverride: 0
|
||||
m_UseViewFrustumForShadowCasterCull: 1
|
||||
m_ForceVisible: 0
|
||||
m_ShapeRadius: 0.025
|
||||
m_ShadowAngle: 0
|
||||
m_LightUnit: 1
|
||||
m_LuxAtDistance: 1
|
||||
m_EnableSpotReflector: 1
|
||||
--- !u!4 &1935466180
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1935466178}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.40821794, y: -0.23456973, z: 0.10938166, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
m_Roots:
|
||||
- {fileID: 110794174}
|
||||
- {fileID: 1935466180}
|
||||
- {fileID: 1225367367}
|
||||
- {fileID: 429953708}
|
||||
- {fileID: 1080091921}
|
||||
- {fileID: 842753595}
|
||||
- {fileID: 695231209}
|
||||
@@ -0,0 +1,352 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 9
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 3
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 12
|
||||
m_GIWorkflowMode: 1
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 0
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_FinalGather: 0
|
||||
m_FinalGatherFiltering: 1
|
||||
m_FinalGatherRayCount: 256
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 5
|
||||
m_PVRFilteringGaussRadiusAO: 2
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 2
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
accuratePlacement: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &519420028
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 519420032}
|
||||
- component: {fileID: 519420031}
|
||||
- component: {fileID: 519420029}
|
||||
- component: {fileID: 519420030}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!81 &519420029
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 519420028}
|
||||
m_Enabled: 1
|
||||
--- !u!114 &519420030
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 519420028}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_RenderShadows: 1
|
||||
m_RequiresDepthTextureOption: 2
|
||||
m_RequiresOpaqueTextureOption: 2
|
||||
m_CameraType: 0
|
||||
m_Cameras: []
|
||||
m_RendererIndex: -1
|
||||
m_VolumeLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1
|
||||
m_VolumeTrigger: {fileID: 0}
|
||||
m_RenderPostProcessing: 0
|
||||
m_Antialiasing: 0
|
||||
m_AntialiasingQuality: 2
|
||||
m_StopNaN: 0
|
||||
m_Dithering: 0
|
||||
m_ClearDepth: 1
|
||||
m_AllowXRRendering: 1
|
||||
m_RequiresDepthTexture: 0
|
||||
m_RequiresColorTexture: 0
|
||||
m_Version: 2
|
||||
--- !u!20 &519420031
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 519420028}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 2
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_FocalLength: 50
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 34
|
||||
orthographic: 1
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 0
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 0
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 0
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &519420032
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 519420028}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: -10}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &619394800
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 619394802}
|
||||
- component: {fileID: 619394801}
|
||||
m_Layer: 0
|
||||
m_Name: Global Light 2D
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &619394801
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 619394800}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 073797afb82c5a1438f328866b10b3f0, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_ComponentVersion: 1
|
||||
m_LightType: 4
|
||||
m_BlendStyleIndex: 0
|
||||
m_FalloffIntensity: 0.5
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_Intensity: 1
|
||||
m_LightVolumeIntensity: 1
|
||||
m_LightVolumeIntensityEnabled: 0
|
||||
m_ApplyToSortingLayers: 00000000
|
||||
m_LightCookieSprite: {fileID: 0}
|
||||
m_DeprecatedPointLightCookieSprite: {fileID: 0}
|
||||
m_LightOrder: 0
|
||||
m_OverlapOperation: 0
|
||||
m_NormalMapDistance: 3
|
||||
m_NormalMapQuality: 2
|
||||
m_UseNormalMap: 0
|
||||
m_ShadowIntensityEnabled: 0
|
||||
m_ShadowIntensity: 0.75
|
||||
m_ShadowVolumeIntensityEnabled: 0
|
||||
m_ShadowVolumeIntensity: 0.75
|
||||
m_Vertices:
|
||||
- position: {x: 0.9985302, y: 0.9985302, z: 0}
|
||||
color: {r: 0.70710677, g: 0.70710677, b: 0, a: 0}
|
||||
uv: {x: 0, y: 0}
|
||||
- position: {x: 0.9985302, y: 0.9985302, z: 0}
|
||||
color: {r: 0, g: 0, b: 0, a: 1}
|
||||
uv: {x: 0, y: 0}
|
||||
- position: {x: -0.9985302, y: 0.9985302, z: 0}
|
||||
color: {r: -0.70710677, g: 0.70710677, b: 0, a: 0}
|
||||
uv: {x: 0, y: 0}
|
||||
- position: {x: -0.9985302, y: 0.9985302, z: 0}
|
||||
color: {r: 0, g: 0, b: 0, a: 1}
|
||||
uv: {x: 0, y: 0}
|
||||
- position: {x: -0.99853003, y: -0.9985304, z: 0}
|
||||
color: {r: -0.70710665, g: -0.7071069, b: 0, a: 0}
|
||||
uv: {x: 0, y: 0}
|
||||
- position: {x: -0.99853003, y: -0.9985304, z: 0}
|
||||
color: {r: 0, g: 0, b: 0, a: 1}
|
||||
uv: {x: 0, y: 0}
|
||||
- position: {x: 0.99853003, y: -0.9985304, z: 0}
|
||||
color: {r: 0.70710665, g: -0.7071069, b: 0, a: 0}
|
||||
uv: {x: 0, y: 0}
|
||||
- position: {x: 0.99853003, y: -0.9985304, z: 0}
|
||||
color: {r: 0, g: 0, b: 0, a: 1}
|
||||
uv: {x: 0, y: 0}
|
||||
- position: {x: 0, y: 0, z: 0}
|
||||
color: {r: 0, g: 0, b: 0, a: 1}
|
||||
uv: {x: 0, y: 0}
|
||||
m_Triangles: 030001000800020000000100030002000100050003000800040002000300050004000300070005000800060004000500070006000500010007000800000006000700010000000700
|
||||
m_LocalBounds:
|
||||
m_Center: {x: 0, y: -0.00000011920929, z: 0}
|
||||
m_Extent: {x: 0.9985302, y: 0.99853027, z: 0}
|
||||
m_PointLightInnerAngle: 360
|
||||
m_PointLightOuterAngle: 360
|
||||
m_PointLightInnerRadius: 0
|
||||
m_PointLightOuterRadius: 1
|
||||
m_ShapeLightParametricSides: 5
|
||||
m_ShapeLightParametricAngleOffset: 0
|
||||
m_ShapeLightParametricRadius: 1
|
||||
m_ShapeLightFalloffSize: 0.5
|
||||
m_ShapeLightFalloffOffset: {x: 0, y: 0}
|
||||
m_ShapePath:
|
||||
- {x: -0.5, y: -0.5, z: 0}
|
||||
- {x: 0.5, y: -0.5, z: 0}
|
||||
- {x: 0.5, y: 0.5, z: 0}
|
||||
- {x: -0.5, y: 0.5, z: 0}
|
||||
--- !u!4 &619394802
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 619394800}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1c34719f0605c247a3575d077f3d656
|
||||
guid: 8c9cfa26abfee488c85f1582747f6a02
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb59bee2c4134da4b8a13896c9b2e177
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,90 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
// ТАБЛИЦА БАЛАНСА БОЯ. Все числа боевой системы -- здесь, а не в коде модуля (ревью от 27.10.2026, п. 2.5).
|
||||
//
|
||||
// Ассет: Assets/Resources/BattleConfig.asset (выдели его в окне Project и меняй числа в Inspector,
|
||||
// перекомпиляция не нужна; изменения действуют со следующего запуска игры). Создать заново:
|
||||
// правый клик в Project -> Create -> Bogatyri -> Battle Config, имя файла -- BattleConfig, папка -- Resources.
|
||||
//
|
||||
// Модуль Боя таблицу не видит: App/GameModules переводит её в BattleRules.
|
||||
// Сила воинов -- в GameData.Troops (рядом с ценой и временем обучения), здесь её нет.
|
||||
// Числа -- стартовые для тестирования (план от 27.10.2026), утверждает команда.
|
||||
[CreateAssetMenu(fileName = "BattleConfig", menuName = "Bogatyri/Battle Config")]
|
||||
public class BattleConfig : ScriptableObject
|
||||
{
|
||||
[Tooltip("Версия баланса боя. Поменял числа -- подними версию: итог боя запоминает, по какой версии посчитан")]
|
||||
public int version = 4;
|
||||
|
||||
[Tooltip("Контра, %: пехота > кавалерия > лучники > пехота. Взвешивается по доле нужного типа у врага")]
|
||||
public int counterPercent = 25;
|
||||
[Tooltip("Стены, %: усиление защитников замка и городов")]
|
||||
public int wallsPercent = 20;
|
||||
[Tooltip("Осадные при штурме стен, %")]
|
||||
public int siegeVsWallsPercent = 50;
|
||||
[Tooltip("Осадные в поле и в обороне, % (минус)")]
|
||||
public int siegeInFieldPercent = -25;
|
||||
[Tooltip("Случайность каждой стороны: от -N% до +N%. 0 -- без случайности")]
|
||||
public int randomPercent = 5;
|
||||
|
||||
[Header("Кривая потерь (решение №14, регламент 31.10, ТЕСТ)")]
|
||||
[Tooltip("Опорные точки соотношения сил (сильная / слабая) в десятых долях процента: 1000 = 1:1, 3000 = 3:1. Первая -- обязательно 1000, дальше по возрастанию")]
|
||||
public int[] lossRatioPermille = { 1000, 3000, 5000, 7000, 10000 };
|
||||
[Tooltip("Доля выбывших у сильной стороны в каждой точке, десятые доли процента: 450 = 45%")]
|
||||
public int[] strongLossPermille = { 450, 160, 110, 74, 20 };
|
||||
[Tooltip("Доля выбывших у слабой стороны в каждой точке, десятые доли процента: 740 = 74%")]
|
||||
public int[] weakLossPermille = { 450, 740, 880, 920, 950 };
|
||||
[Tooltip("Разгром дальше последней точки: слабая сторона плавно стремится к этому пределу, десятые доли процента. Решения 31.10, п. 5: 990 = 99%")]
|
||||
public int weakLossMaxPermille = 990;
|
||||
|
||||
[Header("Погибшие и раненые (регламент 31.10, решения 31.10, п. 7, ТЕСТ)")]
|
||||
[Tooltip("Соотношение сил стороны (своя / чужая) в десятых долях процента: 200 = 1:5, 1000 = 1:1, 3000 = 3:1. По возрастанию")]
|
||||
public int[] deadRatioPermille = { 200, 500, 1000, 3000 };
|
||||
[Tooltip("Доля погибших среди выбывших в каждой точке, десятые доли процента: 700 = 70%. Остальные выбывшие -- раненые")]
|
||||
public int[] deadSharePermille = { 700, 450, 300, 200 };
|
||||
|
||||
[Tooltip("Пропавшие без вести (решения 01.11, п. 5): доля выбывших, десятые доли процента. НЕ УТВЕРЖДЕНА -- 0, механика выключена. Для проверки -- например 150 (15%)")]
|
||||
public int missingSharePermille = 0;
|
||||
|
||||
[Tooltip("Голод: сколько силы остаётся, %. Утверждено 27.10.2026: -50% (остаётся 50). Проверяется на момент боя")]
|
||||
public int famineStrengthPercent = 50;
|
||||
[Tooltip("Усталость: сколько силы остаётся, %. Утверждено 27.10.2026: -20% (остаётся 80). Вместе с голодом -- 40%")]
|
||||
public int fatigueStrengthPercent = 80;
|
||||
|
||||
static BattleConfig cached;
|
||||
|
||||
public static BattleConfig Load()
|
||||
{
|
||||
if (cached != null) return cached;
|
||||
cached = Resources.Load<BattleConfig>("BattleConfig");
|
||||
if (cached == null)
|
||||
{
|
||||
Debug.LogWarning("[БОЙ] Нет Assets/Resources/BattleConfig.asset -- берутся числа по умолчанию из BattleConfig.cs.");
|
||||
cached = CreateInstance<BattleConfig>();
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Правила для модуля Боя
|
||||
public BattleRules ToRules(int[] unitPower)
|
||||
{
|
||||
return new BattleRules
|
||||
{
|
||||
Version = version,
|
||||
UnitPower = unitPower,
|
||||
CounterPercent = counterPercent,
|
||||
WallsPercent = wallsPercent,
|
||||
SiegeVsWallsPercent = siegeVsWallsPercent,
|
||||
SiegeInFieldPercent = siegeInFieldPercent,
|
||||
LossRatioPermille = lossRatioPermille,
|
||||
StrongLossPermille = strongLossPermille,
|
||||
WeakLossPermille = weakLossPermille,
|
||||
WeakLossMaxPermille = weakLossMaxPermille,
|
||||
DeadRatioPermille = deadRatioPermille,
|
||||
DeadSharePermille = deadSharePermille,
|
||||
MissingSharePermille = Mathf.Clamp(missingSharePermille, 0, 1000),
|
||||
RandomPercent = Mathf.Max(0, randomPercent),
|
||||
FamineStrengthPercent = Mathf.Clamp(famineStrengthPercent, 0, 100),
|
||||
FatigueStrengthPercent = Mathf.Clamp(fatigueStrengthPercent, 0, 100)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7264ce138a844adc81fa69b77a27c8a6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,838 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
// Окно здания. Открывается тапом по зданию (подписей над зданиями больше нет -- всё здесь).
|
||||
// У каждого здания своё окно:
|
||||
// - добывающее (Лесопилка, Ферма, Каменоломня, Шахта, Рудник): рабочие этой профессии и кнопки "−" / "+",
|
||||
// добыча в час, сколько ресурса на складе и когда склад заполнится, что даст улучшение и его цена;
|
||||
// - Склад и Амбар: запас / лимит по их ресурсам, что даст улучшение и цена;
|
||||
// - Дом: сколько жилья даёт, население / предел, улучшение и цена;
|
||||
// - Ратуша: жильё, резерв для найма, что откроется на следующем уровне, цена улучшения;
|
||||
// - Кристалл: где добывается, запас / лимит;
|
||||
// - Казармы: очередь обучения, какие войска открыты и что нужно для остальных, улучшение (открывает войска),
|
||||
// кнопка "Нанять войска" (панель "Войска").
|
||||
// - Стройка и улучшение идут по времени (BuildTimer, 01.11, ТЕСТ): полоска, сколько осталось, ускорение за кристаллы.
|
||||
// Пока здание строится, в окне только стройка; при улучшении здание работает на старом уровне.
|
||||
// - У построенных зданий (кроме Ратуши) -- кнопка "Перенести" (бесплатно, уровень и стройка сохраняются). Сноса нет (04.11).
|
||||
// Без здания (кнопка "Рабочие" в панели DEV и "Все рабочие" в окне) -- список рабочих по всем профессиям.
|
||||
// Окно -- только интерфейс: рабочих ставит модуль Населения, добычу считает модуль Экономики.
|
||||
// Пока окно открыто, цифры обновляются сами (раз в полсекунды).
|
||||
public static class BuildingWindow
|
||||
{
|
||||
static GameObject root;
|
||||
static GameObject focus; // здание, по которому тапнули (null -- список рабочих)
|
||||
static readonly List<Action> live = new List<Action>(); // что обновлять, пока окно открыто
|
||||
|
||||
public static void OnWorkplaceTapped(ResourceCollector building) { Open(building != null ? building.gameObject : null); }
|
||||
public static void OnStorageTapped(StorageBuilding building) { Open(building != null ? building.gameObject : null); }
|
||||
public static void OnTownHallTapped(TownHall townHall) { Open(townHall != null ? townHall.gameObject : null); }
|
||||
|
||||
public static void Open(GameObject building)
|
||||
{
|
||||
// В режиме стройки тап -- это выбор места, а не здания
|
||||
if (BuildModeController.Instance != null && BuildModeController.Instance.IsActive) return;
|
||||
focus = building;
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
public static void Close()
|
||||
{
|
||||
if (root != null) UnityEngine.Object.Destroy(root);
|
||||
root = null;
|
||||
focus = null;
|
||||
live.Clear();
|
||||
}
|
||||
|
||||
static void Rebuild()
|
||||
{
|
||||
if (root != null) UnityEngine.Object.Destroy(root);
|
||||
live.Clear();
|
||||
var modal = UIKit.Modal(TitleFor(focus), out root);
|
||||
root.AddComponent<BuildingWindowTicker>();
|
||||
var card = ScrollBody(modal); // длинное окно прокручивается, "Закрыть" всегда видна внизу (05.11)
|
||||
|
||||
var rc = focus != null ? focus.GetComponent<ResourceCollector>() : null;
|
||||
var storage = focus != null ? focus.GetComponent<StorageBuilding>() : null;
|
||||
var townHall = focus != null ? focus.GetComponent<TownHall>() : null;
|
||||
var barracks = focus != null ? focus.GetComponent<Barracks>() : null;
|
||||
var hospital = focus != null ? focus.GetComponent<Hospital>() : null;
|
||||
var heroCastle = focus != null ? focus.GetComponent<HeroCastle>() : null;
|
||||
|
||||
bool constructing = focus != null && BuildTimer.IsConstructing(focus.transform);
|
||||
if (focus != null) TimerBlock(card, focus);
|
||||
|
||||
if (focus == null) WorkersList(card);
|
||||
else if (constructing) UIKit.Text(card, "Пока здание строится, оно не работает.", 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
else if (townHall != null) TownHallSection(card, townHall);
|
||||
else if (barracks != null) BarracksSection(card, barracks);
|
||||
else if (hospital != null) HospitalSection(card, hospital);
|
||||
else if (heroCastle != null) HeroCastleSection(card);
|
||||
else if (rc != null && rc.IsWorkplace) WorkplaceSection(card, rc);
|
||||
else if (rc != null && rc.IsHouse) HouseSection(card, rc);
|
||||
else if (rc != null && rc.resourceType == ResourceType.Gems) CrystalSection(card);
|
||||
else if (storage != null) StorageSection(card, storage);
|
||||
|
||||
if (focus != null) ActionsBlock(card, focus);
|
||||
UIKit.Button(modal, "Закрыть", UIKit.Theme.Gray, Close, -1, 80, 28);
|
||||
}
|
||||
|
||||
// Окно не выше экрана: заголовок сверху, содержимое прокручивается, кнопка "Закрыть" -- внизу, вне прокрутки.
|
||||
// Закрыть можно и тапом по затемнению вокруг окна, и кнопкой "Назад" (Escape).
|
||||
static RectTransform ScrollBody(RectTransform modal)
|
||||
{
|
||||
var fit = modal.GetComponent<ContentSizeFitter>();
|
||||
if (fit != null) UnityEngine.Object.DestroyImmediate(fit);
|
||||
modal.anchorMin = new Vector2(0.06f, 0.05f);
|
||||
modal.anchorMax = new Vector2(0.94f, 0.95f);
|
||||
modal.offsetMin = Vector2.zero;
|
||||
modal.offsetMax = Vector2.zero;
|
||||
|
||||
var dim = root.GetComponent<Image>();
|
||||
if (dim != null)
|
||||
{
|
||||
var outside = root.AddComponent<Button>();
|
||||
outside.transition = Selectable.Transition.None;
|
||||
outside.onClick.AddListener(Close);
|
||||
}
|
||||
|
||||
var holder = UIKit.Make("Body", modal);
|
||||
UIKit.Size(holder, -1, -1).flexibleHeight = 1;
|
||||
ScrollRect scroll;
|
||||
return UIKit.ScrollList(holder, out scroll, 14);
|
||||
}
|
||||
|
||||
// Стройка закончилась (BuildTimer.Completed): окно этого здания показывает новый уровень
|
||||
internal static void OnBuildCompleted(BuildTimer timer)
|
||||
{
|
||||
if (root != null && timer != null && focus == timer.gameObject) Rebuild();
|
||||
}
|
||||
|
||||
internal static void Tick()
|
||||
{
|
||||
if (root == null) return;
|
||||
if (focus == null && FocusWasDestroyed) { Close(); return; }
|
||||
foreach (var update in live) update();
|
||||
}
|
||||
|
||||
static bool focusSet;
|
||||
static bool FocusWasDestroyed => focusSet && focus == null;
|
||||
|
||||
static string TitleFor(GameObject building)
|
||||
{
|
||||
focusSet = building != null;
|
||||
if (building == null) return "РАБОЧИЕ";
|
||||
var townHall = building.GetComponent<TownHall>();
|
||||
if (townHall != null) return $"РАТУША — {townHall.currentLevel} УР.";
|
||||
var id = building.GetComponent<PlacedBuildingId>();
|
||||
var leveled = building.GetComponent<ILeveledBuilding>();
|
||||
string name = id != null && !string.IsNullOrEmpty(id.buildingName) ? id.buildingName : building.name;
|
||||
return leveled != null ? $"{name.ToUpper()} — {leveled.Level} УР." : name.ToUpper();
|
||||
}
|
||||
|
||||
// ---------------- Добывающее здание ----------------
|
||||
|
||||
static void WorkplaceSection(RectTransform card, ResourceCollector rc)
|
||||
{
|
||||
if (!WorkJobs.TryFromResource(rc.resourceType, out WorkJob job)) return;
|
||||
var production = GameModules.Production;
|
||||
if (production == null || !GameServices.TryGet(out IPopulation population) || !GameServices.TryGet(out IEconomy economy))
|
||||
{
|
||||
UIKit.Text(card, "Модули Населения и Экономики не подключены", 26, UIKit.Theme.Red, TextAlignmentOptions.Center);
|
||||
return;
|
||||
}
|
||||
ResourceType res = WorkJobs.Resource(job);
|
||||
string resName = GameData.ResName(res).ToLower();
|
||||
double norm = production.Rules.PerWorkerPerHour[(int)job];
|
||||
|
||||
// Что даёт это здание
|
||||
int slots = production.BuildingSlots(rc.level);
|
||||
double eff = production.BuildingEfficiency(rc.level);
|
||||
UIKit.Text(card, $"Здание даёт {slots} мест, норма ×{eff:0.##} — до {slots * norm * eff:0.#} {resName} в час при полном штате.",
|
||||
24, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
|
||||
// Рабочие профессии и кнопки
|
||||
Header(card, $"Рабочие: {WorkJobs.Name(job).ToLower()}");
|
||||
Live(card, () => $"{population.GetWorkers(job)} / {production.Slots(job)} мест в городе • свободных жителей: {population.FreeWorkers}",
|
||||
26, UIKit.Theme.Text);
|
||||
var row = UIKit.Row(card, 72, 8);
|
||||
StepButton(row, "−5", job, -5);
|
||||
StepButton(row, "−1", job, -1);
|
||||
StepButton(row, "+1", job, +1);
|
||||
StepButton(row, "+5", job, +5);
|
||||
|
||||
// Добыча и склад
|
||||
Header(card, "Добыча и склад");
|
||||
Live(card, () =>
|
||||
{
|
||||
float rate = production.RatePerHour(res);
|
||||
string famine = population.IsStarving ? " <color=#ff7a7a>голод: добыча ×0.5</color>" : "";
|
||||
return $"Сейчас: <color=#9be29b>+{rate:0.#} {resName} в час</color>{famine}";
|
||||
}, 26, UIKit.Theme.Text);
|
||||
StorageBar(card, economy, res);
|
||||
Live(card, () => FillTimeText(economy, res, production.RatePerHour(res)), 22, UIKit.Theme.TextDim);
|
||||
|
||||
// Улучшение
|
||||
int nextSlots = production.BuildingSlots(rc.level + 1);
|
||||
double nextEff = production.BuildingEfficiency(rc.level + 1);
|
||||
UpgradeBlock(card, rc.level,
|
||||
$"мест {slots} → {nextSlots}, норма ×{eff:0.##} → ×{nextEff:0.##}",
|
||||
() => rc.Level = rc.level + 1);
|
||||
|
||||
UIKit.Button(card, "Все рабочие", UIKit.Theme.Blue, () => Open(null), -1, 70, 24);
|
||||
}
|
||||
|
||||
// ---------------- Дом ----------------
|
||||
|
||||
static void HouseSection(RectTransform card, ResourceCollector house)
|
||||
{
|
||||
int perLevel = EconomyConfig.Load().population.housingPerHouseLevel;
|
||||
UIKit.Text(card, $"Дом даёт +{perLevel * house.level} к пределу жилья (+{perLevel} за уровень).", 24, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
if (GameServices.TryGet(out IPopulation population))
|
||||
{
|
||||
Header(card, "Население");
|
||||
LiveBar(card, () => population.HousingCap > 0 ? (float)population.Total / population.HousingCap : 0f,
|
||||
() => $"{population.Total} / {population.HousingCap} человек", UIKit.Theme.Blue);
|
||||
Live(card, () => population.Total >= population.HousingCap
|
||||
? "Предел жилья: прирост стоит, нужны Дома или Ратуша выше"
|
||||
: $"Прирост: +{population.GrowthPerHour:0.#} человека в час", 22, UIKit.Theme.TextDim);
|
||||
}
|
||||
UpgradeBlock(card, house.level, $"жильё +{perLevel * house.level} → +{perLevel * (house.level + 1)}", () => house.Level = house.level + 1);
|
||||
}
|
||||
|
||||
// ---------------- Кристалл ----------------
|
||||
|
||||
// Кристальная шахта (ответ 06.11, п. 3): 3 синих кристалла за 7 реальных суток, при полном хранилище -- пауза
|
||||
static void CrystalSection(RectTransform card)
|
||||
{
|
||||
var gp = GameProgress.Instance;
|
||||
if (gp != null)
|
||||
UIKit.Text(card, $"Кристальная шахта: {gp.CrystalMinePerPeriod} синих кристалла за {gp.CrystalMinePeriodDays} реальных суток, " +
|
||||
"и пока игра закрыта. Не входит в недельный предел. Здание не улучшается.", 24, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
if (GameServices.TryGet(out IEconomy economy)) StorageBar(card, economy, ResourceType.Gems);
|
||||
if (gp == null) return;
|
||||
Live(card, () =>
|
||||
{
|
||||
int working = gp.CrystalMinesWorking;
|
||||
if (working == 0) return "Шахта ещё не работает";
|
||||
if (CrystalRewards.Free(GameProgress.GemsHave, GameProgress.GemsCap) <= 0)
|
||||
return "Пауза: хранилище кристаллов заполнено. Освободите место или расширьте его в окне Ратуши";
|
||||
return $"Работает шахт: {working}. Следующий кристалл через {GameData.FormatTime(gp.CrystalMineSecondsToNext)}";
|
||||
}, 24, UIKit.Theme.Text);
|
||||
}
|
||||
|
||||
// ---------------- Склад и Амбар ----------------
|
||||
|
||||
static void StorageSection(RectTransform card, StorageBuilding storage)
|
||||
{
|
||||
var s = EconomyConfig.Load().storage;
|
||||
bool warehouse = storage.kind == StorageBuilding.Kind.Warehouse;
|
||||
float perLevel = warehouse ? s.perWarehouseLevel : s.perGranaryLevel;
|
||||
UIKit.Text(card, warehouse
|
||||
? $"Склад даёт +{perLevel * storage.level:0} к лимиту дерева, камня, металла и золота."
|
||||
: $"Амбар даёт +{perLevel * storage.level:0} к лимиту еды.",
|
||||
24, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
|
||||
if (GameServices.TryGet(out IEconomy economy))
|
||||
{
|
||||
Header(card, "Запас / лимит");
|
||||
if (warehouse)
|
||||
{
|
||||
StorageBar(card, economy, ResourceType.Wood);
|
||||
StorageBar(card, economy, ResourceType.Stone);
|
||||
StorageBar(card, economy, ResourceType.Iron);
|
||||
StorageBar(card, economy, ResourceType.Gold);
|
||||
}
|
||||
else StorageBar(card, economy, ResourceType.Food);
|
||||
UIKit.Text(card, "Сверх лимита ресурс не прибавляется — пропадает.", 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
|
||||
if (GameServices.TryGet(out IResourceProtection protection))
|
||||
{
|
||||
Header(card, "Защита от грабежа");
|
||||
var p = EconomyConfig.Load().protection;
|
||||
ResourceType[] kinds = warehouse
|
||||
? new[] { ResourceType.Wood, ResourceType.Stone, ResourceType.Iron, ResourceType.Gold }
|
||||
: new[] { ResourceType.Food };
|
||||
Live(card, () =>
|
||||
{
|
||||
var lines = new List<string>();
|
||||
foreach (var k in kinds)
|
||||
lines.Add($"{GameData.ResName(k)}: спрятано {protection.Protected(k):0}, можно унести {protection.Lootable(k):0}");
|
||||
return string.Join("\n", lines.ToArray());
|
||||
}, 22, UIKit.Theme.Text);
|
||||
UIKit.Text(card, warehouse
|
||||
? $"Каждый уровень Склада прячет ещё {p.perWarehouseLevel:0} каждого ресурса — в набеге их не унесут."
|
||||
: $"Каждый уровень Амбара прячет ещё {p.perGranaryLevel:0} еды — в набеге её не унесут.",
|
||||
22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
}
|
||||
}
|
||||
|
||||
UpgradeBlock(card, storage.level, $"лимит +{perLevel * storage.level:0} → +{perLevel * (storage.level + 1):0}",
|
||||
() => storage.Level = storage.level + 1);
|
||||
}
|
||||
|
||||
// ---------------- Ратуша ----------------
|
||||
|
||||
static void TownHallSection(RectTransform card, TownHall townHall)
|
||||
{
|
||||
var config = EconomyConfig.Load();
|
||||
int level = townHall.currentLevel;
|
||||
int next = level + 1;
|
||||
var p = config.population;
|
||||
|
||||
UIKit.Text(card, $"Ратуша даёт {p.housingBase + p.housingPerTownHallLevel * (level - 1)} мест жилья. От её уровня зависят резерв для найма и доступные здания и войска.",
|
||||
24, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
|
||||
if (GameServices.TryGet(out IPopulation population))
|
||||
{
|
||||
Header(card, "Город");
|
||||
LiveBar(card, () => population.HousingCap > 0 ? (float)population.Total / population.HousingCap : 0f,
|
||||
() => $"Население {population.Total} / {population.HousingCap}", UIKit.Theme.Blue);
|
||||
Live(card, () => $"Жителей {population.Civilians}: рабочих {population.Workers}, свободных {population.Idle} • воинов {population.Soldiers}",
|
||||
22, UIKit.Theme.TextDim);
|
||||
Live(card, () => $"Резерв для найма: {population.CivilianReserve} • можно нанять: {population.Recruitable}", 22, UIKit.Theme.TextDim);
|
||||
}
|
||||
|
||||
CrystalStorageBlock(card, level);
|
||||
|
||||
if (level >= townHall.maxLevel)
|
||||
{
|
||||
UIKit.Text(card, "Максимальный уровень", 26, UIKit.Theme.Gold, TextAlignmentOptions.Center);
|
||||
return;
|
||||
}
|
||||
|
||||
// Что даст следующий уровень
|
||||
Header(card, $"Уровень {next}");
|
||||
var gains = new List<string> { $"жильё +{p.housingPerTownHallLevel}" };
|
||||
int nowReserve = ReserveAt(level), nextReserve = ReserveAt(next);
|
||||
if (nextReserve != nowReserve) gains.Add($"резерв для найма {nowReserve} → {nextReserve}");
|
||||
UIKit.Text(card, string.Join(", ", gains.ToArray()), 24, UIKit.Theme.Text, TextAlignmentOptions.Center);
|
||||
string unlocks = UnlocksAt(next);
|
||||
if (unlocks.Length > 0) UIKit.Text(card, "Откроется: " + unlocks, 24, UIKit.Theme.Gold, TextAlignmentOptions.Center);
|
||||
|
||||
string more = MoreBuildingsAt(level, next);
|
||||
if (more.Length > 0) UIKit.Text(card, "Больше зданий: " + more, 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
if (TimerRunning(townHall.gameObject)) return; // улучшение уже идёт -- полоска сверху
|
||||
|
||||
var cost = BuildingData.ToAmounts(townHall.GetUpgradeCost());
|
||||
LiveCost(card, cost);
|
||||
long ms = config.ConstructionMs(true, next);
|
||||
TimeText(card, ms);
|
||||
bool canPay = townHall.CanUpgrade();
|
||||
var b = UIKit.Button(card, $"Улучшить до {next} ур.", canPay ? UIKit.Theme.Green : UIKit.Theme.Gray, () =>
|
||||
{
|
||||
if (!townHall.CanUpgrade()) { UIKit.Toast("Не хватает ресурсов"); return; }
|
||||
if (ms > 0 && BuildTimer.FreeBuilders == 0) { UIKit.Toast("Все строители заняты"); return; }
|
||||
if (!GameServices.TryGet(out IEconomy e) || !e.TrySpend(cost)) { UIKit.Toast("Не хватает ресурсов"); return; }
|
||||
if (ms <= 0) BuildTimer.Begin(townHall.gameObject, BuildTimer.Kind.Upgrade, next, 0, 0).Complete();
|
||||
else
|
||||
{
|
||||
long now = GameClock.UnixNowMs;
|
||||
BuildTimer.Begin(townHall.gameObject, BuildTimer.Kind.Upgrade, next, now, now + ms);
|
||||
UIKit.Toast($"Ратуша улучшается до {next} уровня");
|
||||
}
|
||||
Rebuild();
|
||||
}, -1, 80, 26);
|
||||
LiveAfford(b, canPay, () => townHall != null && townHall.CanUpgrade());
|
||||
}
|
||||
|
||||
// Хранилище синих кристаллов (регламент валют 05.11, разд. 8; ответ 06.11, п. 1): вместимость и платное
|
||||
// расширение. Ратуша только разрешает ступень -- игрок покупает её здесь за золото и обычные ресурсы
|
||||
static void CrystalStorageBlock(RectTransform card, int townHallLevel)
|
||||
{
|
||||
var storage = EconomyConfig.CrystalStorageNow();
|
||||
Header(card, "Хранилище синих кристаллов");
|
||||
Live(card, () =>
|
||||
{
|
||||
float have = GameServices.TryGet(out IEconomy e) ? e.Get(ResourceType.Gems) : 0f;
|
||||
return $"Кристаллов {have:0} / {EconomyConfig.CrystalStorageNow().Capacity}";
|
||||
}, 24, UIKit.Theme.Text);
|
||||
// Недельный PvE-предел (регламент валют 05.11, ответ 06.11): сколько новых кристаллов из заданий, событий и боёв
|
||||
Live(card, () =>
|
||||
{
|
||||
var gp = GameProgress.Instance;
|
||||
if (gp == null) return "";
|
||||
string text = $"Новых кристаллов из заданий, событий и боёв за 7 дней: {gp.CrystalQuotaUsed} / {gp.CrystalQuotaLimit}";
|
||||
if (gp.CrystalQuotaLeft <= 0) text += $". Квота освободится через {GameData.FormatTime(gp.CrystalQuotaSecondsToFree)}, до тех пор -- золото";
|
||||
return text;
|
||||
}, 22, UIKit.Theme.TextDim);
|
||||
|
||||
var next = storage.Next;
|
||||
string why = storage.WhyNot(townHallLevel);
|
||||
if (next == null)
|
||||
{
|
||||
UIKit.Text(card, "Хранилище расширено до предела", 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
return;
|
||||
}
|
||||
var cost = next.Cost ?? new ResourceAmount[0];
|
||||
if (why != null)
|
||||
{
|
||||
UIKit.Text(card, why + ". Цена: " + CostText(cost), 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
return;
|
||||
}
|
||||
LiveCost(card, cost);
|
||||
bool canPay = GameServices.TryGet(out IEconomy economy) && economy.CanAfford(cost);
|
||||
var b = UIKit.Button(card, $"Расширить до {next.Capacity}", canPay ? UIKit.Theme.Green : UIKit.Theme.Gray, () =>
|
||||
{
|
||||
if (EconomyConfig.TryExpandCrystalStorage(townHallLevel, out string error))
|
||||
{
|
||||
UIKit.Toast($"Хранилище кристаллов: {EconomyConfig.CrystalStorageNow().Capacity}");
|
||||
Rebuild();
|
||||
}
|
||||
else UIKit.Toast(error);
|
||||
}, -1, 80, 26);
|
||||
LiveAfford(b, canPay, () => GameServices.TryGet(out IEconomy now) && now.CanAfford(cost));
|
||||
}
|
||||
|
||||
// Каких зданий на следующем уровне Ратуши можно построить больше: "Лес 2 → 3, Дом 3 → 4"
|
||||
static string MoreBuildingsAt(int level, int next)
|
||||
{
|
||||
var config = EconomyConfig.Load();
|
||||
var parts = new List<string>();
|
||||
if (config.buildingLimits != null)
|
||||
foreach (var l in config.buildingLimits)
|
||||
{
|
||||
if (l == null) continue;
|
||||
int a = config.BuildingLimitFor(l.buildingName, level), b = config.BuildingLimitFor(l.buildingName, next);
|
||||
if (b > a) parts.Add($"{l.buildingName} {a} → {b}");
|
||||
}
|
||||
return string.Join(", ", parts.ToArray());
|
||||
}
|
||||
|
||||
// ---------------- Казармы ----------------
|
||||
|
||||
static void BarracksSection(RectTransform card, Barracks barracks)
|
||||
{
|
||||
var gp = GameProgress.Instance;
|
||||
UIKit.Text(card, $"Казармы обучают войска. Казарм в городе: {Barracks.Count}, мест в очереди: {Barracks.QueueSize}. " +
|
||||
$"Новые войска открывает уровень самых прокачанных Казарм ({Barracks.MaxLevel} ур.) вместе с уровнем Ратуши.",
|
||||
24, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
|
||||
if (gp != null)
|
||||
{
|
||||
Header(card, "Обучение");
|
||||
Live(card, () =>
|
||||
{
|
||||
var training = gp.Training;
|
||||
if (training.Count == 0) return "Никто не обучается";
|
||||
var o = training[0];
|
||||
string more = training.Count > 1 ? $" (ещё заказов: {training.Count - 1})" : "";
|
||||
return $"{GameData.Troops[o.Type].name} ×{o.Count} — осталось {GameData.FormatTime(gp.TrainingSecondsLeft(o))}{more}";
|
||||
}, 24, UIKit.Theme.Text);
|
||||
if (GameServices.TryGet(out IPopulation population))
|
||||
Live(card, () => $"Можно нанять из жителей: {population.Recruitable} • в Казармах: {population.InTraining}", 22, UIKit.Theme.TextDim);
|
||||
|
||||
Header(card, "Войска");
|
||||
for (int i = 0; i < GameData.Troops.Count; i++)
|
||||
{
|
||||
string lockText = gp.TroopLockText(i);
|
||||
UIKit.Text(card, lockText.Length == 0
|
||||
? $"<color=#9be59b>{GameData.Troops[i].name} — открыты</color>"
|
||||
: $"{GameData.Troops[i].name} — нужно: {lockText}",
|
||||
24, lockText.Length == 0 ? UIKit.Theme.Text : UIKit.Theme.Gold, TextAlignmentOptions.Center);
|
||||
}
|
||||
}
|
||||
|
||||
// Что откроет следующий уровень этих Казарм
|
||||
var opens = new List<string>();
|
||||
if (gp != null)
|
||||
for (int i = 0; i < GameData.Troops.Count; i++)
|
||||
if (gp.RequiredBarracksLevel(i) == barracks.level + 1 && Barracks.MaxLevel <= barracks.level) opens.Add(GameData.Troops[i].name);
|
||||
string effect = opens.Count > 0
|
||||
? $"откроются: {string.Join(", ", opens.ToArray())} (если хватает уровня Ратуши)"
|
||||
: "ближе к новым войскам";
|
||||
UpgradeBlock(card, barracks.level, effect, () => barracks.Level = barracks.level + 1);
|
||||
|
||||
UIKit.Button(card, "Нанять войска", UIKit.Theme.Blue, () =>
|
||||
{
|
||||
Close();
|
||||
if (UIPanelManager.Instance != null) UIPanelManager.Instance.Open("Troops");
|
||||
}, -1, 80, 26);
|
||||
}
|
||||
|
||||
// ---------------- Госпиталь (решения 31.10, п. 7) ----------------
|
||||
|
||||
static void HospitalSection(RectTransform card, Hospital hospital)
|
||||
{
|
||||
UIKit.Text(card, $"Госпиталь лечит раненых после боёв. Мест: {Hospital.BedsAt(Hospital.MaxLevel)} " +
|
||||
"(считается самый прокачанный госпиталь). Кто не поместился -- ждёт места.",
|
||||
24, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
|
||||
if (GameServices.TryGet(out IArmy army))
|
||||
{
|
||||
Header(card, "Раненые");
|
||||
Live(card, () =>
|
||||
{
|
||||
int healingNow = 0, waiting = 0;
|
||||
long readyMs = long.MaxValue;
|
||||
foreach (var h in army.Healing) { healingNow += h.Count; readyMs = Math.Min(readyMs, h.EndMs); }
|
||||
for (int t = 0; t < army.TypeCount; t++) waiting += army.GetWoundedWaiting(t);
|
||||
if (healingNow + waiting == 0) return "Раненых нет";
|
||||
string next = readyMs != long.MaxValue
|
||||
? $", ближайшие вылечатся через {GameData.FormatTime(Math.Max(0, readyMs - GameClock.UnixNowMs) / 1000.0)}"
|
||||
: "";
|
||||
return $"Лечатся {healingNow} из {army.HospitalBeds} мест, ждут места {waiting}{next}";
|
||||
}, 24, UIKit.Theme.Text);
|
||||
Live(card, () =>
|
||||
{
|
||||
var lines = new List<string>();
|
||||
foreach (var h in army.Healing)
|
||||
if (h.Type >= 0 && h.Type < GameData.Troops.Count)
|
||||
lines.Add($"{GameData.Troops[h.Type].name} ×{h.Count} — {GameData.FormatTime(Math.Max(0, h.EndMs - GameClock.UnixNowMs) / 1000.0)}");
|
||||
return lines.Count > 0 ? string.Join("\n", lines.ToArray()) : "";
|
||||
}, 22, UIKit.Theme.TextDim);
|
||||
var rules = HospitalConfig.Load().healGameHours;
|
||||
UIKit.Text(card, $"Лечение одного воина: пехота и лучники {rules[0]}, кавалерия {rules[2]}, осадные {rules[3]} игровых ч " +
|
||||
"(игровой час = 5 мин). Лечатся и пока игра закрыта.", 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
}
|
||||
|
||||
UpgradeBlock(card, hospital.level, $"мест {Hospital.BedsAt(hospital.level)} → {Hospital.BedsAt(hospital.level + 1)}",
|
||||
() => hospital.Level = hospital.level + 1, Hospital.MaxBuildLevel);
|
||||
}
|
||||
|
||||
// ---------------- Терем богатыря: окно героя ----------------
|
||||
// Числа героя -- те же, что в панели "Герой" (GameProgress / GameData), механика не меняется.
|
||||
|
||||
static void HeroCastleSection(RectTransform card)
|
||||
{
|
||||
var gp = GameProgress.Instance;
|
||||
UIKit.Text(card, "В Тереме живёт богатырь -- верховный командир. Он гуляет по городу; тапните по нему, чтобы поговорить.",
|
||||
24, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
if (gp == null) return;
|
||||
|
||||
int lvl = gp.Data.heroLevel, maxLvl = gp.HeroMaxLevel;
|
||||
var head = UIKit.Row(card, 140, 16);
|
||||
UIKit.Icon(head, new Color(0.55f, 0.4f, 0.2f), "Б", 130); // место под портрет
|
||||
var info = UIKit.Card(head, new Color(0, 0, 0, 0), 0, 4);
|
||||
UIKit.Size(info, -1, -1, 1);
|
||||
UIKit.Text(info, $"Уровень {lvl} / {maxLvl}", 30, UIKit.Theme.Gold).fontStyle = FontStyles.Bold;
|
||||
UIKit.Text(info, $"Сила героя: {GameData.HeroPower(lvl)}", 24, UIKit.Theme.Text);
|
||||
UIKit.Text(info, $"Бонус к силе армии: +{Mathf.RoundToInt(gp.ArmyBonus * 100)}%", 24, UIKit.Theme.Text);
|
||||
|
||||
Header(card, "Навыки");
|
||||
for (int i = 0; i < GameData.HeroSkillNames.Length; i++)
|
||||
{
|
||||
bool has = gp.HasHeroSkill(i);
|
||||
UIKit.Text(card, has ? $"<color=#9be59b>•</color> {GameData.HeroSkillNames[i]} — открыт" // без «✓»: его нет в шрифте LiberationSans (06.11)
|
||||
: $"{GameData.HeroSkillNames[i]} — с {GameData.HeroSkillLevels[i]} ур.",
|
||||
22, has ? UIKit.Theme.Text : UIKit.Theme.TextDim);
|
||||
}
|
||||
|
||||
Header(card, "Улучшение героя");
|
||||
if (lvl >= maxLvl)
|
||||
{
|
||||
UIKit.Text(card, $"Предел уровня. Улучшите Ратушу до {gp.TownHallLevel + 1} ур., чтобы поднять предел до " +
|
||||
$"{GameData.HeroMaxLevel(gp.TownHallLevel + 1)}.", 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cost = GameData.HeroUpgradeCost(lvl);
|
||||
Live(card, () =>
|
||||
{
|
||||
var parts = new List<string>();
|
||||
foreach (var c in cost)
|
||||
{
|
||||
bool enough = gp.Res(c.type) >= c.amount;
|
||||
parts.Add($"<color={(enough ? "#9be59b" : "#ff7a7a")}>{GameData.ResName(c.type)} {c.amount:N0}</color>");
|
||||
}
|
||||
return "Цена: " + string.Join(", ", parts.ToArray());
|
||||
}, 22, UIKit.Theme.Text);
|
||||
UIKit.Text(card, $"Дальше: сила {GameData.HeroPower(lvl)} → {GameData.HeroPower(lvl + 1)}, " +
|
||||
$"бонус армии +{GameData.HeroArmyBonusPercent(lvl)}% → +{GameData.HeroArmyBonusPercent(lvl + 1)}%",
|
||||
22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
var up = UIKit.Button(card, $"Улучшить до {lvl + 1} ур.", UIKit.Theme.Green, () =>
|
||||
{
|
||||
if (!gp.CanUpgradeHero) { UIKit.Toast("Не хватает ресурсов"); return; }
|
||||
gp.UpgradeHero();
|
||||
Rebuild();
|
||||
}, -1, 80, 26);
|
||||
live.Add(() => up.interactable = gp.CanUpgradeHero);
|
||||
}
|
||||
|
||||
UIKit.Button(card, "Подробнее о герое", UIKit.Theme.Blue, () =>
|
||||
{
|
||||
Close();
|
||||
if (UIPanelManager.Instance != null) UIPanelManager.Instance.Open("Hero");
|
||||
}, -1, 80, 26);
|
||||
}
|
||||
|
||||
// Резерв для найма на уровне Ратуши (те же числа, что у модуля Населения)
|
||||
static int ReserveAt(int townHallLevel)
|
||||
{
|
||||
var table = GameModules.Population != null ? GameModules.Population.Rules.CivilianReserveByTownHallLevel : null;
|
||||
if (table == null || table.Length == 0) return 0;
|
||||
return table[Mathf.Clamp(townHallLevel, 1, table.Length) - 1];
|
||||
}
|
||||
|
||||
// Здания и войска, которые открываются ровно на этом уровне Ратуши
|
||||
static string UnlocksAt(int townHallLevel)
|
||||
{
|
||||
var names = new List<string>();
|
||||
names.AddRange(City3DGameplay.TypesUnlockedAt(townHallLevel));
|
||||
foreach (var t in GameData.Troops)
|
||||
if (t.requiredTownHall == townHallLevel) names.Add(t.name);
|
||||
return string.Join(", ", names.ToArray());
|
||||
}
|
||||
|
||||
// ---------------- Улучшение (общее для зданий с уровнем, кроме Ратуши) ----------------
|
||||
|
||||
static void UpgradeBlock(RectTransform card, int level, string effect, Action raiseLevel, int maxLevel = 0)
|
||||
{
|
||||
var config = EconomyConfig.Load();
|
||||
if (level >= (maxLevel > 0 ? maxLevel : config.upgrade.maxLevel)) // у госпиталя свой предел: I-V
|
||||
{
|
||||
UIKit.Text(card, "Максимальный уровень", 26, UIKit.Theme.Gold, TextAlignmentOptions.Center);
|
||||
return;
|
||||
}
|
||||
Header(card, $"Улучшение до {level + 1} ур.");
|
||||
UIKit.Text(card, effect, 24, UIKit.Theme.Text, TextAlignmentOptions.Center);
|
||||
if (focus != null && TimerRunning(focus)) return; // улучшение уже идёт -- полоска сверху
|
||||
var cost = config.UpgradeCost(level);
|
||||
LiveCost(card, cost);
|
||||
long ms = config.ConstructionMs(false, level + 1);
|
||||
TimeText(card, ms);
|
||||
var building = focus;
|
||||
|
||||
bool canPay = GameServices.TryGet(out IEconomy economy) && economy.CanAfford(cost);
|
||||
var b = UIKit.Button(card, $"Улучшить до {level + 1} ур.", canPay ? UIKit.Theme.Green : UIKit.Theme.Gray, () =>
|
||||
{
|
||||
bool timed = ms > 0 && building != null;
|
||||
if (timed && BuildTimer.FreeBuilders == 0) { UIKit.Toast("Все строители заняты"); return; }
|
||||
if (!GameServices.TryGet(out IEconomy e) || !e.TrySpend(cost)) { UIKit.Toast("Не хватает ресурсов"); return; }
|
||||
if (timed)
|
||||
{
|
||||
long now = GameClock.UnixNowMs;
|
||||
BuildTimer.Begin(building, BuildTimer.Kind.Upgrade, level + 1, now, now + ms);
|
||||
UIKit.Toast($"Улучшение до {level + 1} уровня началось");
|
||||
}
|
||||
else
|
||||
{
|
||||
raiseLevel();
|
||||
if (CitySync.Instance != null) CitySync.Instance.SyncNow();
|
||||
UIKit.Toast($"Улучшено до {level + 1} уровня");
|
||||
}
|
||||
Rebuild();
|
||||
}, -1, 80, 26);
|
||||
LiveAfford(b, canPay, () => GameServices.TryGet(out IEconomy now) && now.CanAfford(cost));
|
||||
}
|
||||
|
||||
// Кнопка «Улучшить» следит за ресурсами, пока окно открыто: ресурсов стало хватать -- зелёная и нажимается,
|
||||
// перестало -- серая (05.11: раньше нужно было закрыть и открыть окно)
|
||||
static void LiveAfford(Button b, bool canPay, Func<bool> can)
|
||||
{
|
||||
b.interactable = canPay;
|
||||
bool shown = canPay;
|
||||
live.Add(() =>
|
||||
{
|
||||
if (b == null) return;
|
||||
bool c = can();
|
||||
if (c == shown) return;
|
||||
shown = c;
|
||||
b.interactable = c;
|
||||
var img = b.GetComponent<Image>();
|
||||
if (img != null) img.color = c ? UIKit.Theme.Green : UIKit.Theme.Gray;
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------- Стройка по времени (BuildTimer) ----------------
|
||||
|
||||
static bool TimerRunning(GameObject building)
|
||||
{
|
||||
var t = BuildTimer.Of(building);
|
||||
return t != null && BuildTimer.All.Contains(t);
|
||||
}
|
||||
|
||||
static void TimeText(RectTransform card, long ms)
|
||||
{
|
||||
if (ms <= 0) return;
|
||||
Live(card, () => $"Время: {GameData.FormatTime(ms / 1000.0)} • строителей свободно: {BuildTimer.FreeBuilders} из {BuildTimer.Builders}",
|
||||
22, UIKit.Theme.TextDim);
|
||||
}
|
||||
|
||||
// Полоска стройки или улучшения, сколько осталось, ускорение за кристаллы
|
||||
static void TimerBlock(RectTransform card, GameObject building)
|
||||
{
|
||||
var t = BuildTimer.Of(building);
|
||||
if (t == null || !BuildTimer.All.Contains(t)) return;
|
||||
Header(card, t.IsConstruction ? "Идёт стройка" : $"Идёт улучшение до {t.targetLevel} ур.");
|
||||
var bar = UIKit.Bar(card, t.Progress, UIKit.Theme.Gold, 26);
|
||||
live.Add(() => { if (t != null) UIKit.SetBar(bar, t.Progress); });
|
||||
Live(card, () => t != null ? "Осталось " + GameData.FormatTime(t.SecondsLeft) : "Готово", 24, UIKit.Theme.Text);
|
||||
|
||||
// Ускорение -- за золото (06.11, ТЕСТ: EconomyConfig.construction.secondsPerGold)
|
||||
Func<int> gold = () => t != null ? EconomyConfig.SpeedUpGold(t.SecondsLeft) : 0;
|
||||
var speed = UIKit.Button(card, "", UIKit.Theme.Blue, () =>
|
||||
{
|
||||
if (t == null) return;
|
||||
int price = gold();
|
||||
if (!GameServices.TryGet(out IEconomy e) || !e.TrySpend(ResourceType.Gold, price))
|
||||
{
|
||||
UIKit.Toast($"Нужно {price} золота");
|
||||
return;
|
||||
}
|
||||
t.Complete(); // окно перестроит OnBuildCompleted
|
||||
}, -1, 80, 26);
|
||||
live.Add(() => { if (t != null) UIKit.SetButton(speed, $"Ускорить за {gold()} золота", true); });
|
||||
UIKit.SetButton(speed, $"Ускорить за {gold()} золота", true);
|
||||
}
|
||||
|
||||
// ---------------- Перенос (решения 04.11: сноса нет) ----------------
|
||||
// Здание можно бесплатно переставить в пределах поселения: уровень, стройка или улучшение сохраняются.
|
||||
// Снести здание и вернуть ресурсы нельзя -- такой кнопки нет.
|
||||
|
||||
static void ActionsBlock(RectTransform card, GameObject building)
|
||||
{
|
||||
var id = building.GetComponent<PlacedBuildingId>();
|
||||
if (id == null || string.IsNullOrEmpty(id.buildingName)) return; // Ратуша и стартовые постройки сцены
|
||||
UIKit.Button(card, "Перенести", UIKit.Theme.Blue, () =>
|
||||
{
|
||||
Close();
|
||||
if (BuildModeController.Instance != null) BuildModeController.Instance.StartMove(building);
|
||||
}, -1, 80, 26);
|
||||
}
|
||||
|
||||
// Цена: чего хватает -- зелёным, чего нет -- красным, со сколько есть
|
||||
// Строка «Цена: …» обновляется, пока окно открыто: хватает ресурса -- зелёная цифра, нет -- красная и «есть N» (05.11)
|
||||
static void LiveCost(Transform card, ResourceAmount[] cost)
|
||||
{
|
||||
var text = UIKit.Text(card, "Цена: " + CostText(cost), 24, UIKit.Theme.Text, TextAlignmentOptions.Center);
|
||||
string shown = text.text;
|
||||
live.Add(() =>
|
||||
{
|
||||
if (text == null) return;
|
||||
string now = "Цена: " + CostText(cost);
|
||||
if (now != shown) { shown = now; text.text = now; }
|
||||
});
|
||||
}
|
||||
|
||||
static string CostText(ResourceAmount[] cost)
|
||||
{
|
||||
GameServices.TryGet(out IEconomy economy);
|
||||
var parts = new string[cost.Length];
|
||||
for (int i = 0; i < cost.Length; i++)
|
||||
{
|
||||
float have = economy != null ? economy.Get(cost[i].Type) : 0f;
|
||||
bool ok = have >= cost[i].Amount;
|
||||
string name = GameData.ResName(cost[i].Type).ToLower();
|
||||
parts[i] = ok
|
||||
? $"<color=#9be59b>{cost[i].Amount:0}</color> {name}"
|
||||
: $"<color=#ff7a7a>{cost[i].Amount:0}</color> {name} (есть {have:0})";
|
||||
}
|
||||
return string.Join(", ", parts);
|
||||
}
|
||||
|
||||
// ---------------- Склад: полоска и время до заполнения ----------------
|
||||
|
||||
static void StorageBar(RectTransform card, IEconomy economy, ResourceType res)
|
||||
{
|
||||
string name = GameData.ResName(res);
|
||||
LiveBar(card, () =>
|
||||
{
|
||||
float cap = economy.GetCapacity(res);
|
||||
return float.IsInfinity(cap) || cap <= 0 ? 0f : economy.Get(res) / cap;
|
||||
},
|
||||
() =>
|
||||
{
|
||||
float cap = economy.GetCapacity(res);
|
||||
return float.IsInfinity(cap) ? $"{name}: {economy.Get(res):0}" : $"{name}: {economy.Get(res):0} / {cap:0}";
|
||||
},
|
||||
new Color(0.85f, 0.65f, 0.25f));
|
||||
}
|
||||
|
||||
static string FillTimeText(IEconomy economy, ResourceType res, float ratePerHour)
|
||||
{
|
||||
float cap = economy.GetCapacity(res), have = economy.Get(res);
|
||||
if (float.IsInfinity(cap)) return "";
|
||||
if (have >= cap) return "<color=#ff7a7a>Склад полон — добыча пропадает. Улучшите Склад или Амбар.</color>";
|
||||
if (ratePerHour <= 0) return "Добычи нет — поставьте рабочих";
|
||||
float hours = (cap - have) / ratePerHour;
|
||||
return hours < 1f ? $"Склад заполнится через {Mathf.CeilToInt(hours * 60)} мин" : $"Склад заполнится через {hours:0.#} ч";
|
||||
}
|
||||
|
||||
// ---------------- Список рабочих по всем профессиям ----------------
|
||||
|
||||
static void WorkersList(RectTransform card)
|
||||
{
|
||||
if (!GameServices.TryGet(out IPopulation pop) || !GameServices.TryGet(out IProduction prod))
|
||||
{
|
||||
UIKit.Text(card, "Модули Населения и Экономики не подключены", 26, UIKit.Theme.Red, TextAlignmentOptions.Center);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int j = 0; j < WorkJobs.Count; j++)
|
||||
{
|
||||
var job = (WorkJob)j;
|
||||
var row = UIKit.Row(card, 72, 8);
|
||||
var label = Live(row, () =>
|
||||
{
|
||||
int slots = prod.Slots(job);
|
||||
if (slots <= 0) return $"{WorkJobs.Name(job)}: нет зданий";
|
||||
return $"{WorkJobs.Name(job)}: {pop.GetWorkers(job)} / {slots} <color=#9be29b>+{prod.RatePerHour(WorkJobs.Resource(job)):0.#}/ч</color>";
|
||||
}, 24, UIKit.Theme.Text);
|
||||
label.enableAutoSizing = true; label.fontSizeMin = 14; label.fontSizeMax = 24;
|
||||
label.alignment = TextAlignmentOptions.Left;
|
||||
UIKit.Size(label, -1, 72, 1);
|
||||
|
||||
StepButton(row, "−5", job, -5);
|
||||
StepButton(row, "−1", job, -1);
|
||||
StepButton(row, "+1", job, +1);
|
||||
StepButton(row, "+5", job, +5);
|
||||
}
|
||||
|
||||
Live(card, () => $"Свободных жителей: <b>{pop.FreeWorkers}</b> из {pop.Civilians}", 24, UIKit.Theme.Gold);
|
||||
}
|
||||
|
||||
// Кнопка "−" / "+": активна, только если есть кого снять или куда и кого поставить
|
||||
static void StepButton(Transform row, string label, WorkJob job, int delta)
|
||||
{
|
||||
Button b = null;
|
||||
b = UIKit.Button(row, label, delta > 0 ? UIKit.Theme.Blue : UIKit.Theme.Gray, () =>
|
||||
{
|
||||
if (GameServices.TryGet(out IPopulation pop)) pop.SetWorkers(job, pop.GetWorkers(job) + delta);
|
||||
Tick();
|
||||
}, 86, 64, 24);
|
||||
live.Add(() =>
|
||||
{
|
||||
if (b == null || !GameServices.TryGet(out IPopulation pop) || !GameServices.TryGet(out IProduction prod)) return;
|
||||
int workers = pop.GetWorkers(job), slots = prod.Slots(job);
|
||||
b.interactable = delta < 0 ? workers > 0 : slots > workers && pop.FreeWorkers > 0;
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------- Мелочи ----------------
|
||||
|
||||
static void Header(RectTransform card, string text)
|
||||
{
|
||||
UIKit.Text(card, text, 28, UIKit.Theme.Gold, TextAlignmentOptions.Center).fontStyle = FontStyles.Bold;
|
||||
}
|
||||
|
||||
// Текст, который обновляется, пока окно открыто
|
||||
static TextMeshProUGUI Live(Transform parent, Func<string> text, float size, Color color)
|
||||
{
|
||||
var t = UIKit.Text(parent, text(), size, color, TextAlignmentOptions.Center);
|
||||
live.Add(() => { if (t != null) t.text = text(); });
|
||||
return t;
|
||||
}
|
||||
|
||||
static void LiveBar(RectTransform card, Func<float> fraction, Func<string> label, Color color)
|
||||
{
|
||||
var bar = UIKit.Bar(card, fraction(), color, 34, label());
|
||||
live.Add(() => { if (bar != null) UIKit.SetBar(bar, fraction(), label()); });
|
||||
}
|
||||
}
|
||||
|
||||
// Обновляет открытое окно здания раз в полсекунды (добыча идёт, склад наполняется)
|
||||
public class BuildingWindowTicker : MonoBehaviour
|
||||
{
|
||||
float next;
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.Escape)) { BuildingWindow.Close(); return; } // "Назад" на телефоне
|
||||
if (Time.unscaledTime < next) return;
|
||||
next = Time.unscaledTime + 0.5f;
|
||||
BuildingWindow.Tick();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50195a725abc4fe9b74871cd8cc4b273
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,136 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
// Слой приложения: сообщает модулям, какие здания стоят в городе.
|
||||
// - рабочие места (Лес, Еда, Камень, Металл, Золото) и их уровни -> модуль добычи (места и эффективность);
|
||||
// - Дома (здание "Население") -> модуль Населения (предел жилья);
|
||||
// - Склады и Амбары -> лимиты склада в Экономике и защита ресурсов от грабежа (App/ResourceProtection).
|
||||
// Здания, которые ещё строятся (BuildTimer), не считаются: они пока не работают.
|
||||
// Модули сами здания не ищут: они без Unity и живут и на карте мира, где города нет.
|
||||
//
|
||||
// Проверяет город раз в полсекунды и применяет изменения, только когда картина устоялась
|
||||
// (два одинаковых замера подряд) -- чтобы не поймать город посреди восстановления из сохранения.
|
||||
public class CitySync : MonoBehaviour
|
||||
{
|
||||
const float Interval = 0.5f;
|
||||
|
||||
public static CitySync Instance { get; private set; }
|
||||
|
||||
// Город (3D-поселение, City3D) открыт -- задаёт City3DController
|
||||
public static bool ExternalCity;
|
||||
static bool CityOpen => ExternalCity;
|
||||
|
||||
float next;
|
||||
string pending; // замер, который ждёт подтверждения
|
||||
string applied; // последний применённый замер
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
static void Create()
|
||||
{
|
||||
if (Instance != null) return;
|
||||
var go = new GameObject("CitySync");
|
||||
DontDestroyOnLoad(go);
|
||||
Instance = go.AddComponent<CitySync>();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (Instance == this) Instance = null;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (Time.unscaledTime < next) return;
|
||||
next = Time.unscaledTime + Interval;
|
||||
if (!CityOpen) return; // город не открыт -- модули помнят последнее
|
||||
|
||||
var city = Scan();
|
||||
if (city.Signature == applied) { pending = null; return; }
|
||||
if (city.Signature != pending) { pending = city.Signature; return; } // ждём подтверждения
|
||||
Apply(city);
|
||||
}
|
||||
|
||||
// Применить сразу (после улучшения здания в окне -- чтобы окно показало новые места)
|
||||
public void SyncNow()
|
||||
{
|
||||
if (!CityOpen) return;
|
||||
Apply(Scan());
|
||||
}
|
||||
|
||||
class City
|
||||
{
|
||||
public readonly List<KeyValuePair<WorkJob, int>> Workplaces = new List<KeyValuePair<WorkJob, int>>();
|
||||
public int HouseLevels, WarehouseLevels, GranaryLevels;
|
||||
public string Signature;
|
||||
}
|
||||
|
||||
static City Scan()
|
||||
{
|
||||
var city = new City();
|
||||
var sig = new List<string>();
|
||||
foreach (var placed in FindObjectsByType<PlacedBuildingId>(FindObjectsSortMode.None))
|
||||
{
|
||||
if (string.IsNullOrEmpty(placed.buildingName)) continue; // убирается в этом кадре
|
||||
if (BuildTimer.IsConstructing(placed)) continue; // ещё строится -- не работает
|
||||
|
||||
var rc = placed.GetComponent<ResourceCollector>();
|
||||
if (rc != null)
|
||||
{
|
||||
if (WorkJobs.TryFromResource(rc.resourceType, out WorkJob job))
|
||||
{
|
||||
city.Workplaces.Add(new KeyValuePair<WorkJob, int>(job, rc.level));
|
||||
sig.Add("w" + (int)job + ":" + rc.level);
|
||||
}
|
||||
else if (rc.IsHouse)
|
||||
{
|
||||
city.HouseLevels += Mathf.Max(1, rc.level);
|
||||
sig.Add("h:" + rc.level);
|
||||
}
|
||||
}
|
||||
|
||||
var sb = placed.GetComponent<StorageBuilding>();
|
||||
if (sb != null)
|
||||
{
|
||||
if (sb.kind == StorageBuilding.Kind.Warehouse) city.WarehouseLevels += Mathf.Max(1, sb.level);
|
||||
else city.GranaryLevels += Mathf.Max(1, sb.level);
|
||||
sig.Add("s" + (int)sb.kind + ":" + sb.level);
|
||||
}
|
||||
}
|
||||
sig.Sort(System.StringComparer.Ordinal);
|
||||
var text = new StringBuilder();
|
||||
foreach (var s in sig) text.Append(s).Append(';');
|
||||
city.Signature = text.ToString();
|
||||
return city;
|
||||
}
|
||||
|
||||
void Apply(City city)
|
||||
{
|
||||
applied = city.Signature;
|
||||
pending = null;
|
||||
|
||||
var config = EconomyConfig.Load();
|
||||
|
||||
// Лимиты склада
|
||||
var rm = ResourceManager.Instance;
|
||||
if (rm != null)
|
||||
{
|
||||
ResourceType[] stored = { ResourceType.Wood, ResourceType.Stone, ResourceType.Iron, ResourceType.Gold, ResourceType.Food, ResourceType.Gems };
|
||||
foreach (var t in stored) rm.SetCapacity(t, config.GetCapacity(t, city.WarehouseLevels, city.GranaryLevels));
|
||||
}
|
||||
ResourceProtection.SetLevels(city.WarehouseLevels, city.GranaryLevels); // защита от грабежа
|
||||
|
||||
var production = GameModules.Production;
|
||||
var population = GameModules.Population;
|
||||
if (production == null || population == null) return;
|
||||
|
||||
// Места для рабочих. Новые места (построили, улучшили) остаются пустыми, пока игрок сам
|
||||
// не поставит рабочих (регламент 31.10). Мест стало меньше -- лишних рабочих снимаем.
|
||||
production.SetBuildings(city.Workplaces);
|
||||
population.SetHouseLevels(city.HouseLevels);
|
||||
population.ClampJobs();
|
||||
|
||||
// Единственное автозаполнение -- стартовая расстановка, один раз: новая игра или первая загрузка старого сохранения
|
||||
if (!population.JobsInitialized) population.AutoFillAll();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fbcccbb94e6f46c89d4c49bf589b9f19
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,40 +0,0 @@
|
||||
// Стройка и офлайн (решения 04.11): стройка, которая закончилась, пока игра была закрыта, вступает в силу
|
||||
// в тот момент офлайн-расчёта, когда время дошло до её конца. Тогда добыча здания (и его новый уровень)
|
||||
// считается ровно с момента завершения, а не с начала офлайна и не с момента входа в игру.
|
||||
//
|
||||
// Как: модуль Населения на каждом шаге расчёта сообщает, до какого момента дошёл (GameTimeSimulatedEvent).
|
||||
// Здесь завершаются стройки с концом не позже этого момента, и город сразу пересчитывается (CitySync) --
|
||||
// следующий шаг добычи идёт уже с новым зданием. Пока расчёт не дошёл до конца стройки, BuildTimer сам её не
|
||||
// завершает (BuildTimer.MayComplete). Шаг расчёта -- 10 минут, поэтому добыча начинается не раньше конца
|
||||
// стройки и не позже чем через 10 минут после него: лишнего не начисляется никогда.
|
||||
public static class ConstructionClock
|
||||
{
|
||||
// До какого момента (мс) расчёт добычи уже дошёл. 0 -- ещё ни разу не считал (сразу после запуска)
|
||||
public static long SimulatedUntilMs { get; private set; }
|
||||
|
||||
static bool subscribed;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
SimulatedUntilMs = 0;
|
||||
BuildTimer.MayComplete = endMs => endMs <= SimulatedUntilMs;
|
||||
if (subscribed) return;
|
||||
subscribed = true;
|
||||
EventBus.Subscribe<GameTimeSimulatedEvent>(OnSimulated);
|
||||
}
|
||||
|
||||
static void OnSimulated(GameTimeSimulatedEvent e)
|
||||
{
|
||||
long ms = e.UnixNow * 1000;
|
||||
if (ms > SimulatedUntilMs) SimulatedUntilMs = ms; // время расчёта только растёт
|
||||
|
||||
bool any = false;
|
||||
foreach (var timer in BuildTimer.All.ToArray())
|
||||
{
|
||||
if (timer == null || timer.endMs > SimulatedUntilMs) continue;
|
||||
timer.Complete(); // уровень только растёт, второй раз не завершается
|
||||
any = true;
|
||||
}
|
||||
if (any && CitySync.Instance != null) CitySync.Instance.SyncNow();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6104b5c531ff479eb569880ebd75d68c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,311 +0,0 @@
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
// ОТЛАДОЧНАЯ ПАНЕЛЬ для проверки баланса: кнопка "DEV" слева на экране.
|
||||
//
|
||||
// Есть только в редакторе и в Development Build -- в релизную сборку этот файл не попадает совсем.
|
||||
// "+1 час" не имеет своей логики: он двигает GameClock, и модули досчитывают время по своим обычным правилам,
|
||||
// ровно как при возвращении игрока в игру. Поэтому проверяешь настоящие правила, а не кнопку.
|
||||
//
|
||||
// Перемотка действует на то, что берёт время из GameClock (сейчас -- Население и походы). Обучение
|
||||
// войск пока идёт по реальным часам.
|
||||
public class DebugPanel : MonoBehaviour
|
||||
{
|
||||
static DebugPanel instance;
|
||||
|
||||
GameObject panel;
|
||||
TextMeshProUGUI info;
|
||||
UnityEngine.UI.Button gridButton;
|
||||
float nextRefresh;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
static void Create()
|
||||
{
|
||||
if (instance != null) return;
|
||||
var go = new GameObject("DebugPanel", typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
||||
DontDestroyOnLoad(go);
|
||||
var canvas = go.GetComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvas.sortingOrder = 1500; // поверх HUD и окон
|
||||
var scaler = go.GetComponent<CanvasScaler>();
|
||||
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||||
scaler.referenceResolution = new Vector2(1920, 1080);
|
||||
scaler.matchWidthOrHeight = 0.5f;
|
||||
instance = go.AddComponent<DebugPanel>();
|
||||
instance.Build();
|
||||
}
|
||||
|
||||
void Build()
|
||||
{
|
||||
// Панель -- внутри безопасной зоны, чтобы не уходила под вырез камеры
|
||||
var safe = new GameObject("SafeArea", typeof(RectTransform)).GetComponent<RectTransform>();
|
||||
safe.SetParent(transform, false);
|
||||
safe.anchorMin = Vector2.zero;
|
||||
safe.anchorMax = Vector2.one;
|
||||
safe.offsetMin = safe.offsetMax = Vector2.zero;
|
||||
safe.gameObject.AddComponent<SafeArea>();
|
||||
|
||||
// Кнопка-переключатель слева посередине: там нет кнопок HUD
|
||||
var toggle = UIKit.Button(transform, "DEV", new Color(0.8f, 0.2f, 0.8f, 1f), Toggle, 110, 64, 28);
|
||||
var trt = (RectTransform)toggle.transform;
|
||||
trt.anchorMin = trt.anchorMax = new Vector2(0, 0.5f);
|
||||
trt.pivot = new Vector2(0, 0.5f);
|
||||
trt.anchoredPosition = new Vector2(12, 0);
|
||||
trt.sizeDelta = new Vector2(110, 64);
|
||||
|
||||
var card = UIKit.Card(safe, new Color(0.08f, 0.08f, 0.11f, 0.95f), 20, 10);
|
||||
card.anchorMin = card.anchorMax = new Vector2(0, 0.5f);
|
||||
card.pivot = new Vector2(0, 0.5f);
|
||||
card.anchoredPosition = new Vector2(135, 0);
|
||||
card.sizeDelta = new Vector2(620, 0);
|
||||
card.gameObject.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
panel = card.gameObject;
|
||||
|
||||
var title = UIKit.Text(card, "ОТЛАДКА БАЛАНСА", 30, UIKit.Theme.Gold, TextAlignmentOptions.Center);
|
||||
title.fontStyle = FontStyles.Bold;
|
||||
|
||||
info = UIKit.Text(card, "", 24, UIKit.Theme.Text);
|
||||
|
||||
var row1 = UIKit.Row(card, 70, 10);
|
||||
Btn(row1, "+1 час", UIKit.Theme.Blue, () => GameClock.Advance(3600));
|
||||
Btn(row1, "+10 часов", UIKit.Theme.Blue, () => GameClock.Advance(36000));
|
||||
|
||||
var row2 = UIKit.Row(card, 70, 10);
|
||||
Btn(row2, "+1000 всех ресурсов", UIKit.Theme.Green, AddAllResources);
|
||||
Btn(row2, "+1000 еды", UIKit.Theme.Green, () => AddResource(ResourceType.Food, 1000));
|
||||
Btn(row2, "Кристаллы = макс", UIKit.Theme.Blue, FillGems);
|
||||
|
||||
// Недельный PvE-предел кристаллов: «+1 PvE-кристалл» выдаёт как настоящая награда (квота, место, компенсация)
|
||||
var rowQ = UIKit.Row(card, 70, 10);
|
||||
Btn(rowQ, "+1 PvE-кристалл", UIKit.Theme.Blue, () => { if (GameProgress.Instance != null) GameProgress.Instance.Grant(new[] { new RewardItem(ResourceType.Gems, 1) }); Refresh(); });
|
||||
// Только для проверки торговца (30 000 / 60 000 золота) и Кристальной шахты (12 000 золота, 3200 камня...):
|
||||
// обычные ресурсы ставятся напрямую, мимо лимита склада
|
||||
Btn(rowQ, "Ресурсы 100 000", UIKit.Theme.Green, () =>
|
||||
{
|
||||
var rm = ResourceManager.Instance;
|
||||
if (rm != null)
|
||||
foreach (var t in new[] { ResourceType.Gold, ResourceType.Wood, ResourceType.Stone, ResourceType.Iron, ResourceType.Food })
|
||||
rm.SetResource(t, 100000);
|
||||
Refresh();
|
||||
});
|
||||
Btn(rowQ, "Сбросить квоту", UIKit.Theme.Gray, () => { if (GameProgress.Instance != null) GameProgress.Instance.DevResetCrystalQuota(); UIKit.Toast("Недельная квота кристаллов сброшена"); Refresh(); });
|
||||
|
||||
var row3 = UIKit.Row(card, 70, 10);
|
||||
Btn(row3, "Еда = 0", UIKit.Theme.Red, ZeroFood);
|
||||
Btn(row3, "Сбросить перемотку", UIKit.Theme.Gray, () => { GameClock.ResetOffset(); Refresh(); });
|
||||
|
||||
var row4 = UIKit.Row(card, 70, 10);
|
||||
Btn(row4, "Рабочие", UIKit.Theme.Blue, () => BuildingWindow.Open(null));
|
||||
gridButton = UIKit.Button(row4, GridLabel, UIKit.Theme.Blue, ToggleGrid, -1, 70, 24);
|
||||
UIKit.Size(gridButton, -1, 70, 1);
|
||||
Btn(row4, "Камера", UIKit.Theme.Blue, ToggleCameraCard);
|
||||
Btn(row4, "Закрыть", UIKit.Theme.Gray, () => { panel.SetActive(false); if (cameraCard != null) cameraCard.SetActive(false); });
|
||||
|
||||
panel.SetActive(false);
|
||||
}
|
||||
|
||||
// ---------------- Камера 3D-поселения (решение 05.11: перспектива, числа подбираем в Unity и на телефоне) ----------------
|
||||
|
||||
GameObject cameraCard;
|
||||
TextMeshProUGUI cameraInfo;
|
||||
|
||||
// Ползунок: подпись, значение, диапазон. get/set -- поле камеры
|
||||
struct CamSlider { public string Label, Format; public float Min, Max; public System.Func<City3DCamera, float> Get; public System.Action<City3DCamera, float> Set; public string Pref; }
|
||||
|
||||
static readonly CamSlider[] CamSliders =
|
||||
{
|
||||
new CamSlider { Label = "Наклон", Format = "0°", Min = 25, Max = 85, Pref = City3DCamera.PrefPitch,
|
||||
Get = c => c.pitch, Set = (c, v) => c.pitch = v },
|
||||
new CamSlider { Label = "Перспектива", Format = "0°", Min = 15, Max = 75, Pref = City3DCamera.PrefFov,
|
||||
Get = c => c.fieldOfView, Set = (c, v) => c.SetFieldOfView(v) },
|
||||
new CamSlider { Label = "Ближе всего", Format = "0", Min = 3, Max = 30, Pref = City3DCamera.PrefMin,
|
||||
Get = c => c.minDistance, Set = (c, v) => { c.minDistance = Mathf.Min(v, c.maxDistance - 1); c.distance = Mathf.Max(c.distance, c.minDistance); } },
|
||||
new CamSlider { Label = "Дальше всего", Format = "0", Min = 20, Max = 120, Pref = City3DCamera.PrefMax,
|
||||
Get = c => c.maxDistance, Set = (c, v) => { c.maxDistance = Mathf.Max(v, c.minDistance + 1); c.distance = Mathf.Min(c.distance, c.maxDistance); } },
|
||||
new CamSlider { Label = "Сейчас", Format = "0", Min = 3, Max = 120, Pref = null,
|
||||
Get = c => c.distance, Set = (c, v) => c.distance = Mathf.Clamp(v, c.minDistance, c.maxDistance) },
|
||||
};
|
||||
|
||||
static City3DCamera Cam3D => Object.FindAnyObjectByType<City3DCamera>();
|
||||
|
||||
void ToggleCameraCard()
|
||||
{
|
||||
if (cameraCard != null) { bool show = !cameraCard.activeSelf; if (show) BuildCameraCard(); else cameraCard.SetActive(false); return; }
|
||||
BuildCameraCard();
|
||||
}
|
||||
|
||||
void BuildCameraCard()
|
||||
{
|
||||
if (cameraCard != null) Destroy(cameraCard);
|
||||
var cam = Cam3D;
|
||||
var safe = panel.transform.parent;
|
||||
var card = UIKit.Card(safe, new Color(0.08f, 0.08f, 0.11f, 0.95f), 20, 10);
|
||||
card.anchorMin = card.anchorMax = new Vector2(0, 0.5f);
|
||||
card.pivot = new Vector2(0, 0.5f);
|
||||
card.anchoredPosition = new Vector2(135 + 640, 0);
|
||||
card.sizeDelta = new Vector2(640, 0);
|
||||
card.gameObject.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
cameraCard = card.gameObject;
|
||||
|
||||
var title = UIKit.Text(card, "КАМЕРА 3D-ПОСЕЛЕНИЯ (ТЕСТ)", 28, UIKit.Theme.Gold, TextAlignmentOptions.Center);
|
||||
title.fontStyle = FontStyles.Bold;
|
||||
if (cam == null)
|
||||
{
|
||||
UIKit.Text(card, "Откройте 3D-город (сцена City3D)", 24, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var def in CamSliders)
|
||||
{
|
||||
var d = def;
|
||||
var row = UIKit.Row(card, 60, 12);
|
||||
var label = UIKit.Text(row, d.Label, 24);
|
||||
UIKit.Size(label, 170);
|
||||
var value = UIKit.Text(row, d.Get(cam).ToString(d.Format), 24, UIKit.Theme.TextDim, TextAlignmentOptions.Right);
|
||||
UIKit.Slider(row, Mathf.InverseLerp(d.Min, d.Max, d.Get(cam)), t =>
|
||||
{
|
||||
var c = Cam3D;
|
||||
if (c == null) return;
|
||||
d.Set(c, Mathf.Lerp(d.Min, d.Max, t));
|
||||
value.text = d.Get(c).ToString(d.Format);
|
||||
if (d.Pref != null) PlayerPrefs.SetFloat(d.Pref, d.Get(c)); // запомнить на этом устройстве
|
||||
c.Apply();
|
||||
UpdateCameraInfo();
|
||||
});
|
||||
value.transform.SetAsLastSibling();
|
||||
UIKit.Size(value, 80);
|
||||
}
|
||||
}
|
||||
cameraInfo = UIKit.Text(card, "", 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
||||
UpdateCameraInfo();
|
||||
|
||||
var row2 = UIKit.Row(card, 64, 10);
|
||||
Btn(row2, "Сбросить", UIKit.Theme.Gray, ResetCamera);
|
||||
Btn(row2, "Закрыть", UIKit.Theme.Gray, () => cameraCard.SetActive(false));
|
||||
}
|
||||
|
||||
// Строка, которую удобно переписать в отчёт / в сцену
|
||||
void UpdateCameraInfo()
|
||||
{
|
||||
var c = Cam3D;
|
||||
if (cameraInfo == null) return;
|
||||
cameraInfo.text = c == null ? "" :
|
||||
$"pitch {c.pitch:0} • fieldOfView {c.fieldOfView:0} • minDistance {c.minDistance:0} • maxDistance {c.maxDistance:0} • distance {c.distance:0}\n" +
|
||||
"Подобранное запоминается на этом устройстве. В сцену (для всех) -- вписать числа в Main Camera → City3DCamera";
|
||||
}
|
||||
|
||||
void ResetCamera()
|
||||
{
|
||||
foreach (var d in CamSliders) if (d.Pref != null) PlayerPrefs.DeleteKey(d.Pref);
|
||||
var c = Cam3D;
|
||||
if (c != null)
|
||||
{
|
||||
c.pitch = c.DefaultPitch;
|
||||
c.SetFieldOfView(c.DefaultFov);
|
||||
c.minDistance = c.DefaultMin;
|
||||
c.maxDistance = c.DefaultMax;
|
||||
c.distance = Mathf.Clamp(c.distance, c.minDistance, c.maxDistance);
|
||||
c.Apply();
|
||||
}
|
||||
BuildCameraCard();
|
||||
}
|
||||
|
||||
// Отладочная сетка стройки 3D-поселения: видна всегда, ярче, каждая 5-я линия жёлтая (05.11, просьба Миши)
|
||||
void ToggleGrid()
|
||||
{
|
||||
City3DController.SetDebugGrid(!City3DController.DebugGrid);
|
||||
if (gridButton != null) UIKit.SetButton(gridButton, GridLabel, true);
|
||||
}
|
||||
|
||||
static string GridLabel => City3DController.DebugGrid ? "Сетка: вкл" : "Сетка: выкл";
|
||||
|
||||
static void Btn(Transform row, string label, Color color, System.Action onClick)
|
||||
{
|
||||
var b = UIKit.Button(row, label, color, onClick, -1, 70, 24);
|
||||
UIKit.Size(b, -1, 70, 1);
|
||||
}
|
||||
|
||||
void Toggle()
|
||||
{
|
||||
panel.SetActive(!panel.activeSelf);
|
||||
if (panel.activeSelf) Refresh();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (panel == null || !panel.activeSelf || Time.unscaledTime < nextRefresh) return;
|
||||
nextRefresh = Time.unscaledTime + 0.5f;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void Refresh()
|
||||
{
|
||||
if (info == null) return;
|
||||
var sb = new System.Text.StringBuilder();
|
||||
|
||||
if (GameServices.TryGet(out IPopulation p))
|
||||
{
|
||||
sb.Append($"Население: <b>{p.Total}</b> / {p.HousingCap}\n");
|
||||
sb.Append($" жители {p.Civilians}: на работе {p.Workers}, свободно {p.FreeWorkers} из {p.Workforce}; воины {p.Soldiers}\n");
|
||||
sb.Append($"Еда в час: <color=#ff8a80>-{p.FoodPerHour:0.#}</color> прирост в час: +{p.GrowthPerHour:0.#}\n");
|
||||
if (p.IsStarving) sb.Append("<color=#ff5252><b>ГОЛОД: еды не хватает, прирост остановлен</b></color>\n");
|
||||
}
|
||||
else sb.Append("Модуль Населения не зарегистрирован\n");
|
||||
|
||||
if (GameServices.TryGet(out IProduction prod))
|
||||
sb.Append($"Добыча в час: еда +{prod.RatePerHour(ResourceType.Food):0.#}, дерево +{prod.RatePerHour(ResourceType.Wood):0.#}, " +
|
||||
$"камень +{prod.RatePerHour(ResourceType.Stone):0.#}, металл +{prod.RatePerHour(ResourceType.Iron):0.#}, золото +{prod.RatePerHour(ResourceType.Gold):0.#}\n");
|
||||
|
||||
if (GameServices.TryGet(out IEconomy e))
|
||||
sb.Append($"Склад: еда {e.Get(ResourceType.Food):0}/{Cap(e, ResourceType.Food)} дерево {e.Get(ResourceType.Wood):0}/{Cap(e, ResourceType.Wood)} " +
|
||||
$"камень {e.Get(ResourceType.Stone):0}/{Cap(e, ResourceType.Stone)}\n");
|
||||
else sb.Append("Экономика не найдена (нет ResourceManager на сцене)\n");
|
||||
|
||||
long off = GameClock.OffsetSeconds;
|
||||
sb.Append(off > 0 ? $"Перемотано: +{off / 3600f:0.#} ч" : "Время: реальное");
|
||||
info.text = sb.ToString();
|
||||
}
|
||||
|
||||
static string Cap(IEconomy e, ResourceType t)
|
||||
{
|
||||
float c = e.GetCapacity(t);
|
||||
return float.IsInfinity(c) ? "∞" : c.ToString("0");
|
||||
}
|
||||
|
||||
void AddAllResources()
|
||||
{
|
||||
ResourceType[] types = { ResourceType.Wood, ResourceType.Food, ResourceType.Stone, ResourceType.Iron, ResourceType.Gold, ResourceType.Gems };
|
||||
foreach (var t in types) AddResource(t, 1000); // лишнее обрежет лимит склада
|
||||
}
|
||||
|
||||
// Кристаллы до предела склада (лимит 50 -- решение 19.10, предел не меняется)
|
||||
void FillGems()
|
||||
{
|
||||
var rm = ResourceManager.Instance;
|
||||
if (rm == null) { UIKit.Toast("Нет склада (ResourceManager)"); return; }
|
||||
float cap = rm.GetCapacity(ResourceType.Gems);
|
||||
if (float.IsInfinity(cap)) cap = EconomyConfig.Load().storage.crystalCap;
|
||||
rm.SetResource(ResourceType.Gems, cap);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void AddResource(ResourceType type, float amount)
|
||||
{
|
||||
if (GameServices.TryGet(out IEconomy e)) e.Add(type, amount);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ZeroFood()
|
||||
{
|
||||
if (GameServices.TryGet(out IEconomy e)) e.Add(ResourceType.Food, -e.Get(ResourceType.Food));
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (instance == this) instance = null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2cba3db366a45feaaa3e1f4867ae423
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,416 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
// ТАБЛИЦА БАЛАНСА ЭКОНОМИКИ. Все числа экономики -- здесь, а не в коде модулей.
|
||||
//
|
||||
// Ассет: Assets/Resources/EconomyConfig.asset (выдели его в окне Project и меняй числа в Inspector,
|
||||
// перекомпиляция не нужна). Создать заново: правый клик в Project -> Create -> Bogatyri -> Economy Config,
|
||||
// имя файла -- EconomyConfig, папка -- Resources.
|
||||
//
|
||||
// Модули конфиг не видят: App/GameModules переводит его в правила модулей (например, правила Населения).
|
||||
// Числа -- из дизайн-документа. Помеченные "не утверждено" -- временные, их решает команда.
|
||||
[CreateAssetMenu(fileName = "EconomyConfig", menuName = "Bogatyri/Economy Config")]
|
||||
public class EconomyConfig : ScriptableObject
|
||||
{
|
||||
[Serializable]
|
||||
public class PopulationSettings
|
||||
{
|
||||
[Tooltip("Стартовый город T1: всего людей")]
|
||||
public int startTotal = 300;
|
||||
[Tooltip("Из них рабочих")]
|
||||
public int startWorkers = 120;
|
||||
[Tooltip("Из них воинов (выдаются в Армию пехотой при новой игре)")]
|
||||
public int startSoldiers = 30;
|
||||
|
||||
[Tooltip("Еды в час на одного жителя")]
|
||||
public float foodPerCivilianPerHour = 0.1f;
|
||||
[Tooltip("Еды в час на одного воина (и новобранца в Казармах), если норма по типам не задана")]
|
||||
public float foodPerSoldierPerHour = 0.2f;
|
||||
[Tooltip("Еды в час на воина по типам: пехота, лучники, кавалерия, осадные. Решения 01.11, п. 4: 0,15 / 0,20 / 0,15 / 0,15. " +
|
||||
"Раненые едят так же, даже ожидая койку. Пусто -- все по foodPerSoldierPerHour")]
|
||||
public float[] foodPerTroopTypePerHour = { 0.15f, 0.2f, 0.15f, 0.15f };
|
||||
|
||||
[Tooltip("Прирост людей в час, если есть еда (документ: 2–3)")]
|
||||
public float growthPerHour = 2.5f;
|
||||
|
||||
[Tooltip("Предел населения без Домов на 1 уровне Ратуши (решение команды от 19.10: 300 при 300 -- прирост стоит)")]
|
||||
public int housingBase = 300;
|
||||
[Tooltip("Прибавка к пределу за каждый следующий уровень Ратуши. Не утверждено")]
|
||||
public int housingPerTownHallLevel = 50;
|
||||
[Tooltip("Прибавка к пределу за каждый уровень Дома (здание \"Население\"). Не утверждено")]
|
||||
public int housingPerHouseLevel = 50;
|
||||
|
||||
[Tooltip("Офлайн-прогресс считается не больше чем за столько часов. Не утверждено")]
|
||||
public int maxOfflineHours = 48;
|
||||
|
||||
[Tooltip("Гражданский резерв по уровню Ратуши: [0] -- уровень 1, [1] -- уровень 2... Столько свободных жителей нельзя забрать в войско. Регламент 31.10, ТЕСТ: 1-2 -- 10, 3-5 -- 20, 6-10 -- 30")]
|
||||
public int[] civilianReserveByTownHallLevel = { 10, 10, 20, 20, 20, 30, 30, 30, 30, 30 };
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ProductionSettings // этап 3: производство через рабочих
|
||||
{
|
||||
[Tooltip("Лесоруб T1: дерева в час")]
|
||||
public float woodcutterPerHour = 2f;
|
||||
[Tooltip("Фермер T1: еды в час")]
|
||||
public float farmerPerHour = 2f;
|
||||
[Tooltip("Каменотёс T1: камня в час")]
|
||||
public float stonecutterPerHour = 1.5f;
|
||||
[Tooltip("Шахтёр T1: металла в час")]
|
||||
public float minerIronPerHour = 1f;
|
||||
[Tooltip("Шахтёр T1: золота в час")]
|
||||
public float minerGoldPerHour = 0.4f;
|
||||
[Tooltip("Мест для рабочих в здании T1 (Лесопилка и т.п.)")]
|
||||
public int workerSlotsT1 = 30;
|
||||
[Tooltip("+ мест за каждый уровень здания выше первого. Не утверждено")]
|
||||
public int workerSlotsPerLevel = 10;
|
||||
[Tooltip("+ к норме за каждый уровень здания выше первого (0.1 = +10%). Не утверждено")]
|
||||
public float efficiencyPerLevel = 0.1f;
|
||||
[Tooltip("Голод: множитель добычи всех ресурсов, кроме еды (0.5 -- вдвое меньше). Еда добывается без штрафа. Регламент 31.10, ТЕСТ")]
|
||||
[Range(0f, 1f)] public float famineNonFoodMultiplier = 0.5f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CrystalExpansion
|
||||
{
|
||||
public int townHallLevel = 3;
|
||||
public int capacity = 75;
|
||||
public float gold, wood, stone, iron;
|
||||
|
||||
public CrystalExpansion() { }
|
||||
public CrystalExpansion(int townHall, int cap, float gold, float wood, float stone, float iron)
|
||||
{
|
||||
townHallLevel = townHall; capacity = cap; this.gold = gold; this.wood = wood; this.stone = stone; this.iron = iron;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class StorageSettings // этап 1: лимиты хранения
|
||||
{
|
||||
[Tooltip("Лимит без Складов: дерево, камень, металл, золото. Не утверждено")]
|
||||
public float baseGoods = 1000f;
|
||||
[Tooltip("Лимит еды без Амбаров. Не утверждено")]
|
||||
public float baseFood = 1500f;
|
||||
[Tooltip("+ к лимиту дерева, камня, металла, золота за каждый уровень Склада. Не утверждено")]
|
||||
public float perWarehouseLevel = 1000f;
|
||||
[Tooltip("+ к лимиту еды за каждый уровень Амбара. Не утверждено")]
|
||||
public float perGranaryLevel = 1500f;
|
||||
[Tooltip("Стартовая вместимость синих кристаллов (регламент валют 05.11, разд. 8). ТЕСТ")]
|
||||
public float crystalCap = 50f;
|
||||
[Tooltip("Недельный PvE-предел: не больше стольких НОВЫХ синих кристаллов за окно из заданий, событий, боёв, PvE-торговца. " +
|
||||
"Шахта, торговля и грабёж -- вне предела (регламент валют 05.11, разд. 5.2; ответ 06.11, п. 2). ТЕСТ")]
|
||||
public int crystalPveLimit = 6;
|
||||
[Tooltip("Окно недельного PvE-предела, реальных суток (скользящее). ТЕСТ")]
|
||||
public int crystalPveWindowDays = 7;
|
||||
[Tooltip("Компенсация золотом за каждый синий кристалл PvE-награды сверх недельного предела (величина не согласована). ТЕСТ")]
|
||||
public float crystalCompensationGold = 1000f;
|
||||
[Tooltip("Кристальная шахта: синих кристаллов за период на одну работающую шахту (ответ 06.11, п. 3). ТЕСТ")]
|
||||
public int crystalMinePerPeriod = 3;
|
||||
[Tooltip("Кристальная шахта: период, РЕАЛЬНЫХ суток (не игровые часы ×12). ТЕСТ")]
|
||||
public int crystalMinePeriodDays = 7;
|
||||
[Tooltip("Редкий PvE-торговец: цена синего кристалла в золоте по порядку покупок за 7 реальных суток; покупок не больше, " +
|
||||
"чем цен (регламент валют 05.11, разд. 3). Входит в недельный PvE-предел. ТЕСТ")]
|
||||
public int[] crystalTraderPrices = { 30000, 60000 };
|
||||
[Tooltip("Платные расширения хранилища кристаллов по порядку: с какой Ратуши, до скольки, цена (золото и обычные ресурсы). " +
|
||||
"Ратуша только разрешает -- само не растёт (регламент валют 05.11, разд. 8; ответ 06.11, п. 1). Числа и цены ТЕСТ")]
|
||||
public CrystalExpansion[] crystalExpansions =
|
||||
{
|
||||
new CrystalExpansion(3, 75, 2000, 1500, 1500, 0),
|
||||
new CrystalExpansion(5, 100, 4000, 3000, 3000, 1000),
|
||||
new CrystalExpansion(7, 150, 7000, 5000, 5000, 2000),
|
||||
new CrystalExpansion(9, 200, 10000, 8000, 8000, 4000),
|
||||
};
|
||||
|
||||
[Tooltip("Цена Склада: дерево / камень. Не утверждено")]
|
||||
public float warehouseWood = 200f;
|
||||
public float warehouseStone = 100f;
|
||||
[Tooltip("Цена Амбара: дерево / камень. Не утверждено")]
|
||||
public float granaryWood = 150f;
|
||||
public float granaryStone = 50f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UpgradeSettings // улучшение зданий (рабочие места, Дома, Склады, Амбары)
|
||||
{
|
||||
[Tooltip("Цена улучшения с 1 на 2 уровень: дерево / камень. Не утверждено")]
|
||||
public float baseWood = 150f;
|
||||
public float baseStone = 100f;
|
||||
[Tooltip("Во сколько раз дорожает каждый следующий уровень. Не утверждено")]
|
||||
public float multiplierPerLevel = 1.6f;
|
||||
public int maxLevel = 10;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DepositSettings // этап 4: качество месторождения (пока не используется)
|
||||
{
|
||||
public float poor = 0.6f;
|
||||
public float normal = 1f;
|
||||
public float rich = 1.5f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SoldierSettings // этап 5: цена солдата
|
||||
{
|
||||
[Tooltip("Сколько жителей стоит один воин (берутся из жителей при начале обучения)")]
|
||||
public int people = 1;
|
||||
public float food = 15f;
|
||||
public float iron = 5f;
|
||||
public float gold = 5f;
|
||||
[Tooltip("Время подготовки, секунд. Не утверждено")]
|
||||
public int trainSeconds = 60;
|
||||
[Tooltip("Уровень Казарм, с которого открывается тип войск (порядок как в GameData.Troops: пехота, лучники, кавалерия, осадные). " +
|
||||
"Нужен и уровень Ратуши. Считаются самые прокачанные Казармы. ТЕСТ, не утверждено")]
|
||||
public int[] requiredBarracksLevel = { 1, 2, 3, 5 };
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ConstructionSettings // время стройки и улучшения, строители (01.11, ТЕСТ)
|
||||
{
|
||||
[Tooltip("Сколько строек и улучшений идёт одновременно (строители). ТЕСТ")]
|
||||
public int builders = 2;
|
||||
[Tooltip("Постройка нового здания, секунд. ТЕСТ")]
|
||||
public int buildSeconds = 60;
|
||||
[Tooltip("Улучшение до уровня 2, 3, 4... (секунд): [0] -- до 2 ур., [1] -- до 3 ур. ... ТЕСТ")]
|
||||
public int[] upgradeSecondsToLevel = { 60, 120, 300, 600, 1200, 1800, 3600, 5400, 7200 };
|
||||
[Tooltip("Ратуша строится дольше остальных: множитель времени улучшения. ТЕСТ")]
|
||||
public float townHallTimeMultiplier = 2f;
|
||||
[Tooltip("Устарело (06.11): ускорение теперь за золото -- secondsPerGold")]
|
||||
public int secondsPerGem = 30;
|
||||
[Tooltip("Ускорение стройки, улучшения и обучения войск -- за золото (06.11; регламент валют: золото -- основной ресурс " +
|
||||
"построек и улучшений, синие кристаллы на ускорения не тратятся): секунд за 1 золото. ТЕСТ")]
|
||||
public int secondsPerGold = 10;
|
||||
// Сноса зданий нет (решение 04.11): только бесплатный перенос
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class BuildingLimit
|
||||
{
|
||||
public string buildingName;
|
||||
[Tooltip("Сколько таких зданий можно иметь на Ратуше 1, 2, 3 ... уровня. 0 -- нельзя")]
|
||||
public int[] maxByTownHall;
|
||||
|
||||
public BuildingLimit() { }
|
||||
public BuildingLimit(string name, params int[] max) { buildingName = name; maxByTownHall = max; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ProtectionSettings // защита ресурсов от грабежа (01.11, ТЕСТ)
|
||||
{
|
||||
[Tooltip("Защищено без Складов: дерево, камень, металл, золото. ТЕСТ")]
|
||||
public float baseGoods = 100f;
|
||||
[Tooltip("+ к защите дерева, камня, металла, золота за каждый уровень Склада. ТЕСТ")]
|
||||
public float perWarehouseLevel = 200f;
|
||||
[Tooltip("Защищено без Амбаров: еда. ТЕСТ")]
|
||||
public float baseFood = 100f;
|
||||
[Tooltip("+ к защите еды за каждый уровень Амбара. ТЕСТ")]
|
||||
public float perGranaryLevel = 300f;
|
||||
[Tooltip("Кристаллы: без технологии «Грабёж» нападающий не уносит ничего (решение 04.11). С ней -- не больше этой доли запаса. ТЕСТ, точные ограничения определят отдельно")]
|
||||
[Range(0f, 1f)] public float plunderGemsShare = 0.1f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class StartResources
|
||||
{
|
||||
[Tooltip("Ресурсы новой игры. Не утверждено")]
|
||||
public float wood = 500f;
|
||||
public float food = 500f;
|
||||
public float stone = 200f;
|
||||
public float iron = 100f;
|
||||
public float gold = 100f;
|
||||
public float gems = 0f;
|
||||
}
|
||||
|
||||
[Header("Население (этап 2)")]
|
||||
public PopulationSettings population = new PopulationSettings();
|
||||
|
||||
[Header("Стартовые ресурсы новой игры")]
|
||||
public StartResources startResources = new StartResources();
|
||||
|
||||
[Header("Склады и лимиты (этап 1)")]
|
||||
public StorageSettings storage = new StorageSettings();
|
||||
|
||||
[Header("Производство T1 (этап 3)")]
|
||||
public ProductionSettings production = new ProductionSettings();
|
||||
|
||||
[Header("Улучшение зданий")]
|
||||
public UpgradeSettings upgrade = new UpgradeSettings();
|
||||
|
||||
[Header("Месторождения (этап 4)")]
|
||||
public DepositSettings deposits = new DepositSettings();
|
||||
|
||||
[Header("Солдат T1 (этап 5)")]
|
||||
public SoldierSettings soldier = new SoldierSettings();
|
||||
|
||||
[Header("Стройка: время, строители, снос (ТЕСТ)")]
|
||||
public ConstructionSettings construction = new ConstructionSettings();
|
||||
|
||||
[Header("Лимит зданий по уровню Ратуши (ТЕСТ). Здания не из списка -- без лимита")]
|
||||
public BuildingLimit[] buildingLimits =
|
||||
{
|
||||
// Ратуша: 1 2 3 4 5 6 7 8 9 10
|
||||
new BuildingLimit("Лес", 2, 3, 3, 4, 4, 5, 5, 6, 6, 7),
|
||||
new BuildingLimit("Еда", 2, 3, 3, 4, 4, 5, 5, 6, 6, 7),
|
||||
new BuildingLimit("Камень", 2, 2, 3, 3, 4, 4, 5, 5, 6, 6),
|
||||
new BuildingLimit("Металл", 0, 0, 1, 1, 2, 2, 3, 3, 4, 4),
|
||||
new BuildingLimit("Золото", 0, 1, 1, 2, 2, 3, 3, 3, 4, 4),
|
||||
new BuildingLimit("Население", 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),
|
||||
new BuildingLimit("Склад", 1, 1, 2, 2, 2, 3, 3, 3, 4, 4),
|
||||
new BuildingLimit("Амбар", 1, 1, 2, 2, 2, 3, 3, 3, 4, 4),
|
||||
new BuildingLimit("Казармы", 1, 1, 2, 2, 3, 3, 3, 3, 3, 3),
|
||||
new BuildingLimit("Госпиталь", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||
new BuildingLimit("Терем богатыря", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||
new BuildingLimit("Кристалл", 0, 0, 0, 0, 0, 1, 1, 1, 2, 2), // Кристальная шахта: вторая -- на Ратуше 9 (ответ 06.11, п. 3; уровень ТЕСТ)
|
||||
};
|
||||
|
||||
[Header("Защита ресурсов от грабежа (ТЕСТ)")]
|
||||
public ProtectionSettings protection = new ProtectionSettings();
|
||||
|
||||
// ---------------- Загрузка ----------------
|
||||
|
||||
static EconomyConfig cached;
|
||||
|
||||
// Конфиг из Assets/Resources/EconomyConfig.asset. Нет ассета -- числа по умолчанию (из этого файла) и предупреждение.
|
||||
public static EconomyConfig Load()
|
||||
{
|
||||
if (cached != null) return cached;
|
||||
cached = Resources.Load<EconomyConfig>("EconomyConfig");
|
||||
if (cached == null)
|
||||
{
|
||||
Debug.LogWarning("[ЭКОНОМИКА] Нет Assets/Resources/EconomyConfig.asset -- берутся числа по умолчанию из EconomyConfig.cs.");
|
||||
cached = CreateInstance<EconomyConfig>();
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Лимит склада для ресурса по уровням Складов и Амбаров. PositiveInfinity -- без лимита (люди, сила)
|
||||
public float GetCapacity(ResourceType type, int warehouseLevels, int granaryLevels)
|
||||
{
|
||||
var s = storage;
|
||||
switch (type)
|
||||
{
|
||||
case ResourceType.Wood:
|
||||
case ResourceType.Stone:
|
||||
case ResourceType.Iron:
|
||||
case ResourceType.Gold: return s.baseGoods + s.perWarehouseLevel * warehouseLevels;
|
||||
case ResourceType.Food: return s.baseFood + s.perGranaryLevel * granaryLevels;
|
||||
case ResourceType.Gems: return CrystalStorageNow().Capacity; // стартовая + купленные расширения
|
||||
default: return float.PositiveInfinity;
|
||||
}
|
||||
}
|
||||
|
||||
// Сколько зданий с таким именем можно иметь на этом уровне Ратуши. -1 -- без лимита (здания нет в списке)
|
||||
public int BuildingLimitFor(string buildingName, int townHallLevel)
|
||||
{
|
||||
if (buildingLimits == null || string.IsNullOrEmpty(buildingName)) return -1;
|
||||
foreach (var l in buildingLimits)
|
||||
{
|
||||
if (l == null || l.buildingName != buildingName || l.maxByTownHall == null || l.maxByTownHall.Length == 0) continue;
|
||||
return Mathf.Max(0, l.maxByTownHall[Mathf.Clamp(townHallLevel, 1, l.maxByTownHall.Length) - 1]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Время стройки (targetLevel = 1) или улучшения до targetLevel, мс. Ратуша -- с множителем
|
||||
public long ConstructionMs(bool townHall, int targetLevel)
|
||||
{
|
||||
var c = construction;
|
||||
double seconds;
|
||||
if (targetLevel <= 1) seconds = c.buildSeconds;
|
||||
else
|
||||
{
|
||||
var t = c.upgradeSecondsToLevel;
|
||||
seconds = t == null || t.Length == 0 ? c.buildSeconds : t[Mathf.Clamp(targetLevel - 2, 0, t.Length - 1)];
|
||||
}
|
||||
if (townHall) seconds *= Mathf.Max(0.01f, c.townHallTimeMultiplier);
|
||||
return (long)(Math.Max(0, seconds) * 1000.0);
|
||||
}
|
||||
|
||||
// Сколько ресурса не отнять при грабеже: часть запаса, которую прячут Склады и Амбары
|
||||
public float GetProtected(ResourceType type, int warehouseLevels, int granaryLevels)
|
||||
{
|
||||
var p = protection;
|
||||
switch (type)
|
||||
{
|
||||
case ResourceType.Wood:
|
||||
case ResourceType.Stone:
|
||||
case ResourceType.Iron:
|
||||
case ResourceType.Gold: return p.baseGoods + p.perWarehouseLevel * warehouseLevels;
|
||||
case ResourceType.Food: return p.baseFood + p.perGranaryLevel * granaryLevels;
|
||||
case ResourceType.Gems: return float.PositiveInfinity; // без «Грабежа» кристаллы не грабятся (с ним -- ResourceProtection)
|
||||
default: return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Цена улучшения здания с уровня level на level + 1
|
||||
public ResourceAmount[] UpgradeCost(int level)
|
||||
{
|
||||
float m = Mathf.Pow(upgrade.multiplierPerLevel, Mathf.Max(0, level - 1));
|
||||
return new[]
|
||||
{
|
||||
new ResourceAmount(ResourceType.Wood, Mathf.Round(upgrade.baseWood * m)),
|
||||
new ResourceAmount(ResourceType.Stone, Mathf.Round(upgrade.baseStone * m)),
|
||||
};
|
||||
}
|
||||
|
||||
public float GetStartResource(ResourceType type)
|
||||
{
|
||||
var s = startResources;
|
||||
switch (type)
|
||||
{
|
||||
case ResourceType.Wood: return s.wood;
|
||||
case ResourceType.Food: return s.food;
|
||||
case ResourceType.Stone: return s.stone;
|
||||
case ResourceType.Iron: return s.iron;
|
||||
case ResourceType.Gold: return s.gold;
|
||||
case ResourceType.Gems: return s.gems;
|
||||
default: return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Цена ускорения в золоте: сколько осталось секунд -> золото (не меньше 1). Стройка, улучшение и обучение войск
|
||||
public static int SpeedUpGold(double secondsLeft)
|
||||
{
|
||||
int perGold = Mathf.Max(1, Load().construction.secondsPerGold);
|
||||
return Mathf.Max(1, Mathf.CeilToInt((float)(secondsLeft / perGold)));
|
||||
}
|
||||
|
||||
// ---------------- Хранилище синих кристаллов (регламент валют 05.11, разд. 8; правила -- CrystalStorage) ----------------
|
||||
|
||||
// Правила с купленной ступенью (её хранит Экономика: ResourceManager.CrystalStorageLevel, секция "economy")
|
||||
public static CrystalStorage CrystalStorageNow()
|
||||
{
|
||||
var c = Load();
|
||||
var tiers = new List<CrystalStorage.Tier>();
|
||||
if (c.storage.crystalExpansions != null)
|
||||
foreach (var e in c.storage.crystalExpansions)
|
||||
{
|
||||
if (e == null) continue;
|
||||
var cost = new List<ResourceAmount>();
|
||||
if (e.gold > 0) cost.Add(new ResourceAmount(ResourceType.Gold, e.gold));
|
||||
if (e.wood > 0) cost.Add(new ResourceAmount(ResourceType.Wood, e.wood));
|
||||
if (e.stone > 0) cost.Add(new ResourceAmount(ResourceType.Stone, e.stone));
|
||||
if (e.iron > 0) cost.Add(new ResourceAmount(ResourceType.Iron, e.iron));
|
||||
tiers.Add(new CrystalStorage.Tier { TownHallLevel = e.townHallLevel, Capacity = e.capacity, Cost = cost.ToArray() });
|
||||
}
|
||||
var storage = new CrystalStorage(Mathf.RoundToInt(c.storage.crystalCap), tiers);
|
||||
if (ResourceManager.Instance != null) storage.SetLevel(ResourceManager.Instance.CrystalStorageLevel);
|
||||
return storage;
|
||||
}
|
||||
|
||||
// Купить следующую ступень: цена списывается один раз (всё или ничего), ступень и ресурсы -- в одной секции
|
||||
// сохранения "economy", сохраняется сразу. error -- почему нельзя
|
||||
public static bool TryExpandCrystalStorage(int townHallLevel, out string error)
|
||||
{
|
||||
var rm = ResourceManager.Instance;
|
||||
var storage = CrystalStorageNow();
|
||||
error = storage.WhyNot(townHallLevel);
|
||||
if (error != null) return false;
|
||||
if (rm == null || !GameServices.TryGet(out IEconomy economy)) { error = "Склад не найден"; return false; }
|
||||
if (!storage.TryExpand(townHallLevel, cost => economy.TrySpend(cost))) { error = "Не хватает ресурсов"; return false; }
|
||||
rm.CrystalStorageLevel = storage.Level;
|
||||
rm.SetCapacity(ResourceType.Gems, storage.Capacity);
|
||||
if (SaveManager.Instance != null) SaveManager.Instance.SaveGame();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ddb59400c6d4b08b3afaaadc461ec1e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,320 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
// ТОЧКА СБОРКИ МОДУЛЕЙ (слой приложения). Документ: Docs/Modules/README.md
|
||||
//
|
||||
// Здесь и только здесь модули соединяются с остальной игрой:
|
||||
// 1. регистрируются реализации контрактов, которые не регистрируются сами;
|
||||
// 2. события модулей подключаются к системам, которые ещё не разнесены по модулям (GameProgress, Казармы).
|
||||
// Модули друг о друге ничего не знают -- всю "проводку" видно в одном файле.
|
||||
public static class GameModules
|
||||
{
|
||||
// Модули экономики для слоя приложения (окна, синхронизация города). Модули друг друга так не берут -- только через контракты.
|
||||
public static PopulationService Population { get; private set; }
|
||||
public static ProductionService Production { get; private set; }
|
||||
|
||||
// После SubsystemRegistration (там ядро очищается) и раньше любых Awake
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
|
||||
static void Register()
|
||||
{
|
||||
// Боевая система -- чистый C#, сама зарегистрироваться не может.
|
||||
// Числа боя -- из таблицы BattleConfig, сила воина каждого типа -- из GameData.Troops
|
||||
var unitPower = new int[TroopType.Count];
|
||||
for (int i = 0; i < TroopType.Count && i < GameData.Troops.Count; i++) unitPower[i] = GameData.Troops[i].power;
|
||||
GameServices.Register<IBattleSystem>(new BattleSystem(BattleConfig.Load().ToRules(unitPower)));
|
||||
|
||||
// Марши: скорости по типам -- в модуль Мира, слоты похода -- в модуль Армии (таблица MarchConfig)
|
||||
var marchConfig = MarchConfig.Load();
|
||||
MarchManager.SpeedRules = marchConfig.ToSpeedRules();
|
||||
MarchManager.PointRules = WorldResourceConfig.Load().ToRules(); // ресурсные точки (регламент 31.10)
|
||||
MarchManager.Patrols = marchConfig.ToPatrolRules(); // патрули-заглушки соседей (Разведка М1)
|
||||
|
||||
// Разведка -- чистый C#: туман, обзор, чужие армии (секция "intelligence"). Карту и армии читает у Мира
|
||||
// через IWorldMap; досчитывает наблюдение перед каждым расчётом мира (WorldAdvancingEvent)
|
||||
var intelConfig = IntelligenceConfig.Load();
|
||||
var intelligence = new IntelligenceService(intelConfig.ToRules());
|
||||
GameServices.Register<IIntelligence>(intelligence);
|
||||
SaveRegistry.Register(intelligence);
|
||||
EventBus.Subscribe<WorldAdvancingEvent>(e => intelligence.ObserveUntil(e.ToMs));
|
||||
// «Обзор» для теста, пока нет дерева технологий (после загрузки и в новой игре)
|
||||
void TestObservation() { if (intelConfig.testObservationLevel > 0) intelligence.ObservationLevel = intelConfig.testObservationLevel; }
|
||||
TestObservation();
|
||||
EventBus.Subscribe<GameLoadedEvent>(_ => TestObservation());
|
||||
EventBus.Subscribe<GameResetEvent>(_ => TestObservation());
|
||||
|
||||
// Армия -- чистый C#, единственное место хранения войск (секция "army")
|
||||
var army = new ArmyService(marchConfig.baseMarchSlots, HospitalConfig.Load().ToRules(), HospitalConfig.Load().ToMissingRules()); // госпиталь, пропавшие
|
||||
GameServices.Register<IArmy>(army);
|
||||
SaveRegistry.Register(army);
|
||||
EventBus.Subscribe<TroopsChangedEvent>(_ => { if (GameProgress.HasInstance) GameProgress.Instance.MarkChanged(); });
|
||||
EventBus.Subscribe<GameLoadedEvent>(_ => MigrateLegacyArmy(army));
|
||||
// Очередь обучения -- у Армии (решения 31.10, п. 8): статистика заданий по её событию, старая очередь -- переносится
|
||||
EventBus.Subscribe<TroopsTrainedEvent>(e => { if (GameProgress.HasInstance) GameProgress.Instance.OnTroopsTrained(e); });
|
||||
EventBus.Subscribe<GameLoadedEvent>(_ => MigrateLegacyTraining(army));
|
||||
// Пропавшие без вести вернулись / списаны (решения 01.11, п. 5)
|
||||
EventBus.Subscribe<MissingReturnedEvent>(e =>
|
||||
{
|
||||
if (e.Healthy + e.Wounded > 0)
|
||||
UIKit.Toast($"Вернулись пропавшие без вести: {e.Healthy + e.Wounded}" + (e.Wounded > 0 ? $" (ранены {e.Wounded} -- в госпиталь)" : ""));
|
||||
if (GameProgress.HasInstance) GameProgress.Instance.MarkChanged();
|
||||
});
|
||||
EventBus.Subscribe<MissingWrittenOffEvent>(e =>
|
||||
{
|
||||
if (e.Count > 0) UIKit.Toast($"Не вернулись и списаны пропавшие без вести: {e.Count}");
|
||||
if (GameProgress.HasInstance) GameProgress.Instance.MarkChanged();
|
||||
});
|
||||
// Раненые вылечены -- вернулись в город
|
||||
EventBus.Subscribe<SoldiersHealedEvent>(e =>
|
||||
{
|
||||
if (e.Type >= 0 && e.Type < GameData.Troops.Count) UIKit.Toast("Вылечены в госпитале: " + e.Count + " × " + GameData.Troops[e.Type].name);
|
||||
if (GameProgress.HasInstance) GameProgress.Instance.MarkChanged();
|
||||
});
|
||||
|
||||
// Население -- чистый C#: жители, рабочие, еда, прирост (секция "population"). Числа -- из EconomyConfig.
|
||||
var config = EconomyConfig.Load();
|
||||
var population = new PopulationService(ToPopulationRules(config));
|
||||
Population = population;
|
||||
GameServices.Register<IPopulation>(population);
|
||||
SaveRegistry.Register(population);
|
||||
|
||||
// Добыча через рабочих -- чистый C# (секция "production"). Какие здания стоят, сообщает App/CitySync.
|
||||
var production = new ProductionService(ToProductionRules(config));
|
||||
Production = production;
|
||||
GameServices.Register<IProduction>(production);
|
||||
SaveRegistry.Register(production);
|
||||
|
||||
|
||||
// Тап по зданию -> окно здания и рабочих (статические события переживают Play в редакторе -- сначала отписка)
|
||||
ResourceCollector.Tapped -= BuildingWindow.OnWorkplaceTapped;
|
||||
ResourceCollector.Tapped += BuildingWindow.OnWorkplaceTapped;
|
||||
StorageBuilding.Tapped -= BuildingWindow.OnStorageTapped;
|
||||
StorageBuilding.Tapped += BuildingWindow.OnStorageTapped;
|
||||
TownHall.Tapped -= BuildingWindow.OnTownHallTapped;
|
||||
TownHall.Tapped += BuildingWindow.OnTownHallTapped;
|
||||
|
||||
// Стройка и улучшение по времени, строители (01.11, ТЕСТ): числа -- EconomyConfig, раздел "Стройка"
|
||||
BuildTimer.Builders = Mathf.Max(1, config.construction.builders);
|
||||
ConstructionClock.Init(); // офлайн: стройка вступает в силу в момент своего завершения (решения 04.11)
|
||||
BuildTimer.DurationMs = (townHall, level) => EconomyConfig.Load().ConstructionMs(townHall, level);
|
||||
BuildTimer.Tapped -= OnBuildTimerTapped;
|
||||
BuildTimer.Tapped += OnBuildTimerTapped;
|
||||
BuildTimer.Completed -= OnBuildCompleted;
|
||||
BuildTimer.Completed += OnBuildCompleted;
|
||||
|
||||
// 3D-поселение (сцена City3D, 05.11): свой город, своя секция сохранения "settlement3d"
|
||||
SaveRegistry.Register(City3DSave.Instance);
|
||||
|
||||
// Защита ресурсов от грабежа (01.11, ТЕСТ): сколько у города нельзя унести в набеге
|
||||
GameServices.Register<IResourceProtection>(new ResourceProtection());
|
||||
EventBus.Subscribe<GameLoadedEvent>(_ => population.OnGameLoaded(GameClock.UnixNow)); // офлайн-прогресс
|
||||
EventBus.Subscribe<GameTimeAdvancedEvent>(e => population.Tick(e.UnixNow)); // перемотка (отладка)
|
||||
EventBus.Subscribe<TroopsChangedEvent>(_ => population.Changed()); // воины -- тоже люди
|
||||
EventBus.Subscribe<TownHallLevelChangedEvent>(_ => population.Changed()); // предел жилья вырос
|
||||
EventBus.Subscribe<PopulationChangedEvent>(SyncPopulationResource);
|
||||
EventBus.Subscribe<NewGameStartedEvent>(_ => GiveStartingKit(population, army, config));
|
||||
EventBus.Subscribe<GameResetEvent>(_ => GiveStartingKit(population, army, config));
|
||||
|
||||
// Круг -- чистый C#, сам сохраняет, в каком Круге игрок (секция "circle")
|
||||
var circle = new CircleService();
|
||||
GameServices.Register<ICircle>(circle);
|
||||
SaveRegistry.Register(circle);
|
||||
|
||||
// Чат -- чистый C#; история живёт, пока открыта игра (между сценами тоже)
|
||||
GameServices.Register<IChat>(new ChatService(System.DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()));
|
||||
|
||||
|
||||
// Госпиталь (решения 31.10, п. 7): здание города даёт места для лечения Армии через контракт IHospital
|
||||
GameServices.Register<IHospital>(new HospitalBeds());
|
||||
|
||||
|
||||
// Статистика для заданий (GameProgress) -- по событиям Мира
|
||||
EventBus.Subscribe<MarchStartedEvent>(OnMarchStarted);
|
||||
EventBus.Subscribe<MarchReturnedEvent>(OnMarchReturned);
|
||||
|
||||
// Окно "ВОЙСКО ВЕРНУЛОСЬ" в городе
|
||||
MarchReturnPopup.Reset();
|
||||
EventBus.Subscribe<MarchReturnedEvent>(MarchReturnPopup.OnMarchReturned);
|
||||
}
|
||||
|
||||
static void OnBuildTimerTapped(BuildTimer timer)
|
||||
{
|
||||
if (timer != null) BuildingWindow.Open(timer.gameObject);
|
||||
}
|
||||
|
||||
static void OnBuildCompleted(BuildTimer timer)
|
||||
{
|
||||
if (timer == null) return;
|
||||
if (CitySync.Instance != null) CitySync.Instance.SyncNow();
|
||||
var id = timer.GetComponent<PlacedBuildingId>();
|
||||
string name = timer.GetComponent<TownHall>() != null ? "Ратуша" : id != null ? id.buildingName : timer.name;
|
||||
UIKit.Toast(timer.IsConstruction ? $"Построено: {name}" : $"{name}: улучшено до {timer.targetLevel} уровня");
|
||||
BuildingWindow.OnBuildCompleted(timer);
|
||||
}
|
||||
|
||||
// Сцена загружена, Awake всех объектов прошли, Start -- ещё нет (сохранение грузится в Start)
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
static void MigrateLegacyMarches()
|
||||
{
|
||||
// Раньше походы хранились в progress.json (GameProgress.Data.sashaMarches).
|
||||
// Теперь их сохраняет модуль Мира в save.json, секция "world". Переносим один раз.
|
||||
var data = GameProgress.Instance.Data;
|
||||
if (data.sashaMarches == null || data.sashaMarches.Count == 0 || MarchManager.Instance == null) return;
|
||||
|
||||
// Если в save.json уже есть секция "world", загрузка сохранения заменит эти походы на сохранённые
|
||||
MarchManager.Instance.LoadMarches(new System.Collections.Generic.List<Bogatyri.World.MarchData>(data.sashaMarches));
|
||||
|
||||
// Из progress.json убираем, только когда походы уже записаны в save.json -- иначе вылет игры их потеряет
|
||||
EventBus.Subscribe<GameSavedEvent>(ClearLegacyMarches);
|
||||
}
|
||||
|
||||
static void ClearLegacyMarches(GameSavedEvent e)
|
||||
{
|
||||
EventBus.Unsubscribe<GameSavedEvent>(ClearLegacyMarches);
|
||||
GameProgress.Instance.Data.sashaMarches.Clear();
|
||||
GameProgress.Instance.MarkChanged();
|
||||
Debug.Log("[МОДУЛИ] Походы перенесены из progress.json в модуль Мира.");
|
||||
}
|
||||
|
||||
// Раньше войска хранились в progress.json (GameProgress.Data.troops и sashaMarchUnits моста ArmyBridge).
|
||||
// Теперь -- в модуле Армии, save.json, секция "army". Переносим один раз, после загрузки сохранения.
|
||||
static void MigrateLegacyArmy(ArmyService army)
|
||||
{
|
||||
var data = GameProgress.Instance.Data;
|
||||
if (data.armyInModule) return;
|
||||
|
||||
if (!army.RestoredFromSave)
|
||||
{
|
||||
var legacyMarches = new System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<int, int[]>>();
|
||||
foreach (var m in data.sashaMarchUnits)
|
||||
legacyMarches.Add(new System.Collections.Generic.KeyValuePair<int, int[]>(m.marchId, m.units));
|
||||
army.ImportLegacy(data.troops, legacyMarches);
|
||||
Debug.Log($"[МОДУЛИ] Войска перенесены из progress.json в модуль Армии: в городе {army.SoldiersAtHome}, в походах {army.TroopsInMarches}.");
|
||||
}
|
||||
|
||||
// Из progress.json убираем, только когда войска уже записаны в save.json -- иначе вылет игры их потеряет
|
||||
if (army.RestoredFromSave) ClearLegacyArmy(default);
|
||||
else EventBus.Subscribe<GameSavedEvent>(ClearLegacyArmy);
|
||||
}
|
||||
|
||||
static void ClearLegacyArmy(GameSavedEvent e)
|
||||
{
|
||||
EventBus.Unsubscribe<GameSavedEvent>(ClearLegacyArmy);
|
||||
var data = GameProgress.Instance.Data;
|
||||
data.armyInModule = true;
|
||||
System.Array.Clear(data.troops, 0, data.troops.Length);
|
||||
data.sashaMarchUnits.Clear();
|
||||
GameProgress.Instance.MarkChanged();
|
||||
}
|
||||
|
||||
// Очередь обучения раньше вёл GameProgress (progress.json, Data.training, время -- тики реальных часов).
|
||||
// Теперь -- Армия (save.json, "army", мс часов игры). Переносим один раз, с тем же оставшимся временем.
|
||||
static void MigrateLegacyTraining(ArmyService army)
|
||||
{
|
||||
var data = GameProgress.Instance.Data;
|
||||
if (data.training == null || data.training.Count == 0) return;
|
||||
|
||||
// В Армии уже есть заказы -- перенос уже был (сохранение записалось, а progress.json -- ещё нет): только убрать старое
|
||||
if (army.Training.Count == 0)
|
||||
{
|
||||
long epochTicks = new System.DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc).Ticks;
|
||||
long offsetMs = GameClock.OffsetSeconds * 1000; // старые тики -- реальное время, Армия -- часы игры
|
||||
var orders = new System.Collections.Generic.List<(int, int, long, long, bool)>();
|
||||
foreach (var o in data.training)
|
||||
orders.Add((o.troopIndex, o.count, (o.startTicks - epochTicks) / 10_000 + offsetMs, (o.endTicks - epochTicks) / 10_000 + offsetMs, o.recruited));
|
||||
army.ImportLegacyTraining(orders);
|
||||
Debug.Log($"[МОДУЛИ] Очередь обучения перенесена из progress.json в модуль Армии: заказов {orders.Count}.");
|
||||
}
|
||||
|
||||
// Из progress.json убираем, только когда очередь уже записана в save.json -- иначе вылет игры её потеряет
|
||||
EventBus.Subscribe<GameSavedEvent>(ClearLegacyTraining);
|
||||
}
|
||||
|
||||
static void ClearLegacyTraining(GameSavedEvent e)
|
||||
{
|
||||
EventBus.Unsubscribe<GameSavedEvent>(ClearLegacyTraining);
|
||||
GameProgress.Instance.Data.training.Clear();
|
||||
GameProgress.Instance.MarkChanged();
|
||||
}
|
||||
|
||||
// ---------------- Население и экономика ----------------
|
||||
|
||||
static PopulationRules ToPopulationRules(EconomyConfig config)
|
||||
{
|
||||
var p = config.population;
|
||||
return new PopulationRules
|
||||
{
|
||||
StartTotal = p.startTotal,
|
||||
StartWorkers = p.startWorkers,
|
||||
StartSoldiers = p.startSoldiers,
|
||||
FoodPerCivilianPerHour = p.foodPerCivilianPerHour,
|
||||
FoodPerSoldierPerHour = p.foodPerSoldierPerHour,
|
||||
FoodPerTroopTypePerHour = p.foodPerTroopTypePerHour != null && p.foodPerTroopTypePerHour.Length > 0
|
||||
? System.Array.ConvertAll(p.foodPerTroopTypePerHour, f => (double)f) : null,
|
||||
GrowthPerHour = p.growthPerHour,
|
||||
HousingBase = p.housingBase,
|
||||
HousingPerTownHallLevel = p.housingPerTownHallLevel,
|
||||
HousingPerHouseLevel = p.housingPerHouseLevel,
|
||||
MaxOfflineHours = p.maxOfflineHours,
|
||||
CivilianReserveByTownHallLevel = p.civilianReserveByTownHallLevel != null && p.civilianReserveByTownHallLevel.Length > 0
|
||||
? (int[])p.civilianReserveByTownHallLevel.Clone()
|
||||
: new PopulationRules().CivilianReserveByTownHallLevel,
|
||||
PeoplePerSoldier = Mathf.Max(1, config.soldier.people)
|
||||
};
|
||||
}
|
||||
|
||||
static ProductionRules ToProductionRules(EconomyConfig config)
|
||||
{
|
||||
var p = config.production;
|
||||
return new ProductionRules
|
||||
{
|
||||
// порядок как в WorkJob: лесоруб, фермер, каменотёс, шахтёр-металл, шахтёр-золото
|
||||
PerWorkerPerHour = new double[] { p.woodcutterPerHour, p.farmerPerHour, p.stonecutterPerHour, p.minerIronPerHour, p.minerGoldPerHour },
|
||||
SlotsPerBuilding = p.workerSlotsT1,
|
||||
SlotsPerLevel = p.workerSlotsPerLevel,
|
||||
EfficiencyPerLevel = p.efficiencyPerLevel,
|
||||
FamineNonFoodMultiplier = p.famineNonFoodMultiplier
|
||||
};
|
||||
}
|
||||
|
||||
// Новая игра: стартовое войско (пехота) и стартовые ресурсы из EconomyConfig. Один раз на новую игру.
|
||||
static void GiveStartingKit(PopulationService population, ArmyService army, EconomyConfig config)
|
||||
{
|
||||
if (!population.TryTakeStartingKit()) return;
|
||||
|
||||
if (population.Rules.StartSoldiers > 0) army.AddTroops(0, population.Rules.StartSoldiers);
|
||||
|
||||
if (GameServices.TryGet(out IEconomy economy))
|
||||
{
|
||||
ResourceType[] types = { ResourceType.Wood, ResourceType.Food, ResourceType.Stone, ResourceType.Iron, ResourceType.Gold, ResourceType.Gems };
|
||||
foreach (var t in types)
|
||||
{
|
||||
float target = config.GetStartResource(t);
|
||||
float current = economy.Get(t);
|
||||
if (target > current) economy.Add(t, target - current); // не меньше стартового
|
||||
}
|
||||
}
|
||||
Debug.Log($"[НАСЕЛЕНИЕ] Новая игра: {population.Total} человек, воинов {population.Soldiers}.");
|
||||
}
|
||||
|
||||
// Верхняя панель показывает людей как ресурс "Люди" -- держим его равным населению
|
||||
static void SyncPopulationResource(PopulationChangedEvent e)
|
||||
{
|
||||
if (!GameServices.TryGet(out IEconomy economy)) return;
|
||||
float delta = e.Total - economy.Get(ResourceType.Population);
|
||||
if (Mathf.Abs(delta) >= 0.5f) economy.Add(ResourceType.Population, delta);
|
||||
}
|
||||
|
||||
static void OnMarchStarted(MarchStartedEvent e)
|
||||
{
|
||||
var data = GameProgress.Instance.Data;
|
||||
data.marchesSent++;
|
||||
data.dailyMarches++;
|
||||
GameProgress.Instance.MarkChanged();
|
||||
}
|
||||
|
||||
static void OnMarchReturned(MarchReturnedEvent e)
|
||||
{
|
||||
if (!e.Victory) return;
|
||||
GameProgress.Instance.Data.worldWins++;
|
||||
GameProgress.Instance.MarkChanged();
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2afadf4e4024d5c48a2c26eddb75cbb1
|
||||
@@ -1,50 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
// Версия игры на экране (05.11, требование Миши: в каждом билде видно, что это за версия).
|
||||
// В углу экрана: версия из Player Settings (Application.version) и сборка -- ветка, коммит, дата.
|
||||
// Сборку пишет Editor/BuildInfoWriter перед билдом в Resources/BuildInfo.txt; в редакторе -- "редактор".
|
||||
// Создаётся сам при запуске, живёт между сценами.
|
||||
public class GameVersionLabel : MonoBehaviour
|
||||
{
|
||||
static GameVersionLabel instance;
|
||||
string text;
|
||||
GUIStyle style, shadow;
|
||||
|
||||
public static string BuildInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
var asset = Resources.Load<TextAsset>("BuildInfo");
|
||||
return asset != null ? asset.text.Trim() : "редактор";
|
||||
}
|
||||
}
|
||||
|
||||
public static string FullVersion => $"v{Application.version} • {BuildInfo}";
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
static void Create()
|
||||
{
|
||||
if (instance != null) return;
|
||||
var go = new GameObject("GameVersionLabel");
|
||||
DontDestroyOnLoad(go);
|
||||
instance = go.AddComponent<GameVersionLabel>();
|
||||
instance.text = FullVersion;
|
||||
Debug.Log("[ВЕРСИЯ] " + instance.text);
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
if (style == null)
|
||||
{
|
||||
int size = Mathf.Max(12, Screen.height / 60);
|
||||
style = new GUIStyle(GUI.skin.label) { fontSize = size, alignment = TextAnchor.LowerLeft };
|
||||
style.normal.textColor = new Color(1f, 1f, 1f, 0.75f);
|
||||
shadow = new GUIStyle(style);
|
||||
shadow.normal.textColor = new Color(0f, 0f, 0f, 0.6f);
|
||||
}
|
||||
GUI.depth = -1000; // поверх всего
|
||||
var r = new Rect(8, Screen.height - style.fontSize * 2 - 4, Screen.width * 0.6f, style.fontSize * 2);
|
||||
GUI.Label(new Rect(r.x + 1, r.y + 1, r.width, r.height), text, shadow);
|
||||
GUI.Label(r, text, style);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4dae86c365fe55948b878547633d712a
|
||||
@@ -1,89 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
// ТАБЛИЦА ГОСПИТАЛЯ: места и время лечения раненых (регламент 31.10, решения по 8 вопросам Саши от 31.10, п. 7). Числа -- ТЕСТ.
|
||||
//
|
||||
// Ассет: Assets/Resources/HospitalConfig.asset (выдели его в окне Project и меняй числа в Inspector;
|
||||
// изменения действуют со следующего запуска игры). Создать заново: правый клик в Project ->
|
||||
// Create -> Bogatyri -> Hospital Config, имя файла -- HospitalConfig, папка -- Resources.
|
||||
//
|
||||
// Модуль Армии таблицу не видит: App/GameModules передаёт числа в ArmyService.
|
||||
// Места даёт здание «Госпиталь» (Meta/Build/Hospital.cs) через контракт IHospital.
|
||||
[CreateAssetMenu(fileName = "HospitalConfig", menuName = "Bogatyri/Hospital Config")]
|
||||
public class HospitalConfig : ScriptableObject
|
||||
{
|
||||
[Header("Здание «Госпиталь» (ТЕСТ)")]
|
||||
[Tooltip("Мест для лечения по уровням госпиталя I..V. Регламент 31.10: 20..60. Число строк -- число уровней")]
|
||||
public int[] bedsByLevel = { 20, 30, 40, 50, 60 };
|
||||
[Tooltip("Мест, пока в городе нет госпиталя. Решения 01.11, п. 3: 5. После постройки заменяются штатными, не складываются")]
|
||||
public int temporaryBeds = 5;
|
||||
[Tooltip("С какого уровня Ратуши можно строить")]
|
||||
public int requiredTownHallLevel = 1;
|
||||
[Tooltip("Цена постройки: дерево")]
|
||||
public int buildWood = 250;
|
||||
[Tooltip("Цена постройки: камень")]
|
||||
public int buildStone = 150;
|
||||
|
||||
[Header("Лечение одного воина, ИГРОВЫХ часов (1 игровой час = 5 реальных минут)")]
|
||||
[Tooltip("Пехота, лучники, кавалерия, осадные. Регламент 31.10: 4 / 4 / 6 / 8")]
|
||||
public int[] healGameHours = { 4, 4, 6, 8 };
|
||||
|
||||
[Header("Цена лечения одного воина (решения 01.11, п. 3: золото 1 / 1 / 2 / 3)")]
|
||||
[Tooltip("Еды за воина: пехота, лучники, кавалерия, осадные")]
|
||||
public int[] healFoodPerSoldier = { 0, 0, 0, 0 };
|
||||
[Tooltip("Золота за воина: пехота, лучники, кавалерия, осадные")]
|
||||
public int[] healGoldPerSoldier = { 1, 1, 2, 3 };
|
||||
|
||||
[Header("Пропавшие без вести (решения 01.11, п. 5). Долю пропавших в бою задаёт BattleConfig.missingSharePermille")]
|
||||
[Tooltip("Сколько пропавших вернётся, %. Утверждён диапазон 10-90%, формула НЕ утверждена -- ТЕСТ")]
|
||||
public int missingReturnPercent = 50;
|
||||
[Tooltip("Из вернувшихся ранены, %. Утверждено: 30 (70% здоровыми)")]
|
||||
public int missingWoundedPercent = 30;
|
||||
[Tooltip("Окно возвращения после обратного пути, ИГРОВЫХ часов. Утверждено: до 6")]
|
||||
public int missingWindowGameHours = 6;
|
||||
[Tooltip("Возвращаются группами: от 2 до стольких. Утверждено: 2-4")]
|
||||
public int missingMaxGroups = 4;
|
||||
|
||||
static HospitalConfig cached;
|
||||
|
||||
public static HospitalConfig Load()
|
||||
{
|
||||
if (cached != null) return cached;
|
||||
cached = Resources.Load<HospitalConfig>("HospitalConfig");
|
||||
if (cached == null)
|
||||
{
|
||||
Debug.LogWarning("[ГОСПИТАЛЬ] Нет Assets/Resources/HospitalConfig.asset -- берутся числа по умолчанию из HospitalConfig.cs.");
|
||||
cached = CreateInstance<HospitalConfig>();
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
public MissingRules ToMissingRules()
|
||||
{
|
||||
return new MissingRules
|
||||
{
|
||||
ReturnPercent = Mathf.Clamp(missingReturnPercent, 0, 100),
|
||||
WoundedPercent = Mathf.Clamp(missingWoundedPercent, 0, 100),
|
||||
WindowGameHours = Mathf.Max(0, missingWindowGameHours),
|
||||
MaxGroups = Mathf.Max(2, missingMaxGroups)
|
||||
};
|
||||
}
|
||||
|
||||
public HospitalRules ToRules()
|
||||
{
|
||||
var hours = new int[TroopType.Count];
|
||||
var cost = new ResourceAmount[TroopType.Count][];
|
||||
for (int i = 0; i < TroopType.Count; i++)
|
||||
{
|
||||
hours[i] = healGameHours != null && i < healGameHours.Length ? Mathf.Max(0, healGameHours[i]) : 4;
|
||||
int food = healFoodPerSoldier != null && i < healFoodPerSoldier.Length ? Mathf.Max(0, healFoodPerSoldier[i]) : 0;
|
||||
int gold = healGoldPerSoldier != null && i < healGoldPerSoldier.Length ? Mathf.Max(0, healGoldPerSoldier[i]) : 0;
|
||||
cost[i] = new[] { new ResourceAmount(ResourceType.Food, food), new ResourceAmount(ResourceType.Gold, gold) };
|
||||
}
|
||||
return new HospitalRules
|
||||
{
|
||||
TemporaryBeds = Mathf.Max(0, temporaryBeds),
|
||||
HealGameHours = hours,
|
||||
HealCostPerSoldier = cost
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c25bbe993b314a15a6d1a8aaf0017d55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,73 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
// ТАБЛИЦА РАЗВЕДКИ, этап М1: радиусы обзора, последний контакт, примерная численность, опознание (ТЗ разведки 31.10).
|
||||
// Числа -- ТЕСТ.
|
||||
//
|
||||
// Ассет: Assets/Resources/IntelligenceConfig.asset (выдели его в окне Project и меняй числа в Inspector;
|
||||
// изменения действуют со следующего запуска игры). Создать заново: правый клик в Project ->
|
||||
// Create -> Bogatyri -> Intelligence Config, имя файла -- IntelligenceConfig, папка -- Resources.
|
||||
//
|
||||
// Модуль таблицу не видит: App/GameModules передаёт числа в модуль Разведки.
|
||||
// Патрули-заглушки соседей включаются в таблице MarchConfig (это походы Мира).
|
||||
[CreateAssetMenu(fileName = "IntelligenceConfig", menuName = "Bogatyri/Intelligence Config")]
|
||||
public class IntelligenceConfig : ScriptableObject
|
||||
{
|
||||
[Header("Радиус обзора, клеток карты")]
|
||||
[Tooltip("Своё поселение. ТЗ разведки: 5")]
|
||||
public float settlementRadius = 5f;
|
||||
[Tooltip("Обычная армия в походе. ТЗ разведки: 1")]
|
||||
public float armyRadius = 1f;
|
||||
|
||||
[Header("Технология «Обзор» (не складывается, уровень заменяет)")]
|
||||
[Tooltip("Прибавка к радиусам на уровнях I, II, III, %. ТЗ: 10 / 20 / 30")]
|
||||
public int[] observationPercent = { 10, 20, 30 };
|
||||
[Tooltip("ТОЛЬКО ДЛЯ ТЕСТА: уровень «Обзора» 0..3, пока нет дерева технологий")]
|
||||
public int testObservationLevel = 0;
|
||||
|
||||
[Header("Чужие армии")]
|
||||
[Tooltip("Метка последнего контакта живёт, РЕАЛЬНЫХ минут. ТЗ: 10")]
|
||||
public float lastContactMinutes = 10f;
|
||||
[Tooltip("Примерная численность: ±%. ТЗ: 20")]
|
||||
public int approxPercent = 20;
|
||||
[Tooltip("Шанс опознать владельца за одно событие наблюдения, %. ТЗ: 50")]
|
||||
public int identifyPercent = 50;
|
||||
[Tooltip("Шаг расчёта наблюдения за время, пока игра была закрыта, РЕАЛЬНЫХ секунд")]
|
||||
public float sampleStepSeconds = 10f;
|
||||
|
||||
[Header("Туман на карте")]
|
||||
[Tooltip("Выключено (по умолчанию): города, лагеря и точки видны всегда, туман только затемняет землю. " +
|
||||
"Включено: неисследованная местность закрыта полностью вместе с объектами. Решения 31.10, п. 1: для М1 не требуется, оставлено переключателем")]
|
||||
public bool hideUnexploredObjects = false;
|
||||
|
||||
static IntelligenceConfig cached;
|
||||
|
||||
public static IntelligenceConfig Load()
|
||||
{
|
||||
if (cached != null) return cached;
|
||||
cached = Resources.Load<IntelligenceConfig>("IntelligenceConfig");
|
||||
if (cached == null)
|
||||
{
|
||||
Debug.LogWarning("[РАЗВЕДКА] Нет Assets/Resources/IntelligenceConfig.asset -- берутся числа по умолчанию из IntelligenceConfig.cs.");
|
||||
cached = CreateInstance<IntelligenceConfig>();
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
public IntelligenceRules ToRules()
|
||||
{
|
||||
var levels = new int[4];
|
||||
for (int i = 0; i < 3 && observationPercent != null && i < observationPercent.Length; i++) levels[i + 1] = Mathf.Max(0, observationPercent[i]);
|
||||
return new IntelligenceRules
|
||||
{
|
||||
SettlementRadius = Milli(settlementRadius),
|
||||
ArmyRadius = Milli(armyRadius),
|
||||
ObservationPercent = levels,
|
||||
LastContactMs = (long)(Mathf.Max(0f, lastContactMinutes) * 60_000f),
|
||||
ApproxPercent = Mathf.Clamp(approxPercent, 0, 100),
|
||||
IdentifyPercent = Mathf.Clamp(identifyPercent, 0, 100),
|
||||
SampleStepMs = (long)(Mathf.Max(1f, sampleStepSeconds) * 1000f)
|
||||
};
|
||||
}
|
||||
|
||||
static int Milli(float cells) => Mathf.Max(0, Mathf.RoundToInt(cells * 1000f));
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c249ad34e2f94460b9f99b49c71760c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,82 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
// ТАБЛИЦА МАРШЕЙ: скорости войск по типам и слоты похода (регламент 31.10, раздел 7). Числа -- ТЕСТ.
|
||||
//
|
||||
// Ассет: Assets/Resources/MarchConfig.asset (выдели его в окне Project и меняй числа в Inspector;
|
||||
// изменения действуют со следующего запуска игры). Создать заново: правый клик в Project ->
|
||||
// Create -> Bogatyri -> March Config, имя файла -- MarchConfig, папка -- Resources.
|
||||
//
|
||||
// Модули таблицу не видят: App/GameModules передаёт скорости в модуль Мира, слоты -- в модуль Армии.
|
||||
// Бонусы героя (+15%) и технологии (+20%) к скорости появятся вместе с героями и технологиями.
|
||||
[CreateAssetMenu(fileName = "MarchConfig", menuName = "Bogatyri/March Config")]
|
||||
public class MarchConfig : ScriptableObject
|
||||
{
|
||||
[Header("Скорость: клеток карты за 1 РЕАЛЬНЫЙ час (марш идёт в реальном времени)")]
|
||||
[Tooltip("Пехота. Регламент 31.10: 8")]
|
||||
public float infantry = 8f;
|
||||
[Tooltip("Лучники. Регламент 31.10: 9")]
|
||||
public float archers = 9f;
|
||||
[Tooltip("Кавалерия. Регламент 31.10: 12")]
|
||||
public float cavalry = 12f;
|
||||
[Tooltip("Осадные. Регламент 31.10: 4")]
|
||||
public float siege = 4f;
|
||||
|
||||
[Header("Модификаторы скорости, % (решения 31.10, п. 4: смешанное войско -- средняя по численности)")]
|
||||
[Tooltip("По типам: пехота, лучники, кавалерия, осадные. +10 -- быстрее на 10%. Позже -- бонусы героев и технологий. ТЕСТ")]
|
||||
public int[] typeSpeedBonusPercent = { 0, 0, 0, 0 };
|
||||
[Tooltip("Общий модификатор всего войска, применяется один раз. ТЕСТ")]
|
||||
public int armySpeedBonusPercent = 0;
|
||||
|
||||
[Header("Слоты похода")]
|
||||
[Tooltip("Сколько походов игрока могут идти одновременно. Регламент 31.10: 2 (технология «Военная логистика» -- до 7)")]
|
||||
public int baseMarchSlots = 2;
|
||||
|
||||
[Header("Патрули-заглушки соседей (Разведка М1, ВРЕМЕННАЯ тестовая механика)")]
|
||||
[Tooltip("Соседние города иногда отправляют отряды к другим городам и лагерям -- проверить обнаружение, численность, опознание и потерю контакта. Выключить, когда появится настоящий PvP")]
|
||||
public bool patrolsEnabled = true;
|
||||
[Tooltip("Сколько патрулей одновременно на карте")]
|
||||
public int patrolMaxActive = 3;
|
||||
[Tooltip("Новый патруль -- раз в столько РЕАЛЬНЫХ минут (если не превышен предел)")]
|
||||
public float patrolIntervalMinutes = 10f;
|
||||
public int patrolSoldiersMin = 10;
|
||||
public int patrolSoldiersMax = 40;
|
||||
|
||||
static MarchConfig cached;
|
||||
|
||||
public static MarchConfig Load()
|
||||
{
|
||||
if (cached != null) return cached;
|
||||
cached = Resources.Load<MarchConfig>("MarchConfig");
|
||||
if (cached == null)
|
||||
{
|
||||
Debug.LogWarning("[МАРШИ] Нет Assets/Resources/MarchConfig.asset -- берутся числа по умолчанию из MarchConfig.cs.");
|
||||
cached = CreateInstance<MarchConfig>();
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Скорости для модуля Мира: милли-единиц карты (1000 = клетка) за реальный час
|
||||
public Bogatyri.World.MarchSpeedRules ToSpeedRules()
|
||||
{
|
||||
return new Bogatyri.World.MarchSpeedRules
|
||||
{
|
||||
SpeedMilliPerHour = new[] { Milli(infantry), Milli(archers), Milli(cavalry), Milli(siege) },
|
||||
TypeBonusPercent = (int[])(typeSpeedBonusPercent ?? new int[4]).Clone(),
|
||||
ArmyBonusPercent = armySpeedBonusPercent
|
||||
};
|
||||
}
|
||||
|
||||
public Bogatyri.World.PatrolRules ToPatrolRules()
|
||||
{
|
||||
return new Bogatyri.World.PatrolRules
|
||||
{
|
||||
Enabled = patrolsEnabled,
|
||||
MaxActive = Mathf.Max(0, patrolMaxActive),
|
||||
IntervalMs = (long)(Mathf.Max(1f, patrolIntervalMinutes) * 60_000f),
|
||||
SoldiersMin = Mathf.Max(1, patrolSoldiersMin),
|
||||
SoldiersMax = Mathf.Max(1, patrolSoldiersMax)
|
||||
};
|
||||
}
|
||||
|
||||
static int Milli(float cellsPerHour) => Mathf.Max(1, Mathf.RoundToInt(cellsPerHour * 1000f));
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0427eef4612041d2b8a359f9bf358779
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,38 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
// Слой приложения: "сердцебиение" модуля Населения. Модуль -- чистый C# и не знает про Update,
|
||||
// поэтому раз в несколько секунд его досчитывает этот объект. Живёт между сценами (город <-> карта мира).
|
||||
// Создаётся сам при запуске игры, вешать никуда не нужно.
|
||||
public class PopulationTicker : MonoBehaviour
|
||||
{
|
||||
const float Interval = 2f; // секунд реального времени между расчётами
|
||||
|
||||
static PopulationTicker instance;
|
||||
float next;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
static void Create()
|
||||
{
|
||||
if (instance != null) return;
|
||||
var go = new GameObject("PopulationTicker");
|
||||
DontDestroyOnLoad(go);
|
||||
instance = go.AddComponent<PopulationTicker>();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
next = Time.unscaledTime + Interval; // первый расчёт -- после загрузки сохранения (она идёт в Start сцены)
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (Time.unscaledTime < next) return;
|
||||
next = Time.unscaledTime + Interval;
|
||||
if (GameServices.TryGet(out IPopulation population)) population.Tick(GameClock.UnixNow);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (instance == this) instance = null;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d7b4f971ff440eaa5744fbe2a1e834d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,33 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
// Защита ресурсов от грабежа (контракт IResourceProtection, 01.11, ТЕСТ).
|
||||
// Часть запаса прячут Склады и Амбары: EconomyConfig, раздел "Защита ресурсов". Уровни зданий сообщает
|
||||
// App/CitySync; на карте мира, где города нет, действуют последние известные уровни.
|
||||
public sealed class ResourceProtection : IResourceProtection
|
||||
{
|
||||
public static int WarehouseLevels { get; private set; }
|
||||
public static int GranaryLevels { get; private set; }
|
||||
|
||||
public static void SetLevels(int warehouseLevels, int granaryLevels)
|
||||
{
|
||||
WarehouseLevels = Mathf.Max(0, warehouseLevels);
|
||||
GranaryLevels = Mathf.Max(0, granaryLevels);
|
||||
}
|
||||
|
||||
public float Protected(ResourceType type)
|
||||
{
|
||||
return EconomyConfig.Load().GetProtected(type, WarehouseLevels, GranaryLevels);
|
||||
}
|
||||
|
||||
public float Lootable(ResourceType type) => Lootable(type, false);
|
||||
|
||||
// Кристаллы: без «Грабежа» -- 0; с ним -- не больше доли запаса (EconomyConfig, ТЕСТ, точные ограничения -- позже)
|
||||
public float Lootable(ResourceType type, bool attackerHasPlunder)
|
||||
{
|
||||
if (!GameServices.TryGet(out IEconomy economy)) return 0f;
|
||||
float have = Mathf.Max(0f, economy.Get(type));
|
||||
if (type == ResourceType.Gems)
|
||||
return attackerHasPlunder ? Mathf.Floor(have * Mathf.Clamp01(EconomyConfig.Load().protection.plunderGemsShare)) : 0f;
|
||||
return Mathf.Max(0f, have - Protected(type));
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cea38890d8f5d8d4f804c095c4c113b4
|
||||
@@ -1,7 +0,0 @@
|
||||
// Имена зданий «Склад» и «Амбар». 2D-шаблоны удалены 06.11 вместе с 2D-городом; 3D-здания -- City3D/City3DGameplay,
|
||||
// цены и лимиты -- EconomyConfig (раздел «Склады и лимиты»)
|
||||
public static class StorageBootstrap
|
||||
{
|
||||
public const string WarehouseName = "Склад";
|
||||
public const string GranaryName = "Амбар";
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 87b085cfbef5449b97456bea718c7466
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,35 +0,0 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
// Склад или Амбар (этап 1): каждый уровень поднимает лимит хранения.
|
||||
// Склад -- дерево, камень, металл, золото; Амбар -- еда. Числа -- EconomyConfig, раздел "Склады и лимиты".
|
||||
// Здание появляется в списке стройки кодом (App/StorageBootstrap), отдельных префабов пока нет.
|
||||
// Уровень сохраняет модуль Поселения (ILeveledBuilding), лимиты считает App/CitySync.
|
||||
public class StorageBuilding : MonoBehaviour, ILeveledBuilding
|
||||
{
|
||||
public enum Kind { Warehouse, Granary }
|
||||
|
||||
public static event Action<StorageBuilding> Tapped;
|
||||
|
||||
public Kind kind;
|
||||
public int level = 1;
|
||||
|
||||
public int Level { get => level; set => level = Mathf.Max(1, value); }
|
||||
|
||||
public string Title => kind == Kind.Warehouse ? StorageBootstrap.WarehouseName : StorageBootstrap.GranaryName;
|
||||
|
||||
Vector3 downPos;
|
||||
|
||||
void OnMouseDown()
|
||||
{
|
||||
downPos = Input.mousePosition;
|
||||
}
|
||||
|
||||
// Тап, а не перетаскивание камеры
|
||||
void OnMouseUpAsButton()
|
||||
{
|
||||
if (UIBlocker.IsPointerOverUI()) return;
|
||||
if ((Input.mousePosition - downPos).magnitude > 25f) return;
|
||||
Tapped?.Invoke(this);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bea97fb12da84c7daeb07f65d4947cae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,84 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
// ТАБЛИЦА РЕСУРСНЫХ ТОЧЕК (регламент 31.10, раздел 7: «Изменять числа через WorldResourceConfig.asset»). Числа -- ТЕСТ.
|
||||
//
|
||||
// Ассет: Assets/Resources/WorldResourceConfig.asset (выдели его в окне Project и меняй числа в Inspector;
|
||||
// изменения действуют со следующего запуска игры). Создать заново: правый клик в Project ->
|
||||
// Create -> Bogatyri -> World Resource Config, имя файла -- WorldResourceConfig, папка -- Resources.
|
||||
//
|
||||
// Модуль Мира таблицу не видит: App/GameModules переводит её в ResourcePointRules.
|
||||
// Состояние точек (запас, сроки) ведёт Мир; зачисление добычи и складские лимиты -- Экономика (решение №46).
|
||||
[CreateAssetMenu(fileName = "WorldResourceConfig", menuName = "Bogatyri/World Resource Config")]
|
||||
public class WorldResourceConfig : ScriptableObject
|
||||
{
|
||||
[Header("Запас точки: малая / средняя / крупная")]
|
||||
public int[] woodStock = { 200, 600, 1500 };
|
||||
public int[] stoneStock = { 150, 450, 1000 };
|
||||
public int[] metalStock = { 80, 250, 600 };
|
||||
public int[] goldStock = { 30, 100, 250 };
|
||||
|
||||
[Header("Добыча одного воина за ИГРОВОЙ час (1 игровой час = 5 реальных минут)")]
|
||||
public float woodPerGameHour = 2f;
|
||||
public float stonePerGameHour = 1.5f;
|
||||
public float metalPerGameHour = 1f;
|
||||
public float goldPerGameHour = 0.5f;
|
||||
|
||||
[Header("Груз одного воина (осадные машины груза не несут)")]
|
||||
public int woodCargo = 12;
|
||||
public int stoneCargo = 10;
|
||||
public int metalCargo = 8;
|
||||
public int goldCargo = 5;
|
||||
|
||||
[Header("Срок жизни точки, РЕАЛЬНЫХ часов с момента появления: малая / средняя / крупная")]
|
||||
public int[] lifetimeRealHours = { 12, 24, 48 };
|
||||
|
||||
[Header("Сколько точек на карте: малых / средних / крупных (решения 31.10, п. 6, ТЕСТ)")]
|
||||
[Tooltip("Решения 31.10: всего 24 -- лес 8, камень 7, металл 5, золото 4; по размерам 12 / 8 / 4. Раскладка по ресурсам -- здесь")]
|
||||
public int[] woodCount = { 4, 3, 1 };
|
||||
public int[] stoneCount = { 4, 2, 1 };
|
||||
public int[] metalCount = { 2, 2, 1 };
|
||||
public int[] goldCount = { 2, 1, 1 };
|
||||
|
||||
static WorldResourceConfig cached;
|
||||
|
||||
public static WorldResourceConfig Load()
|
||||
{
|
||||
if (cached != null) return cached;
|
||||
cached = Resources.Load<WorldResourceConfig>("WorldResourceConfig");
|
||||
if (cached == null)
|
||||
{
|
||||
Debug.LogWarning("[РЕСУРСНЫЕ ТОЧКИ] Нет Assets/Resources/WorldResourceConfig.asset -- берутся числа по умолчанию.");
|
||||
cached = CreateInstance<WorldResourceConfig>();
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
public Bogatyri.World.ResourcePointRules ToRules()
|
||||
{
|
||||
var d = Bogatyri.World.ResourcePointRules.Default;
|
||||
return new Bogatyri.World.ResourcePointRules
|
||||
{
|
||||
Stock = new[] { Three(woodStock, d.Stock[0]), Three(stoneStock, d.Stock[1]), Three(metalStock, d.Stock[2]), Three(goldStock, d.Stock[3]) },
|
||||
MiningMilliPerGameHour = new[] { Milli(woodPerGameHour), Milli(stonePerGameHour), Milli(metalPerGameHour), Milli(goldPerGameHour) },
|
||||
CargoPerSoldier = new[] { Mathf.Max(0, woodCargo), Mathf.Max(0, stoneCargo), Mathf.Max(0, metalCargo), Mathf.Max(0, goldCargo) },
|
||||
LifetimeRealHours = Three(lifetimeRealHours, d.LifetimeRealHours),
|
||||
Count = new[] { Counts(woodCount, d.Count[0]), Counts(stoneCount, d.Count[1]), Counts(metalCount, d.Count[2]), Counts(goldCount, d.Count[3]) }
|
||||
};
|
||||
}
|
||||
|
||||
static int Milli(float perHour) => Mathf.Max(0, Mathf.RoundToInt(perHour * 1000f));
|
||||
|
||||
// Число точек: ровно три значения, не меньше 0 (0 -- такого размера нет), иначе -- значения по умолчанию
|
||||
static int[] Counts(int[] values, int[] fallback)
|
||||
{
|
||||
if (values == null || values.Length != 3) return (int[])fallback.Clone();
|
||||
return new[] { Mathf.Max(0, values[0]), Mathf.Max(0, values[1]), Mathf.Max(0, values[2]) };
|
||||
}
|
||||
|
||||
// Ровно три значения (малая / средняя / крупная), иначе -- значения по умолчанию
|
||||
static int[] Three(int[] values, int[] fallback)
|
||||
{
|
||||
if (values == null || values.Length != 3) return (int[])fallback.Clone();
|
||||
return new[] { Mathf.Max(1, values[0]), Mathf.Max(1, values[1]), Mathf.Max(1, values[2]) };
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ee3759b377140c0b759a4a386327510
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class CameraZoom : MonoBehaviour
|
||||
{
|
||||
public Camera targetCamera;
|
||||
private float lastCameraSize;
|
||||
|
||||
void Start()
|
||||
{
|
||||
|
||||
if(targetCamera == null)
|
||||
targetCamera = Camera.main;
|
||||
|
||||
lastCameraSize = targetCamera.orthographicSize;
|
||||
UpdateCanvasScale();
|
||||
}
|
||||
void LateUpdate()
|
||||
{
|
||||
if (Mathf.Abs(targetCamera.orthographicSize - lastCameraSize) > 0.01f) {
|
||||
lastCameraSize = targetCamera.orthographicSize;
|
||||
UpdateCanvasScale();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateCanvasScale()
|
||||
{
|
||||
float scaleFactor = 10f / targetCamera.orthographicSize;
|
||||
|
||||
transform.localScale = Vector3.one * scaleFactor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06759f249d7820c43a347cac857fc6d3
|
||||
@@ -1,54 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class CameraController : MonoBehaviour
|
||||
{
|
||||
[Header("Настройки перемещения")]
|
||||
public float moveSpeed = 10f;
|
||||
|
||||
[Header("Настройки зума")]
|
||||
public float zoomSpeed = 2f; // скорость изменения размера
|
||||
public float minOrthoSize = 2f; // минимальный размер (максимальное приближение)
|
||||
public float maxOrthoSize = 50f; // максимальный размер (максимальное удаление)
|
||||
|
||||
private Vector3 _lastMousePosition;
|
||||
private bool _isDragging = false;
|
||||
|
||||
void Update()
|
||||
{
|
||||
// --- Перемещение по зажатой левой кнопке мыши ---
|
||||
if (UIBlocker.WorldInputCaptured) _isDragging = false; // палец тащит призрак здания -- камера стоит
|
||||
|
||||
if (Input.GetMouseButtonDown(0) && !UIBlocker.IsPointerOverUI() && !UIBlocker.WorldInputCaptured) // не тащим камеру, когда листаем панель
|
||||
{
|
||||
_lastMousePosition = Input.mousePosition;
|
||||
_isDragging = true;
|
||||
}
|
||||
else if (Input.GetMouseButtonUp(0))
|
||||
{
|
||||
_isDragging = false;
|
||||
}
|
||||
|
||||
if (_isDragging)
|
||||
{
|
||||
Vector3 currentMousePosition = Input.mousePosition;
|
||||
Vector3 delta = currentMousePosition - _lastMousePosition;
|
||||
|
||||
// Двигаем камеру (инвертируем Y, потому что экранные координаты вверх, а мир вниз)
|
||||
transform.position += new Vector3(-delta.x, delta.y, 0) * moveSpeed * Time.deltaTime;
|
||||
|
||||
_lastMousePosition = currentMousePosition;
|
||||
}
|
||||
|
||||
// --- Зум по колёсику мыши ---
|
||||
float scroll = Input.GetAxis("Mouse ScrollWheel");
|
||||
if (scroll != 0f && !UIBlocker.IsPointerOverUI())
|
||||
{
|
||||
Camera cam = GetComponent<Camera>();
|
||||
float targetSize = cam.orthographicSize - scroll * zoomSpeed;
|
||||
|
||||
// Ограничиваем зум диапазоном
|
||||
targetSize = Mathf.Clamp(targetSize, minOrthoSize, maxOrthoSize);
|
||||
cam.orthographicSize = targetSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 772c25e1ad11ec84191ddd62a2559d77
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: efa023adb1ce408c971a4f76ced19585
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,38 +0,0 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
// 3D-здание игры: его тип в 3D-сцене и подпись над ним -- имя, уровень, идёт ли стройка (05.11, Валдемар).
|
||||
// Вешает City3DGameplay.Attach. Подпись обновляется дважды в секунду.
|
||||
public class City3DBuildingTag : MonoBehaviour
|
||||
{
|
||||
public string type3d;
|
||||
|
||||
TextMeshPro label;
|
||||
float next;
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (Time.unscaledTime < next) return;
|
||||
next = Time.unscaledTime + 0.5f;
|
||||
if (label == null)
|
||||
{
|
||||
var t = transform.Find("Label");
|
||||
if (t != null) label = t.GetComponent<TextMeshPro>();
|
||||
if (label == null) return;
|
||||
}
|
||||
string name = type3d;
|
||||
int level = City3DGameplay.LevelOf(gameObject);
|
||||
var timer = BuildTimer.Of(gameObject);
|
||||
if (timer != null && BuildTimer.All.Contains(timer))
|
||||
{
|
||||
string left = GameData.FormatTime(timer.SecondsLeft);
|
||||
label.text = timer.IsConstruction ? $"{name}\n<size=70%>стройка {left}</size>" : $"{name} {level} ур.\n<size=70%>→ {timer.targetLevel} ур. {left}</size>";
|
||||
label.color = new Color(1f, 0.85f, 0.3f);
|
||||
}
|
||||
else
|
||||
{
|
||||
label.text = $"{name} {level} ур.";
|
||||
label.color = Color.white;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0bcf6ea4f7ae10b4686b3fd647b0c02f
|
||||
@@ -1,278 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
// КАМЕРА 3D-ПОСЕЛЕНИЯ (2.5D): наклонный вид под фиксированным углом, без вращения.
|
||||
// Перемещение -- перетаскиванием (палец / мышь), приближение -- двумя пальцами или колесом.
|
||||
// Не уходит за пределы полезной карты. FocusOn -- плавно показать область (открытие новой зоны).
|
||||
public class City3DCamera : MonoBehaviour
|
||||
{
|
||||
public float pitch = 52f; // наклон, градусы от горизонта
|
||||
public float yaw = 45f; // поворот вида вокруг вертикали
|
||||
public float distance = 26f;
|
||||
public float minDistance = 8f;
|
||||
public float maxDistance = 55f;
|
||||
|
||||
public Vector3 target; // точка на земле, на которую смотрит камера
|
||||
public Rect bounds = new Rect(0, 0, 48, 48); // где может быть target (x, z)
|
||||
|
||||
// Открытая часть поселения (зоны по уровню Ратуши), 06.11: центр кадра из неё не уходит, а при самом дальнем
|
||||
// отдалении она видна целиком -- даже если "Дальше всего" (maxDistance) подобрано меньше. Ширина 0 -- не задана
|
||||
Rect openArea;
|
||||
float fitOpenDistance; // с какого расстояния открытая часть видна целиком (между HUD сверху и снизу)
|
||||
public const float HudTopShare = 0.16f, HudBottomShare = 0.16f; // какую долю экрана закрывает HUD (ТЕСТ)
|
||||
|
||||
// Дальше всего сейчас: подобранное число, но не ближе, чем нужно, чтобы увидеть всё открытое поселение
|
||||
public float MaxDistanceNow => Mathf.Max(maxDistance, fitOpenDistance);
|
||||
|
||||
public void SetOpenArea(Rect area)
|
||||
{
|
||||
openArea = area;
|
||||
fitOpenDistance = FitDistance(area);
|
||||
Clamp();
|
||||
}
|
||||
|
||||
// Плавное приближение кнопками "+ / −" и колесом: камера доезжает до нужного расстояния за доли секунды
|
||||
float zoomTo = -1f;
|
||||
|
||||
Camera cam;
|
||||
Vector3 focusTarget;
|
||||
float focusDistance;
|
||||
bool focusing;
|
||||
Vector3 lastPointer;
|
||||
bool panning;
|
||||
|
||||
// Перемещение камеры пальцем можно выключить на время перетаскивания здания
|
||||
public bool AllowPan { get; set; } = true;
|
||||
|
||||
// Камера перспективная (решение 05.11): поселение объёмнее, есть глубина. Угол, высота и поле зрения -- ТЕСТ,
|
||||
// подбираются в Unity и на телефоне. Ортографическую можно вернуть галочкой (тогда зум -- размер кадра, см. Apply)
|
||||
public bool perspective = true;
|
||||
public float fieldOfView = 40f;
|
||||
|
||||
// Подбор камеры в панели DEV (окно «Камера»): наклон, поле зрения, ближе / дальше всего. Запоминается на этом
|
||||
// устройстве -- только в редакторе и отладочной сборке; в обычной сборке -- числа из сцены (05.11)
|
||||
public const string PrefPitch = "dev_cam3d_pitch", PrefFov = "dev_cam3d_fov", PrefMin = "dev_cam3d_min", PrefMax = "dev_cam3d_max";
|
||||
|
||||
// Числа по умолчанию (из сцены) -- для кнопки «Сбросить» в DEV
|
||||
public float DefaultPitch { get; private set; }
|
||||
public float DefaultFov { get; private set; }
|
||||
public float DefaultMin { get; private set; }
|
||||
public float DefaultMax { get; private set; }
|
||||
|
||||
public void SetFieldOfView(float fov)
|
||||
{
|
||||
fieldOfView = fov;
|
||||
if (cam != null && !cam.orthographic) cam.fieldOfView = fov;
|
||||
}
|
||||
|
||||
void LoadDevTuning()
|
||||
{
|
||||
DefaultPitch = pitch; DefaultFov = fieldOfView; DefaultMin = minDistance; DefaultMax = maxDistance;
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
pitch = PlayerPrefs.GetFloat(PrefPitch, pitch);
|
||||
fieldOfView = PlayerPrefs.GetFloat(PrefFov, fieldOfView);
|
||||
minDistance = PlayerPrefs.GetFloat(PrefMin, minDistance);
|
||||
maxDistance = Mathf.Max(minDistance + 1f, PlayerPrefs.GetFloat(PrefMax, maxDistance));
|
||||
distance = Mathf.Clamp(distance, minDistance, maxDistance);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
cam = GetComponent<Camera>();
|
||||
LoadDevTuning();
|
||||
if (cam != null && perspective)
|
||||
{
|
||||
cam.orthographic = false;
|
||||
cam.fieldOfView = fieldOfView;
|
||||
}
|
||||
}
|
||||
|
||||
// onlyZoomOut -- не приближать: если камера уже дальше, расстояние остаётся (открытие зоны, 06.11)
|
||||
public void FocusOn(Rect area, bool onlyZoomOut = false)
|
||||
{
|
||||
focusTarget = new Vector3(area.center.x, 0, area.center.y);
|
||||
// отойти так, чтобы область поместилась между HUD сверху и снизу
|
||||
focusDistance = Mathf.Clamp(FitDistance(area), minDistance, MaxDistanceNow);
|
||||
if (onlyZoomOut) focusDistance = Mathf.Max(focusDistance, Mathf.Min(distance, MaxDistanceNow));
|
||||
focusing = true;
|
||||
zoomTo = -1f;
|
||||
}
|
||||
|
||||
// Плавно подъехать к точке, не меняя приближения
|
||||
public void MoveTo(Vector3 point)
|
||||
{
|
||||
focusTarget = new Vector3(point.x, 0, point.z);
|
||||
focusDistance = distance;
|
||||
focusing = true;
|
||||
zoomTo = -1f;
|
||||
}
|
||||
|
||||
// Расстояние, с которого прямоугольник земли (x, z) виден целиком, если смотреть на его центр. Считается по
|
||||
// настоящей проекции камеры (наклон, поле зрения, форма экрана), поэтому верно при любых числах из DEV
|
||||
public float FitDistance(Rect area)
|
||||
{
|
||||
if (cam == null) cam = GetComponent<Camera>();
|
||||
if (cam == null || area.width <= 0 || area.height <= 0) return 0f;
|
||||
Vector3 keepPos = transform.position;
|
||||
Quaternion keepRot = transform.rotation;
|
||||
float keepOrtho = cam.orthographicSize;
|
||||
var center = new Vector3(area.center.x, 0, area.center.y);
|
||||
Vector3[] corners =
|
||||
{
|
||||
new Vector3(area.xMin, 0, area.yMin), new Vector3(area.xMax, 0, area.yMin),
|
||||
new Vector3(area.xMin, 0, area.yMax), new Vector3(area.xMax, 0, area.yMax),
|
||||
};
|
||||
float lo = 1f, hi = 400f;
|
||||
for (int i = 0; i < 24; i++) // двоичный поиск: ближе -- не помещается, дальше -- помещается
|
||||
{
|
||||
float d = (lo + hi) * 0.5f;
|
||||
PlaceAt(center, d);
|
||||
bool fits = true;
|
||||
foreach (var c in corners)
|
||||
{
|
||||
Vector3 v = cam.WorldToViewportPoint(c);
|
||||
if (v.z <= 0 || v.x < 0.02f || v.x > 0.98f || v.y < HudBottomShare || v.y > 1f - HudTopShare) { fits = false; break; }
|
||||
}
|
||||
if (fits) hi = d; else lo = d;
|
||||
}
|
||||
transform.SetPositionAndRotation(keepPos, keepRot);
|
||||
cam.orthographicSize = keepOrtho;
|
||||
return hi;
|
||||
}
|
||||
|
||||
// Начать / продолжить / закончить перемещение камеры. Зовёт контроллер ввода (он решает, что под пальцем)
|
||||
public void BeginPan(Vector3 screen)
|
||||
{
|
||||
panning = AllowPan; lastPointer = screen; focusing = false; zoomTo = -1f;
|
||||
panPlaneY = GroundHeightUnder(screen);
|
||||
}
|
||||
public void EndPan() { panning = false; }
|
||||
|
||||
// Палец ещё не сдвинулся настолько, чтобы считать это перетаскиванием: камера стоит, но точка запоминается --
|
||||
// когда перетаскивание начнётся, карта не прыгнет (как на карте мира, 05.11)
|
||||
public void Track(Vector3 screen) { lastPointer = screen; }
|
||||
|
||||
public void Pan(Vector3 screen)
|
||||
{
|
||||
if (!panning || cam == null) return;
|
||||
Vector3 a = GroundPoint(lastPointer), b = GroundPoint(screen);
|
||||
target += a - b;
|
||||
lastPointer = screen;
|
||||
Clamp();
|
||||
Apply(); // сразу: иначе следующий шаг считался бы от старого положения камеры -- камера дёргалась (05.11)
|
||||
}
|
||||
|
||||
// Кнопки "+ / −" и колесо: плавно (06.11). delta > 0 -- ближе
|
||||
public void Zoom(float delta)
|
||||
{
|
||||
focusing = false;
|
||||
float from = zoomTo > 0 ? zoomTo : distance;
|
||||
zoomTo = Mathf.Clamp(from * (1f - delta), minDistance, MaxDistanceNow);
|
||||
}
|
||||
|
||||
float fitPitch, fitFov, fitAspect; // при каких наклоне, поле зрения и форме экрана считали fitOpenDistance
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Наклон или поле зрения поменяли в DEV, повернули экран -- пересчитать, откуда видно всё поселение
|
||||
if (openArea.width > 0 && cam != null && (fitPitch != pitch || fitFov != cam.fieldOfView || fitAspect != cam.aspect))
|
||||
{
|
||||
fitPitch = pitch; fitFov = cam.fieldOfView; fitAspect = cam.aspect;
|
||||
fitOpenDistance = FitDistance(openArea);
|
||||
}
|
||||
|
||||
// Колесо мыши (редактор, ПК)
|
||||
float wheel = Input.mouseScrollDelta.y;
|
||||
if (City3DInput.OverHud(Input.mousePosition)) wheel = 0f; // колесо над окнами HUD -- не приближать камеру (05.11)
|
||||
if (Mathf.Abs(wheel) > 0.01f) Zoom(wheel * 0.1f);
|
||||
|
||||
// Два пальца -- приближение, как на карте мира: расстояние камеры меняется во столько раз,
|
||||
// во сколько изменилось расстояние между пальцами за кадр (не зависит от того, увидели ли касание) (05.11)
|
||||
if (Input.touchCount >= 2)
|
||||
{
|
||||
Touch a = Input.GetTouch(0), b = Input.GetTouch(1);
|
||||
float previous = ((a.position - a.deltaPosition) - (b.position - b.deltaPosition)).magnitude;
|
||||
float current = (a.position - b.position).magnitude;
|
||||
if (previous > 0.01f && current > 0.01f)
|
||||
{
|
||||
focusing = false;
|
||||
zoomTo = -1f;
|
||||
distance = Mathf.Clamp(distance * previous / current, minDistance, MaxDistanceNow);
|
||||
}
|
||||
}
|
||||
|
||||
if (zoomTo > 0)
|
||||
{
|
||||
distance = Mathf.Lerp(distance, zoomTo, 1f - Mathf.Exp(-Time.unscaledDeltaTime * 12f));
|
||||
if (Mathf.Abs(distance - zoomTo) < 0.02f) { distance = zoomTo; zoomTo = -1f; }
|
||||
}
|
||||
distance = Mathf.Clamp(distance, minDistance, MaxDistanceNow);
|
||||
|
||||
if (focusing)
|
||||
{
|
||||
target = Vector3.Lerp(target, focusTarget, Time.unscaledDeltaTime * 2.5f);
|
||||
distance = Mathf.Lerp(distance, focusDistance, Time.unscaledDeltaTime * 2.5f);
|
||||
if (Vector3.Distance(target, focusTarget) < 0.05f && Mathf.Abs(distance - focusDistance) < 0.05f) focusing = false;
|
||||
}
|
||||
Apply();
|
||||
}
|
||||
|
||||
void Clamp()
|
||||
{
|
||||
Rect r = bounds;
|
||||
if (openArea.width > 0 && openArea.height > 0) // центр кадра -- только над открытой частью поселения
|
||||
r = Rect.MinMaxRect(Mathf.Max(r.xMin, openArea.xMin), Mathf.Max(r.yMin, openArea.yMin),
|
||||
Mathf.Min(r.xMax, openArea.xMax), Mathf.Min(r.yMax, openArea.yMax));
|
||||
target.x = Mathf.Clamp(target.x, r.xMin, r.xMax);
|
||||
target.z = Mathf.Clamp(target.z, r.yMin, r.yMax);
|
||||
target.y = 0;
|
||||
}
|
||||
|
||||
// Дымка начинается за городом, а не на фиксированном расстоянии от камеры: иначе при отдалении весь город
|
||||
// уходил в серую дымку -- казалось, что камера не отдаляется, а сцена сереет (05.11)
|
||||
public float fogStartBehind = 4f, fogEndBehind = 59f;
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
if (cam == null) cam = GetComponent<Camera>();
|
||||
PlaceAt(target, distance);
|
||||
if (RenderSettings.fog && RenderSettings.fogMode == FogMode.Linear)
|
||||
{
|
||||
RenderSettings.fogStartDistance = distance + fogStartBehind;
|
||||
RenderSettings.fogEndDistance = distance + fogEndBehind;
|
||||
}
|
||||
}
|
||||
|
||||
// Поставить камеру: смотрит на точку center с расстояния d.
|
||||
// Камера сцены может быть ортографической (вид 2.5D без перспективы): у неё расстояние не меняет размер города
|
||||
// на экране -- «+ / −» и щипок ничего не делали (05.11). Тогда приближение -- через размер кадра: столько же
|
||||
// земли, сколько видно перспективной камерой с этого расстояния
|
||||
void PlaceAt(Vector3 center, float d)
|
||||
{
|
||||
Quaternion rot = Quaternion.Euler(pitch, yaw, 0);
|
||||
transform.rotation = rot;
|
||||
transform.position = center - rot * Vector3.forward * d;
|
||||
if (cam != null && cam.orthographic)
|
||||
cam.orthographicSize = d * Mathf.Tan(cam.fieldOfView * 0.5f * Mathf.Deg2Rad);
|
||||
}
|
||||
|
||||
// Точка под пальцем на горизонтальной плоскости на высоте земли, где палец коснулся (06.11): на холме карта
|
||||
// идёт за пальцем точно, а не чуть быстрее или медленнее, как было с плоскостью y = 0
|
||||
float panPlaneY;
|
||||
|
||||
Vector3 GroundPoint(Vector3 screen)
|
||||
{
|
||||
Ray ray = cam.ScreenPointToRay(screen);
|
||||
var plane = new Plane(Vector3.up, new Vector3(0, panPlaneY, 0));
|
||||
return plane.Raycast(ray, out float t) ? ray.GetPoint(t) : target;
|
||||
}
|
||||
|
||||
// Высота земли (объект "Ground" 3D-поселения) под экранной точкой; не попали -- 0
|
||||
float GroundHeightUnder(Vector3 screen)
|
||||
{
|
||||
if (cam == null) return 0f;
|
||||
foreach (var hit in Physics.RaycastAll(cam.ScreenPointToRay(screen), 500f))
|
||||
if (hit.collider.gameObject.name == "Ground") return hit.point.y;
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8645f0028574904969b5802443f100f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44d822c903694362bef912c055adda5b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,272 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
// 3D-ПОСЕЛЕНИЕ: здания 3D-сцены становятся зданиями игры (05.11, Валдемар).
|
||||
// На 3D-здание вешаются те же компоненты, что на префабы 2D-города: рабочее место или Дом (ResourceCollector),
|
||||
// Склад / Амбар (StorageBuilding), Казармы, Госпиталь, Ратуша (TownHall) и PlacedBuildingId с именем здания.
|
||||
// Поэтому дальше всё работает так же, как в 2D: CitySync даёт места, жильё и лимиты; окно здания (BuildingWindow) --
|
||||
// рабочие, добыча, улучшение по таймеру; стройка идёт через BuildTimer (цена, строители, лимиты, ускорение, офлайн).
|
||||
// Место на карте проверяет SettlementMap Саши, цену, время и лимиты -- этот код и EconomyConfig.
|
||||
public static class City3DGameplay
|
||||
{
|
||||
// Имя типа 3D-сцены -> имя здания игры (как в 2D: по нему лимиты, окна, сохранение)
|
||||
public static string GameName(string type3d)
|
||||
{
|
||||
switch (type3d)
|
||||
{
|
||||
case "Дом": return "Население";
|
||||
case "Ферма": return "Еда";
|
||||
case "Лесопилка": return "Лес";
|
||||
case "Каменоломня": return "Камень";
|
||||
case "Шахта": return "Металл";
|
||||
case "Склад": return StorageBootstrap.WarehouseName;
|
||||
case "Амбар": return StorageBootstrap.GranaryName;
|
||||
case "Казармы": return BarracksBootstrap.BarracksName;
|
||||
case "Госпиталь": return HospitalBootstrap.HospitalName;
|
||||
case "Золотой рудник": return "Золото";
|
||||
case "Кристалл": return "Кристалл";
|
||||
case "Терем": return HeroCastleBootstrap.CastleName;
|
||||
default: return type3d; // "Ратуша"
|
||||
}
|
||||
}
|
||||
|
||||
// Обратно: имя здания игры (как в 2D-сохранении) -> тип 3D-сцены. null -- такого здания в 3D нет
|
||||
public static string Type3DFor(string gameName)
|
||||
{
|
||||
foreach (var t in AllTypes3D)
|
||||
if (GameName(t) == gameName) return t;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Здания, которые открываются ровно на этом уровне Ратуши (окно Ратуши: «Откроется: …»)
|
||||
public static List<string> TypesUnlockedAt(int townHallLevel)
|
||||
{
|
||||
var names = new List<string>();
|
||||
foreach (var t in AllTypes3D)
|
||||
if (!IsTownHall(t) && RequiredTownHall(t) == townHallLevel) names.Add(t);
|
||||
return names;
|
||||
}
|
||||
|
||||
static readonly string[] AllTypes3D =
|
||||
{
|
||||
"Ратуша", "Дом", "Ферма", "Лесопилка", "Каменоломня", "Шахта", "Склад", "Амбар", "Казармы", "Госпиталь",
|
||||
"Золотой рудник", "Кристалл", "Терем",
|
||||
};
|
||||
|
||||
public static bool IsTownHall(string type3d) => type3d == "Ратуша";
|
||||
|
||||
// Цена постройки -- та же, что у зданий 2D-города (Data_*.asset, Склады -- EconomyConfig, Госпиталь -- HospitalConfig)
|
||||
public static ResourceAmount[] BuildCost(string type3d)
|
||||
{
|
||||
var e = EconomyConfig.Load().storage;
|
||||
switch (type3d)
|
||||
{
|
||||
case "Дом": return Cost(ResourceType.Wood, 100, ResourceType.Food, 200, ResourceType.Stone, 150);
|
||||
case "Ферма": return Cost(ResourceType.Wood, 100);
|
||||
case "Лесопилка": return Cost();
|
||||
case "Каменоломня": return Cost(ResourceType.Wood, 50);
|
||||
case "Шахта": return Cost(ResourceType.Wood, 600, ResourceType.Stone, 550);
|
||||
case "Склад": return Cost(ResourceType.Wood, e.warehouseWood, ResourceType.Stone, e.warehouseStone);
|
||||
case "Амбар": return Cost(ResourceType.Wood, e.granaryWood, ResourceType.Stone, e.granaryStone);
|
||||
case "Казармы": return Cost(ResourceType.Wood, 300, ResourceType.Stone, 200);
|
||||
case "Госпиталь":
|
||||
var h = HospitalConfig.Load();
|
||||
return Cost(ResourceType.Wood, h.buildWood, ResourceType.Stone, h.buildStone);
|
||||
// Перенесены из 2D-города (решение 05.11): цены -- из Data_GoldMine, Data_Kristall и HeroCastleBootstrap
|
||||
case "Золотой рудник": return Cost(ResourceType.Stone, 50);
|
||||
case "Кристалл": return Cost(ResourceType.Wood, 2000, ResourceType.Stone, 3200, ResourceType.Gold, 12000, ResourceType.Iron, 1600);
|
||||
case "Терем": return Cost(ResourceType.Wood, HeroCastleBootstrap.CostWood, ResourceType.Stone, HeroCastleBootstrap.CostStone);
|
||||
default: return Cost();
|
||||
}
|
||||
}
|
||||
|
||||
// С какого уровня Ратуши можно строить (как в 2D)
|
||||
public static int RequiredTownHall(string type3d)
|
||||
{
|
||||
switch (type3d)
|
||||
{
|
||||
case "Шахта": return 3;
|
||||
case "Золотой рудник": return 2;
|
||||
case "Кристалл": return 6;
|
||||
case "Госпиталь": return Mathf.Max(1, HospitalConfig.Load().requiredTownHallLevel);
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static ResourceAmount[] Cost(params object[] pairs)
|
||||
{
|
||||
var list = new List<ResourceAmount>();
|
||||
for (int i = 0; i + 1 < pairs.Length; i += 2)
|
||||
list.Add(new ResourceAmount((ResourceType)pairs[i], System.Convert.ToSingle(pairs[i + 1])));
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public static int TownHallLevel => GameServices.TryGet(out ISettlement s) ? s.TownHallLevel : 1;
|
||||
|
||||
// Можно ли строить: уровень Ратуши, лимит зданий, строители, ресурсы. null -- можно, иначе текст для игрока
|
||||
public static string CanBuild(string type3d)
|
||||
{
|
||||
int th = TownHallLevel;
|
||||
if (th < RequiredTownHall(type3d)) return $"Откроется на Ратуше {RequiredTownHall(type3d)} ур.";
|
||||
string name = GameName(type3d);
|
||||
int max = EconomyConfig.Load().BuildingLimitFor(name, th), have = CountBuilt(name);
|
||||
if (max >= 0 && have >= max) return $"Лимит: {have} / {max} на Ратуше {th} ур.";
|
||||
if (BuildTimer.DurationMs(false, 1) > 0 && BuildTimer.FreeBuilders == 0) return "Все строители заняты";
|
||||
var cost = BuildCost(type3d);
|
||||
if (cost.Length > 0 && (!GameServices.TryGet(out IEconomy economy) || !economy.CanAfford(cost))) return "Не хватает ресурсов: " + CostText(cost);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string CostText(ResourceAmount[] cost)
|
||||
{
|
||||
if (cost.Length == 0) return "бесплатно";
|
||||
var parts = new List<string>();
|
||||
foreach (var c in cost) parts.Add($"{c.Amount:0} {GameData.ResName(c.Type).ToLower()}");
|
||||
return string.Join(", ", parts.ToArray());
|
||||
}
|
||||
|
||||
// Кристальные шахты: с какого момента каждая добывает (мс; long.MinValue -- давно достроена; конец стройки -- если
|
||||
// ещё строится или достроилась офлайн). Город открыт -- по сцене, иначе -- по сохранению settlement3d.
|
||||
// null -- город ещё не загружен: считать рано (иначе офлайн-время шахты пропало бы)
|
||||
public static List<long> CrystalMinesActiveFrom()
|
||||
{
|
||||
var list = new List<long>();
|
||||
var city = City3DController.Instance;
|
||||
if (city != null)
|
||||
{
|
||||
if (!city.StateApplied) return null;
|
||||
foreach (var id in Object.FindObjectsByType<PlacedBuildingId>(FindObjectsSortMode.None))
|
||||
{
|
||||
if (id.buildingName != "Кристалл") continue;
|
||||
var t = BuildTimer.Of(id.gameObject);
|
||||
list.Add(t != null && BuildTimer.IsConstructing(id.transform) ? t.endMs : long.MinValue);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
if (!City3DSave.Loaded) return null;
|
||||
if (City3DSave.Cached != null)
|
||||
foreach (var e in City3DSave.Cached.buildings)
|
||||
{
|
||||
if (e == null || e.type != "Кристалл") continue;
|
||||
bool building = e.timerKind == (int)BuildTimer.Kind.Build && e.level <= 1 && e.timerEndMs > 0;
|
||||
list.Add(building ? e.timerEndMs : long.MinValue);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static int CountBuilt(string gameName)
|
||||
{
|
||||
int n = 0;
|
||||
foreach (var id in Object.FindObjectsByType<PlacedBuildingId>(FindObjectsSortMode.None))
|
||||
if (id.buildingName == gameName) n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
// Новое здание поставлено на карту: списать цену и начать стройку по времени (BuildTimer)
|
||||
public static void StartConstruction(GameObject view)
|
||||
{
|
||||
var tag = view.GetComponent<City3DBuildingTag>();
|
||||
if (tag == null) return;
|
||||
var cost = BuildCost(tag.type3d);
|
||||
if (cost.Length > 0 && GameServices.TryGet(out IEconomy economy)) economy.TrySpend(cost);
|
||||
long ms = BuildTimer.DurationMs(false, 1);
|
||||
if (ms > 0)
|
||||
{
|
||||
long now = GameClock.UnixNowMs;
|
||||
BuildTimer.Begin(view, BuildTimer.Kind.Build, 1, now, now + ms);
|
||||
}
|
||||
if (CitySync.Instance != null) CitySync.Instance.SyncNow();
|
||||
}
|
||||
|
||||
// Повесить на 3D-здание компоненты здания игры (как у префабов 2D). Без цены и таймера
|
||||
public static void Attach(GameObject view, string type3d, int level)
|
||||
{
|
||||
var tag = view.GetComponent<City3DBuildingTag>() ?? view.AddComponent<City3DBuildingTag>();
|
||||
tag.type3d = type3d;
|
||||
|
||||
if (IsTownHall(type3d))
|
||||
{
|
||||
var th = view.GetComponent<TownHall>() ?? view.AddComponent<TownHall>();
|
||||
// Та же цена улучшения, что у Ратуши 2D-города (сцена MVP1): 250 дерева, 160 камня, 25 людей, ×1.5 за уровень
|
||||
th.baseUpgradeCost = new[]
|
||||
{
|
||||
new ResourceCost { type = ResourceType.Wood, amount = 250 },
|
||||
new ResourceCost { type = ResourceType.Stone, amount = 160 },
|
||||
new ResourceCost { type = ResourceType.Population, amount = 25 },
|
||||
};
|
||||
th.costMultiplierPerLevel = 1.5f;
|
||||
th.maxLevel = 10;
|
||||
th.currentLevel = Mathf.Max(1, level);
|
||||
return;
|
||||
}
|
||||
|
||||
view.AddComponent<PlacedBuildingId>().buildingName = GameName(type3d);
|
||||
switch (type3d)
|
||||
{
|
||||
case "Дом": Collector(view, ResourceType.Population, level); break;
|
||||
case "Ферма": Collector(view, ResourceType.Food, level); break;
|
||||
case "Лесопилка": Collector(view, ResourceType.Wood, level); break;
|
||||
case "Каменоломня": Collector(view, ResourceType.Stone, level); break;
|
||||
case "Шахта": Collector(view, ResourceType.Iron, level); break;
|
||||
case "Склад": Storage(view, StorageBuilding.Kind.Warehouse, level); break;
|
||||
case "Амбар": Storage(view, StorageBuilding.Kind.Granary, level); break;
|
||||
case "Казармы": view.AddComponent<Barracks>().Level = Mathf.Max(1, level); break;
|
||||
case "Госпиталь": view.AddComponent<Hospital>().Level = Mathf.Max(1, level); break;
|
||||
case "Золотой рудник": Collector(view, ResourceType.Gold, level); break;
|
||||
case "Кристалл": Collector(view, ResourceType.Gems, level); break; // рабочих нет, как в 2D
|
||||
case "Терем": view.AddComponent<HeroCastle>(); break; // окно героя -- BuildingWindow
|
||||
}
|
||||
}
|
||||
|
||||
static void Collector(GameObject view, ResourceType type, int level)
|
||||
{
|
||||
var rc = view.AddComponent<ResourceCollector>();
|
||||
rc.resourceType = type;
|
||||
rc.Level = Mathf.Max(1, level);
|
||||
}
|
||||
|
||||
static void Storage(GameObject view, StorageBuilding.Kind kind, int level)
|
||||
{
|
||||
var sb = view.AddComponent<StorageBuilding>();
|
||||
sb.kind = kind;
|
||||
sb.Level = Mathf.Max(1, level);
|
||||
}
|
||||
|
||||
// Уровень здания (для сохранения и подписи)
|
||||
public static int LevelOf(GameObject view)
|
||||
{
|
||||
var th = view.GetComponent<TownHall>();
|
||||
if (th != null) return th.currentLevel;
|
||||
var leveled = view.GetComponent<ILeveledBuilding>();
|
||||
return leveled != null ? leveled.Level : 1;
|
||||
}
|
||||
|
||||
// HUD 2D-города нарисован на холсте "в камере" (Screen Space - Camera). В 3D-сцене холст стоял бы на расстоянии
|
||||
// от камеры, и холмы и деревья перекрывали бы кнопки. Ставим холст вплотную к камере -- он всегда поверх 3D.
|
||||
public static void PutHudInFront(Camera cam)
|
||||
{
|
||||
if (cam == null) return;
|
||||
foreach (var canvas in Object.FindObjectsByType<Canvas>(FindObjectsSortMode.None))
|
||||
{
|
||||
if (!canvas.isRootCanvas || canvas.renderMode != RenderMode.ScreenSpaceCamera) continue;
|
||||
canvas.worldCamera = cam;
|
||||
canvas.planeDistance = cam.nearClipPlane + 0.05f;
|
||||
HideWorldSprites(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
// В HUD есть картинка-спрайт мира (SpriteRenderer) -- плашка с мечом по центру нижней панели. На холсте у
|
||||
// камеры она рисуется поверх всего HUD, даже поверх открытых окон, а сама картинка без прозрачности
|
||||
// ("шахматка"). Решение 05.11 (Миша, Володя): в 3D-сцене её убираем, пока не будет новой картинки или кнопки.
|
||||
static void HideWorldSprites(Canvas canvas)
|
||||
{
|
||||
foreach (var sr in canvas.GetComponentsInChildren<SpriteRenderer>(true))
|
||||
sr.enabled = false;
|
||||
}
|
||||
|
||||
// Награда за расчистку (решение 04.11): +10 дерева за дерево или +10 камня за валун, один раз, в пределах склада
|
||||
public static void RewardClearing(bool tree)
|
||||
{
|
||||
if (GameServices.TryGet(out IEconomy economy)) economy.Add(tree ? ResourceType.Wood : ResourceType.Stone, 10);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b010df83fccefa4aa9c394f84b8214f
|
||||
@@ -1,186 +0,0 @@
|
||||
using Bogatyri.Settlement.Placement;
|
||||
using UnityEngine;
|
||||
|
||||
// ГЕРОЙ В 3D-ПОСЕЛЕНИИ (перенос из 2D, решение 05.11: всё из 2D-города -- в 3D). Как HeroWalker в 2D-городе:
|
||||
// - живёт у Терема богатыря: появляется у его ворот, когда Терем достроен, и исчезает, если Терема нет;
|
||||
// - гуляет по свободным клеткам 3D-карты (земля, открытая зона, без зданий и деревьев) не дальше WalkRadius от Терема:
|
||||
// шаг -- на соседнюю свободную клетку, поэтому сквозь здания и деревья не проходит; после нескольких шагов стоит 1-3 с;
|
||||
// - тап по герою -- фраза и окно героя (окно Терема); в режиме стройки и переноса прячется;
|
||||
// - пока без арта: фигурка из простых форм (тело, голова, шлем, щит, копьё). Арт подставим в BuildFigure.
|
||||
// Только оживление города: на экономику, бой и сохранение не влияет. Создаёт и убирает City3DController.
|
||||
public class City3DHero : MonoBehaviour
|
||||
{
|
||||
public static City3DHero Instance { get; private set; }
|
||||
|
||||
const float Speed = 1.3f; // клеток в секунду
|
||||
const int WalkRadius = 6; // насколько далеко уходит от Терема
|
||||
|
||||
static readonly string[] Phrases =
|
||||
{
|
||||
"Город под защитой!",
|
||||
"Веди, воевода, — не подведу.",
|
||||
"Силушка богатырская при мне.",
|
||||
"Дозором обхожу владения.",
|
||||
"Есть ли где ворог? Только скажи.",
|
||||
};
|
||||
|
||||
SettlementMap map;
|
||||
System.Func<int> townHallLevel;
|
||||
public string CastleId { get; private set; }
|
||||
public GameObject CastleView { get; private set; }
|
||||
|
||||
Transform figure;
|
||||
Vector2Int cell; // клетка, к которой идёт (или на которой стоит)
|
||||
Vector2Int previous; // откуда пришёл -- чтобы не топтаться туда-обратно
|
||||
int stepsLeft;
|
||||
float waitLeft;
|
||||
|
||||
// Видимость: в режиме стройки и переноса герой прячется, чтобы не мешать ставить здания
|
||||
public bool Hidden
|
||||
{
|
||||
get => figure != null && !figure.gameObject.activeSelf;
|
||||
set { if (figure != null && figure.gameObject.activeSelf == value) figure.gameObject.SetActive(!value); }
|
||||
}
|
||||
|
||||
public static City3DHero Spawn(Transform parent, SettlementMap map, string castleId, GameObject castleView, System.Func<int> townHallLevel)
|
||||
{
|
||||
if (Instance != null) Destroy(Instance.gameObject);
|
||||
var go = new GameObject("Hero3D");
|
||||
go.transform.SetParent(parent, false);
|
||||
var h = go.AddComponent<City3DHero>();
|
||||
h.map = map;
|
||||
h.CastleId = castleId;
|
||||
h.CastleView = castleView;
|
||||
h.townHallLevel = townHallLevel;
|
||||
h.BuildFigure();
|
||||
h.cell = h.previous = h.Gate();
|
||||
go.transform.position = CellWorld(h.cell);
|
||||
h.waitLeft = 1f;
|
||||
Instance = h;
|
||||
return h;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (Instance == this) Instance = null;
|
||||
}
|
||||
|
||||
// ---------------- Внешний вид (заглушка) ----------------
|
||||
|
||||
void BuildFigure()
|
||||
{
|
||||
figure = new GameObject("Figure").transform;
|
||||
figure.SetParent(transform, false);
|
||||
Part(PrimitiveType.Capsule, new Vector3(0, 0.32f, 0), new Vector3(0.28f, 0.3f, 0.22f), new Color(0.55f, 0.4f, 0.2f)); // кафтан
|
||||
Part(PrimitiveType.Sphere, new Vector3(0, 0.72f, 0), new Vector3(0.2f, 0.2f, 0.2f), new Color(1f, 0.85f, 0.7f)); // голова
|
||||
Part(PrimitiveType.Sphere, new Vector3(0, 0.8f, 0), new Vector3(0.22f, 0.14f, 0.22f), new Color(0.75f, 0.75f, 0.8f)); // шлем
|
||||
Part(PrimitiveType.Cylinder, new Vector3(0, 0.9f, 0), new Vector3(0.03f, 0.06f, 0.03f), new Color(0.75f, 0.75f, 0.8f)); // шишак
|
||||
Part(PrimitiveType.Cube, new Vector3(0.17f, 0.36f, 0.04f), new Vector3(0.04f, 0.26f, 0.2f), new Color(0.8f, 0.15f, 0.15f)); // щит
|
||||
Part(PrimitiveType.Cylinder, new Vector3(-0.17f, 0.5f, 0), new Vector3(0.025f, 0.5f, 0.025f), new Color(0.45f, 0.3f, 0.15f)); // копьё
|
||||
Part(PrimitiveType.Cylinder, new Vector3(0, 0.005f, 0), new Vector3(0.36f, 0.005f, 0.36f), new Color(0.18f, 0.22f, 0.14f)); // тень
|
||||
|
||||
// Тап по герою: коллайдер чуть больше фигурки (City3DController ищет City3DHero под пальцем)
|
||||
var hit = gameObject.AddComponent<BoxCollider>();
|
||||
hit.center = new Vector3(0, 0.5f, 0);
|
||||
hit.size = new Vector3(0.7f, 1.1f, 0.7f);
|
||||
}
|
||||
|
||||
void Part(PrimitiveType type, Vector3 pos, Vector3 scale, Color color)
|
||||
{
|
||||
var p = GameObject.CreatePrimitive(type);
|
||||
Destroy(p.GetComponent<Collider>());
|
||||
p.transform.SetParent(figure, false);
|
||||
p.transform.localPosition = pos;
|
||||
p.transform.localScale = scale;
|
||||
p.GetComponent<Renderer>().sharedMaterial = Map3D.Mat(color);
|
||||
}
|
||||
|
||||
// ---------------- Прогулка по клеткам ----------------
|
||||
|
||||
static Vector3 CellWorld(Vector2Int c)
|
||||
{
|
||||
float x = c.x + 0.5f, z = c.y + 0.5f;
|
||||
return new Vector3(x, City3DLayout.HeightAt(x, z), z);
|
||||
}
|
||||
|
||||
bool Walkable(int x, int y)
|
||||
{
|
||||
if (map == null || !map.InMap(x, y)) return false;
|
||||
if (map.KindAt(x, y) != CellKind.Ground || !map.IsOpen(x, y, townHallLevel != null ? townHallLevel() : 1)) return false;
|
||||
return map.BuildingAt(x, y) == null && map.ObstacleAt(x, y) == null;
|
||||
}
|
||||
|
||||
bool CastleRect(out PlacedRect r)
|
||||
{
|
||||
r = default;
|
||||
return map != null && map.TryGetBuilding(CastleId, out r);
|
||||
}
|
||||
|
||||
bool NearHome(int x, int y)
|
||||
{
|
||||
if (!CastleRect(out var r)) return true;
|
||||
float cx = r.X + r.W / 2f, cy = r.Y + r.H / 2f;
|
||||
return Mathf.Abs(x + 0.5f - cx) <= WalkRadius + r.W / 2f && Mathf.Abs(y + 0.5f - cy) <= WalkRadius + r.H / 2f;
|
||||
}
|
||||
|
||||
// Ворота: ближайшая свободная клетка вокруг Терема (с нижней стороны -- первой)
|
||||
Vector2Int Gate()
|
||||
{
|
||||
if (!CastleRect(out var r)) return Vector2Int.zero;
|
||||
for (int ring = 1; ring <= WalkRadius; ring++)
|
||||
for (int x = r.X - ring; x < r.X + r.W + ring; x++)
|
||||
foreach (int y in new[] { r.Y - ring, r.Y + r.H - 1 + ring })
|
||||
if (Walkable(x, y)) return new Vector2Int(x, y);
|
||||
return new Vector2Int(r.X + r.W / 2, r.Y - 1);
|
||||
}
|
||||
|
||||
static readonly Vector2Int[] Dirs = { Vector2Int.up, Vector2Int.down, Vector2Int.left, Vector2Int.right };
|
||||
|
||||
// Следующая клетка: соседняя свободная рядом с Теремом; назад -- только если больше некуда
|
||||
Vector2Int NextCell()
|
||||
{
|
||||
var options = new System.Collections.Generic.List<Vector2Int>();
|
||||
foreach (var d in Dirs)
|
||||
{
|
||||
var c = cell + d;
|
||||
if (c != previous && Walkable(c.x, c.y) && NearHome(c.x, c.y)) options.Add(c);
|
||||
}
|
||||
if (options.Count == 0 && Walkable(previous.x, previous.y)) return previous;
|
||||
return options.Count > 0 ? options[Random.Range(0, options.Count)] : cell;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (Hidden) return;
|
||||
if (!Walkable(cell.x, cell.y) && cell != Gate()) { cell = previous = Gate(); transform.position = CellWorld(cell); } // на клетку поставили здание
|
||||
if (waitLeft > 0f) { waitLeft -= Time.deltaTime; figure.localPosition = Vector3.zero; return; }
|
||||
|
||||
Vector3 goal = CellWorld(cell), pos = transform.position;
|
||||
Vector3 flat = new Vector3(goal.x - pos.x, 0, goal.z - pos.z);
|
||||
if (flat.magnitude < 0.03f)
|
||||
{
|
||||
if (stepsLeft <= 0) { stepsLeft = Random.Range(3, 7); waitLeft = Random.Range(1f, 3f); return; }
|
||||
stepsLeft--;
|
||||
var next = NextCell();
|
||||
previous = cell;
|
||||
cell = next;
|
||||
return;
|
||||
}
|
||||
Vector3 step = flat.normalized * Speed * Time.deltaTime;
|
||||
if (step.magnitude > flat.magnitude) step = flat;
|
||||
pos += step;
|
||||
pos.y = City3DLayout.HeightAt(pos.x, pos.z); // по земле, через холмы
|
||||
transform.position = pos;
|
||||
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(flat), Time.deltaTime * 10f); // смотрит, куда идёт
|
||||
figure.localPosition = new Vector3(0f, Mathf.Abs(Mathf.Sin(Time.time * 10f)) * 0.04f, 0f); // покачивание при ходьбе
|
||||
}
|
||||
|
||||
// ---------------- Тап ----------------
|
||||
|
||||
public void OnTapped()
|
||||
{
|
||||
UIKit.Toast("Богатырь: " + Phrases[Random.Range(0, Phrases.Length)]);
|
||||
waitLeft = 2f; // остановился поговорить
|
||||
if (CastleView != null) BuildingWindow.Open(CastleView);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d4c2a7e1f5b4e93b6a1c0d9e2f73a58
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,66 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
// Палец над HUD в 3D-поселении (05.11): «над интерфейсом» -- только если под пальцем то, что нажимают или крутят
|
||||
// (кнопка, поле, ползунок, прокручиваемое окно) или небольшая панель. Невидимый слой во весь экран
|
||||
// (затемнение, контейнер безопасной зоны, фон) карту не блокирует -- иначе не работают ни тап, ни зум, ни стройка.
|
||||
// Такой слой пишется в Console один раз, чтобы его можно было найти и убрать.
|
||||
public static class City3DInput
|
||||
{
|
||||
static readonly List<RaycastResult> hits = new List<RaycastResult>();
|
||||
static readonly HashSet<string> reported = new HashSet<string>();
|
||||
|
||||
public static bool OverHud(Vector2 screen)
|
||||
{
|
||||
var es = EventSystem.current;
|
||||
if (es == null) return false;
|
||||
// Открыто окно HUD (панель) -- весь ввод его, карта не трогается
|
||||
if (UIPanelManager.Instance != null && UIPanelManager.Instance.CurrentId != "")
|
||||
{
|
||||
if (reported.Add("panel:" + UIPanelManager.Instance.CurrentId))
|
||||
Debug.Log($"[3D] Открыта панель HUD «{UIPanelManager.Instance.CurrentId}» -- 3D-карта не принимает нажатия, пока её не закрыть");
|
||||
return true;
|
||||
}
|
||||
hits.Clear();
|
||||
es.RaycastAll(new PointerEventData(es) { position = screen }, hits);
|
||||
foreach (var h in hits)
|
||||
{
|
||||
if (h.gameObject == null) continue;
|
||||
if (IsInteractive(h.gameObject) || !CoversScreen(h.gameObject)) return true;
|
||||
if (reported.Add(h.gameObject.name))
|
||||
Debug.Log($"[3D] Слой интерфейса во весь экран ловит нажатия, 3D-карта его пропускает: «{Path(h.gameObject.transform)}». " +
|
||||
"Если он не нужен -- выключить у него Raycast Target");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsInteractive(GameObject go)
|
||||
{
|
||||
// кнопки, прокрутка, поля, а также всё, что само ловит нажатие (затемнение модального окна закрывает его тапом)
|
||||
return go.GetComponentInParent<Selectable>() != null || go.GetComponentInParent<ScrollRect>() != null
|
||||
|| go.GetComponentInParent<IPointerClickHandler>() != null || go.GetComponentInParent<IPointerDownHandler>() != null
|
||||
|| go.name == "Modal" || go.GetComponentInParent<TMPro.TMP_InputField>() != null;
|
||||
}
|
||||
|
||||
// Занимает почти весь экран (больше 80% по ширине и высоте)
|
||||
static bool CoversScreen(GameObject go)
|
||||
{
|
||||
if (!(go.transform is RectTransform rt)) return false;
|
||||
var corners = new Vector3[4];
|
||||
rt.GetWorldCorners(corners);
|
||||
var canvas = go.GetComponentInParent<Canvas>();
|
||||
Camera cam = canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay ? canvas.worldCamera : null;
|
||||
Vector2 a = RectTransformUtility.WorldToScreenPoint(cam, corners[0]);
|
||||
Vector2 b = RectTransformUtility.WorldToScreenPoint(cam, corners[2]);
|
||||
return Mathf.Abs(b.x - a.x) > Screen.width * 0.8f && Mathf.Abs(b.y - a.y) > Screen.height * 0.8f;
|
||||
}
|
||||
|
||||
static string Path(Transform t)
|
||||
{
|
||||
string p = t.name;
|
||||
for (var x = t.parent; x != null; x = x.parent) p = x.name + "/" + p;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c71f24a7cb164064b98f198eec9665d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,110 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Bogatyri.Settlement.Placement;
|
||||
using UnityEngine;
|
||||
|
||||
// ТЕСТОВЫЙ УЧАСТОК 3D-ПОСЕЛЕНИЯ (ТЗ «Система строительства поселения» от 04.11.2026, этап I, прототип).
|
||||
// Холмы, река, скалы, деревья и валуны строятся из seed -- одинаково при каждом запуске.
|
||||
// Зоны стройки открываются уровнем Ратуши квадратами от центра (ТЕСТ, число и пороги зон -- на согласование).
|
||||
// Все числа здесь -- предложение для прототипа, не утверждённый баланс.
|
||||
public sealed class City3DLayout
|
||||
{
|
||||
public const int Size = 48; // клеток по каждой стороне
|
||||
public const int Seed = 4112026;
|
||||
|
||||
public struct Obstacle
|
||||
{
|
||||
public string Id;
|
||||
public bool IsTree; // дерево (иначе валун)
|
||||
public List<(int x, int y)> Cells;
|
||||
}
|
||||
|
||||
public SettlementMap Map;
|
||||
public readonly List<Obstacle> Obstacles = new List<Obstacle>();
|
||||
public readonly List<Vector2> Grass = new List<Vector2>(); // мелкий декор: скрывается под зданиями
|
||||
|
||||
// Сколько клеток от центра открывает уровень Ратуши: 1 -- 16 x 16, 2 -- 24 x 24, ... 5 -- весь участок
|
||||
public static int ZoneHalfSize(int townHallLevel) => Mathf.Min(Size / 2, 8 + 4 * (Mathf.Max(1, townHallLevel) - 1));
|
||||
|
||||
// Высота земли в точке (мировые единицы, клетка = 1)
|
||||
public static float HeightAt(float x, float z)
|
||||
{
|
||||
float cx = x - Size / 2f, cz = z - Size / 2f;
|
||||
float fromCenter = Mathf.Sqrt(cx * cx + cz * cz) / (Size / 2f);
|
||||
// Холмы: в центре почти ровно, к краям -- выше и круче
|
||||
float hills = (Mathf.Sin(x * 0.27f) * 0.5f + Mathf.Cos(z * 0.21f) * 0.45f + Mathf.PerlinNoise(x * 0.11f, z * 0.11f) * 0.9f)
|
||||
* Mathf.Lerp(0.25f, 1.6f, Mathf.Clamp01(fromCenter));
|
||||
if (IsMountain(x, z)) hills += 2.5f + Mathf.PerlinNoise(x * 0.3f, z * 0.3f) * 2f;
|
||||
if (IsRiver(x, z)) hills = -0.5f;
|
||||
return hills;
|
||||
}
|
||||
|
||||
public static float RiverX(float z) => 37f + 3f * Mathf.Sin(z * 0.17f);
|
||||
public static bool IsRiver(float x, float z) => Mathf.Abs(x - RiverX(z)) < 1.3f;
|
||||
public static bool IsMountain(float x, float z) => x < 9f && z > 34f || x < 4f && z > 24f;
|
||||
|
||||
// Местность за краем участка (только вид, стройки нет): те же холмы, река и горы продолжаются без шва,
|
||||
// к дальнему краю (SkirtCells) плавно опускаются до общей равнины SkirtFloor (06.11: при отдалении край участка
|
||||
// выглядел обрезанным -- холмы и река обрывались уступом)
|
||||
public const int SkirtCells = 60;
|
||||
public const float SkirtFloor = -0.7f;
|
||||
|
||||
public static float SkirtHeight(float x, float z)
|
||||
{
|
||||
float outside = Mathf.Max(0f, Mathf.Max(Mathf.Max(-x, x - Size), Mathf.Max(-z, z - Size)));
|
||||
float fade = 1f - Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(SkirtCells - 20f, SkirtCells, outside));
|
||||
return Mathf.Lerp(SkirtFloor, HeightAt(x, z), fade);
|
||||
}
|
||||
|
||||
public static City3DLayout Create(PlacementRules rules = null)
|
||||
{
|
||||
var layout = new City3DLayout { Map = new SettlementMap(Size, Size, rules) };
|
||||
var map = layout.Map;
|
||||
int c = Size / 2;
|
||||
|
||||
for (int y = 0; y < Size; y++)
|
||||
for (int x = 0; x < Size; x++)
|
||||
{
|
||||
float px = x + 0.5f, pz = y + 0.5f;
|
||||
CellKind kind = IsRiver(px, pz) ? CellKind.Water : IsMountain(px, pz) ? CellKind.Mountain : CellKind.Ground;
|
||||
float dist = Mathf.Max(Mathf.Abs(px - c), Mathf.Abs(pz - c)); // квадратное кольцо от центра
|
||||
int zone = 0;
|
||||
for (int lvl = 1; lvl <= 5; lvl++) if (dist < ZoneHalfSize(lvl)) { zone = lvl; break; }
|
||||
map.SetCell(x, y, kind, Mathf.RoundToInt(HeightAt(px, pz) * 1000f), zone);
|
||||
}
|
||||
|
||||
// Деревья и валуны: не в центральной поляне 10 x 10 (там Ратуша), не на воде и скалах
|
||||
var rng = new System.Random(Seed);
|
||||
var taken = new HashSet<int>();
|
||||
bool Free(int x, int y) => map.InMap(x, y) && map.KindAt(x, y) == CellKind.Ground && !taken.Contains(y * Size + x)
|
||||
&& (Math.Abs(x - c) > 5 || Math.Abs(y - c) > 5);
|
||||
int trees = 0, rocks = 0;
|
||||
for (int attempt = 0; attempt < 4000 && trees < 90; attempt++)
|
||||
{
|
||||
int x = rng.Next(Size), y = rng.Next(Size);
|
||||
if (!Free(x, y)) continue;
|
||||
taken.Add(y * Size + x);
|
||||
layout.Obstacles.Add(new Obstacle { Id = "tree_" + (++trees), IsTree = true, Cells = new List<(int, int)> { (x, y) } });
|
||||
}
|
||||
for (int attempt = 0; attempt < 4000 && rocks < 22; attempt++)
|
||||
{
|
||||
int x = rng.Next(Size - 1), y = rng.Next(Size - 1);
|
||||
bool big = rng.Next(3) == 0; // каждый третий валун -- 2 x 2
|
||||
var cells = big ? new List<(int, int)> { (x, y), (x + 1, y), (x, y + 1), (x + 1, y + 1) } : new List<(int, int)> { (x, y) };
|
||||
bool ok = true;
|
||||
foreach (var cell in cells) if (!Free(cell.Item1, cell.Item2)) { ok = false; break; }
|
||||
if (!ok) continue;
|
||||
foreach (var cell in cells) taken.Add(cell.Item2 * Size + cell.Item1);
|
||||
layout.Obstacles.Add(new Obstacle { Id = "rock_" + (++rocks), IsTree = false, Cells = cells });
|
||||
}
|
||||
foreach (var o in layout.Obstacles) map.AddObstacle(o.Id, o.Cells);
|
||||
|
||||
// Трава -- только украшение: не мешает стройке, прячется под зданием и возвращается, когда здание ушло
|
||||
for (int i = 0; i < 260; i++)
|
||||
{
|
||||
float x = (float)rng.NextDouble() * Size, z = (float)rng.NextDouble() * Size;
|
||||
if (map.KindAt((int)x, (int)z) == CellKind.Ground) layout.Grass.Add(new Vector2(x, z));
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31e2f86e3dff430ea8a67cc6f1cb4038
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,93 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
// Сохранение 3D-поселения: секция "settlement3d" (05.11, Валдемар). 2D-город сохраняется отдельно ("settlement"),
|
||||
// поэтому 3D-сцена его не трогает. Здания: id, тип, клетка, поворот, уровень, стройка или улучшение, которое идёт;
|
||||
// Ратуша: уровень и её улучшение; расчищенные деревья и валуны.
|
||||
// Регистрирует App/GameModules. Пока сцена City3D открыта, данные берутся у неё (Live), иначе -- последние известные.
|
||||
public sealed class City3DSave : ISaveParticipant
|
||||
{
|
||||
[Serializable]
|
||||
public class Entry
|
||||
{
|
||||
public string id;
|
||||
public string type; // тип 3D-сцены: "Ратуша", "Дом", "Ферма"...
|
||||
public int x, y, rot;
|
||||
public int level = 1;
|
||||
public int timerKind; // 0 -- нет, 1 -- постройка, 2 -- улучшение (BuildTimer.Kind)
|
||||
public long timerStartMs, timerEndMs;
|
||||
public int timerTargetLevel;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Data
|
||||
{
|
||||
public int nextId = 1;
|
||||
public List<Entry> buildings = new List<Entry>();
|
||||
public List<string> cleared = new List<string>();
|
||||
// Здания 2D-города, которым при переносе не нашлось места (зона закрыта, всё занято): ждут и ставятся,
|
||||
// как только место появится. Не теряются (05.11)
|
||||
public List<Entry> pendingFrom2D = new List<Entry>();
|
||||
}
|
||||
|
||||
public interface ILive
|
||||
{
|
||||
Data CaptureState();
|
||||
void OnSaveLoaded(); // загрузили другое сохранение или сбросили -- сцена должна перестроиться
|
||||
}
|
||||
|
||||
public static readonly City3DSave Instance = new City3DSave();
|
||||
public static Data Cached { get; private set; }
|
||||
// Сохранение уже прочитано (или новая игра): по Cached можно считать здания вне 3D-сцены (Кристальная шахта на карте мира)
|
||||
public static bool Loaded { get; private set; }
|
||||
public static ILive Live;
|
||||
|
||||
public string SaveKey => "settlement3d";
|
||||
|
||||
public string Capture()
|
||||
{
|
||||
if (Live != null) Cached = Live.CaptureState();
|
||||
return Cached != null ? JsonUtility.ToJson(Cached) : null;
|
||||
}
|
||||
|
||||
public void Restore(string json)
|
||||
{
|
||||
Loaded = true;
|
||||
Cached = string.IsNullOrEmpty(json) ? null : JsonUtility.FromJson<Data>(json);
|
||||
if (Cached != null)
|
||||
{
|
||||
if (Cached.buildings == null) Cached.buildings = new List<Entry>();
|
||||
if (Cached.cleared == null) Cached.cleared = new List<string>();
|
||||
if (Cached.pendingFrom2D == null) Cached.pendingFrom2D = new List<Entry>();
|
||||
}
|
||||
Live?.OnSaveLoaded();
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
Loaded = true;
|
||||
Cached = null;
|
||||
Live?.OnSaveLoaded();
|
||||
}
|
||||
|
||||
// Запись таймера здания в Entry и обратно
|
||||
public static void WriteTimer(Entry e, GameObject view)
|
||||
{
|
||||
var t = BuildTimer.Of(view);
|
||||
bool on = t != null && BuildTimer.All.Contains(t);
|
||||
e.timerKind = on ? (int)t.kind : 0;
|
||||
e.timerStartMs = on ? t.startMs : 0;
|
||||
e.timerEndMs = on ? t.endMs : 0;
|
||||
e.timerTargetLevel = on ? t.targetLevel : 0;
|
||||
}
|
||||
|
||||
// Таймер из файла -- только осмысленный: постройка у 1 уровня, улучшение до уровня выше текущего
|
||||
public static void ReadTimer(Entry e, GameObject view)
|
||||
{
|
||||
bool build = e.timerKind == (int)BuildTimer.Kind.Build && e.level <= 1;
|
||||
bool upgrade = e.timerKind == (int)BuildTimer.Kind.Upgrade && e.timerTargetLevel > Mathf.Max(1, e.level);
|
||||
if ((build || upgrade) && e.timerEndMs > 0)
|
||||
BuildTimer.Begin(view, (BuildTimer.Kind)e.timerKind, Mathf.Max(1, e.timerTargetLevel), e.timerStartMs, e.timerEndMs);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 037c1ed1fee52f74da1eaaeb4c20e3b1
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3baf0cc9c3e937b43b5085d21429d613
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user