Refactor products and buildings to support modding
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@ export_presets.cfg
|
||||
mono/
|
||||
*.csproj
|
||||
*.sln
|
||||
*.slnx
|
||||
obj/
|
||||
bin/
|
||||
*.pidb
|
||||
|
||||
@@ -1,51 +1,29 @@
|
||||
namespace MyBiz.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Тип здания
|
||||
/// Конфигурация типа здания - для моддинга
|
||||
/// </summary>
|
||||
public enum BuildingType
|
||||
public class BuildingTypeConfig
|
||||
{
|
||||
// Добыча сырья
|
||||
Farm, // Ферма
|
||||
CottonField, // Хлопковое поле
|
||||
Mine, // Шахта
|
||||
|
||||
// Производство
|
||||
FoodFactory, // Пищекомбинат
|
||||
TextileFactory, // Текстильная фабрика
|
||||
SteelMill, // Сталелитейный завод
|
||||
PlasticPlant, // Завод пластика
|
||||
ElectronicsFactory, // Завод электроники
|
||||
AutoFactory, // Автозавод
|
||||
|
||||
// Торговля
|
||||
GroceryStore, // Продуктовый магазин
|
||||
ClothingStore, // Магазин одежды
|
||||
ElectronicsStore, // Магазин электроники
|
||||
AutoDealer, // Автосалон
|
||||
Mall, // Торговый центр
|
||||
|
||||
// Исследования
|
||||
Laboratory, // Лаборатория
|
||||
|
||||
// Склад
|
||||
Warehouse // Склад
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Здание
|
||||
/// </summary>
|
||||
public class Building
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
public BuildingType Type { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string CityId { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Уникальный идентификатор типа здания
|
||||
/// </summary>
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Уровень здания (влияет на эффективность)
|
||||
/// Отображаемое имя
|
||||
/// </summary>
|
||||
public int Level { get; set; } = 1;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Описание
|
||||
/// </summary>
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Категория здания
|
||||
/// </summary>
|
||||
public BuildingCategory Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Стоимость постройки
|
||||
@@ -67,8 +45,140 @@ public class Building
|
||||
/// </summary>
|
||||
public int WorkerSlots { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Базовая эффективность (0-100)
|
||||
/// </summary>
|
||||
public int BaseEfficiency { get; set; } = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Максимальный уровень здания
|
||||
/// </summary>
|
||||
public int MaxLevel { get; set; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Иконка здания
|
||||
/// </summary>
|
||||
public string IconPath { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Требуемые технологии для разблокировки
|
||||
/// </summary>
|
||||
public List<string> RequiredTechnologies { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Год, когда здание становится доступным
|
||||
/// </summary>
|
||||
public int AvailableFromYear { get; set; } = 1900;
|
||||
|
||||
/// <summary>
|
||||
/// Производимые продукты (для фабрик)
|
||||
/// </summary>
|
||||
public List<string> OutputProducts { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Потребляемые продукты (для фабрик)
|
||||
/// </summary>
|
||||
public List<string> InputProducts { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Категория здания
|
||||
/// </summary>
|
||||
public enum BuildingCategory
|
||||
{
|
||||
RawMaterial, // Добыча сырья
|
||||
Production, // Производство
|
||||
Trade, // Торговля
|
||||
Research, // Исследования
|
||||
Storage, // Склад
|
||||
Office // Офис
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Здание - экземпляр в игре
|
||||
/// </summary>
|
||||
public class Building
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// Конфигурация типа здания
|
||||
/// </summary>
|
||||
public BuildingTypeConfig TypeConfig { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Отображаемое имя (можно кастомизировать)
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// ID города, где находится здание
|
||||
/// </summary>
|
||||
public string CityId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Координаты на карте
|
||||
/// </summary>
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Уровень здания (влияет на эффективность)
|
||||
/// </summary>
|
||||
public int Level { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Текущая эффективность (зависит от уровня и рабочих)
|
||||
/// </summary>
|
||||
public int CurrentEfficiency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Количество рабочих мест
|
||||
/// </summary>
|
||||
public int WorkerSlots => TypeConfig.WorkerSlots;
|
||||
|
||||
/// <summary>
|
||||
/// Заполненность рабочими
|
||||
/// </summary>
|
||||
public int Workers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Вместимость склада
|
||||
/// </summary>
|
||||
public int StorageCapacity => TypeConfig.StorageCapacity;
|
||||
|
||||
/// <summary>
|
||||
/// Текущие запасы на складе
|
||||
/// </summary>
|
||||
public Dictionary<string, int> Inventory { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Здание активно/работает
|
||||
/// </summary>
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Построено в тик
|
||||
/// </summary>
|
||||
public int BuiltAtTick { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Рассчитать текущую эффективность
|
||||
/// </summary>
|
||||
public void CalculateEfficiency()
|
||||
{
|
||||
int baseEff = TypeConfig.BaseEfficiency;
|
||||
int levelBonus = (Level - 1) * 5; // +5% за уровень
|
||||
int workerPenalty = WorkerSlots > 0 ? ((WorkerSlots - Workers) * 100 / WorkerSlots) : 0;
|
||||
|
||||
CurrentEfficiency = Math.Max(0, baseEff + levelBonus - workerPenalty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновить состояние
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
CalculateEfficiency();
|
||||
}
|
||||
}
|
||||
|
||||
202
backend/src/MyBiz.Core/DefaultProducts.cs
Normal file
202
backend/src/MyBiz.Core/DefaultProducts.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using MyBiz.Core;
|
||||
|
||||
namespace MyBiz.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Дефолтные конфигурации продуктов
|
||||
/// </summary>
|
||||
public static class DefaultProducts
|
||||
{
|
||||
public static IEnumerable<ProductType> GetAll()
|
||||
{
|
||||
return new List<ProductType>
|
||||
{
|
||||
// === СЫРЬЁ ===
|
||||
|
||||
new ProductType
|
||||
{
|
||||
Id = "raw_cotton",
|
||||
Name = "Хлопок",
|
||||
Description = "Сырьё для производства ткани",
|
||||
Category = ProductCategory.RawMaterial,
|
||||
BasePrice = 5m,
|
||||
BaseDemand = 50,
|
||||
DemandElasticity = 0.3f,
|
||||
StackSize = 500,
|
||||
AvailableFromYear = 1900,
|
||||
IconPath = "res://assets/sprites/products/cotton.png"
|
||||
},
|
||||
|
||||
new ProductType
|
||||
{
|
||||
Id = "raw_steel",
|
||||
Name = "Сталь",
|
||||
Description = "Основной металл для промышленности",
|
||||
Category = ProductCategory.RawMaterial,
|
||||
BasePrice = 15m,
|
||||
BaseDemand = 80,
|
||||
DemandElasticity = 0.4f,
|
||||
StackSize = 200,
|
||||
AvailableFromYear = 1900,
|
||||
IconPath = "res://assets/sprites/products/steel.png"
|
||||
},
|
||||
|
||||
new ProductType
|
||||
{
|
||||
Id = "raw_plastic",
|
||||
Name = "Пластик",
|
||||
Description = "Синтетический материал",
|
||||
Category = ProductCategory.RawMaterial,
|
||||
BasePrice = 8m,
|
||||
BaseDemand = 60,
|
||||
DemandElasticity = 0.5f,
|
||||
StackSize = 300,
|
||||
AvailableFromYear = 1950,
|
||||
IconPath = "res://assets/sprites/products/plastic.png"
|
||||
},
|
||||
|
||||
new ProductType
|
||||
{
|
||||
Id = "raw_food",
|
||||
Name = "Сельхозпродукция",
|
||||
Description = "Пшеница, овощи, фрукты",
|
||||
Category = ProductCategory.RawMaterial,
|
||||
BasePrice = 3m,
|
||||
BaseDemand = 100,
|
||||
DemandElasticity = 0.2f,
|
||||
ShelfLife = 10,
|
||||
StackSize = 500,
|
||||
AvailableFromYear = 1900,
|
||||
IconPath = "res://assets/sprites/products/food_raw.png"
|
||||
},
|
||||
|
||||
// === КОМПОНЕНТЫ ===
|
||||
|
||||
new ProductType
|
||||
{
|
||||
Id = "comp_fabric",
|
||||
Name = "Ткань",
|
||||
Description = "Материал для пошива одежды",
|
||||
Category = ProductCategory.Component,
|
||||
BasePrice = 12m,
|
||||
BaseDemand = 40,
|
||||
DemandElasticity = 0.6f,
|
||||
StackSize = 200,
|
||||
AvailableFromYear = 1900,
|
||||
IconPath = "res://assets/sprites/products/fabric.png"
|
||||
},
|
||||
|
||||
new ProductType
|
||||
{
|
||||
Id = "comp_metal_parts",
|
||||
Name = "Металлоизделия",
|
||||
Description = "Детали и запчасти из металла",
|
||||
Category = ProductCategory.Component,
|
||||
BasePrice = 25m,
|
||||
BaseDemand = 50,
|
||||
DemandElasticity = 0.5f,
|
||||
StackSize = 100,
|
||||
AvailableFromYear = 1900,
|
||||
IconPath = "res://assets/sprites/products/metal_parts.png"
|
||||
},
|
||||
|
||||
new ProductType
|
||||
{
|
||||
Id = "comp_plastic_parts",
|
||||
Name = "Пластиковые детали",
|
||||
Description = "Компоненты из пластика",
|
||||
Category = ProductCategory.Component,
|
||||
BasePrice = 18m,
|
||||
BaseDemand = 45,
|
||||
DemandElasticity = 0.5f,
|
||||
StackSize = 150,
|
||||
AvailableFromYear = 1950,
|
||||
IconPath = "res://assets/sprites/products/plastic_parts.png"
|
||||
},
|
||||
|
||||
new ProductType
|
||||
{
|
||||
Id = "comp_electronics",
|
||||
Name = "Электронные компоненты",
|
||||
Description = "Микросхемы, транзисторы, платы",
|
||||
Category = ProductCategory.Component,
|
||||
BasePrice = 50m,
|
||||
BaseDemand = 30,
|
||||
DemandElasticity = 0.7f,
|
||||
StackSize = 50,
|
||||
AvailableFromYear = 1960,
|
||||
IconPath = "res://assets/sprites/products/electronics.png"
|
||||
},
|
||||
|
||||
// === ТОВАРЫ ===
|
||||
|
||||
new ProductType
|
||||
{
|
||||
Id = "goods_food",
|
||||
Name = "Продукты питания",
|
||||
Description = "Готовая еда для потребителей",
|
||||
Category = ProductCategory.ConsumerGoods,
|
||||
BasePrice = 8m,
|
||||
BaseDemand = 150,
|
||||
DemandElasticity = 0.3f,
|
||||
ShelfLife = 5,
|
||||
StackSize = 200,
|
||||
AvailableFromYear = 1900,
|
||||
IconPath = "res://assets/sprites/products/food.png"
|
||||
},
|
||||
|
||||
new ProductType
|
||||
{
|
||||
Id = "goods_clothing",
|
||||
Name = "Одежда",
|
||||
Description = "Одежда и обувь",
|
||||
Category = ProductCategory.ConsumerGoods,
|
||||
BasePrice = 35m,
|
||||
BaseDemand = 60,
|
||||
DemandElasticity = 0.8f,
|
||||
StackSize = 50,
|
||||
AvailableFromYear = 1900,
|
||||
IconPath = "res://assets/sprites/products/clothing.png"
|
||||
},
|
||||
|
||||
new ProductType
|
||||
{
|
||||
Id = "goods_electronics",
|
||||
Name = "Электроника",
|
||||
Description = "Бытовая техника и гаджеты",
|
||||
Category = ProductCategory.ConsumerGoods,
|
||||
BasePrice = 150m,
|
||||
BaseDemand = 25,
|
||||
DemandElasticity = 0.9f,
|
||||
StackSize = 20,
|
||||
AvailableFromYear = 1960,
|
||||
IconPath = "res://assets/sprites/products/electronics_consumer.png"
|
||||
},
|
||||
|
||||
new ProductType
|
||||
{
|
||||
Id = "goods_automobile",
|
||||
Name = "Автомобили",
|
||||
Description = "Легковые автомобили",
|
||||
Category = ProductCategory.ConsumerGoods,
|
||||
BasePrice = 5000m,
|
||||
BaseDemand = 10,
|
||||
DemandElasticity = 1.0f,
|
||||
StackSize = 5,
|
||||
AvailableFromYear = 1920,
|
||||
IconPath = "res://assets/sprites/products/automobile.png"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Зарегистрировать все дефолтные продукты в реестре
|
||||
/// </summary>
|
||||
public static void RegisterAll(ProductRegistry registry)
|
||||
{
|
||||
foreach (var product in GetAll())
|
||||
{
|
||||
registry.Register(product);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +1,104 @@
|
||||
namespace MyBiz.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Категория продукта
|
||||
/// </summary>
|
||||
public enum ProductCategory
|
||||
{
|
||||
RawMaterial, // Сырьё
|
||||
Component, // Компоненты
|
||||
ConsumerGoods // Товары народного потребления
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тип продукта
|
||||
/// </summary>
|
||||
public enum ProductType
|
||||
{
|
||||
// Сырьё
|
||||
Cotton, // Хлопок
|
||||
Steel, // Сталь
|
||||
Plastic, // Пластик
|
||||
FoodRaw, // Сырьё для еды
|
||||
|
||||
// Компоненты
|
||||
Fabric, // Ткань
|
||||
MetalParts, // Металлические детали
|
||||
PlasticParts, // Пластиковые детали
|
||||
ElectronicsComponents, // Электронные компоненты
|
||||
|
||||
// Товары
|
||||
Food, // Еда
|
||||
Clothing, // Одежда
|
||||
Electronics, // Электроника
|
||||
Automobile // Автомобили
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Продукт
|
||||
/// Продукт - экземпляр товара в игре
|
||||
/// </summary>
|
||||
public class Product
|
||||
{
|
||||
public ProductType Type { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public ProductCategory Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Базовая цена продукта
|
||||
/// Уникальный идентификатор экземпляра продукта
|
||||
/// </summary>
|
||||
public decimal BasePrice { get; set; }
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// Текущая цена (с учётом спроса/предложения)
|
||||
/// Тип продукта (ссылка на конфигурацию)
|
||||
/// </summary>
|
||||
public ProductType Type { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Количество продукта
|
||||
/// </summary>
|
||||
public int Quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Текущая цена за единицу
|
||||
/// </summary>
|
||||
public decimal CurrentPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Доступное количество на рынке
|
||||
/// Дата создания (для отслеживания срока годности)
|
||||
/// </summary>
|
||||
public int AvailableQuantity { get; set; }
|
||||
public int CreatedAtTick { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Спрос на продукт
|
||||
/// Качество продукта (0-100, 100 = идеальное)
|
||||
/// </summary>
|
||||
public int Demand { get; set; }
|
||||
public int Quality { get; set; } = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Проверка: испорчен ли продукт
|
||||
/// </summary>
|
||||
public bool IsSpoiled { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Общая стоимость продукта
|
||||
/// </summary>
|
||||
public decimal TotalValue => Quantity * CurrentPrice;
|
||||
|
||||
/// <summary>
|
||||
/// Проверка: может ли продукт быть использован/продан
|
||||
/// </summary>
|
||||
public bool IsUsable => Quantity > 0 && !IsSpoiled;
|
||||
|
||||
/// <summary>
|
||||
/// Добавить количество
|
||||
/// </summary>
|
||||
public void Add(int amount)
|
||||
{
|
||||
if (amount > 0)
|
||||
{
|
||||
Quantity += amount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удалить количество
|
||||
/// </summary>
|
||||
/// <returns>Фактически удалённое количество</returns>
|
||||
public int Remove(int amount)
|
||||
{
|
||||
if (amount <= 0 || Quantity <= 0)
|
||||
return 0;
|
||||
|
||||
int removed = Math.Min(amount, Quantity);
|
||||
Quantity -= removed;
|
||||
|
||||
if (Quantity <= 0)
|
||||
{
|
||||
IsSpoiled = true;
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновить состояние (проверка срока годности)
|
||||
/// </summary>
|
||||
public void Update(int currentTick)
|
||||
{
|
||||
if (Type.IsPerishable)
|
||||
{
|
||||
int age = currentTick - CreatedAtTick;
|
||||
if (age >= Type.ShelfLife)
|
||||
{
|
||||
IsSpoiled = true;
|
||||
}
|
||||
|
||||
// Ухудшение качества со временем
|
||||
if (age > 0 && Type.ShelfLife > 0)
|
||||
{
|
||||
Quality = Math.Max(0, 100 - (age * 100 / Type.ShelfLife));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
116
backend/src/MyBiz.Core/ProductRegistry.cs
Normal file
116
backend/src/MyBiz.Core/ProductRegistry.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
namespace MyBiz.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Реестр типов продуктов - центрлизованное хранилище конфигураций
|
||||
/// Поддерживает моддинг через загрузку внешних конфигураций
|
||||
/// </summary>
|
||||
public class ProductRegistry
|
||||
{
|
||||
private readonly Dictionary<string, ProductType> _productTypes = new();
|
||||
|
||||
/// <summary>
|
||||
/// Все зарегистрированные типы продуктов
|
||||
/// </summary>
|
||||
public IEnumerable<ProductType> AllProductTypes => _productTypes.Values;
|
||||
|
||||
/// <summary>
|
||||
/// Количество зарегистрированных типов
|
||||
/// </summary>
|
||||
public int Count => _productTypes.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Событие: добавлен новый тип продукта (для моддинга)
|
||||
/// </summary>
|
||||
public event Action<ProductType>? ProductTypeAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Событие: изменён тип продукта
|
||||
/// </summary>
|
||||
public event Action<ProductType>? ProductTypeModified;
|
||||
|
||||
/// <summary>
|
||||
/// Зарегистрировать тип продукта
|
||||
/// </summary>
|
||||
public void Register(ProductType productType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(productType.Id))
|
||||
throw new ArgumentException("Product type must have an Id", nameof(productType));
|
||||
|
||||
if (_productTypes.ContainsKey(productType.Id))
|
||||
throw new InvalidOperationException($"Product type '{productType.Id}' is already registered");
|
||||
|
||||
_productTypes[productType.Id] = productType;
|
||||
ProductTypeAdded?.Invoke(productType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить тип продукта по ID
|
||||
/// </summary>
|
||||
public ProductType? GetById(string id)
|
||||
{
|
||||
return _productTypes.TryGetValue(id, out var type) ? type : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить тип продукта или выбросить исключение
|
||||
/// </summary>
|
||||
public ProductType GetOrThrow(string id)
|
||||
{
|
||||
return GetById(id) ?? throw new KeyNotFoundException($"Product type '{id}' not found");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверка: существует ли тип продукта
|
||||
/// </summary>
|
||||
public bool Exists(string id)
|
||||
{
|
||||
return _productTypes.ContainsKey(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить продукты по категории
|
||||
/// </summary>
|
||||
public IEnumerable<ProductType> GetByCategory(ProductCategory category)
|
||||
{
|
||||
return _productTypes.Values.Where(p => p.Category == category);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить продукты, доступные в указанном году
|
||||
/// </summary>
|
||||
public IEnumerable<ProductType> GetAvailableInYear(int year)
|
||||
{
|
||||
return _productTypes.Values.Where(p => p.AvailableFromYear <= year);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удалить тип продукта (для моддинга)
|
||||
/// </summary>
|
||||
public bool Remove(string id)
|
||||
{
|
||||
if (_productTypes.Remove(id))
|
||||
{
|
||||
ProductTypeModified?.Invoke(new ProductType { Id = id });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Загрузить типы продуктов из конфигурации (JSON и т.п.)
|
||||
/// </summary>
|
||||
public void LoadFromConfig(string jsonConfig)
|
||||
{
|
||||
// TODO: Реализовать загрузку из JSON
|
||||
// Это позволит модам добавлять свои продукты
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Экспортировать все типы в JSON (для моддинга)
|
||||
/// </summary>
|
||||
public string ExportToJson()
|
||||
{
|
||||
// TODO: Реализовать экспорт в JSON
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
105
backend/src/MyBiz.Core/ProductType.cs
Normal file
105
backend/src/MyBiz.Core/ProductType.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
namespace MyBiz.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Категория продукта
|
||||
/// </summary>
|
||||
public enum ProductCategory
|
||||
{
|
||||
RawMaterial, // Сырьё
|
||||
Component, // Компоненты
|
||||
ConsumerGoods, // Товары народного потребления
|
||||
Luxury // Предметы роскоши (для будущего)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тип продукта - конфигурация для моддинга
|
||||
/// </summary>
|
||||
public class ProductType
|
||||
{
|
||||
/// <summary>
|
||||
/// Уникальный идентификатор типа продукта (для моддинга)
|
||||
/// </summary>
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Отображаемое имя
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Описание
|
||||
/// </summary>
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Категория продукта
|
||||
/// </summary>
|
||||
public ProductCategory Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Базовая цена продукта
|
||||
/// </summary>
|
||||
public decimal BasePrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Базовый спрос (единиц в тик)
|
||||
/// </summary>
|
||||
public int BaseDemand { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Эластичность спроса по цене (0-1, где 1 - высокая эластичность)
|
||||
/// </summary>
|
||||
public float DemandElasticity { get; set; } = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Срок хранения (тиков), 0 = бессрочно
|
||||
/// </summary>
|
||||
public int ShelfLife { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Размер стека (для инвентаря)
|
||||
/// </summary>
|
||||
public int StackSize { get; set; } = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Иконка продукта (путь к ресурсу)
|
||||
/// </summary>
|
||||
public string IconPath { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Требуемые технологии для разблокировки (пусто = доступно сразу)
|
||||
/// </summary>
|
||||
public List<string> RequiredTechnologies { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Год, когда продукт становится доступным (для исторического режима)
|
||||
/// </summary>
|
||||
public int AvailableFromYear { get; set; } = 1900;
|
||||
|
||||
/// <summary>
|
||||
/// Может ли продукт быть испорчен
|
||||
/// </summary>
|
||||
public bool IsPerishable => ShelfLife > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Создать копию типа продукта
|
||||
/// </summary>
|
||||
public ProductType Clone()
|
||||
{
|
||||
return new ProductType
|
||||
{
|
||||
Id = this.Id,
|
||||
Name = this.Name,
|
||||
Description = this.Description,
|
||||
Category = this.Category,
|
||||
BasePrice = this.BasePrice,
|
||||
BaseDemand = this.BaseDemand,
|
||||
DemandElasticity = this.DemandElasticity,
|
||||
ShelfLife = this.ShelfLife,
|
||||
StackSize = this.StackSize,
|
||||
IconPath = this.IconPath,
|
||||
RequiredTechnologies = new List<string>(this.RequiredTechnologies),
|
||||
AvailableFromYear = this.AvailableFromYear
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,9 @@ namespace MyBiz.Core;
|
||||
public class ProductionStep
|
||||
{
|
||||
/// <summary>
|
||||
/// Требуемый продукт
|
||||
/// ID требуемого продукта
|
||||
/// </summary>
|
||||
public ProductType InputProduct { get; set; }
|
||||
public string InputProductId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Количество требуемого продукта
|
||||
@@ -22,16 +22,104 @@ public class ProductionStep
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Производственная цепочка
|
||||
/// Конфигурация производственной цепочки - для моддинга
|
||||
/// </summary>
|
||||
public class ProductionChain
|
||||
public class ProductionChainConfig
|
||||
{
|
||||
public ProductType OutputProduct { get; set; }
|
||||
public BuildingType RequiredBuilding { get; set; }
|
||||
public List<ProductionStep> Steps { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Уникальный идентификатор цепочки
|
||||
/// </summary>
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Название цепочки
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// ID выходного продукта
|
||||
/// </summary>
|
||||
public string OutputProductId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Количество выходного продукта за цикл
|
||||
/// </summary>
|
||||
public int OutputQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ID требуемого здания
|
||||
/// </summary>
|
||||
public string RequiredBuildingId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Шаги производства
|
||||
/// </summary>
|
||||
public List<ProductionStep> Steps { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Требуемые технологии
|
||||
/// </summary>
|
||||
public List<string> RequiredTechnologies { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Год, когда цепочка становится доступной
|
||||
/// </summary>
|
||||
public int AvailableFromYear { get; set; } = 1900;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Активная производственная цепочка на здании
|
||||
/// </summary>
|
||||
public class ActiveProductionChain
|
||||
{
|
||||
public ProductionChainConfig Config { get; set; } = null!;
|
||||
public Building Building { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Текущий шаг производства
|
||||
/// </summary>
|
||||
public int CurrentStep { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Прогресс текущего шага (в тиках)
|
||||
/// </summary>
|
||||
public int Progress { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Цепочка активна
|
||||
/// </summary>
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Запущена в тик
|
||||
/// </summary>
|
||||
public int StartedAtTick { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Проверка: завершён ли текущий шаг
|
||||
/// </summary>
|
||||
public bool IsStepComplete => CurrentStep < Config.Steps.Count &&
|
||||
Progress >= Config.Steps[CurrentStep].ProductionTime;
|
||||
|
||||
/// <summary>
|
||||
/// Проверка: завершено ли всё производство
|
||||
/// </summary>
|
||||
public bool IsComplete => CurrentStep >= Config.Steps.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Продвинуть производство на 1 тик
|
||||
/// </summary>
|
||||
public void Tick()
|
||||
{
|
||||
if (!IsActive || IsComplete)
|
||||
return;
|
||||
|
||||
Progress++;
|
||||
|
||||
if (IsStepComplete)
|
||||
{
|
||||
CurrentStep++;
|
||||
Progress = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
81
backend/tests/MyBiz.Tests/BuildingTests.cs
Normal file
81
backend/tests/MyBiz.Tests/BuildingTests.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using MyBiz.Core;
|
||||
|
||||
namespace MyBiz.Tests;
|
||||
|
||||
public class BuildingTests
|
||||
{
|
||||
[Fact]
|
||||
public void Building_CalculateEfficiency_FullWorkers_ShouldBeMax()
|
||||
{
|
||||
// Arrange
|
||||
var config = new BuildingTypeConfig
|
||||
{
|
||||
Id = "factory",
|
||||
BaseEfficiency = 100,
|
||||
WorkerSlots = 10
|
||||
};
|
||||
|
||||
var building = new Building
|
||||
{
|
||||
TypeConfig = config,
|
||||
Workers = 10,
|
||||
Level = 1
|
||||
};
|
||||
|
||||
// Act
|
||||
building.CalculateEfficiency();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(100, building.CurrentEfficiency);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Building_CalculateEfficiency_NoWorkers_ShouldBeZero()
|
||||
{
|
||||
// Arrange
|
||||
var config = new BuildingTypeConfig
|
||||
{
|
||||
Id = "factory",
|
||||
BaseEfficiency = 100,
|
||||
WorkerSlots = 10
|
||||
};
|
||||
|
||||
var building = new Building
|
||||
{
|
||||
TypeConfig = config,
|
||||
Workers = 0,
|
||||
Level = 1
|
||||
};
|
||||
|
||||
// Act
|
||||
building.CalculateEfficiency();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0, building.CurrentEfficiency);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Building_LevelBonus_ShouldIncreaseEfficiency()
|
||||
{
|
||||
// Arrange
|
||||
var config = new BuildingTypeConfig
|
||||
{
|
||||
Id = "factory",
|
||||
BaseEfficiency = 100,
|
||||
WorkerSlots = 10
|
||||
};
|
||||
|
||||
var building = new Building
|
||||
{
|
||||
TypeConfig = config,
|
||||
Workers = 10,
|
||||
Level = 5
|
||||
};
|
||||
|
||||
// Act
|
||||
building.CalculateEfficiency();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(120, building.CurrentEfficiency); // 100 + (4 * 5)
|
||||
}
|
||||
}
|
||||
67
backend/tests/MyBiz.Tests/DefaultProductsTests.cs
Normal file
67
backend/tests/MyBiz.Tests/DefaultProductsTests.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using MyBiz.Core;
|
||||
|
||||
namespace MyBiz.Tests;
|
||||
|
||||
public class DefaultProductsTests
|
||||
{
|
||||
[Fact]
|
||||
public void DefaultProducts_GetAll_ShouldReturn12Products()
|
||||
{
|
||||
// Act
|
||||
var products = DefaultProducts.GetAll().ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(12, products.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultProducts_ShouldHaveAllCategories()
|
||||
{
|
||||
// Act
|
||||
var products = DefaultProducts.GetAll().ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Contains(products, p => p.Category == ProductCategory.RawMaterial);
|
||||
Assert.Contains(products, p => p.Category == ProductCategory.Component);
|
||||
Assert.Contains(products, p => p.Category == ProductCategory.ConsumerGoods);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultProducts_RegisterAll_ShouldAddToRegistry()
|
||||
{
|
||||
// Arrange
|
||||
var registry = new ProductRegistry();
|
||||
|
||||
// Act
|
||||
DefaultProducts.RegisterAll(registry);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(12, registry.Count);
|
||||
Assert.NotNull(registry.GetById("goods_food"));
|
||||
Assert.NotNull(registry.GetById("goods_automobile"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultProducts_Automobile_ShouldHaveCorrectProperties()
|
||||
{
|
||||
// Act
|
||||
var automobile = DefaultProducts.GetAll().First(p => p.Id == "goods_automobile");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Автомобили", automobile.Name);
|
||||
Assert.Equal(5000m, automobile.BasePrice);
|
||||
Assert.Equal(1920, automobile.AvailableFromYear);
|
||||
Assert.False(automobile.IsPerishable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultProducts_Food_ShouldBePerishable()
|
||||
{
|
||||
// Act
|
||||
var food = DefaultProducts.GetAll().First(p => p.Id == "goods_food");
|
||||
|
||||
// Assert
|
||||
Assert.True(food.IsPerishable);
|
||||
Assert.Equal(5, food.ShelfLife);
|
||||
}
|
||||
}
|
||||
@@ -2,40 +2,192 @@ using MyBiz.Core;
|
||||
|
||||
namespace MyBiz.Tests;
|
||||
|
||||
public class ProductTests
|
||||
public class ProductTypeTests
|
||||
{
|
||||
[Fact]
|
||||
public void Product_Creation_ShouldInitializeProperties()
|
||||
public void ProductType_Creation_ShouldInitializeProperties()
|
||||
{
|
||||
// Arrange & Act
|
||||
var product = new Product
|
||||
var productType = new ProductType
|
||||
{
|
||||
Type = ProductType.Food,
|
||||
Id = "food_bread",
|
||||
Name = "Bread",
|
||||
Description = "Fresh baked bread",
|
||||
Category = ProductCategory.ConsumerGoods,
|
||||
BasePrice = 10m
|
||||
BasePrice = 10m,
|
||||
BaseDemand = 100,
|
||||
AvailableFromYear = 1950
|
||||
};
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ProductType.Food, product.Type);
|
||||
Assert.Equal("Bread", product.Name);
|
||||
Assert.Equal(ProductCategory.ConsumerGoods, product.Category);
|
||||
Assert.Equal(10m, product.BasePrice);
|
||||
Assert.Equal("food_bread", productType.Id);
|
||||
Assert.Equal("Bread", productType.Name);
|
||||
Assert.Equal(ProductCategory.ConsumerGoods, productType.Category);
|
||||
Assert.Equal(10m, productType.BasePrice);
|
||||
Assert.Equal(1950, productType.AvailableFromYear);
|
||||
Assert.False(productType.IsPerishable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Product_CurrentPrice_ShouldDefaultToBasePrice()
|
||||
public void ProductType_Clone_ShouldCreateIndependentCopy()
|
||||
{
|
||||
// Arrange
|
||||
var original = new ProductType
|
||||
{
|
||||
Id = "test",
|
||||
Name = "Test Product",
|
||||
BasePrice = 50m
|
||||
};
|
||||
original.RequiredTechnologies.Add("tech1");
|
||||
|
||||
// Act
|
||||
var clone = original.Clone();
|
||||
clone.Name = "Modified";
|
||||
clone.RequiredTechnologies.Add("tech2");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Test Product", original.Name);
|
||||
Assert.Equal("Modified", clone.Name);
|
||||
Assert.Single(original.RequiredTechnologies);
|
||||
Assert.Equal(2, clone.RequiredTechnologies.Count);
|
||||
}
|
||||
}
|
||||
|
||||
public class ProductTests
|
||||
{
|
||||
[Fact]
|
||||
public void Product_Add_ShouldIncreaseQuantity()
|
||||
{
|
||||
// Arrange
|
||||
var product = new Product
|
||||
{
|
||||
BasePrice = 25m
|
||||
Type = new ProductType { Id = "test", BasePrice = 10m },
|
||||
Quantity = 5
|
||||
};
|
||||
|
||||
// Act
|
||||
product.CurrentPrice = product.BasePrice;
|
||||
product.Add(10);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(25m, product.CurrentPrice);
|
||||
Assert.Equal(15, product.Quantity);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Product_Remove_ShouldDecreaseQuantity()
|
||||
{
|
||||
// Arrange
|
||||
var product = new Product
|
||||
{
|
||||
Type = new ProductType { Id = "test", BasePrice = 10m },
|
||||
Quantity = 20
|
||||
};
|
||||
|
||||
// Act
|
||||
var removed = product.Remove(15);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(15, removed);
|
||||
Assert.Equal(5, product.Quantity);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Product_Remove_MoreThanAvailable_ShouldRemoveAll()
|
||||
{
|
||||
// Arrange
|
||||
var product = new Product
|
||||
{
|
||||
Type = new ProductType { Id = "test", BasePrice = 10m },
|
||||
Quantity = 10
|
||||
};
|
||||
|
||||
// Act
|
||||
var removed = product.Remove(50);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(10, removed);
|
||||
Assert.Equal(0, product.Quantity);
|
||||
Assert.True(product.IsSpoiled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Product_Perishable_ShouldSpoilAfterShelfLife()
|
||||
{
|
||||
// Arrange
|
||||
var product = new Product
|
||||
{
|
||||
Type = new ProductType { Id = "food", BasePrice = 10m, ShelfLife = 5 },
|
||||
Quantity = 10,
|
||||
CreatedAtTick = 0
|
||||
};
|
||||
|
||||
// Act
|
||||
product.Update(6); // 6 ticks later
|
||||
|
||||
// Assert
|
||||
Assert.True(product.IsSpoiled);
|
||||
Assert.False(product.IsUsable);
|
||||
}
|
||||
}
|
||||
|
||||
public class ProductRegistryTests
|
||||
{
|
||||
[Fact]
|
||||
public void Registry_Register_ShouldAddProductType()
|
||||
{
|
||||
// Arrange
|
||||
var registry = new ProductRegistry();
|
||||
var productType = new ProductType { Id = "test", Name = "Test" };
|
||||
|
||||
// Act
|
||||
registry.Register(productType);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, registry.Count);
|
||||
Assert.Same(productType, registry.GetById("test"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Registry_GetById_UnknownId_ShouldReturnNull()
|
||||
{
|
||||
// Arrange
|
||||
var registry = new ProductRegistry();
|
||||
|
||||
// Act
|
||||
var result = registry.GetById("unknown");
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Registry_GetByCategory_ShouldFilterProducts()
|
||||
{
|
||||
// Arrange
|
||||
var registry = new ProductRegistry();
|
||||
registry.Register(new ProductType { Id = "raw1", Category = ProductCategory.RawMaterial });
|
||||
registry.Register(new ProductType { Id = "raw2", Category = ProductCategory.RawMaterial });
|
||||
registry.Register(new ProductType { Id = "consumer1", Category = ProductCategory.ConsumerGoods });
|
||||
|
||||
// Act
|
||||
var rawMaterials = registry.GetByCategory(ProductCategory.RawMaterial);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, rawMaterials.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Registry_GetAvailableInYear_ShouldFilterByYear()
|
||||
{
|
||||
// Arrange
|
||||
var registry = new ProductRegistry();
|
||||
registry.Register(new ProductType { Id = "old", AvailableFromYear = 1950 });
|
||||
registry.Register(new ProductType { Id = "new", AvailableFromYear = 2000 });
|
||||
|
||||
// Act
|
||||
var availableIn1970 = registry.GetAvailableInYear(1970);
|
||||
|
||||
// Assert
|
||||
Assert.Single(availableIn1970);
|
||||
Assert.Equal("old", availableIn1970.First().Id);
|
||||
}
|
||||
}
|
||||
|
||||
37
frontend/icon.svg.import
Normal file
37
frontend/icon.svg.import
Normal file
@@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://csoebibtcgpn2"
|
||||
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.svg"
|
||||
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -12,7 +12,7 @@ config_version=5
|
||||
|
||||
config/name="MyBiz - Economic Simulator"
|
||||
run/main_scene="res://scenes/main.tscn"
|
||||
config/features=PackedStringArray("4.0", "Forward Plus")
|
||||
config/features=PackedStringArray("4.3", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[display]
|
||||
@@ -25,22 +25,22 @@ window/stretch/mode="canvas_items"
|
||||
|
||||
move_up={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"echo":false,"script":null)
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_down={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"echo":false,"script":null)
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_left={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"echo":false,"script":null)
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_right={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"echo":false,"script":null)
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -49,7 +49,3 @@ move_right={
|
||||
2d_physics/layer_1="Default"
|
||||
2d_physics/layer_2="Buildings"
|
||||
2d_physics/layer_3="UI"
|
||||
|
||||
[rendering]
|
||||
|
||||
renderer/rendering_method="forward_plus"
|
||||
|
||||
Reference in New Issue
Block a user