272 lines
12 KiB
JSON
272 lines
12 KiB
JSON
{
|
||
"_meta": {
|
||
"file": "balance.json",
|
||
"version": "1.1",
|
||
"rules": "docs/GDD.md (компактная сводка) + docs/DECISIONS.md (D01–D17)",
|
||
"note": "null = параметр не задан в постановке, уточнить до кода (см. _todo). Никаких выдуманных чисел.",
|
||
"_todo": [
|
||
"attacks.junk.spawn_probability — не задан в спецификации (явно null)",
|
||
"income_target_by_hour: часы 4,6,7,8 — интерполяция, уточнить",
|
||
"difficulty.h2 packet_timer/request_timer: формула даёт 26.325/50.15 (19.5×1.35 и 29.5×1.7 — у запросов отдельный grace)"
|
||
]
|
||
},
|
||
"constants": {
|
||
"hour_seconds": 300,
|
||
"run_hours": 9,
|
||
"sla_start": 128,
|
||
"sla_max": 200,
|
||
"money_start": 150,
|
||
"buffer_base": 4,
|
||
"buffer_per_upgrade": 2,
|
||
"buffer_max": 8,
|
||
"slots_start": 2,
|
||
"slots_max": 4,
|
||
"max_sections": 6,
|
||
"files_per_folder": 16,
|
||
"folders_per_section": 12,
|
||
"send_window_visible_cards": 4,
|
||
"send_scroll_card_height_px": 56,
|
||
"required_name_char_limit": 12,
|
||
"file_cooldown_after_send_sec": 20,
|
||
"required_per_request": { "min": 1, "max": 3 }
|
||
},
|
||
"types": {
|
||
"text": {
|
||
"name_ru": "Текст",
|
||
"ext": ".txt",
|
||
"icon": "▤",
|
||
"recv_time": 1.0,
|
||
"send_time": 1.0,
|
||
"recv_fee": 2,
|
||
"base_pay": 20,
|
||
"weight": 6,
|
||
"minigame_coeff": 1.0,
|
||
"sla_loss": 2
|
||
},
|
||
"sound": {
|
||
"name_ru": "Звук",
|
||
"ext": ".wav",
|
||
"icon": "♪",
|
||
"recv_time": 1.5,
|
||
"send_time": 2.0,
|
||
"recv_fee": 3,
|
||
"base_pay": 35,
|
||
"weight": 10,
|
||
"minigame_coeff": 2.0,
|
||
"sla_loss": 4
|
||
},
|
||
"photo": {
|
||
"name_ru": "Фото",
|
||
"ext": ".jpg",
|
||
"icon": "▣",
|
||
"recv_time": 2.0,
|
||
"send_time": 3.0,
|
||
"recv_fee": 5,
|
||
"base_pay": 55,
|
||
"weight": 14,
|
||
"minigame_coeff": 3.0,
|
||
"sla_loss": 8
|
||
},
|
||
"video": {
|
||
"name_ru": "Видео",
|
||
"ext": ".mp4",
|
||
"icon": "▶",
|
||
"recv_time": 3.0,
|
||
"send_time": 5.0,
|
||
"recv_fee": 8,
|
||
"base_pay": 90,
|
||
"weight": 22,
|
||
"minigame_coeff": 4.0,
|
||
"sla_loss": 16
|
||
}
|
||
},
|
||
"difficulty": [
|
||
{ "hour": 1, "recv_interval": 12, "send_interval": 22, "max_requests": 2, "buffer": 4, "packet_timer": 30, "request_timer": 60, "attacks": 0 },
|
||
{ "hour": 2, "recv_interval": 11, "send_interval": 20, "max_requests": 2, "buffer": 4, "packet_timer": 26.325, "request_timer": 50.15, "attacks": 1 },
|
||
{ "hour": 3, "recv_interval": 10, "send_interval": 18, "max_requests": 3, "buffer": 5, "packet_timer": 22.8, "request_timer": 40.6, "attacks": 1 },
|
||
{ "hour": 4, "recv_interval": 9, "send_interval": 17, "max_requests": 3, "buffer": 5, "packet_timer": 18.5, "request_timer": 28.5, "attacks": 2 },
|
||
{ "hour": 5, "recv_interval": 8, "send_interval": 15, "max_requests": 4, "buffer": 6, "packet_timer": 18, "request_timer": 28, "attacks": 2 },
|
||
{ "hour": 6, "recv_interval": 7.5, "send_interval": 14, "max_requests": 4, "buffer": 6, "packet_timer": 17.5, "request_timer": 27.5, "attacks": 3 },
|
||
{ "hour": 7, "recv_interval": 7, "send_interval": 13, "max_requests": 4, "buffer": 6, "packet_timer": 17, "request_timer": 27, "attacks": 3 },
|
||
{ "hour": 8, "recv_interval": 6.5, "send_interval": 12, "max_requests": 4, "buffer": 7, "packet_timer": 16.5, "request_timer": 26.5, "attacks": 4 },
|
||
{ "hour": 9, "recv_interval": 6, "send_interval": 12, "max_requests": 4, "buffer": 7, "packet_timer": 16, "request_timer": 26, "attacks": 4 }
|
||
],
|
||
"difficulty_rules": {
|
||
"table_end_hour": 8,
|
||
"after_end_hour": "значения часа 8; +5% к скорости спавна каждые 2 часа",
|
||
"spawn_speedup_after_end": 0.05,
|
||
"spawn_speedup_period_hours": 2,
|
||
"type_availability": "час 1 — text; час 2 — text и sound; час 3 — +photo; час 4+ — все 4 (D23); атаки — с часа 2 (attack_junk)",
|
||
"timer_grace": "packet_timer: grace h1 1.5, h2 1.35, h3 1.2, h4+ 1.0 (Economy.TIMER_GRACE). request_timer: отдельный grace h1 2.0, h2 1.7, h3 1.4, h4+ 1.0 (Economy.REQUEST_TIMER_GRACE). Формула: (base − 0.5*(h−1))*grace + 4*level. h2: packet 19.5*1.35=26.325, request 29.5*1.7=50.15."
|
||
},
|
||
"progression": {
|
||
"unlocks": {
|
||
"shop": {"run_hour": 2, "label": "Магазин"},
|
||
"search": {"meta": "survive_hour_3", "label": "Поиск"},
|
||
"shield": {"meta": "survive_hour_5", "label": "Щит"},
|
||
"minigame_text": {"run_hour": 1},
|
||
"minigame_sound": {"run_hour": 2},
|
||
"minigame_photo": {"run_hour": 3},
|
||
"minigame_video": {"run_hour": 4},
|
||
"minigame_advanced": {"meta": "complete_run", "label": "Продвинутые мини-игры"},
|
||
"attack_junk": {"run_hour": 2},
|
||
"attack_ddos": {"run_hour": 2},
|
||
"attack_phishing": {"run_hour": 3},
|
||
"attack_ransomware": {"run_hour": 4}
|
||
},
|
||
"meta_rewards": {
|
||
"survive_hour_3": {"min_hours": 3, "unlocks": ["search"]},
|
||
"survive_hour_5": {"min_hours": 5, "unlocks": ["shield"]},
|
||
"complete_run": {"min_hours": 9, "unlocks": ["minigame_advanced"]}
|
||
}
|
||
},
|
||
"timers": {
|
||
"packet_timer": 20,
|
||
"request_timer": 30,
|
||
"attack_alert": 3.0,
|
||
"minigame_timeout": null,
|
||
"delete_long_press_sec": 0.5
|
||
},
|
||
"sla": {
|
||
"recv_bonus": 0.5,
|
||
"send_bonus": 1.0,
|
||
"reflect_bonus": 1.0,
|
||
"timeout_fine_base": 10,
|
||
"timeout_fine_per_hour": 2,
|
||
"timeout_fine_formula": "-(10 + 2*hour) — таймаут и подобные штрафы (D12)",
|
||
"wrong_attach_fine": -2,
|
||
"lost_file_fine": -5,
|
||
"buffer_overflow_fine": -5,
|
||
"sla_lose": "SLA <= 0"
|
||
},
|
||
"payout": {
|
||
"base_pay": "см. types.*.base_pay (значения в types)",
|
||
"recv_fee": "см. types.*.recv_fee (значения в types)",
|
||
"speed_formula": "speed = 0.6 + 0.4*(time_left / timer_total)",
|
||
"quality_formula": "quality = 0.5 + 0.5*mini_quality",
|
||
"multi_formula": "multi = 1 + 0.1*(n - 1)",
|
||
"protocol_mult": { "TCP": 1.15, "UDP": 1.0 },
|
||
"streak_formula": "streak = 1 + 0.02*min(10, streak)",
|
||
"income_target_by_hour": {
|
||
"hours": [1, 2, 3, 5, 9],
|
||
"approx": [500, 800, 1200, 1800, 2200],
|
||
"note": "ожидаемо ±10%; часы 4,6,7,8 — интерполяция, уточнить"
|
||
}
|
||
},
|
||
"connections": {
|
||
"W_formula": "W = clamp(0.30 - 0.005*(hour-1) - 0.005*successes + 0.05*zone_upgrade_level, 0.18, 0.36)",
|
||
"W_base": 0.3,
|
||
"W_hour_coef": -0.005,
|
||
"W_success_coef": -0.005,
|
||
"W_zone_bonus_per_level": 0.05,
|
||
"W_min": 0.18,
|
||
"W_max": 0.36,
|
||
"bar_speeds_px_per_sec": [440, 340, 280],
|
||
"hit_window_ms": [130, 200]
|
||
},
|
||
"channel": {
|
||
"CHANNEL_BG_BASE": 0,
|
||
"CHANNEL_BG_PER_HOUR": 0,
|
||
"SLOT_LOAD": 0,
|
||
"BG_note": "фоновый шум и занятые слоты НЕ грузят канал; load = только активные передачи (loading/sending)",
|
||
"CHANNEL_CAPACITY": 80,
|
||
"capacity_per_upgrade": 25,
|
||
"capacity_upgrade_max_level": 4,
|
||
"formula": "load = sum(weight(active_transfers)); factor = max(1.0, load / CHANNEL_CAPACITY); actual_time = base_time * factor. idle load = 0"
|
||
},
|
||
"attacks": {
|
||
"junk": {
|
||
"name_ru": "Мусорный пакет",
|
||
"spawn_probability": null,
|
||
"duration_sec": 15,
|
||
"tell": "серая иконка ?, размер 0 Б, нет таймера",
|
||
"effect": "авто-занос через duration_sec, занимает место в приёме; уничтожить можно как malicious в любой фазе (D02)",
|
||
"reward_money": 5,
|
||
"reward_sla": 1,
|
||
"fail_money": -10,
|
||
"fail_sla": -5,
|
||
"filter_disabled_sec": 10
|
||
},
|
||
"ddos": {
|
||
"name_ru": "DDoS",
|
||
"cards_min": 8,
|
||
"cards_max": 12,
|
||
"spawn_window_sec": 2.0,
|
||
"shield_hold_sec": 1.5,
|
||
"tell": "8–12 идентичных карточек без типа и таймера",
|
||
"effect": "волна карточек; отбивается удержанием щита",
|
||
"reward_money": 30,
|
||
"reward_sla": 2,
|
||
"fail_sla": -15,
|
||
"channel_debuff": 0.4,
|
||
"channel_debuff_sec": 15,
|
||
"packets_lost": 2,
|
||
"recv_disabled_sec": 5
|
||
},
|
||
"phishing": {
|
||
"name_ru": "Фишинг",
|
||
"reward_money": 20,
|
||
"reward_sla": 2,
|
||
"fail_money": -40,
|
||
"fail_sla": -10,
|
||
"file_lock_sec": 60,
|
||
"send_disabled_sec": 5,
|
||
"timeout_money": -20,
|
||
"timeout_sla": -5,
|
||
"timeout_lock_sec": 30,
|
||
"tell": "client_id формата id_XXXX с неверным символом (например id_7F3К — кириллическая К), серый аватар",
|
||
"effect": "фальшивый запрос/пакет"
|
||
},
|
||
"ransomware": {
|
||
"name_ru": "Шифровальщик",
|
||
"reward_money": 25,
|
||
"reward_sla": 2,
|
||
"folder_block_delay_sec": 3,
|
||
"auto_unlock_sec": 30,
|
||
"auto_unlock_money": -30,
|
||
"auto_unlock_sla": -10,
|
||
"tell": "расширение .exe, красный квадрат 4×4",
|
||
"effect": "файл становится locked (lock_until), кликабелен для уничтожения (D08)"
|
||
}
|
||
},
|
||
"shop": [
|
||
{ "id": "shop_slot3", "name_ru": "Третий слот", "effect": "connection_slots", "value": 1, "price": 250, "max_level": 1 },
|
||
{ "id": "shop_slot4", "name_ru": "Четвёртый слот", "effect": "connection_slots", "value": 1, "price": 450, "max_level": 1 },
|
||
{ "id": "shop_buffer", "name_ru": "Буфер +2", "effect": "buffer_slots", "value": 2, "price": 200, "max_level": 2 },
|
||
{ "id": "shop_timer", "name_ru": "Таймер пакетов +4 с", "effect": "packet_timer", "value": 4, "unit": "сек (аддитивно)", "price": 350, "max_level": 3 },
|
||
{ "id": "shop_zone", "name_ru": "Зона +5%", "effect": "zone_width", "value": 0.05, "unit": "доля W (аддитивно в формуле D04)", "price": 300, "max_level": 3 },
|
||
{ "id": "shop_channel", "name_ru": "Канал +25", "effect": "channel_capacity", "value": 25, "price": 250, "max_level": 4 },
|
||
{ "id": "shop_section", "name_ru": "Новая секция", "effect": "sections", "value": 1, "price": 300, "max_level": 5 },
|
||
{ "id": "shop_search", "name_ru": "Поиск", "effect": "search_highlight", "value": 1, "price": 250, "max_level": 1 },
|
||
{ "id": "shop_shield", "name_ru": "Щит", "effect": "shield", "value": 1, "price": 400, "max_level": 1 },
|
||
{ "id": "shop_sla", "name_ru": "SLA +24", "effect": "sla_add", "value": 24, "price": 300, "max_level": 3 }
|
||
],
|
||
"shop_reserve": {
|
||
"items": ["scanner", "autosort"],
|
||
"note": "резерв — в прототип НЕ входят (D13); позиций в shop НЕТ"
|
||
},
|
||
"spawn": {
|
||
"type_pool_by_hour": {
|
||
"1": ["text"],
|
||
"2": ["text", "sound"],
|
||
"3+": ["text", "sound", "photo", "video"]
|
||
},
|
||
"malicious_ratio": 0.0,
|
||
"required_count_by_hour": {
|
||
"1": { "1": 1.0 },
|
||
"2": { "1": 0.75, "2": 0.25 },
|
||
"3+": { "1": 0.6, "2": 0.3, "3": 0.1 }
|
||
},
|
||
"required_size_distribution": { "1": 0.6, "2": 0.3, "3": 0.1 },
|
||
"required_count_note": "required_count_by_hour — распределение по часу; required_size_distribution — fallback/час 3+",
|
||
"attack_schedule": "с часа 2, далее по difficulty.attacks, окно 60–240 с часа, не во время мини-игры"
|
||
},
|
||
"minigame_selection": {
|
||
"coeff": { "text": 1.0, "sound": 2.0, "photo": 3.0, "video": 4.0 },
|
||
"difficulty_formula": "sum(coeff(required)) / n",
|
||
"thresholds": { "text": "< 1.5", "sound": "< 2.5", "photo": "< 3.5", "else": "video" },
|
||
"call": "MiniGame.start(type, difficulty)",
|
||
"quality_stub": 1.0
|
||
}
|
||
}
|