Pārlūkot izejas kodu

SVI Борьба с косяками

SVI 1 gadu atpakaļ
vecāks
revīzija
5aba0f75ae
96 mainītis faili ar 1866 papildinājumiem un 1470 dzēšanām
  1. 4 0
      app/lev0/cons/cons.go
  2. 1 1
      app/lev0/types/_inetmarket.go
  3. 0 6
      app/lev0/types/iarena.go
  4. 1 2
      app/lev0/types/iarena_arsenal.go
  5. 1 1
      app/lev0/types/iarena_bank.go
  6. 2 0
      app/lev0/types/iarena_base.go
  7. 22 0
      app/lev0/types/iarena_build.go
  8. 1 3
      app/lev0/types/iarena_convoy.go
  9. 2 2
      app/lev0/types/iarena_ctx.go
  10. 0 9
      app/lev0/types/iarena_fuel.go
  11. 8 0
      app/lev0/types/iarena_fuel_storage.go
  12. 1 2
      app/lev0/types/iarena_mine.go
  13. 1 2
      app/lev0/types/iarena_polygon.go
  14. 0 11
      app/lev0/types/ibase_build.go
  15. 2 0
      app/lev0/types/icounttime.go
  16. 0 10
      app/lev0/types/imarket.go
  17. 0 10
      app/lev0/types/inetangar.go
  18. 0 10
      app/lev0/types/inetbase.go
  19. 0 4
      app/lev0/types/iparser_simple.go
  20. 0 8
      app/lev0/types/iproc_fuel_find.go
  21. 0 13
      app/lev0/types/isectionnet.go
  22. 97 0
      app/lev1/down_time/down_time.go
  23. 1 1
      app/lev1/product/parser_time/parse_sec/parse_sec_test.go
  24. 0 14
      app/lev1/product/parser_time/parser_time.go
  25. 0 1
      app/lev1/product/parser_time/parser_time_test.go
  26. 11 30
      app/lev2/arena/arena.go
  27. 11 8
      app/lev2/arena/arena_angar/arena_angar.go
  28. 2 2
      app/lev2/arena/arena_angar/bf_angar_fuel/bf_angar_fuel.go
  29. 36 34
      app/lev2/arena/arena_arsenal/arena_arsenal.go
  30. 1 1
      app/lev2/arena/arena_arsenal/bf_ammo_make/bf_ammo_make.go
  31. 1 1
      app/lev2/arena/arena_arsenal/bf_ammo_stat/bf_ammo_stat.go
  32. 5 5
      app/lev2/arena/arena_arsenal/bf_arsenal_build/bf_bank_build.go
  33. 89 0
      app/lev2/arena/arena_arsenal/bf_arsenal_upgrade/bf_arsenal_upgrade.go
  34. 25 31
      app/lev2/arena/arena_bank/arena_bank.go
  35. 7 7
      app/lev2/arena/arena_bank/bf_bank_build/bf_bank_build.go
  36. 9 14
      app/lev2/arena/arena_bank/bf_bank_prod/bf_bank_prod.go
  37. 89 0
      app/lev2/arena/arena_bank/bf_bank_upgrade/bf_bank_upgrade.go
  38. 49 0
      app/lev2/arena/arena_bank/bf_bank_upgrade_fast/bf_bank_upgrade_fast.go
  39. 39 8
      app/lev2/arena/arena_base/arena_base.go
  40. 9 8
      app/lev2/arena/arena_battle/arena_battle.go
  41. 4 4
      app/lev2/arena/arena_battle/battle_register/battle_register.go
  42. 7 6
      app/lev2/arena/arena_battle/battle_wait/battle_wait.go
  43. 3 5
      app/lev2/arena/arena_battle/battle_worker/battle_worker.go
  44. 6 6
      app/lev2/arena/arena_battle/battle_worker/battle_worker/battle_worker.go
  45. 5 6
      app/lev2/arena/arena_battle/battle_worker/battleon/battleon.go
  46. 54 0
      app/lev2/arena/arena_build/arena_build.go
  47. 1 1
      app/lev2/arena/arena_context/arena_context.go
  48. 18 13
      app/lev2/arena/arena_context/arena_state/arena_state.go
  49. 16 171
      app/lev2/arena/arena_convoy/arena_convoy.go
  50. 44 0
      app/lev2/arena/arena_convoy/bf_glory_find/bf_glory_find.go
  51. 1 1
      app/lev2/arena/arena_convoy/bf_glory_make/bf_glory_make.go
  52. 112 0
      app/lev2/arena/arena_convoy/bf_glory_take/bf_glory_take.go
  53. 9 8
      app/lev2/arena/arena_death/arena_death.go
  54. 4 4
      app/lev2/arena/arena_death/death_register/death_register.go
  55. 8 8
      app/lev2/arena/arena_death/death_wait/death_wait.go
  56. 3 5
      app/lev2/arena/arena_death/death_worker/death_worker.go
  57. 4 6
      app/lev2/arena/arena_death/death_worker/process_death/process_death.go
  58. 16 12
      app/lev2/arena/arena_division/div_war/div_war.go
  59. 6 6
      app/lev2/arena/arena_division/div_war/process_divwar/process_divwar.go
  60. 9 10
      app/lev2/arena/arena_division/divwar/div_war_on/div_war_on.go
  61. 9 12
      app/lev2/arena/arena_division/divwar/divwar.go
  62. 10 11
      app/lev2/arena/arena_division/divwar/divwaron/divwaron.go
  63. 0 53
      app/lev2/arena/arena_duel/arena_duel.go
  64. 0 39
      app/lev2/arena/arena_fuel/arena_fuel.go
  65. 45 0
      app/lev2/arena/arena_fuel_duel/arena_fuel_duel.go
  66. 11 62
      app/lev2/arena/arena_fuel_duel/bf_fuel_duel/bf_fuel_duel.go
  67. 38 183
      app/lev2/arena/arena_fuel_storage/arena_fuel_storage.go
  68. 87 0
      app/lev2/arena/arena_fuel_storage/bf_fuel_build/bf_fuel_build.go
  69. 89 0
      app/lev2/arena/arena_fuel_storage/bf_fuel_upgrade/bf_fuel_upgrade.go
  70. 18 103
      app/lev2/arena/arena_market/arena_market.go
  71. 86 0
      app/lev2/arena/arena_market/bf_market_build/bf_market_build.go
  72. 8 10
      app/lev2/arena/arena_masters/arena_masters.go
  73. 42 0
      app/lev2/arena/arena_medal/arena_medal.go
  74. 45 0
      app/lev2/arena/arena_medal/bf_medal_find/bf_medal_find.go
  75. 94 98
      app/lev2/arena/arena_mine/arena_mine.go
  76. 5 5
      app/lev2/arena/arena_mine/bf_mine_accelerate/bf_mine_accelerate.go
  77. 4 4
      app/lev2/arena/arena_mine/bf_mine_build/bf_mine_build.go
  78. 52 0
      app/lev2/arena/arena_mine/bf_mine_time_work/bf_mine_time_work.go
  79. 1 1
      app/lev2/arena/arena_missions/arena_missions.go
  80. 38 151
      app/lev2/arena/arena_polygon/arena_polygon.go
  81. 1 1
      app/lev2/arena/arena_polygon/bf_polygon_activate/bf_polygon_activate.go
  82. 5 5
      app/lev2/arena/arena_polygon/bf_polygon_build/bf_polygon_build.go
  83. 45 0
      app/lev2/arena/arena_polygon/bf_polygon_level/bf_polygon_level.go
  84. 89 0
      app/lev2/arena/arena_polygon/bf_polygon_upgrade/bf_polygon_upgrade.go
  85. 51 0
      app/lev2/arena/arena_polygon/bf_polygon_upgrade_fast/bf_polygon_upgrade_fast.go
  86. 0 142
      app/lev2/arena/down_time/down_time.go
  87. 27 5
      app/lev2/lev2.go
  88. 35 33
      app/lev3/bot/bot.go
  89. 3 3
      app/lev3/serv_web/web_api/web_api.go
  90. 4 3
      app/lev3/serv_web/web_gui/page_bot_show/bot_show.tmpl.html
  91. 1 1
      app/lev3/serv_web/web_gui/page_bot_show/btn_gold/btn_gold.go
  92. 1 1
      app/lev3/serv_web/web_gui/page_bot_show/btn_mine_mode/btn_mine_mode.go
  93. 49 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_mine_time/btn_mine_time.go
  94. 1 1
      app/lev3/serv_web/web_gui/page_bot_show/btn_power/btn_power.go
  95. 1 2
      app/lev3/serv_web/web_gui/page_bot_show/btn_silver/btn_silver.go
  96. 14 10
      app/lev3/serv_web/web_gui/page_bot_show/page_bot_show.go

+ 4 - 0
app/lev0/cons/cons.go

@@ -16,6 +16,8 @@ const ( // Задержки для работы
 const ( // Режимы работы
 	// РежимНеСуществует -- объект не существует
 	РежимНеСуществует = alias.ААренаСостояние("режим_не_существует")
+	// РежимПостроено -- постройка уже выполнена
+	РежимПостроено = alias.ААренаСостояние("режим_построено")
 	// РежимАпгрейд -- объект в режиме апгрейда
 	РежимАпгрейд = alias.ААренаСостояние("режим_апгрейд")
 	// РежимАпгрейдПлатный -- объект в режиме платного апгрейда
@@ -24,6 +26,8 @@ const ( // Режимы работы
 	РежимОжидание = alias.ААренаСостояние("режим_ожидание")
 	// РежимРабота -- объект что-то делает
 	РежимРабота = alias.ААренаСостояние("режим_работа")
+	// РежимЗабрать -- объект готов отдать продукт
+	РежимЗабрать = alias.ААренаСостояние("режим_забрать")
 )
 
 // const (

+ 1 - 1
app/lev0/types/inetmarket.go → app/lev0/types/_inetmarket.go

@@ -5,4 +5,4 @@ package types
 */
 
 // INetMarket -- интрфейс к сетевому рынку
-type INetMarket interface{}
+type _INetMarket interface{}

+ 0 - 6
app/lev0/types/iarena.go

@@ -1,18 +1,12 @@
 package types
 
-import "wartank/app/lev0/alias"
-
 // ИАрена -- интерфейс арены
 type ИАрена interface {
 	ИАренаКонтекст
-	// ВремяОстат -- возвращает остаток времени до окончания работы
-	ВремяОстат() ИВремяОстат
 	// СписПолучить -- возвращает список строк арены
 	СписПолучить() []string
 	// СтрОбновить -- обновляет список строк арены
 	СтрОбновить(lstString []string)
-	// ОбратВремяУст -- устанавливает новое значение обратного счётчика времени
-	ОбратВремяУст(времяСек alias.АВремя)
 	// Обновить -- обновляет список строк арены
 	Обновить()
 	// Пуск -- запускает арену в обработку на текущий шаг

+ 1 - 2
app/lev0/types/iarena_arsenal.go

@@ -6,8 +6,7 @@ package types
 
 // ИАренаАрсенал -- интерфейс к объекту арсенала
 type ИАренаАрсенал interface {
-	ИБазаСтроение
-	ИАрена
+	ИАренаСтроение
 	// Фугасы -- возвращает объект фугасов
 	Фугасы() ИСтатПарам
 	// Бронебойки -- возвращает объект бронебойных снарядов

+ 1 - 1
app/lev0/types/iarena_bank.go

@@ -6,7 +6,7 @@ package types
 
 // ИБанк -- интерфейс банка на базе
 type ИАренаБанк interface {
-	ИАрена
+	ИАренаСтроение
 	// СереброБот -- сколько серебра заработал бот
 	СереброБот() ИСтатПарам
 	// РежимРаботы1 -- объект первого режима производства

+ 2 - 0
app/lev0/types/iarena_base.go

@@ -7,4 +7,6 @@ package types
 // ИАренаБаза -- интерфейс к базе игры
 type ИАренаБаза interface {
 	ИАрена
+	// ОбновитьПринуд -- обновить состояние базы принудительно
+	ОбновитьПринуд()
 }

+ 22 - 0
app/lev0/types/iarena_build.go

@@ -0,0 +1,22 @@
+package types
+
+import (
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	. "wartank/app/lev0/alias"
+)
+
+// ИАренаСтроение -- базовое строение для строения на базе
+type ИАренаСтроение interface {
+	ИАрена
+	// Уровень -- возвращает уровень шахты
+	Уровень() ИСтатПарам
+	// ПродуктСейчас -- производимый продукта сейчас
+	ПродуктСейчас() ИСтатПарам
+	// ВремяОстат -- объект счётчика времени
+	ВремяОстат() ИВремяОстат
+	// ПродуктВремяСейчас -- сколько времени до окончания продукта ждать
+	ПродуктВремяСейчас() ISafeString
+	// ОбратВремяУст -- устанавливает новое значение обратного счётчика времени
+	ОбратВремяУст(времяСек АВремя)
+}

+ 1 - 3
app/lev0/types/iarena_convoy.go

@@ -6,7 +6,5 @@ package types
 
 // ИАренаКонвой -- интерфейс к объекту конвоя
 type ИАренаКонвой interface {
-	ИАрена
-	// Слава -- возвращает объект славы конвоя
-	Слава() ИСтатПарам
+	ИАренаСтроение
 }

+ 2 - 2
app/lev0/types/iarena_ctx.go

@@ -15,8 +15,8 @@ type ИАренаКонтекст interface {
 	Отменить()
 	// Имя -- имя игровой арены
 	Имя() alias.ААренаИмя
-	// АренаСостояние -- состояние арены
-	АренаСостояние() ИАренаСостояние
+	// Состояние -- состояние арены
+	Состояние() ИАренаСостояние
 	// ВебЛог -- возвращает веб-лог арены
 	ВебЛог() ИВебЛог
 }

+ 0 - 9
app/lev0/types/iarena_fuel.go

@@ -1,9 +0,0 @@
-package types
-
-// ИАренаТопливо -- топливная база
-type ИАренаТопливо interface {
-	ИБазаСтроение
-	ИАрена
-	// Топливо -- возвращает предельное количество топлива
-	Топливо() ИСтатПарам
-}

+ 8 - 0
app/lev0/types/iarena_fuel_storage.go

@@ -0,0 +1,8 @@
+package types
+
+// ИАренаТопливо -- топливная база
+type ИАренаБак interface {
+	ИАренаСтроение
+	// Бак -- возвращает предельное количество топлива
+	Бак() ИСтатПарам
+}

+ 1 - 2
app/lev0/types/iarena_mine.go

@@ -6,8 +6,7 @@ package types
 
 // ИАренаШахта -- интерфейс к объекту шахты
 type ИАренаШахта interface {
-	ИБазаСтроение
-	ИАрена
+	ИАренаСтроение
 	// Руда -- возвращает объект руды
 	Руда() ИСтатПарам
 	// Железо -- возвращает объект железа

+ 1 - 2
app/lev0/types/iarena_polygon.go

@@ -6,6 +6,5 @@ package types
 
 // ИАренаПолигон -- интерфейс к полигону
 type ИАренаПолигон interface {
-	ИБазаСтроение
-	ИАрена
+	ИАренаСтроение
 }

+ 0 - 11
app/lev0/types/ibase_build.go

@@ -1,11 +0,0 @@
-package types
-
-// ИБазаСтроение -- базовое строение для строения на базе
-type ИБазаСтроение interface {
-	// Уровень -- возвращает уровень шахты
-	Уровень() ИСтатПарам
-	// ПродуктСейчас -- производимый продукта сейчас
-	ПродуктСейчас() ИСтатПарам
-	// ПродуктВремяСейчас -- сколько времени до окончания продукта ждать
-	ПродуктВремяСейчас() string
-}

+ 2 - 0
app/lev0/types/icounttime.go

@@ -9,6 +9,8 @@ type ИВремяОстат interface {
 	ИПарсерПростой
 	// КаналСиг -- возвращает канал тиков
 	КаналСиг() <-chan int
+	// String -- возвращает строковое представление остатка времени
+	String() string
 }
 
 func FakeTest() {

+ 0 - 10
app/lev0/types/imarket.go

@@ -1,10 +0,0 @@
-package types
-
-/*
-	Интерфейс к рынку
-*/
-
-// ИРынок -- интерфейс к рынку
-type ИРынок interface {
-	ИАрена
-}

+ 0 - 10
app/lev0/types/inetangar.go

@@ -1,10 +0,0 @@
-package types
-
-/*
-	Интерфейс к сетевому ангару
-*/
-
-// INetAngar -- интерфейс к сетевому ангару
-type INetAngar interface {
-	ИСекцияСеть
-}

+ 0 - 10
app/lev0/types/inetbase.go

@@ -1,10 +0,0 @@
-package types
-
-/*
-	Интерфейс к сетевой базе.
-*/
-
-// INetBase -- интерфейс к сетевой базе
-type INetBase interface {
-	ИСекцияСеть
-}

+ 0 - 4
app/lev0/types/iparser_simple.go

@@ -8,10 +8,6 @@ import (
 type ИПарсерПростой interface {
 	// Уст -- устанавливает часы
 	Уст(АВремя)
-	// Сброс -- сбрасывает хранимое значение
-	Сброс()
-	// String -- возвращает строковое представление
-	String() string
 	// ПолучМилСек -- возвращает хранимое количество мсек
 	ПолучМилСек() АМилСек
 }

+ 0 - 8
app/lev0/types/iproc_fuel_find.go

@@ -1,8 +0,0 @@
-package types
-
-// ИПроцессТопливоНайти -- процесс поиска топлива в баке
-type ИПроцессТопливоНайти interface {
-	ИПроцесс
-	// Обновить -- обновляет количество топлива
-	Обновить()
-}

+ 0 - 13
app/lev0/types/isectionnet.go

@@ -1,13 +0,0 @@
-package types
-
-/*
-	Базовый тип для сетевой секции
-*/
-
-// ИСекцияСеть --	базовый тип для сетевой секции
-type ИСекцияСеть interface {
-	// ОбновитьСеть -- обновляет список строк секции
-	ОбновитьСеть() error
-	// Get -- выполняет GET-запрос по указанному URL
-	Get(strLink string) ([]string, error)
-}

+ 97 - 0
app/lev1/down_time/down_time.go

@@ -0,0 +1,97 @@
+// package down_time -- счётчик обратного времени в мсек
+package down_time
+
+import (
+	"sync"
+	"time"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	. "wartank/app/lev0/alias"
+	. "wartank/app/lev0/types"
+	"wartank/app/lev1/product/parser_time"
+)
+
+const (
+	спатьИнтервал = time.Millisecond * 1000 // Малый интервал сна, в мсек
+)
+
+// ВремОбрат -- счётчик обратного времени для игровой зоны (анга, база, битва и т.п.)
+type ВремОбрат struct {
+	конт        ILocalCtx
+	остатПарсер ИПарсерВремя // Парсер значения (мсек)
+
+	времПорог ISafeInt // Целевое время срабатывания, мсек
+
+	канВызов chan int // Канал для отправки сигналов (для верхнего уровня)
+	блок     sync.RWMutex
+}
+
+// НовВремОбрат -- возвращает новый *CountTime
+func НовВремОбрат(конт ILocalCtx, время АМилСек) *ВремОбрат {
+	сам := &ВремОбрат{
+		конт: конт,
+
+		времПорог:   NewSafeInt(),
+		канВызов:    make(chan int, 2),
+		остатПарсер: parser_time.НовПарсерВремя(),
+	}
+	мСек := АМилСек(TimeNow()) + время
+	сам.времПорог.Set(int(мСек))
+	go сам.пуск()
+	_ = ИВремяОстат(сам)
+	return сам
+}
+
+// ПолучМилСек -- возвращает оставшееся хранимое время остатка
+func (сам *ВремОбрат) ПолучМилСек() АМилСек {
+	return сам.остатПарсер.ПолучМилСек()
+}
+
+func (сам *ВремОбрат) ждать() {
+	time.Sleep(спатьИнтервал)
+	timeNow := TimeNow()
+	if сам.времПорог.Get() > int(timeNow) {
+		return
+	}
+	сам.канВызов <- 1
+	// сам.времПорог.Set(int(timeNow) + int(сам.остатПарсер.ПолучМилСек()))
+}
+
+// Запускает тикер для интервалов сна (через каждые 1000 мСек)
+func (сам *ВремОбрат) пуск() {
+	defer close(сам.канВызов)
+	for {
+		select {
+		case <-сам.конт.Ctx().Done(): // Отмена контекста тикера (а может и сцены, может и бота)
+			return
+		default:
+			сам.ждать()
+		}
+	}
+}
+
+// Уст -- устанавливает число оставшихся сек
+func (сам *ВремОбрат) Уст(время АВремя) {
+	сам.блок.Lock()
+	defer сам.блок.Unlock()
+	сам.остатПарсер.Уст(время)
+	_val := сам.остатПарсер.ПолучМилСек()
+	сам.времПорог.Set(int(TimeNow()) + int(_val))
+}
+
+// String -- возвращает строковое представление оставшихся сек
+func (сам *ВремОбрат) String() string {
+	сам.блок.RLock()
+	defer сам.блок.RUnlock()
+	цОстат := сам.времПорог.Get() - int(TimeNow())
+	остат := time.Millisecond * time.Duration(цОстат)
+	return  остат.String()
+}
+
+// КаналСиг -- возвращает канал чтения тиков
+func (сам *ВремОбрат) КаналСиг() <-chan int {
+	return сам.канВызов
+}

+ 1 - 1
app/lev1/product/parser_time/parse_sec/parse_sec_test.go

@@ -32,7 +32,7 @@ func (сам *tester) set() {
 
 func (сам *tester) setGood1() {
 	сам.t.Logf("=setGood1=\n")
-	сам.ph.УстСек(26)
+	_ = сам.ph.УстСек(26)
 	if strHour := сам.ph.String(); strHour != "26" {
 		сам.t.Errorf("setGood1(): strHour(%q)!='26'\n", strHour)
 	}

+ 0 - 14
app/lev1/product/parser_time/parser_time.go

@@ -38,13 +38,6 @@ func НовПарсерВремя() *ПарсерВремя {
 	return сам
 }
 
-// Сброс -- сбрасывает значение интервала времени по требованию
-func (сам *ПарсерВремя) Сброс() {
-	сам.час.Сброс()
-	сам.мин.Сброс()
-	сам.сек.Сброс()
-}
-
 // ПолучМилСек -- возвращает общее число секунд
 func (сам *ПарсерВремя) ПолучМилСек() АМилСек {
 	сам.блок.RLock()
@@ -57,7 +50,6 @@ func (сам *ПарсерВремя) Уст(стрВремя АВремя) {
 	сам.блок.Lock()
 	defer сам.блок.Unlock()
 	Hassert(стрВремя != "", "CountTime.Set(): val is empty")
-	сам.Сброс()
 	списВремя := strings.Split(string(стрВремя), ":")
 	фнМинУст := func() {
 		стрСек := АВремя(списВремя[1])
@@ -104,9 +96,3 @@ func (сам *ПарсерВремя) Мин() ИПарсерМин {
 func (сам *ПарсерВремя) Сек() ИПарсерСек {
 	return сам.сек
 }
-
-// String -- возвращает хранимое время
-func (сам *ПарсерВремя) String() string {
-	res := сам.час.String() + ":" + сам.мин.String() + ":" + сам.сек.String()
-	return res
-}

+ 0 - 1
app/lev1/product/parser_time/parser_time_test.go

@@ -38,7 +38,6 @@ func (сам *tester) create() {
 	if val := сам.pars.ПолучМилСек(); val != 0 {
 		сам.t.Errorf("create(): valInt(%d)!=0\n", val)
 	}
-	_ = сам.pars.String()
 }
 
 // Парсинг строки времени

+ 11 - 30
app/lev2/arena/arena.go

@@ -2,8 +2,6 @@
 package arena
 
 import (
-	"sync"
-
 	. "gitp78su.ipnodns.ru/svi/kern"
 	. "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
@@ -14,12 +12,11 @@ import (
 	"wartank/app/lev2/arena/arena_context"
 	"wartank/app/lev2/arena/arena_net"
 	"wartank/app/lev2/arena/arena_string"
-	"wartank/app/lev2/arena/down_time"
 )
 
 // АренаКонфиг -- конфигурация арены
 type АренаКонфиг struct {
-	Бот_         ИБот
+	Конт_        ILocalCtx
 	АренаИмя_    alias.ААренаИмя
 	СтрКонтроль_ string // Контрольная строка для арены
 	СтрУрл_      string // Адрес арены
@@ -27,24 +24,21 @@ type АренаКонфиг struct {
 }
 
 // Контролирует правильность параметров
-func (конф *АренаКонфиг) контроль() {
-	Hassert(конф.СтрКонтроль_ != "", "АренаКонфиг.СтрКонтроль_ == \"\"")
-	Hassert(конф.ФнПуск_ != nil, "АренаКонфиг.ФнПуск_ == nil")
-	Hassert(конф.Бот_ != nil, "АренаКонфиг.Бот_ == nil")
-	Hassert(конф.АренаИмя_ != "", "АренаКонфиг.СценаИмя_ == \"\"")
-	Hassert(конф.СтрУрл_ != "", "АренаКонфиг.СтрУрл_ == \"\"")
+func (сам *АренаКонфиг) контроль() {
+	Hassert(сам.Конт_ != nil, "АренаКонфиг.СтрКонтроль(): конт ==nil")
+	Hassert(сам.ФнПуск_ != nil, "АренаКонфиг.СтрКонтроль(): ФнПуск_ == nil")
+	Hassert(сам.АренаИмя_ != "", "АренаКонфиг.СтрКонтроль(): СценаИмя_ == \"\"")
+	Hassert(сам.СтрУрл_ != "", "АренаКонфиг.СтрКонтроль(): СтрУрл_ == \"\"")
 }
 
 // Арена -- арена игры
 type Арена struct {
 	ИАренаКонтекст
-	времяОстат ИВремяОстат               // Обратный отсчёт до окончания работы режима
-	списСтр    *arena_string.АренаСтроки // Список строк из сети для анализа секции
-	конф       АренаКонфиг
-	уровень    ИСтатПарам
-	сеть       ИАренаСеть
-	блок       sync.RWMutex
-	лог        ILogBuf
+	списСтр *arena_string.АренаСтроки // Список строк из сети для анализа секции
+	конф    АренаКонфиг
+	уровень ИСтатПарам
+	сеть    ИАренаСеть
+	лог     ILogBuf
 }
 
 // НовАрена -- возвращает новую арену игры
@@ -56,7 +50,6 @@ func НовАрена(конт ILocalCtx, конф АренаКонфиг) ИА
 	сам := &Арена{
 		ИАренаКонтекст: аренаКонтекст,
 		уровень:        lev1.НовСтатПарам("уровень"),
-		времяОстат:     down_time.НовВремОбрат(аренаКонтекст, 5),
 		списСтр:        arena_string.НовАренаСтроки(конт, конф.СтрКонтроль_),
 		конф:           конф,
 		лог:            лог,
@@ -97,18 +90,6 @@ func (сам *Арена) СписПолучить() []string {
 	return сам.списСтр.Получ()
 }
 
-// ОбратВремяУст -- устанавливает новое значение обратного счётчика времени
-func (сам *Арена) ОбратВремяУст(времяСек alias.АВремя) {
-	сам.времяОстат.Уст(времяСек)
-}
-
-// ВремяОстат -- объект оставшегося времени
-func (сам *Арена) ВремяОстат() ИВремяОстат {
-	сам.блок.RLock()
-	defer сам.блок.RUnlock()
-	return сам.времяОстат
-}
-
 // Сеть -- возвращает объект сети
 func (сам *Арена) Сеть() ИАренаСеть {
 	return сам.сеть

+ 11 - 8
app/lev2/arena/arena_angar/arena_angar.go

@@ -9,6 +9,7 @@ import (
 	. "wartank/app/lev0/types"
 	"wartank/app/lev1"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_angar/bf_angar_fuel"
 	"wartank/app/lev3/bot/bot_stat/bot_resurs"
 )
 
@@ -20,7 +21,6 @@ import (
 type АренаАнгар struct {
 	ИАрена
 	конт          ILocalCtx
-	бот           ИБот
 	progress      ИСтатПарам
 	топливо       ИСтатПарам
 	серебро       ИСтатПарам
@@ -36,10 +36,8 @@ type АренаАнгар struct {
 // НовАнгар -- возвращает новый *Angar
 func НовАнгар(конт ILocalCtx) ИАренаАнгар {
 	Hassert(конт != nil, "НовАнгар(): ILocalCtx==nil")
-	бот := конт.Get("бот").Val().(ИБот)
 	сам := &АренаАнгар{
 		конт:          конт,
-		бот:           бот,
 		progress:      lev1.НовСтатПарам("прогресс"),
 		игроковОнлайн: lev1.НовСтатПарам("онлайн"),
 		сереброСессия: lev1.НовСтатПарам("серебро сессия"),
@@ -49,7 +47,7 @@ func НовАнгар(конт ILocalCtx) ИАренаАнгар {
 		топливо:       lev1.НовСтатПарам("топливо"),
 	}
 	аренаКонф := arena.АренаКонфиг{
-		Бот_:         бот,
+		Конт_:        конт,
 		АренаИмя_:    "Ангар",
 		СтрКонтроль_: "<title>Ангар</title>",
 		ФнПуск_:      сам.пуск,
@@ -61,6 +59,11 @@ func НовАнгар(конт ILocalCtx) ИАренаАнгар {
 	return сам
 }
 
+func (сам *АренаАнгар)Пуск(){
+	сам.Обновить()
+	bf_angar_fuel.ТопливоНайти(сам.конт)
+}
+
 // Золото -- возвращает объект топлива в ангаре
 func (сам *АренаАнгар) Золото() ИСтатПарам {
 	return сам.золото
@@ -88,16 +91,16 @@ func (сам *АренаАнгар) пуск() {
 	фнЦикл := func() {
 		сам.Обновить()
 		// сам.конвойПроверить()
-		сам.ОбратВремяУст("01:00")
+		// сам.ПродуктВремяСейчас().Set("01:00")
 	}
 	go func() {
-		сам.ОбратВремяУст("01")
+		// сам.ОбратВремяУст("01")
 		for {
 			select {
 			case <-сам.конт.Ctx().Done(): // Отмена контекста
 				return
-			case <-сам.ВремяОстат().КаналСиг(): // Метка времени
-				фнЦикл()
+			// case <-сам.ВремяОстат().КаналСиг(): // Метка времени
+			// фнЦикл()
 			default: // Запускается раз в минуту
 				фнЦикл()
 				time.Sleep(time.Minute * 1)

+ 2 - 2
app/lev0/bfunc/bf_fuel_find/bf_fuel_find.go → app/lev2/arena/arena_angar/bf_angar_fuel/bf_angar_fuel.go

@@ -1,5 +1,5 @@
-// package bf_fuel_find -- бизнес-функция поиска топлива в баке
-package bf_fuel_find
+// package bf_angar_fuel -- бизнес-функция поиска топлива в ангаре
+package bf_angar_fuel
 
 import (
 	"strconv"

+ 36 - 34
app/lev2/arena/arena_arsenal/arena_arsenal.go

@@ -7,13 +7,18 @@ import (
 	"strings"
 	"time"
 
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
 	. "wartank/app/lev0/types"
 	"wartank/app/lev1"
 	"wartank/app/lev1/web_log"
 	"wartank/app/lev2/arena"
-
-	. "gitp78su.ipnodns.ru/svi/kern"
-	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"wartank/app/lev2/arena/arena_arsenal/bf_ammo_make"
+	"wartank/app/lev2/arena/arena_arsenal/bf_ammo_stat"
+	"wartank/app/lev2/arena/arena_arsenal/bf_arsenal_build"
+	"wartank/app/lev2/arena/arena_arsenal/bf_arsenal_upgrade"
+	"wartank/app/lev2/arena/arena_build"
 )
 
 const (
@@ -25,61 +30,59 @@ const (
 
 // Арсенал -- объект оружейной на базе
 type АренаАрсенал struct {
-	ИАрена
-	вЛог         ИВебЛог
-	лог          ILogBuf
-	бот          ИБот
-	база         ИАренаБаза
-	фугас        ИСтатПарам
-	бронебойка   ИСтатПарам
-	кумулятив    ИСтатПарам
-	ремка        ИСтатПарам
-	продукт      ИСтатПарам // Что делается прямо сейчас
-	продуктВремя string     // Сколько осталось времени прямо сейчас
-	конт         ILocalCtx
+	ИАренаСтроение
+	вЛог       ИВебЛог
+	лог        ILogBuf
+	база       ИАренаБаза
+	фугас      ИСтатПарам
+	бронебойка ИСтатПарам
+	кумулятив  ИСтатПарам
+	ремка      ИСтатПарам
+	конт       ILocalCtx
 }
 
 // НовАрсенал -- возвращает новый *Arsenal
-func НовАрсенал(конт ILocalCtx) ИАренаАрсенал {
+func НовАрсенал(конт ILocalCtx) *АренаАрсенал {
 	лог := NewLogBuf()
 	лог.Info("НовАрсенал()\n")
 
 	сам := &АренаАрсенал{
-		бот:        конт.Get("бот").Val().(ИБот),
+		конт:       конт,
 		база:       конт.Get("база").Val().(ИАренаБаза),
 		фугас:      lev1.НовСтатПарам(стрФугасы),
 		бронебойка: lev1.НовСтатПарам(стрБронебойки),
 		кумулятив:  lev1.НовСтатПарам(стрКумулятивы),
 		ремка:      lev1.НовСтатПарам(стрРемки),
-		продукт:    lev1.НовСтатПарам("свинец"),
-		конт:       конт,
-
-		лог: лог,
+		лог:        лог,
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         сам.бот,
+		Конт_:        конт,
 		АренаИмя_:    "Арсенал",
 		СтрКонтроль_: `<span class="green2">Ремкомплект</span><br/>`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/production/Armory",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 	сам.вЛог = web_log.НовВебЛог(true)
 	// go сам.пуск()
 	сам.вЛог.Добавить("НовАрсенал(): Арсенал создан")
 	конт.Set("арсенал", сам, "Арсенал бота")
+	_ = ИАренаАрсенал(сам)
 	return сам
 }
 
-// ПродуктКолСейчас -- возвращает производимый продукт
-func (сам *АренаАрсенал) ПродуктСейчас() ИСтатПарам {
-	return сам.продукт
-}
-
-// ПродуктВремяСейчас -- сколько осталось времени до производства продукта
-func (сам *АренаАрсенал) ПродуктВремяСейчас() string {
-	return сам.продуктВремя
-	// return сам.Секция.ВремяОпрос().Стр()
+func (сам *АренаАрсенал) Пуск() {
+	фнОбновить := func() { // Когда арена не построена -- ничего не вернёт
+		defer func() {
+			_ = recover()
+		}()
+		сам.Обновить()
+	}
+	фнОбновить()
+	bf_arsenal_build.АрсеналПостроить(сам.конт)
+	bf_arsenal_upgrade.АрсеналАпгрейд(сам.конт)
+	bf_ammo_stat.СнарядыСтат(сам.конт)
+	bf_ammo_make.СнарядыСделать(сам.конт)
 }
 
 // запускает обработку арсенала
@@ -87,7 +90,7 @@ func (сам *АренаАрсенал) пуск() {
 	еслиПостроить := true
 	фнРабота := func() {
 		defer func() {
-			for сам.ВремяОстат().ПолучМилСек() > 0 {
+			for сам.ВремяОстат().ПолучМилСек().Сек() > 10 {
 				select {
 				case <-сам.конт.Ctx().Done():
 					return
@@ -119,7 +122,6 @@ func (сам *АренаАрсенал) пуск() {
 		}
 		_ = сам.уровеньОбновить()
 		сам.забрать()
-		сам.лог.Info("пуск(): бот=%q, цикл завершён\n", сам.бот.Имя())
 	}
 	for {
 		фнРабота()

+ 1 - 1
app/lev0/bfunc/bf_ammo_make/bf_ammo_make.go → app/lev2/arena/arena_arsenal/bf_ammo_make/bf_ammo_make.go

@@ -26,7 +26,7 @@ func СнарядыСделать(конт ILocalCtx) {
 		return
 	}
 	арсенал := арсенал_.Val().(ИАренаАрсенал)
-	if арсенал.АренаСостояние().Получ() != cons.РежимОжидание {
+	if арсенал.Состояние().Получ() != cons.РежимОжидание {
 		return
 	}
 	приоритет(конт)

+ 1 - 1
app/lev0/bfunc/bf_ammo_stat/bf_ammo_stat.go → app/lev2/arena/arena_arsenal/bf_ammo_stat/bf_ammo_stat.go

@@ -20,7 +20,7 @@ func СнарядыСтат(конт ILocalCtx) {
 	}
 	арсенал := арсенал_.Val().(ИАренаАрсенал)
 
-	if арсенал.АренаСостояние().Получ() != cons.РежимОжидание {
+	if арсенал.Состояние().Получ() != cons.РежимОжидание {
 		return
 	}
 	фугасыНайти(конт)

+ 5 - 5
app/lev0/bfunc/bf_arsenal_build/bf_bank_build.go → app/lev2/arena/arena_arsenal/bf_arsenal_build/bf_bank_build.go

@@ -13,7 +13,7 @@ import (
 // БанкПостроить -- постройка арсенала
 func АрсеналПостроить(конт ILocalCtx) {
 	арсенал := конт.Get("арсенал").Val().(ИАренаАрсенал)
-	if арсенал.АренаСостояние().Получ() == cons.РежимНеСуществует {
+	if арсенал.Состояние().Получ() == cons.РежимНеСуществует {
 		банкПостроить(конт)
 	}
 }
@@ -33,7 +33,7 @@ func банкПостроить(конт ILocalCtx) {
 			}
 		}
 		if ссыльПостроить == "" {
-			арсенал.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+			арсенал.Состояние().Уст(cons.РежимПостроено)
 			return
 		}
 		// <td style="width:50%;padding-left:1px;"><a class="simple-but border mb5" href="building-upgrade/Armory"><span><span>Построить</span></span></a></td>
@@ -54,7 +54,7 @@ func банкПостроить(конт ILocalCtx) {
 			}
 		}
 		if ссыльПодтвердить == "" {
-			арсенал.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+			арсенал.Состояние().Уст(cons.РежимПостроено)
 			return
 		}
 		ссыльПодтвердить = strings.TrimPrefix(ссыльПодтвердить, `<a class="simple-but border mb5" href="`)
@@ -73,7 +73,7 @@ func банкПостроить(конт ILocalCtx) {
 			}
 		}
 		if ссыльДа == "" {
-			арсенал.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+			арсенал.Состояние().Уст(cons.РежимПостроено)
 			return
 		}
 		ссыльДа = strings.TrimPrefix(ссыльДа, `<a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../`)
@@ -81,6 +81,6 @@ func банкПостроить(конт ILocalCtx) {
 		// https://wartank.ru/wicket/page?52-1.ILinkListener-confirmLink
 		ссыльДа = "http://wartank.ru/" + ссыльДа
 		_ = база.Сеть().ВебВоркер().Получ(ссыльДа)
-		арсенал.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+		арсенал.Состояние().Уст(cons.РежимПостроено)
 	}
 }

+ 89 - 0
app/lev2/arena/arena_arsenal/bf_arsenal_upgrade/bf_arsenal_upgrade.go

@@ -0,0 +1,89 @@
+// package bf_arsenal_upgrade -- бизнес-функция апгрейда арсенала
+package bf_arsenal_upgrade
+
+import (
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	"wartank/app/lev0/cons"
+	. "wartank/app/lev0/types"
+)
+
+// АрсеналАпгрейд -- повышает уровень арсенала
+func АрсеналАпгрейд(конт ILocalCtx) {
+	арсенал := конт.Get("арсенал").Val().(ИАренаАрсенал)
+	еслиПостроено := арсенал.Состояние().Получ() == cons.РежимПостроено
+	еслиОжидание := арсенал.Состояние().Получ() == cons.РежимОжидание
+	if !(еслиПостроено || еслиОжидание) {
+		return
+	}
+	арсеналАпгрейд(конт)
+}
+
+func арсеналАпгрейд(конт ILocalCtx) {
+	арсенал := конт.Get("арсенал").Val().(ИАренаАрсенал)
+	var (
+		еслиНайти = false
+		списСтр   []string
+		стр       = ""
+	)
+	фнКупить := func() bool {
+		списСтр = арсенал.Сеть().ВебВоркер().Получ("https://wartank.ru/building-upgrade/Armory")
+		for _, стр = range списСтр {
+			// <a class="simple-but border mb5" href="Armory?5-1.ILinkListener-upgradeLink-link">
+			if strings.Contains(стр, `ILinkListener-upgradeLink-link`) {
+				еслиНайти = true
+				break
+			}
+		}
+		if !еслиНайти {
+			return true
+		}
+		// Пробуем улучшить здание
+		_стр := strings.TrimPrefix(стр, "<a class=\"simple-but border mb5\" href=\"")
+		_стр = strings.TrimSuffix(_стр, "\">")
+		// https://wartank.ru/building-upgrade/Armory?4-1.ILinkListener-upgradeLink-link
+		// <a class="simple-but border mb5" href="Armory?50-1.ILinkListener-upgradeLink-link">
+		ссылка := "https://wartank.ru/building-upgrade/" + _стр
+		списСтр = арсенал.Сеть().ВебВоркер().Получ(ссылка)
+		// Проверить, что покупка состоялась
+		for _, стр := range списСтр {
+			if strings.Contains(стр, "ILinkListener-upgradeLink-link") {
+				return false // Покупка не оплачена
+			}
+		}
+		return true
+	}
+
+	фнПодтверждение := func() {
+		for _, стр = range списСтр {
+			// <a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../wicket/page?7-1.ILinkListener-confirmLink"><span><span>да, подтверждаю</span></span></a>
+			if strings.Contains(стр, `ILinkListener-confirmLink`) {
+				еслиНайти = true
+				break
+			}
+		}
+		if !еслиНайти {
+			return
+		}
+		// Пробуем оплатить апгрейд
+		_стр := strings.TrimPrefix(стр, `<a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="..`)
+		_стр = strings.TrimSuffix(_стр, `"><span><span>да, подтверждаю</span></span></a>`)
+		// https://wartank.ru/wicket/page?6-1.ILinkListener-confirmLink
+		ссылка := "https://wartank.ru" + _стр
+		списСтр = арсенал.Сеть().ВебВоркер().Получ(ссылка)
+		// Проверить, что оплата состоялась
+		for _, стр := range списСтр {
+			if strings.Contains(стр, "<title>Вы сделали слишком большую паузу</title>") {
+				return // Покупка не оплачена
+			}
+		}
+		арсенал.Состояние().Уст(cons.РежимАпгрейдПлатный)
+	}
+
+	if !фнКупить() {
+		return
+	}
+	фнПодтверждение()
+}

+ 25 - 31
app/lev2/arena/arena_bank/arena_bank.go

@@ -11,6 +11,11 @@ import (
 	"wartank/app/lev1"
 	"wartank/app/lev2/arena"
 	"wartank/app/lev2/arena/arena_bank/bank_mode"
+	"wartank/app/lev2/arena/arena_bank/bf_bank_build"
+	"wartank/app/lev2/arena/arena_bank/bf_bank_prod"
+	"wartank/app/lev2/arena/arena_bank/bf_bank_upgrade"
+	"wartank/app/lev2/arena/arena_bank/bf_bank_upgrade_fast"
+	"wartank/app/lev2/arena/arena_build"
 )
 
 /*
@@ -19,7 +24,8 @@ import (
 
 // Банк -- объект банка на базе
 type АренаБанк struct {
-	ИАрена
+	ИАренаСтроение
+	конт       ILocalCtx
 	сереброБот ИСтатПарам
 	режим1     *bank_mode.BankMode // 1 режим работы на выбор
 	режим2     *bank_mode.BankMode // 2 режим работы на выбор
@@ -29,22 +35,37 @@ type АренаБанк struct {
 func НовБанк(конт ILocalCtx) ИАренаБанк {
 
 	сам := &АренаБанк{
+		конт:       конт,
 		сереброБот: lev1.НовСтатПарам("серебро бота"),
 		режим1:     bank_mode.NewBankMode(конт),
 		режим2:     bank_mode.NewBankMode(конт),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         конт.Get("бот").Val().(ИБот),
+		Конт_:        конт,
 		АренаИмя_:    "Банк",
 		СтрКонтроль_: `<span class="green2">Серебро</span><br/>`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/production/Bank",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
-	конт.Set("арена_банк", сам, "Арена банка бота")
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
+	конт.Set("банк", сам, "Арена банка бота")
 	return сам
 }
 
+func (сам *АренаБанк) Пуск() {
+	фнОбновить := func() { // Когда арена не построена -- ничего не вернёт
+		defer func() {
+			_ = recover()
+		}()
+		сам.Обновить()
+	}
+	фнОбновить()
+	bf_bank_build.БанкПостроить(сам.конт)
+	bf_bank_upgrade.БанкАпгрейд(сам.конт)
+	bf_bank_upgrade_fast.БанкАпгрейдБесплатно(сам.конт)
+	bf_bank_prod.СереброПроизводить(сам.конт)
+}
+
 // запускает банк в опрос
 func (сам *АренаБанк) пуск() {
 	log.Printf("Банк.пуск()\n")
@@ -54,10 +75,6 @@ func (сам *АренаБанк) пуск() {
 			time.Sleep(time.Minute * 25)
 			return
 		}
-		if сам.проверитьУскорить() {
-			time.Sleep(time.Minute * 25)
-			return
-		}
 		сам.Обновить()
 	}
 	for {
@@ -66,29 +83,6 @@ func (сам *АренаБанк) пуск() {
 	}
 }
 
-// Проверка на ускорение строительства
-func (сам *АренаБанк) проверитьУскорить() bool {
-	var (
-		стрСсылка   = ""
-		еслиНайдено = false
-	)
-	списБанк := сам.Сеть().ВебВоркер().Получ("https://wartank.ru/buildings")
-	// <td style="width:50%;padding-left:1px;"><a class="simple-but border" href="buildings?1-1.ILinkListener-buildings-1-building-rootBlock-actionPanel-freeBoostLink"><span><span>Ускорение</span></span></a>
-	for _, стрСсылка = range списБанк {
-		if strings.Contains(стрСсылка, `.ILinkListener-buildings-1-building-rootBlock-actionPanel-freeBoostLink`) {
-			еслиНайдено = true
-			break
-		}
-	}
-	if !еслиНайдено {
-		return false
-	}
-	_ссылка := strings.TrimPrefix(стрСсылка, `<td style="width:50%;padding-left:1px;"><a class="simple-but border" href="`)
-	_ссылка = strings.TrimSuffix(_ссылка, `"><span><span>Ускорение</span></span></a>`)
-	ссылка := "https://wartank.ru/" + _ссылка
-	_ = сам.Сеть().ВебВоркер().Получ(ссылка)
-	return true
-}
 
 // Проверяет необходимость постройки полигона
 func (сам *АренаБанк) построитьУлучшить() bool {

+ 7 - 7
app/lev0/bfunc/bf_bank_build/bf_bank_build.go → app/lev2/arena/arena_bank/bf_bank_build/bf_bank_build.go

@@ -12,15 +12,15 @@ import (
 
 // БанкПостроить -- постройка банка
 func БанкПостроить(конт ILocalCtx) {
-	банк := конт.Get("арена_банк").Val().(ИАренаБанк)
-	if банк.АренаСостояние().Получ() == cons.РежимНеСуществует {
+	банк := конт.Get("банк").Val().(ИАренаБанк)
+	if банк.Состояние().Получ() == cons.РежимНеСуществует {
 		банкПостроить(конт)
 	}
 }
 
 func банкПостроить(конт ILocalCtx) {
 	база := конт.Get("база").Val().(ИАренаБаза)
-	банк := конт.Get("арена_банк").Val().(ИАренаБанк)
+	банк := конт.Get("банк").Val().(ИАренаБанк)
 	списСтр := база.Сеть().ВебВоркер().Получ("https://wartank.ru/buildings")
 	ссыльПостроить := "" // ссылка на постройку
 
@@ -33,7 +33,7 @@ func банкПостроить(конт ILocalCtx) {
 			}
 		}
 		if ссыльПостроить == "" {
-			банк.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+			банк.Состояние().Уст(cons.РежимПостроено)
 			return
 		}
 		// <td style="width:50%;padding-left:1px;"><a class="simple-but border mb5" href="building-upgrade/Bank"><span><span>Построить</span></span></a></td>
@@ -54,7 +54,7 @@ func банкПостроить(конт ILocalCtx) {
 			}
 		}
 		if ссыльПодтвердить == "" {
-			банк.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+			банк.Состояние().Уст(cons.РежимПостроено)
 			return
 		}
 		ссыльПодтвердить = strings.TrimPrefix(ссыльПодтвердить, `<a class="simple-but border mb5" href="`)
@@ -73,7 +73,7 @@ func банкПостроить(конт ILocalCtx) {
 			}
 		}
 		if ссыльДа == "" {
-			банк.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+			банк.Состояние().Уст(cons.РежимПостроено)
 			return
 		}
 		ссыльДа = strings.TrimPrefix(ссыльДа, `<a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../`)
@@ -81,6 +81,6 @@ func банкПостроить(конт ILocalCtx) {
 		// https://wartank.ru/wicket/page?52-1.ILinkListener-confirmLink
 		ссыльДа = "http://wartank.ru/" + ссыльДа
 		_ = база.Сеть().ВебВоркер().Получ(ссыльДа)
-		банк.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+		банк.Состояние().Уст(cons.РежимПостроено)
 	}
 }

+ 9 - 14
app/lev0/bfunc/bf_silver_prod/bf_silver_prod.go → app/lev2/arena/arena_bank/bf_bank_prod/bf_bank_prod.go

@@ -1,5 +1,5 @@
-// package bf_silver_prod -- бизнес-функция производить серебро
-package bf_silver_prod
+// package bf_bank_prod -- бизнес-функция производить серебро
+package bf_bank_prod
 
 import (
 	"strconv"
@@ -8,21 +8,16 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
 
-	. "wartank/app/lev0/alias"
 	"wartank/app/lev0/cons"
 	. "wartank/app/lev0/types"
 )
 
 // СереброПроизводить -- заставляет банк производить серебро
 func СереброПроизводить(конт ILocalCtx) {
-	банк_ := конт.Get("арена_банк")
-	if банк_ == nil { // Возможно ещё не построен
-		return
-	}
-	банк := банк_.Val().(ИАренаБанк)
-	еслиРабота := банк.АренаСостояние().Получ() == cons.РежимРабота
-	еслиОжидание := банк.АренаСостояние().Получ() == cons.РежимОжидание
-	if !(еслиРабота || еслиОжидание) {
+	банк := конт.Get("банк").Val().(ИАренаБанк)
+	еслиОжидание := банк.Состояние().Получ() == cons.РежимОжидание
+	еслиПостроен := банк.Состояние().Получ() == cons.РежимПостроено
+	if !(еслиОжидание || еслиПостроен) {
 		return
 	}
 	получитьВсеРежимы(конт)
@@ -31,7 +26,7 @@ func СереброПроизводить(конт ILocalCtx) {
 
 // Получает все режимы банка
 func получитьВсеРежимы(конт ILocalCtx) {
-	банк := конт.Get("арена_банк").Val().(ИАренаБанк)
+	банк := конт.Get("банк").Val().(ИАренаБанк)
 
 	var (
 		lstBank  = банк.СписПолучить()
@@ -89,7 +84,7 @@ func получитьВсеРежимы(конт ILocalCtx) {
 
 // Запускает в производство серебро
 func сделатьСеребро(конт ILocalCtx) {
-	банк := конт.Get("арена_банк").Val().(ИАренаБанк)
+	банк := конт.Get("банк").Val().(ИАренаБанк)
 	var (
 		lstBank     = банк.СписПолучить()
 		ind         int
@@ -117,6 +112,6 @@ func сделатьСеребро(конт ILocalCtx) {
 		strLink = "https://wartank.ru/production/" + lstLink[0]
 		_, err := банк.Сеть().Get(strLink)
 		Hassert(err == nil, "сделатьСеребро(): при выполнении GET-запроса, ош=\n\t%v", err)
-		банк.ОбратВремяУст(АВремя(time1))
+		банк.ПродуктВремяСейчас().Set(time1)
 	}
 }

+ 89 - 0
app/lev2/arena/arena_bank/bf_bank_upgrade/bf_bank_upgrade.go

@@ -0,0 +1,89 @@
+// package bf_bank_upgrade -- бизнес-функция апгрейда банка
+package bf_bank_upgrade
+
+import (
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	"wartank/app/lev0/cons"
+	. "wartank/app/lev0/types"
+)
+
+// БанкАпгрейд -- повышает уровень банка
+func БанкАпгрейд(конт ILocalCtx) {
+	банк := конт.Get("банк").Val().(ИАренаБанк)
+	еслиПостроено := банк.Состояние().Получ() == cons.РежимПостроено
+	еслиОжидание := банк.Состояние().Получ() == cons.РежимОжидание
+	if !(еслиПостроено || еслиОжидание) {
+		return
+	}
+	банкАпгрейд(конт)
+}
+
+func банкАпгрейд(конт ILocalCtx) {
+	банк := конт.Get("банк").Val().(ИАренаБанк)
+	var (
+		еслиНайти = false
+		списСтр   []string
+		стр       = ""
+	)
+	фнКупить := func() bool {
+		списСтр = банк.Сеть().ВебВоркер().Получ("https://wartank.ru/building-upgrade/Bank")
+		for _, стр = range списСтр {
+			// <a class="simple-but border mb5" href="Bank?5-1.ILinkListener-upgradeLink-link">
+			if strings.Contains(стр, `ILinkListener-upgradeLink-link`) {
+				еслиНайти = true
+				break
+			}
+		}
+		if !еслиНайти {
+			return true
+		}
+		// Пробуем улучшить здание
+		_стр := strings.TrimPrefix(стр, "<a class=\"simple-but border mb5\" href=\"")
+		_стр = strings.TrimSuffix(_стр, "\">")
+		// https://wartank.ru/building-upgrade/Bank?4-1.ILinkListener-upgradeLink-link
+		// <a class="simple-but border mb5" href="Bank?50-1.ILinkListener-upgradeLink-link">
+		ссылка := "https://wartank.ru/building-upgrade/" + _стр
+		списСтр = банк.Сеть().ВебВоркер().Получ(ссылка)
+		// Проверить, что покупка состоялась
+		for _, стр := range списСтр {
+			if strings.Contains(стр, "ILinkListener-upgradeLink-link") {
+				return false // Покупка не оплачена
+			}
+		}
+		return true
+	}
+
+	фнПодтверждение := func() {
+		for _, стр = range списСтр {
+			// <a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../wicket/page?7-1.ILinkListener-confirmLink"><span><span>да, подтверждаю</span></span></a>
+			if strings.Contains(стр, `ILinkListener-confirmLink`) {
+				еслиНайти = true
+				break
+			}
+		}
+		if !еслиНайти {
+			return
+		}
+		// Пробуем оплатить апгрейд
+		_стр := strings.TrimPrefix(стр, `<a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="..`)
+		_стр = strings.TrimSuffix(_стр, `"><span><span>да, подтверждаю</span></span></a>`)
+		// https://wartank.ru/wicket/page?6-1.ILinkListener-confirmLink
+		ссылка := "https://wartank.ru" + _стр
+		списСтр = банк.Сеть().ВебВоркер().Получ(ссылка)
+		// Проверить, что оплата состоялась
+		for _, стр := range списСтр {
+			if strings.Contains(стр, "<title>Вы сделали слишком большую паузу</title>") {
+				return // Покупка не оплачена
+			}
+		}
+		банк.Состояние().Уст(cons.РежимАпгрейдПлатный)
+	}
+
+	if !фнКупить() {
+		return
+	}
+	фнПодтверждение()
+}

+ 49 - 0
app/lev2/arena/arena_bank/bf_bank_upgrade_fast/bf_bank_upgrade_fast.go

@@ -0,0 +1,49 @@
+// package bf_bank_upgrade_fast -- бизнес-функция бесплатного апгрейда банка
+package bf_bank_upgrade_fast
+
+import (
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	"wartank/app/lev0/cons"
+	. "wartank/app/lev0/types"
+)
+
+// БанкАпгрейд -- ускоряет повышение уровня банка бесплатно
+func БанкАпгрейдБесплатно(конт ILocalCtx) {
+	банк := конт.Get("банк").Val().(ИАренаБанк)
+	еслиПлатно := банк.Состояние().Получ() == cons.РежимАпгрейдПлатный
+	еслиПостроено := банк.Состояние().Получ() == cons.РежимПостроено
+	if !(еслиПлатно || еслиПостроено) {
+		return
+	}
+	банкАпгрейдБесплатно(конт)
+}
+
+// https://wartank.ru/buildings?16-1.ILinkListener-buildings-2-building-rootBlock-actionPanel-freeBoostLink
+func банкАпгрейдБесплатно(конт ILocalCtx) {
+	банк := конт.Get("банк").Val().(ИАренаБанк)
+	var (
+		стрСсылка   = ""
+		еслиНайдено = false
+	)
+	списБанк := банк.Сеть().ВебВоркер().Получ("https://wartank.ru/buildings")
+	// <td style="width:50%;padding-left:1px;"><a class="simple-but border" href="buildings?1-1.ILinkListener-buildings-1-building-rootBlock-actionPanel-freeBoostLink"><span><span>Ускорение</span></span></a>
+	for _, стрСсылка = range списБанк {
+		if strings.Contains(стрСсылка, `.ILinkListener-buildings-1-building-rootBlock-actionPanel-freeBoostLink`) {
+			еслиНайдено = true
+			break
+		}
+	}
+	if !еслиНайдено {
+		return
+	}
+	_ссылка := strings.TrimPrefix(стрСсылка, `<td style="width:50%;padding-left:1px;"><a class="simple-but border" href="`)
+	_ссылка = strings.TrimSuffix(_ссылка, `"><span><span>Ускорение</span></span></a>`)
+	ссылка := "https://wartank.ru/" + _ссылка
+	_ = банк.Сеть().ВебВоркер().Получ(ссылка)
+	if банк.Состояние().Получ() == cons.РежимПостроено {
+		банк.Состояние().Уст(cons.РежимАпгрейдПлатный)
+	}
+}

+ 39 - 8
app/lev2/arena/arena_base/arena_base.go

@@ -7,6 +7,7 @@ import (
 	"sync"
 	"time"
 
+	. "gitp78su.ipnodns.ru/svi/kern"
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
 
 	. "wartank/app/lev0/types"
@@ -25,19 +26,19 @@ const (
 // База -- объект базы
 type АренаБаза struct {
 	ИАрена
-	бот  ИБот
-	блок sync.Mutex
+	конт      ILocalCtx
+	блок      sync.Mutex
+	счётОбнов ISafeInt // Счётчик времени до обновления
 }
 
 // НовБаза -- возвращает новую базу бота
-func НовБаза(конт ILocalCtx) ИАренаБаза {
-	бот := конт.Get("бот").Val().(ИБот)
-	log.Printf("НовБаза(): %q\n", бот.Имя())
+func НовБаза(конт ILocalCtx) *АренаБаза {
 	сам := &АренаБаза{
-		бот: бот,
+		конт:      конт,
+		счётОбнов: NewSafeInt(),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         бот,
+		Конт_:        конт,
 		АренаИмя_:    "База",
 		СтрКонтроль_: `<title>База</title>`,
 		ФнПуск_:      сам.пуск,
@@ -45,12 +46,33 @@ func НовБаза(конт ILocalCtx) ИАренаБаза {
 	}
 	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
 	конт.Set("база", сам, "База бота")
+	go сам.счётОбновВремя()
+	_ = ИАренаБаза(сам)
 	return сам
 }
 
+// Обновить -- обновляет базу (с кэшированием результата)
+func (сам *АренаБаза) Обновить() {
+	списСтр := сам.СписПолучить()
+	if len(списСтр) == 0 {
+		сам.ИАрена.Обновить()
+		сам.счётОбнов.Reset()
+		return
+	}
+	if сам.счётОбнов.Get() < 5000 {
+		return
+	}
+	сам.ИАрена.Обновить()
+}
+
+// ОбновитьПринуд -- обновляет базу принудительно (без учёта кэша)
+func (сам *АренаБаза) ОбновитьПринуд() {
+	сам.ИАрена.Обновить()
+	сам.счётОбнов.Reset()
+}
+
 // Запускает базу в обработку
 func (сам *АренаБаза) Пуск() {
-	log.Printf("Base.Run()\n")
 	if err := сам.runComponent(); err != nil {
 		panic(fmt.Errorf("Base.Run(): run, err=\n\t%w", err))
 	}
@@ -77,6 +99,15 @@ func (сам *АренаБаза) пуск() {
 	}
 }
 
+// Считает время до устаревания базы
+func (сам *АренаБаза) счётОбновВремя() {
+	for {
+		time.Sleep(time.Millisecond * 1000)
+		счёт := сам.счётОбнов.Get()
+		сам.счётОбнов.Set(счёт + 1000)
+	}
+}
+
 // Запускает компоненты
 func (сам *АренаБаза) runComponent() error {
 	log.Printf("Base.runComponent()\n")

+ 9 - 8
app/lev2/arena/arena_battle/arena_battle.go

@@ -16,7 +16,7 @@ import (
 // АренаСражение -- объект сражения
 type АренаСражение struct {
 	ИАрена
-	бот    ИБот
+	конт   ILocalCtx
 	клиент ИХттпВоркер
 
 	регистрация *battle_register.СхваткаРегистрация // Регистратор на сражение
@@ -26,22 +26,23 @@ type АренаСражение struct {
 }
 
 // НовСражение -- возвращает новый *Battle
-func НовСражение(конт ILocalCtx, бот ИБот) *АренаСражение {
+func НовСражение(конт ILocalCtx) *АренаСражение {
+	бот := конт.Get("бот").Val().(ИБот)
 	сам := &АренаСражение{
-		бот:    бот,
+		конт:   конт,
 		клиент: бот.Сеть().ВебВоркер(),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         бот,
+		Конт_:        конт,
 		АренаИмя_:    "Арена сражения",
 		СтрКонтроль_: "<span>до начала ",
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/pve",
 	}
 	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
-	сам.регистрация = battle_register.НовСражениеРегистрация(конт, бот)
-	сам.ожидание = battle_wait.НовСражениеОжидание(конт, бот)
-	сам.действие = battle_worker.НовСражениеИсполнитель(конт, бот)
+	сам.регистрация = battle_register.НовСражениеРегистрация(конт)
+	сам.ожидание = battle_wait.НовСражениеОжидание(конт)
+	сам.действие = battle_worker.НовСражениеИсполнитель(конт)
 
 	// сам.shotTimeFull.Set(8000) // 8000 msec
 	return сам
@@ -55,7 +56,7 @@ func (сам *АренаСражение) Пуск() {
 func (сам *АренаСражение) пуск() {
 	for {
 		select {
-		case <-сам.бот.КонтБот().Ctx().Done():
+		case <-сам.конт.Ctx().Done():
 			return
 		default:
 			сам.регистрация.Зарегистрироваться()

+ 4 - 4
app/lev2/arena/arena_battle/battle_register/battle_register.go

@@ -15,18 +15,18 @@ import (
 // СражениеРегистрация -- регистрирует танк к началу атаки
 type СхваткаРегистрация struct {
 	ИАрена
-	бот          ИБот
+	конт         ILocalCtx
 	счётРегистер int // Счётчик регистраций на сражение
 }
 
 // НовСражениеРегистрация -- возвращает новый ожидатель битвы
-func НовСражениеРегистрация(конт ILocalCtx, бот ИБот) *СхваткаРегистрация {
+func НовСражениеРегистрация(конт ILocalCtx) *СхваткаРегистрация {
 	сам := &СхваткаРегистрация{
-		бот:          бот,
+		конт:         конт,
 		счётРегистер: 10_000,
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         бот,
+		Конт_:        конт,
 		АренаИмя_:    "Сражение",
 		СтрКонтроль_: `<title>Сражения</title>`,
 		ФнПуск_:      сам.пуск,

+ 7 - 6
app/lev2/arena/arena_battle/battle_wait/battle_wait.go

@@ -10,27 +10,28 @@ import (
 	. "wartank/app/lev0/alias"
 	. "wartank/app/lev0/types"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
 )
 
 // СражениеОжидание -- ожидатель начала битвы
 type СхваткаОжидание struct {
-	ИАрена
-	бот ИБот
+	ИАренаСтроение
+	конт ILocalCtx
 }
 
 // НовСражениеОжидание -- возвращает новый ожидатель битвы
-func НовСражениеОжидание(конт ILocalCtx, бот ИБот) *СхваткаОжидание {
+func НовСражениеОжидание(конт ILocalCtx) *СхваткаОжидание {
 	сам := &СхваткаОжидание{
-		бот: бот,
+		конт: конт,
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         бот,
+		Конт_:        конт,
 		АренаИмя_:    "Ожидание сражения",
 		СтрКонтроль_: `<title>Сражения</title>`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/pve",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 	return сам
 }
 

+ 3 - 5
app/lev2/arena/arena_battle/battle_worker/battle_worker.go

@@ -17,7 +17,6 @@ import (
 type СхваткаИсполнитель struct {
 	ИАрена
 	конт ILocalCtx
-	бот  ИБот
 
 	еслиНачало ИСтатПарам
 
@@ -28,15 +27,14 @@ type СхваткаИсполнитель struct {
 }
 
 // НовСражениеДействие -- возвращает новый исполнитель битвы
-func НовСражениеИсполнитель(конт ILocalCtx, bot ИБот) *СхваткаИсполнитель {
+func НовСражениеИсполнитель(конт ILocalCtx) *СхваткаИсполнитель {
 	сам := &СхваткаИсполнитель{
 		конт:       конт,
-		бот:        bot,
 		еслиНачало: lev1.НовСтатПарам("тревога"),
 		sound:      battle_sound.NewBattleSound(),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         bot,
+		Конт_:        конт,
 		АренаИмя_:    "Ход сражения",
 		СтрКонтроль_: `<title>Сражения</title>`,
 		ФнПуск_:      сам.пуск,
@@ -48,7 +46,7 @@ func НовСражениеИсполнитель(конт ILocalCtx, bot ИБо
 
 // выполняет битву
 func (сам *СхваткаИсполнитель) пуск() {
-	сам.действие = battle_worker.НовСражениеДействие(сам.конт, сам.бот) // IBattleOn (онлайн)
+	сам.действие = battle_worker.НовСражениеДействие(сам.конт) // IBattleOn (онлайн)
 	сам.sound.Play()
 	time.Sleep(time.Second * 10) // Задержка для звука на странице
 	<-сам.действие.Контекст().Done()

+ 6 - 6
app/lev2/arena/arena_battle/battle_worker/battle_worker/battle_worker.go

@@ -21,7 +21,7 @@ import (
 // СражениеДействие -- непосредственно танкует в сражении
 type СражениеДействие struct {
 	ИАрена
-	бот        ИБот
+	конт       ILocalCtx
 	кнт        context.Context // Контекст сражения
 	фнОтменить func()          // Функция отмены сражения
 
@@ -33,18 +33,18 @@ type СражениеДействие struct {
 }
 
 // НовСражениеДействие -- возвращает новый *BattleOn
-func НовСражениеДействие(конт ILocalCtx, бот ИБот) ИСражениеПроцесс {
+func НовСражениеДействие(конт ILocalCtx) ИСражениеПроцесс {
 	// Ограничить время сражения бота
-	кнтСражение, фнОтменить := context.WithTimeout(бот.КонтБот().Ctx(), time.Second*305)
+	кнтСражение, фнОтменить := context.WithTimeout(конт.Ctx(), time.Second*305)
 	сам := &СражениеДействие{
-		бот:        бот,
+		конт:       конт,
 		кнт:        кнтСражение,
 		фнОтменить: фнОтменить,
-		логин:      бот.Имя(),
+		логин:      конт.Get("бот_имя").Val().(string),
 		еслиКонец:  NewSafeBool(),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         сам.бот,
+		Конт_:        конт,
 		АренаИмя_:    "Исполнитель сражения",
 		СтрКонтроль_: `<title>Сражения</title>`,
 		ФнПуск_:      сам.пуск,

+ 5 - 6
app/lev2/arena/arena_battle/battle_worker/battleon/battleon.go

@@ -21,7 +21,7 @@ import (
 // СражениеДействие -- непосредственно танкует в сражении
 type СражениеДействие struct {
 	ИАрена
-	бот        ИБот
+	конт       ILocalCtx
 	кнт        context.Context // Контекст сражения
 	фнОтменить func()          // Функция отмены сражения
 
@@ -35,17 +35,16 @@ type СражениеДействие struct {
 // НовСражениеДействие -- возвращает новый *BattleOn
 func НовСражениеДействие(конт ILocalCtx) ИСражениеПроцесс {
 	// Ограничить время сражения бота
-	бот := конт.Get("бот").Val().(ИБот)
-	кнтСражение, фнОтменить := context.WithTimeout(бот.КонтБот().Ctx(), time.Second*305)
+	кнтСражение, фнОтменить := context.WithTimeout(конт.Ctx(), time.Second*305)
 	сам := &СражениеДействие{
-		бот:        бот,
+		конт:       конт,
 		кнт:        кнтСражение,
 		фнОтменить: фнОтменить,
-		логин:      бот.Имя(),
+		логин:      конт.Get("бот_имя").Val().(string),
 		еслиКонец:  NewSafeBool(),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         сам.бот,
+		Конт_:        конт,
 		АренаИмя_:    "Исполнитель сражения",
 		СтрКонтроль_: `<title>Сражения</title>`,
 		ФнПуск_:      сам.пуск,

+ 54 - 0
app/lev2/arena/arena_build/arena_build.go

@@ -0,0 +1,54 @@
+// package arena_build -- арена-строение (что-то производит, есть время производства)
+package arena_build
+
+import (
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	. "wartank/app/lev0/alias"
+	. "wartank/app/lev0/types"
+	"wartank/app/lev1"
+	"wartank/app/lev2/arena"
+	"wartank/app/lev1/down_time"
+)
+
+// АренаСтроение -- арена-строение (что-то производит, есть время производства)
+type АренаСтроение struct {
+	ИАрена
+	времяОстат    ИВремяОстат // Обратный отсчёт до окончания работы режима
+	продуктВремя  ISafeString
+	продуктСейчас ИСтатПарам
+}
+
+// НовАренаСтроение -- возвращает новую арену-строение
+func НовАренаСтроение(конт ILocalCtx, аренаКонфиг arena.АренаКонфиг) *АренаСтроение {
+	сам := &АренаСтроение{
+		ИАрена:        arena.НовАрена(конт, аренаКонфиг),
+		времяОстат:    down_time.НовВремОбрат(конт, 5),
+		продуктВремя:  NewSafeString(),
+		продуктСейчас: lev1.НовСтатПарам("не задано"),
+	}
+	_ = ИАренаСтроение(сам)
+	return сам
+}
+
+// ПродуктСейчас -- возвращает продукт, производимый сейчас
+func (сам *АренаСтроение) ПродуктСейчас() ИСтатПарам {
+	return сам.продуктСейчас
+}
+
+// ОбратВремяУст -- устанавливает новое значение обратного счётчика времени
+func (сам *АренаСтроение) ОбратВремяУст(времяСек АВремя) {
+	сам.времяОстат.Уст(времяСек)
+}
+
+// ВремяОстат -- объект оставшегося времени
+func (сам *АренаСтроение) ВремяОстат() ИВремяОстат {
+	return сам.времяОстат
+}
+
+// ВремяОстат -- объект оставшегося времени
+func (сам *АренаСтроение) ПродуктВремяСейчас() ISafeString {
+	сам.продуктВремя.Set(сам.времяОстат.String())
+	return сам.продуктВремя
+}

+ 1 - 1
app/lev2/arena/arena_context/arena_context.go

@@ -51,7 +51,7 @@ func (сам *АренаКонтекст) ВебЛог() ИВебЛог {
 }
 
 // АренаСостояние -- состояние арены
-func (сам *АренаКонтекст) АренаСостояние() ИАренаСостояние {
+func (сам *АренаКонтекст) Состояние() ИАренаСостояние {
 	return сам.состояние
 }
 

+ 18 - 13
app/lev2/arena/arena_context/arena_state/arena_state.go

@@ -32,17 +32,23 @@ func (сам *АренаСостояние) Уст(состояние alias.АА
 	defer сам.block.Unlock()
 	switch сам.знач {
 	case cons.РежимНеСуществует:
-		if состояние != cons.РежимАпгрейдПлатный {
-			Hassert(false, "АренаСостояние.Уст(): нельзя из не существует перейти в '%v'", состояние)
+		if состояние != cons.РежимПостроено {
+			Hassert(false, "АренаСостояние.Уст(): нельзя из 'не существует' перейти в '%v'", состояние)
 		}
-	case cons.РежимАпгрейд:
-		if состояние == cons.РежимАпгрейдПлатный {
+	case cons.РежимПостроено:
+		switch состояние {
+		case cons.РежимАпгрейд, cons.РежимАпгрейдПлатный, cons.РежимОжидание, cons.РежимРабота:
 			сам.знач = состояние
-			return
+		default:
+			Hassert(false, "АренаСостояние.Уст(): нельзя из 'построено' перейти в '%v'", состояние)
 		}
-		if состояние == cons.РежимОжидание {
-			сам.знач = состояние
+	case cons.РежимАпгрейд:
+		switch состояние{
+		case cons.РежимАпгрейдПлатный, cons.РежимОжидание:
+						сам.знач = состояние
 			return
+		default:
+			Hassert(false, "АренаСостояние.Уст(): нельзя из 'апгрейд' перейти в '%v'", состояние)
 		}
 	case cons.РежимАпгрейдПлатный:
 		if состояние == cons.РежимОжидание {
@@ -63,13 +69,12 @@ func (сам *АренаСостояние) Уст(состояние alias.АА
 			return
 		}
 	case cons.РежимРабота:
-		if состояние == cons.РежимОжидание {
-			сам.знач = состояние
-			return
+		if состояние != cons.РежимЗабрать {
+			Hassert(false, "АренаСостояние.Уст(): нельзя из 'работа' перейти в '%v'", состояние)
 		}
-		if состояние == cons.РежимАпгрейдПлатный {
-			сам.знач = состояние
-			return
+	case cons.РежимЗабрать:
+		if состояние != cons.РежимОжидание {
+			Hassert(false, "АренаСостояние.Уст(): нельзя из 'забрать' перейти в '%v'", состояние)
 		}
 	default:
 		Hassert(false, "АренаСостояние.Уст(): нельзя из '%v' перейти в '%v'", сам.знач, состояние)

+ 16 - 171
app/lev2/arena/arena_convoy/arena_convoy.go

@@ -1,16 +1,18 @@
 package arena_convoy
 
 import (
-	"strconv"
 	"strings"
-	"time"
 
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
 
 	. "wartank/app/lev0/alias"
 	. "wartank/app/lev0/types"
-	"wartank/app/lev1"
+
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
+	"wartank/app/lev2/arena/arena_convoy/bf_glory_find"
+	"wartank/app/lev2/arena/arena_convoy/bf_glory_make"
+	"wartank/app/lev2/arena/arena_convoy/bf_glory_take"
 )
 
 /*
@@ -19,101 +21,38 @@ import (
 
 // АренаКонвой -- объект конвоя в ангаре
 type АренаКонвой struct {
-	ИАрена
-	бот   ИБот
-	слава ИСтатПарам // Количество славы
+	ИАренаСтроение
+	конт ILocalCtx
 }
 
 // НовКонвой -- возвращает новый *Convoy
 func НовКонвой(конт ILocalCtx) *АренаКонвой {
 	сам := &АренаКонвой{
-		бот:   конт.Get("бот").Val().(ИБот),
-		слава: lev1.НовСтатПарам("слава"),
+		конт: конт,
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         сам.бот,
+		Конт_:        конт,
 		АренаИмя_:    "Конвой",
 		СтрКонтроль_: `<title>Конвой</title>`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/convoy",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 	конт.Set("конвой", сам, "Арена конвоя бота")
 	_ = ИАренаКонвой(сам)
 	return сам
 }
 
-// Слава -- возвращает славу конвоя
-func (сам *АренаКонвой) Слава() ИСтатПарам {
-	return сам.слава
-}
-
-// UpdateLst -- принудительно обновляет состояние конвоя
-func (сам *АренаКонвой) UpdateLst() {
+func (сам *АренаКонвой) Пуск() {
 	сам.Обновить()
+	bf_glory_find.СлаваНайти(сам.конт)
+	bf_glory_make.СлаваБой(сам.конт)
+	bf_glory_take.СлаваВзять(сам.конт)
+	сам.обновитьВремя()
 }
 
 // обрабатывает конвой
-func (сам *АренаКонвой) пуск() {
-	фнЦикл := func() {
-		сам.проверитьМиссия6фрагов()
-		сам.проверитьМиссияРазведкаКонвой()
-		сам.проверитьМиссияМастерРазведки()
-		сам.призыВзять()
-	}
-	for {
-		select {
-		case <-сам.бот.КонтБот().Ctx().Done():
-			return
-		default:
-			фнЦикл()
-			time.Sleep(time.Second * 3)
-			фнЦикл() // Для второго шага
-			time.Sleep(time.Minute * 20)
-		}
-	}
-}
-
-func (сам *АренаКонвой) призыВзять() {
-	if false {
-		сам.обновитьВремя()
-	}
-	for {
-		сам.проверитьМиссияРазведкаКонвой()
-		сам.проверитьМиссияМастерРазведки()
-		сам.проверитьМиссия6фрагов()
-		сам.Обновить()
-	}
-}
-
-// Обновляет славу по требованию
-func (сам *АренаКонвой) Обновить() {
-	// Найти строку с упоминанием оставшегося времени конвоя
-	lstConvoy := сам.СписПолучить()
-	var (
-		strGlory    string
-		еслиНайдено bool
-	)
-	for _, lastTime := range lstConvoy {
-		if strings.Contains(lastTime, `alt="Слава" title="Слава"> `) {
-			strGlory = lastTime
-			еслиНайдено = true
-			break
-		}
-	}
-	if !еслиНайдено { // Не найдена строка со славой -- это атака
-		return
-	}
-	// Ищем количество славы
-	lstGlory := strings.Split(strGlory, `alt="Слава" title="Слава"> `)
-	strGlory = lstGlory[1]
-	iGlory, err := strconv.Atoi(strGlory)
-	if err != nil {
-		// log._rintf("ERRO ConvoyNet.updateGlory(): слава(%v) не число, err=\n\t%v\n", strGlory, err)
-		return
-	}
-	сам.слава.Уст(iGlory)
-}
+func (сам *АренаКонвой) пуск() {}
 
 // Обновляет оставшееся время конвоя
 func (сам *АренаКонвой) обновитьВремя() {
@@ -163,97 +102,3 @@ func (сам *АренаКонвой) обновитьВремя() {
 		сам.ОбратВремяУст(АВремя(strLastTime))
 	}
 }
-
-// Забирает награду в конвое "Активируй боевую силу"
-func (сам *АренаКонвой) проверитьМиссияРазведкаКонвой() {
-	var (
-		strOut      string
-		еслиНайдено bool
-	)
-	сам.Сеть().Обновить()
-	lstConvoy := сам.СписПолучить()
-	// <a class="simple-but border" href="convoy?21-1.ILinkListener-missions-cc-0-c-awardLink"><span><span>Получить награду</span></span></a>
-	for _, strOut = range lstConvoy {
-		if strings.Contains(strOut, `.ILinkListener-missions-cc-0-c-awardLink`) {
-			еслиНайдено = true
-			break
-		}
-	}
-	if !еслиНайдено {
-		return
-	}
-	// <a class="simple-but border" href="convoy?21-1.ILinkListener-missions-cc-0-c-awardLink"><span><span>Получить награду</span></span></a>
-	_ссылка := strings.TrimPrefix(strOut, `<a class="simple-but border" href="`)
-	_ссылка = strings.TrimSuffix(_ссылка, `"><span><span>Получить награду</span></span></a>`)
-	// https://wartank.ru/convoy?23-1.ILinkListener-missions-cc-0-c-awardLink
-	ссылка := "https://wartank.ru/" + _ссылка
-	lstConvoy = сам.Сеть().ВебВоркер().Получ(ссылка)
-	сам.СтрОбновить(lstConvoy)
-}
-
-// Забирает награду в конвое "Мастер дозора"
-func (сам *АренаКонвой) проверитьМиссияМастерРазведки() {
-	var (
-		strOut      string
-		еслиНайдено bool
-		lstConvoy   = сам.СписПолучить()
-		ind         int
-	)
-	if len(lstConvoy) == 0 {
-		сам.Обновить()
-		lstConvoy = сам.СписПолучить()
-	}
-	for ind, strOut = range lstConvoy {
-		if strings.Contains(strOut, `Проведи разведку в конвое<br/>`) {
-			еслиНайдено = true
-			ind += 23
-			strOut = lstConvoy[ind]
-			break
-		}
-	}
-	if !еслиНайдено {
-		return
-	}
-	// <a class="simple-but border" href="convoy?61-1.ILinkListener-missions-cc-0-c-awardLink"><span><span>Получить награду</span></span></a>
-	if !strings.Contains(strOut, `ILinkListener-missions-cc-0-c-awardLink`) {
-		return
-	}
-	lstLink := strings.Split(strOut, `<a class="simple-but border" href="`)
-	strLink := lstLink[1]
-	lstLink = strings.Split(strLink, `"><span><span>Получить награду</span></span></a>`)
-	// https://wartank.ru/convoy?61-1.ILinkListener-missions-cc-0-c-awardLink
-	strLink = "https://wartank.ru/" + lstLink[0]
-	lstConvoy, err := сам.Сеть().Get(strLink)
-	if err != nil {
-		// log._rintf("ERRO Конвой.checkMaster(): при выполнении команды GET, err=\n\t%v\n", err)
-		return
-	}
-	сам.СтрОбновить(lstConvoy)
-}
-
-// Забирает награду в конвое "Уничтожь 6 врагов в конвое"
-func (сам *АренаКонвой) проверитьМиссия6фрагов() {
-	var (
-		strOut      string
-		еслиНайдено bool
-	)
-	сам.Обновить()
-	lstConvoy := сам.СписПолучить()
-	// <a class="simple-but border" href="convoy?8-1.ILinkListener-missions-cc-1-c-awardLink"><span><span>Получить награду</span></span></a>
-	for _, strOut = range lstConvoy {
-		if strings.Contains(strOut, `.ILinkListener-missions-cc-1-c-awardLink`) {
-			еслиНайдено = true
-			break
-		}
-	}
-	if !еслиНайдено {
-		return
-	}
-	// <a class="simple-but border" href="convoy?8-1.ILinkListener-missions-cc-1-c-awardLink"><span><span>Получить награду</span></span></a>
-	_ссылка := strings.TrimPrefix(strOut, `<a class="simple-but border" href="`)
-	_ссылка = strings.TrimSuffix(_ссылка, `"><span><span>Получить награду</span></span></a>`)
-	// https://wartank.ru/convoy?15-1.ILinkListener-missions-cc-1-c-awardLink
-	ссылка := "https://wartank.ru/" + _ссылка
-	lstConvoy = сам.Сеть().ВебВоркер().Получ(ссылка)
-	сам.СтрОбновить(lstConvoy)
-}

+ 44 - 0
app/lev2/arena/arena_convoy/bf_glory_find/bf_glory_find.go

@@ -0,0 +1,44 @@
+// package bf_glory_find -- поиск славы
+package bf_glory_find
+
+import (
+	"strconv"
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	. "wartank/app/lev0/types"
+)
+
+// СлаваНайти -- ищет славу бота
+func СлаваНайти(конт ILocalCtx) {
+	конвой := конт.Get("конвой").Val().(ИАренаКонвой)
+	// Найти строку с упоминанием оставшегося времени конвоя
+	lstConvoy := конвой.СписПолучить()
+	var (
+		strGlory    string
+		еслиНайдено bool
+	)
+	if len(lstConvoy) == 0 {
+		конвой.Обновить()
+		lstConvoy = конвой.СписПолучить()
+	}
+	for _, lastTime := range lstConvoy {
+		if strings.Contains(lastTime, `alt="Слава" title="Слава"> `) {
+			strGlory = lastTime
+			еслиНайдено = true
+			break
+		}
+	}
+	if !еслиНайдено { // Может не быть, если началась атака
+		return
+	}
+	// Ищем количество славы
+	// <img class="ico vm" src="/images/icons/glory.png?2" alt="Слава" title="Слава"> 167
+	strGlory = strings.TrimPrefix(strGlory, `<img class="ico vm" src="/images/icons/glory.png?2" alt="Слава" title="Слава"> `)
+	iGlory, err := strconv.Atoi(strGlory)
+	Hassert(err == nil, "СлаваНайти(): слава(%v) не число, err=\n\t%v\n", strGlory, err)
+	танкСтат := конт.Get("танкСтат").Val().(ИТанкСтат)
+	танкСтат.Слава().Уст(iGlory)
+}

+ 1 - 1
app/lev0/bfunc/bf_glory_make/bf_glory_make.go → app/lev2/arena/arena_convoy/bf_glory_make/bf_glory_make.go

@@ -26,7 +26,7 @@ func атакаНачать(конт ILocalCtx, strOut string) {
 	lstConvoy := конвой.Сеть().ВебВоркер().Получ(strLink)
 	конвой.СтрОбновить(lstConvoy)
 	начатьРазведку(конт)
-	конвой.ОбратВремяУст("01")
+	конвой.ПродуктВремяСейчас().Set("01")
 }
 
 func атакаПроверить(конт ILocalCtx) string {

+ 112 - 0
app/lev2/arena/arena_convoy/bf_glory_take/bf_glory_take.go

@@ -0,0 +1,112 @@
+// package bf_glory_take -- забирает призы
+package bf_glory_take
+
+import (
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	. "wartank/app/lev0/types"
+)
+
+// СлаваНайти -- берёт славу бота
+func СлаваВзять(конт ILocalCtx) {
+	проверитьМиссияРазведкаКонвой(конт)
+	проверитьМиссияМастерРазведки(конт)
+	проверитьМиссия6фрагов(конт)
+}
+
+// Забирает награду в конвое "Активируй боевую силу"
+func проверитьМиссияРазведкаКонвой(конт ILocalCtx) {
+	конвой := конт.Get("конвой").Val().(ИАренаКонвой)
+	var (
+		strOut      string
+		еслиНайдено bool
+	)
+	lstConvoy := конвой.СписПолучить()
+	// <a class="simple-but border" href="convoy?21-1.ILinkListener-missions-cc-0-c-awardLink"><span><span>Получить награду</span></span></a>
+	for _, strOut = range lstConvoy {
+		if strings.Contains(strOut, `.ILinkListener-missions-cc-0-c-awardLink`) {
+			еслиНайдено = true
+			break
+		}
+	}
+	if !еслиНайдено {
+		return
+	}
+	// <a class="simple-but border" href="convoy?21-1.ILinkListener-missions-cc-0-c-awardLink"><span><span>Получить награду</span></span></a>
+	_ссылка := strings.TrimPrefix(strOut, `<a class="simple-but border" href="`)
+	_ссылка = strings.TrimSuffix(_ссылка, `"><span><span>Получить награду</span></span></a>`)
+	// https://wartank.ru/convoy?23-1.ILinkListener-missions-cc-0-c-awardLink
+	ссылка := "https://wartank.ru/" + _ссылка
+	lstConvoy = конвой.Сеть().ВебВоркер().Получ(ссылка)
+	конвой.СтрОбновить(lstConvoy)
+}
+
+// Забирает награду в конвое "Мастер дозора"
+func проверитьМиссияМастерРазведки(конт ILocalCtx) {
+	конвой := конт.Get("конвой").Val().(ИАренаКонвой)
+	var (
+		strOut      string
+		еслиНайдено bool
+		lstConvoy   = конвой.СписПолучить()
+		ind         int
+	)
+	if len(lstConvoy) == 0 {
+		конвой.Обновить()
+		lstConvoy = конвой.СписПолучить()
+	}
+	for ind, strOut = range lstConvoy {
+		if strings.Contains(strOut, `Проведи разведку в конвое<br/>`) {
+			еслиНайдено = true
+			ind += 23
+			strOut = lstConvoy[ind]
+			break
+		}
+	}
+	if !еслиНайдено {
+		return
+	}
+	// <a class="simple-but border" href="convoy?61-1.ILinkListener-missions-cc-0-c-awardLink"><span><span>Получить награду</span></span></a>
+	if !strings.Contains(strOut, `ILinkListener-missions-cc-0-c-awardLink`) {
+		return
+	}
+	lstLink := strings.Split(strOut, `<a class="simple-but border" href="`)
+	strLink := lstLink[1]
+	lstLink = strings.Split(strLink, `"><span><span>Получить награду</span></span></a>`)
+	// https://wartank.ru/convoy?61-1.ILinkListener-missions-cc-0-c-awardLink
+	strLink = "https://wartank.ru/" + lstLink[0]
+	lstConvoy, err := конвой.Сеть().Get(strLink)
+	if err != nil {
+		// log._rintf("ERRO Конвой.checkMaster(): при выполнении команды GET, err=\n\t%v\n", err)
+		return
+	}
+	конвой.СтрОбновить(lstConvoy)
+}
+
+// Забирает награду в конвое "Уничтожь 6 врагов в конвое"
+func проверитьМиссия6фрагов(конт ILocalCtx) {
+	конвой := конт.Get("конвой").Val().(ИАренаКонвой)
+	var (
+		strOut      string
+		еслиНайдено bool
+	)
+	lstConvoy := конвой.СписПолучить()
+	// <a class="simple-but border" href="convoy?8-1.ILinkListener-missions-cc-1-c-awardLink"><span><span>Получить награду</span></span></a>
+	for _, strOut = range lstConvoy {
+		if strings.Contains(strOut, `.ILinkListener-missions-cc-1-c-awardLink`) {
+			еслиНайдено = true
+			break
+		}
+	}
+	if !еслиНайдено {
+		return
+	}
+	// <a class="simple-but border" href="convoy?8-1.ILinkListener-missions-cc-1-c-awardLink"><span><span>Получить награду</span></span></a>
+	_ссылка := strings.TrimPrefix(strOut, `<a class="simple-but border" href="`)
+	_ссылка = strings.TrimSuffix(_ссылка, `"><span><span>Получить награду</span></span></a>`)
+	// https://wartank.ru/convoy?15-1.ILinkListener-missions-cc-1-c-awardLink
+	ссылка := "https://wartank.ru/" + _ссылка
+	lstConvoy = конвой.Сеть().ВебВоркер().Получ(ссылка)
+	конвой.СтрОбновить(lstConvoy)
+}

+ 9 - 8
app/lev2/arena/arena_death/arena_death.go

@@ -16,7 +16,7 @@ import (
 // АренаСхватка -- объект схватки
 type АренаСхватка struct {
 	ИАрена
-	бот    ИБот
+	конт   ILocalCtx
 	клиент ИХттпВоркер
 
 	регистрация *death_register.СхваткаРегистрация // Регистратор на сражение
@@ -26,22 +26,23 @@ type АренаСхватка struct {
 }
 
 // НовСражение -- возвращает новый *Battle
-func НовСхватка(конт IKernelCtx, бот ИБот) *АренаСхватка {
+func НовСхватка(конт IKernelCtx) *АренаСхватка {
+	бот := конт.Get("бот").Val().(ИБот)
 	сам := &АренаСхватка{
-		бот:    бот,
+		конт:   конт,
 		клиент: бот.Сеть().ВебВоркер(),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         бот,
+		Конт_:        конт,
 		АренаИмя_:    "Арена схватки",
 		СтрКонтроль_: `<span>до начала `,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/dm",
 	}
 	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
-	сам.регистрация = death_register.НовСхваткаРегистрация(конт, бот)
-	сам.ожидание = death_wait.НовСражениеОжидание(конт, бот)
-	сам.действие = death_worker.НовСхваткаИсполнитель(конт, бот)
+	сам.регистрация = death_register.НовСхваткаРегистрация(конт)
+	сам.ожидание = death_wait.НовСражениеОжидание(конт)
+	сам.действие = death_worker.НовСхваткаИсполнитель(конт)
 	// сам.shotTimeFull.Set(8000) // 8000 msec
 	return сам
 }
@@ -55,7 +56,7 @@ func (сам *АренаСхватка) Пуск() error {
 func (сам *АренаСхватка) пуск() {
 	for {
 		select {
-		case <-сам.бот.КонтБот().Ctx().Done():
+		case <-сам.конт.Ctx().Done():
 			return
 		default:
 			сам.регистрация.Зарегистрироваться()

+ 4 - 4
app/lev2/arena/arena_death/death_register/death_register.go

@@ -15,18 +15,18 @@ import (
 // СхваткаРегистрация -- регистрирует танк к началу схватки
 type СхваткаРегистрация struct {
 	ИАрена
-	бот          ИБот
+	конт         ILocalCtx
 	счётРегистер int // Счётчик регистраций на сражение
 }
 
 // НовСхваткаРегистрация -- возвращает новый ожидатель битвы
-func НовСхваткаРегистрация(конт IKernelCtx, бот ИБот) *СхваткаРегистрация {
+func НовСхваткаРегистрация(конт IKernelCtx) *СхваткаРегистрация {
 	сам := &СхваткаРегистрация{
-		бот:          бот,
+		конт:         конт,
 		счётРегистер: 10_000,
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         бот,
+		Конт_:        конт,
 		АренаИмя_:    "Сражение",
 		СтрКонтроль_: `<title>Сражения</title>`,
 		ФнПуск_:      сам.пуск,

+ 8 - 8
app/lev2/arena/arena_death/death_wait/death_wait.go

@@ -5,32 +5,32 @@ import (
 	"strings"
 	"time"
 
-	. "wartank/app/lev0/alias"
 	. "wartank/app/lev0/types"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
 
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
 )
 
 // СражениеОжидание -- ожидатель начала битвы
 type СражениеОжидание struct {
-	ИАрена
-	бот ИБот
+	ИАренаСтроение
+	конт ILocalCtx
 }
 
 // НовСражениеОжидание -- возвращает новый ожидатель битвы
-func НовСражениеОжидание(конт IKernelCtx, бот ИБот) *СражениеОжидание {
+func НовСражениеОжидание(конт IKernelCtx) *СражениеОжидание {
 	сам := &СражениеОжидание{
-		бот: бот,
+		конт: конт,
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         бот,
+		Конт_:        конт,
 		АренаИмя_:    "Ожидание сражения",
 		СтрКонтроль_: `<title>Сражения</title>`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/pve",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 	return сам
 }
 
@@ -105,6 +105,6 @@ func (сам *СражениеОжидание) ждать() string {
 	strTime := lstTime[1]
 	lstTime = strings.Split(strTime, ` (`)
 	strTime = lstTime[0]
-	сам.ОбратВремяУст(АВремя(strTime))
+	сам.ПродуктВремяСейчас().Set(strTime)
 	return strTime
 }

+ 3 - 5
app/lev2/arena/arena_death/death_worker/death_worker.go

@@ -17,7 +17,6 @@ import (
 type СхваткаИсполнитель struct {
 	ИАрена
 	конт ILocalCtx
-	бот  ИБот
 
 	еслиНачало ИСтатПарам
 
@@ -28,15 +27,14 @@ type СхваткаИсполнитель struct {
 }
 
 // НовСражениеДействие -- возвращает новый исполнитель схватки
-func НовСхваткаИсполнитель(конт ILocalCtx, bot ИБот) *СхваткаИсполнитель {
+func НовСхваткаИсполнитель(конт ILocalCtx) *СхваткаИсполнитель {
 	сам := &СхваткаИсполнитель{
 		конт:       конт,
-		бот:        bot,
 		еслиНачало: lev1.НовСтатПарам("тревога"),
 		sound:      battle_sound.NewBattleSound(),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         bot,
+		Конт_:        конт,
 		АренаИмя_:    "Ход сражения",
 		СтрКонтроль_: `<title>Сражения</title>`,
 		ФнПуск_:      сам.пуск,
@@ -53,7 +51,7 @@ func (сам *СхваткаИсполнитель) пуск() {
 
 // Танковать -- выполняет битву
 func (сам *СхваткаИсполнитель) Танковать() {
-	сам.действие = process_death.НовСхваткаПроцесс(сам.конт, сам.бот) // IBattleOn (онлайн)
+	сам.действие = process_death.НовСхваткаПроцесс(сам.конт) // IBattleOn (онлайн)
 	сам.sound.Play()
 	time.Sleep(time.Second * 10) // Задержка для звука на странице
 	<-сам.действие.Контекст().Done()

+ 4 - 6
app/lev2/arena/arena_death/death_worker/process_death/process_death.go

@@ -21,7 +21,6 @@ import (
 type СхваткаПроцесс struct {
 	ИСражениеПроцесс
 	арена      ИАрена
-	бот        ИБот
 	кнт        context.Context // Контекст сражения
 	фнОтменить func()          // Функция отмены сражения
 
@@ -32,17 +31,16 @@ type СхваткаПроцесс struct {
 }
 
 // НовСхваткаПроцесс -- возвращает новый процесс схватки
-func НовСхваткаПроцесс(конт ILocalCtx, бот ИБот) *СхваткаПроцесс {
+func НовСхваткаПроцесс(конт ILocalCtx) *СхваткаПроцесс {
 	// Ограничить время сражения бота
-	кнтСражение, фнОтменить := context.WithTimeout(бот.КонтБот().Ctx(), time.Second*305)
+	кнтСражение, фнОтменить := context.WithTimeout(конт.Ctx(), time.Second*305)
 	сам := &СхваткаПроцесс{
-		бот:        бот,
 		кнт:        кнтСражение,
 		фнОтменить: фнОтменить,
-		логин:      бот.Имя(),
+		логин:      конт.Get("бот_имя").Val().(string),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         сам.бот,
+		Конт_:        конт,
 		АренаИмя_:    "Исполнитель схватки",
 		СтрКонтроль_: `<title>Схватка</title>`,
 		ФнПуск_:      сам.пуск,

+ 16 - 12
app/lev2/arena/arena_division/div_war/div_war.go

@@ -10,6 +10,7 @@ import (
 	. "wartank/app/lev0/types"
 	"wartank/app/lev1"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
 	"wartank/app/lev2/arena/arena_division/div_war/div_war_net"
 	"wartank/app/lev2/arena/arena_division/div_war/process_divwar"
 	"wartank/app/lev2/arena/arena_division/div_war/process_divwar/div_war_sound"
@@ -23,9 +24,8 @@ import (
 
 // DivWar -- объект ожидания битвы дивизий
 type DivWar struct {
-	ИАрена
+	ИАренаСтроение
 	конт  ILocalCtx
-	bot   ИБот
 	alarm ИСтатПарам
 	net   *div_war_net.DivWarNet
 	conn  ИХттпВоркер
@@ -41,25 +41,23 @@ type DivWar struct {
 }
 
 // NewDivWar -- возвращает новый *DivWar
-func NewDivWar(конт IKernelCtx, bot ИБот) (*DivWar, error) {
-	if bot == nil {
-		return nil, fmt.Errorf("NewDivWar(): IBot == nil")
-	}
+func NewDivWar(конт IKernelCtx) (*DivWar, error) {
+	bot := конт.Get("бот").Val().(ИБот)
 	сам := &DivWar{
-		bot:      bot,
+		конт:     конт,
 		alarm:    lev1.НовСтатПарам("тревога"),
 		chDivWar: make(chan int, 1),
 		sound:    div_war_sound.NewDivWarSound(),
 		conn:     bot.Сеть().ВебВоркер(),
-		логин:    "prospero tank",
+		логин:    bot.Имя(),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         bot,
+		Конт_:        конт,
 		АренаИмя_:    "Битва дивизий",
 		СтрКонтроль_: `<span>до начала `,
 		ФнПуск_:      сам.пуск,
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 	// сам.shotTimeFull.Set(8000) // 8000 msec
 	var err error
 	{ // Net
@@ -80,7 +78,7 @@ func (сам *DivWar) пуск() {
 func (сам *DivWar) резервТик() {
 	for {
 		select {
-		case <-сам.bot.КонтБот().Ctx().Done():
+		case <-сам.конт.Ctx().Done():
 			return
 		default:
 			ct0 := сам.ВремяОстат().ПолучМилСек()
@@ -102,7 +100,7 @@ func (сам *DivWar) run() {
 	сам.chDivWar <- 1
 	for {
 		select {
-		case <-сам.bot.КонтБот().Ctx().Done():
+		case <-сам.конт.Ctx().Done():
 			return
 		case <-сам.ВремяОстат().КаналСиг(): // Время обновить данные по сражению
 			сам.findTimeCount()
@@ -117,6 +115,12 @@ func (сам *DivWar) run() {
 			go сам.DivWar()              // Запустить цикл непосредственного сражения
 			time.Sleep(time.Second * 10) // Задержка для звука на странице
 			сам.alarm.Уст(0)
+		default:
+			врем:=сам.ВремяОстат().ПолучМилСек()
+			if врем>0{
+				continue
+			}
+
 		}
 	}
 }

+ 6 - 6
app/lev2/arena/arena_division/div_war/process_divwar/process_divwar.go

@@ -10,6 +10,7 @@ import (
 	"wartank/app/lev1/manevr"
 	"wartank/app/lev1/shot"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
 
 	. "gitp78su.ipnodns.ru/svi/kern"
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
@@ -21,8 +22,8 @@ import (
 
 // ПроцессДивизияВойна -- непосредственно танкует в сражении
 type ПроцессДивизияВойна struct {
-	ИАрена
-	бот            ИБот
+	ИАренаСтроение
+	конт           ILocalCtx
 	лог            ILogBuf
 	ctxDivWar      context.Context // Контекст сражения
 	fnCancelDivWar func()          // Функция отмены сражения
@@ -41,7 +42,6 @@ func НовПроцессДивизияВойна(конт ILocalCtx) ИСраж
 	бот := конт.Get("бот").Val().(ИБот)
 	ctxDivWar, fnCancelDivWar := context.WithTimeout(бот.КонтБот().Ctx(), time.Second*305)
 	сам := &ПроцессДивизияВойна{
-		бот:            бот,
 		лог:            лог,
 		ctxDivWar:      ctxDivWar,
 		fnCancelDivWar: fnCancelDivWar,
@@ -49,13 +49,13 @@ func НовПроцессДивизияВойна(конт ILocalCtx) ИСраж
 		isEnd:          NewSafeBool(),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         бот,
+		Конт_:        конт,
 		АренаИмя_:    "Сражение",
 		СтрКонтроль_: `<title>Сражения</title>`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/pve",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 	if сам.checkEnd() {
 		return nil
 	}
@@ -86,7 +86,7 @@ func (сам *ПроцессДивизияВойна) makeTick() {
 	}()
 	for !сам.isEnd.Get() {
 		select {
-		case <-сам.бот.КонтБот().Ctx().Done(): // Отмена контекста приложения
+		case <-сам.конт.Ctx().Done(): // Отмена контекста приложения
 			return
 		case <-сам.ctxDivWar.Done(): // Битва закончилась
 			return

+ 9 - 10
app/lev2/arena/arena_division/divwar/div_war_on/div_war_on.go

@@ -11,6 +11,7 @@ import (
 	"wartank/app/lev1/manevr"
 	"wartank/app/lev1/shot"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
 
 	. "gitp78su.ipnodns.ru/svi/kern"
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
@@ -22,8 +23,8 @@ import (
 
 // DivWarOn -- непосредственно танкует в сражении
 type DivWarOn struct {
-	ИАрена
-	bot            ИБот
+	ИАренаСтроение
+	конт           ILocalCtx
 	ctxDivWar      context.Context // Контекст сражения
 	fnCancelDivWar func()          // Функция отмены сражения
 
@@ -36,26 +37,24 @@ type DivWarOn struct {
 }
 
 // NewDivWarOn -- возвращает новый *DivWarOn
-func NewDivWarOn(конт IKernelCtx, bot ИБот) (*DivWarOn, error) {
-	if bot == nil {
-		return nil, fmt.Errorf("NewDivWarOn(): IBot == nil")
-	}
+func NewDivWarOn(конт IKernelCtx) (*DivWarOn, error) {
+	bot := конт.Get("бот").Val().(ИБот)
 	ctxDivWar, fnCancelDivWar := context.WithTimeout(bot.КонтБот().Ctx(), time.Second*305)
 	сам := &DivWarOn{
-		bot:            bot,
+		конт:           конт,
 		ctxDivWar:      ctxDivWar,
 		fnCancelDivWar: fnCancelDivWar,
 		login:          bot.Имя(),
 		isEnd:          NewSafeBool(),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         bot,
+		Конт_:        конт,
 		АренаИмя_:    "Сражение",
 		СтрКонтроль_: `<title>Сражения</title>`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/pve",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 	if сам.checkEnd() {
 		return nil, fmt.Errorf("NewDivWarOn(): нет страницы для сражения")
 	}
@@ -76,7 +75,7 @@ func (сам *DivWarOn) makeTick() {
 	}()
 	for !сам.isEnd.Get() {
 		select {
-		case <-сам.bot.КонтБот().Ctx().Done(): // Отмена контекста приложения
+		case <-сам.конт.Ctx().Done(): // Отмена контекста приложения
 			return
 		case <-сам.ctxDivWar.Done(): // Битва закончилась
 			return

+ 9 - 12
app/lev2/arena/arena_division/divwar/divwar.go

@@ -12,6 +12,7 @@ import (
 	. "wartank/app/lev0/types"
 	"wartank/app/lev1"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
 	"wartank/app/lev2/arena/arena_division/div_war/div_war_net"
 	"wartank/app/lev2/arena/arena_division/div_war/process_divwar"
 	"wartank/app/lev2/arena/arena_division/div_war/process_divwar/div_war_sound"
@@ -23,9 +24,8 @@ import (
 
 // DivWar -- объект ожидания битвы дивизий
 type DivWar struct {
-	ИАрена
+	ИАренаСтроение
 	конт  ILocalCtx
-	bot   ИБот
 	alarm ИСтатПарам
 	net   *div_war_net.DivWarNet
 	conn  ИХттпВоркер
@@ -41,26 +41,23 @@ type DivWar struct {
 }
 
 // NewDivWar -- возвращает новый *DivWar
-func NewDivWar(бот ИБот) (*DivWar, error) {
-	if бот == nil {
-		return nil, fmt.Errorf("NewDivWar(): IBot == nil")
-	}
+func NewDivWar(конт ILocalCtx) (*DivWar, error) {
+	бот := конт.Get("бот").Val().(ИБот)
 	сам := &DivWar{
 		конт:     бот.КонтБот(),
-		bot:      бот,
 		alarm:    lev1.НовСтатПарам("тревога"),
 		chDivWar: make(chan int, 1),
 		sound:    div_war_sound.NewDivWarSound(),
 		conn:     бот.Сеть().ВебВоркер(),
-		login:    "prospero tank",
+		login:    бот.Имя(),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         бот,
+		Конт_:        конт,
 		АренаИмя_:    "Битва дивизий",
 		СтрКонтроль_: `<span>до начала `,
 		ФнПуск_:      сам.пуск,
 	}
-	сам.ИАрена = arena.НовАрена(сам.конт, аренаКонфиг)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(сам.конт, аренаКонфиг)
 	// сам.shotTimeFull.Set(8000) // 8000 msec
 	var err error
 	{ // Net
@@ -81,7 +78,7 @@ func (сам *DivWar) пуск() {
 func (сам *DivWar) резервТик() {
 	for {
 		select {
-		case <-сам.bot.КонтБот().Ctx().Done():
+		case <-сам.конт.Ctx().Done():
 			return
 		default:
 			ct0 := сам.ВремяОстат().ПолучМилСек()
@@ -103,7 +100,7 @@ func (сам *DivWar) run() {
 	сам.chDivWar <- 1
 	for {
 		select {
-		case <-сам.bot.КонтБот().Ctx().Done():
+		case <-сам.конт.Ctx().Done():
 			return
 		case <-сам.ВремяОстат().КаналСиг(): // Время обновить данные по сражению
 			сам.findTimeCount()

+ 10 - 11
app/lev2/arena/arena_division/divwar/divwaron/divwaron.go

@@ -11,6 +11,7 @@ import (
 	"wartank/app/lev1/manevr"
 	"wartank/app/lev1/shot"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
 
 	. "gitp78su.ipnodns.ru/svi/kern"
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
@@ -22,8 +23,8 @@ import (
 
 // DivWarOn -- непосредственно танкует в сражении
 type DivWarOn struct {
-	ИАрена
-	bot            ИБот
+	ИАренаСтроение
+	конт           ILocalCtx
 	ctxDivWar      context.Context // Контекст сражения
 	fnCancelDivWar func()          // Функция отмены сражения
 
@@ -37,13 +38,11 @@ type DivWarOn struct {
 }
 
 // NewDivWarOn -- возвращает новый *DivWarOn
-func NewDivWarOn(конт ILocalCtx, bot ИБот) (*DivWarOn, error) {
-	if bot == nil {
-		return nil, fmt.Errorf("NewDivWarOn(): IBot == nil")
-	}
-	ctxDivWar, fnCancelDivWar := context.WithTimeout(bot.КонтБот().Ctx(), time.Second*305)
+func NewDivWarOn(конт ILocalCtx) (*DivWarOn, error) {
+	bot := конт.Get("бот").Val().(ИБот)
+	ctxDivWar, fnCancelDivWar := context.WithTimeout(конт.Ctx(), time.Second*305)
 	сам := &DivWarOn{
-		bot:            bot,
+		конт:           конт,
 		ctxDivWar:      ctxDivWar,
 		fnCancelDivWar: fnCancelDivWar,
 		login:          bot.Имя(),
@@ -51,13 +50,13 @@ func NewDivWarOn(конт ILocalCtx, bot ИБот) (*DivWarOn, error) {
 		isEnd:          NewSafeBool(),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         bot,
+		Конт_:        конт,
 		АренаИмя_:    "Сражение",
 		СтрКонтроль_: `<title>Сражения</title>`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/pve",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 	if сам.checkEnd() {
 		return nil, fmt.Errorf("NewDivWarOn(): нет страницы для сражения")
 	}
@@ -78,7 +77,7 @@ func (сам *DivWarOn) makeTick() {
 	}()
 	for !сам.isEnd.Get() {
 		select {
-		case <-сам.bot.КонтБот().Ctx().Done(): // Отмена контекста приложения
+		case <-сам.конт.Ctx().Done(): // Отмена контекста приложения
 			return
 		case <-сам.ctxDivWar.Done(): // Битва закончилась
 			return

+ 0 - 53
app/lev2/arena/arena_duel/arena_duel.go

@@ -1,53 +0,0 @@
-// package arena_duel -- арена дуэли за топливо
-package arena_duel
-
-import (
-	"time"
-
-	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
-
-	. "wartank/app/lev0/types"
-	"wartank/app/lev2/arena"
-)
-
-// АренаДуэль -- арена дуэли за топливо
-type АренаДуэль struct {
-	ИАрена
-	бот    ИБот
-	клиент ИХттпВоркер
-}
-
-// НовСражение -- возвращает новую арену дуэли
-func НовАренаДуэль(конт ILocalCtx, бот ИБот) *АренаДуэль {
-	сам := &АренаДуэль{
-		бот:    бот,
-		клиент: бот.Сеть().ВебВоркер(),
-	}
-	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         бот,
-		АренаИмя_:    "Арена дуэли",
-		СтрКонтроль_: "<span>до начала ",
-		ФнПуск_:      сам.пуск,
-		СтрУрл_:      "https://wartank.ru/battle",
-	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
-
-	// сам.shotTimeFull.Set(8000) // 8000 msec
-	return сам
-}
-
-func (сам *АренаДуэль) Пуск() {
-	go сам.пуск()
-}
-
-// запускает в работу дуэль
-func (сам *АренаДуэль) пуск() {
-	for {
-		select {
-		case <-сам.бот.КонтБот().Ctx().Done():
-			return
-		default:
-			time.Sleep(time.Second * 2) // Пауза между циклами, чтобы сервер не долбить запросами
-		}
-	}
-}

+ 0 - 39
app/lev2/arena/arena_fuel/arena_fuel.go

@@ -1,39 +0,0 @@
-// package arena_fuel -- арена боя на топливе
-package arena_fuel
-
-import (
-	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
-
-	. "wartank/app/lev0/types"
-	"wartank/app/lev2/arena"
-)
-
-// АренаТопливо -- арена боя на топливе
-type АренаТопливо struct {
-	ИАрена
-	ангар  ИАренаАнгар
-	клиент ИХттпВоркер
-}
-
-// НовАренаТопливо -- возвращает новую арену топливного боя
-func НовАренаТопливо(конт ILocalCtx) *АренаТопливо {
-	хттпВоркер := конт.Get("хттпВоркер").Val().(ИХттпВоркер)
-	сам := &АренаТопливо{
-		ангар:  конт.Get("ангар").Val().(ИАренаАнгар),
-		клиент: хттпВоркер,
-	}
-
-	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         конт.Get("бот").Val().(ИБот),
-		АренаИмя_:    "ТопливоБой",
-		СтрКонтроль_: `<title>Бой</title>`,
-		ФнПуск_:      сам.пуск,
-		СтрУрл_:      "https://wartank.ru/battle",
-	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
-	конт.Set("арена_топливо", сам, "Арена 'Бой за топливо'")
-	_ = ИАрена(сам)
-	return сам
-}
-
-func (сам *АренаТопливо) пуск() {}

+ 45 - 0
app/lev2/arena/arena_fuel_duel/arena_fuel_duel.go

@@ -0,0 +1,45 @@
+// package arena_fuel_duel -- арена дуэли за топливо
+package arena_fuel_duel
+
+import (
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
+	"wartank/app/lev2/arena/arena_fuel_duel/bf_fuel_duel"
+)
+
+// АренаТопливоДуэль -- арена дуэли за топливо
+type АренаТопливоДуэль struct {
+	ИАренаСтроение
+	конт   ILocalCtx
+	клиент ИХттпВоркер
+}
+
+// НовАренаТопливоДуэль -- возвращает новую арену дуэли
+func НовАренаТопливоДуэль(конт ILocalCtx) *АренаТопливоДуэль {
+	бот := конт.Get("бот").Val().(ИБот)
+	сам := &АренаТопливоДуэль{
+		конт:   конт,
+		клиент: бот.Сеть().ВебВоркер(),
+	}
+	аренаКонфиг := arena.АренаКонфиг{
+		Конт_:        конт,
+		АренаИмя_:    "Арена топливной дуэли",
+		СтрКонтроль_: "<title>Бой</title>",
+		ФнПуск_:      сам.пуск,
+		СтрУрл_:      "https://wartank.ru/battle",
+	}
+	конт.Set("арена_топливо_бой", сам, "Арена боя за топливо")
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
+	return сам
+}
+
+func (сам *АренаТопливоДуэль) Пуск() {
+	сам.Обновить()
+	bf_fuel_duel.ТопливоАтаковать(сам.конт)
+}
+
+// запускает в работу дуэль
+func (сам *АренаТопливоДуэль) пуск() {}

+ 11 - 62
app/lev0/bfunc/bf_fuel_attack/bf_fuel_attack.go → app/lev2/arena/arena_fuel_duel/bf_fuel_duel/bf_fuel_duel.go

@@ -1,11 +1,9 @@
-// package bf_fuel_attack -- бизнес-функция боя на топливе
-package bf_fuel_attack
+// package bf_fuel_duel -- бизнес-функция боя на топливе
+package bf_fuel_duel
 
 import (
 	"fmt"
-	"os"
 	"strings"
-	"time"
 
 	. "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
@@ -49,7 +47,7 @@ func начатьБой(конт ILocalCtx) []string {
 	бойСсылка := списАнгар[1]
 	списАнгар = strings.Split(бойСсылка, `"><span><span>В бой!</span></span></a>`)
 	бойСсылка = "https://wartank.ru/" + списАнгар[0]
-	арена := конт.Get("арена_топливо").Val().(ИАрена)
+	арена := конт.Get("арена_топливо_бой").Val().(ИАренаСтроение)
 	списСтрБой, err := арена.Сеть().Get(бойСсылка)
 	Hassert(err == nil, "начатьБой(): in make GET-request to battle, err=\n\t%v", err)
 	return списСтрБой
@@ -77,8 +75,8 @@ func выбратьБойСлабый(конт ILocalCtx, списСтрБой [
 		списСтрБой = strings.Split(ссылкаБой, `"><img class="tank-img" alt="tank" src="/tankimg?`)
 		ссылкаБой = "https://wartank.ru/" + списСтрБой[0]
 	}
-	арена := конт.Get("арена_топливо").Val().(ИАрена)
-	списСтрВыстрел1, err := арена.Сеть().Get(ссылкаБой)
+	аренаБой := конт.Get("арена_топливо_бой").Val().(ИАрена)
+	списСтрВыстрел1, err := аренаБой.Сеть().Get(ссылкаБой)
 	Hassert(err == nil, "makeSelectBattle(): in GET-response select battle tank, err=\n\t%v", err)
 	return списСтрВыстрел1
 }
@@ -87,37 +85,13 @@ func выбратьБойСлабый(конт ILocalCtx, списСтрБой [
 func сделатьВыстрелы(конт ILocalCtx, lstShoot2 []string) {
 	// _mt.Println("\tAngarNet.makeShooting()")
 	var списВыстрел3 []string // Тело страницы для третьего выстрела
-	арена := конт.Get("арена_топливо").Val().(ИАрена)
+	аренаБой := конт.Get("арена_топливо_бой").Val().(ИАрена)
 	ангар := конт.Get("ангар").Val().(ИАренаАнгар)
 	фнВыстрел2 := func() (err error) { // Второй выстрел
 		// _mt.Println("\tAngarNet.makeShooting().fnShoot2()")
 		defer func() {
-			if _panic := recover(); _panic != nil {
-				msg := time.Now().Local().Format("2006-01-02 15:04:05.000 ") + "fnShoot2\n"
-				msg += "\tNetClient.makeShooting().fnShoot2()\n"
-				msg0 := fmt.Sprintf("%v\n", _panic)
-				msg1 := ""
-				for _, _msg := range strings.Split(msg0, "\n") {
-					if _msg == "" {
-						continue
-					}
-					msg1 += "\t" + _msg + "\n"
-				}
-				msg += msg1
-				// _mt.Println(msg)
-				err = fmt.Errorf("%v", msg)
-				msg1 = "" // Сброс накопленной ошибки
-				for _, _msg := range lstShoot2 {
-					if _msg == "" {
-						continue
-					}
-					msg1 += "\t" + _msg + "\n"
-				}
-				msg += msg1
-				// Выкинуть ошибку в файл
-				_ = os.MkdirAll("./errors", 0700)
-				err = os.WriteFile("./errors/attack_shoot2.html", []byte(msg), 0600)
-			}
+
+			_ = recover()
 		}()
 		// Получить ссылку на второй выстрел
 		var strOut string
@@ -152,7 +126,7 @@ func сделатьВыстрелы(конт ILocalCtx, lstShoot2 []string) {
 			lstShoot2 = strings.Split(linkShoot2, `"><span><span>Добить</span></span></a>`)
 			linkShoot2 = "https://wartank.ru/" + lstShoot2[0]
 		}
-		_, err = арена.Сеть().Get(linkShoot2)
+		_, err = аренаБой.Сеть().Get(linkShoot2)
 		Hassert(err == nil, "сделатьВыстрелы(): in Get-response shoot2, err=\n\t%v", err)
 
 		fuel := ангар.Топливо().Получ()
@@ -165,32 +139,7 @@ func сделатьВыстрелы(конт ILocalCtx, lstShoot2 []string) {
 	фнВыстрел3 := func() (err error) { // Третий выстрел
 		// _mt.Println("\tAngarNet.makeShooting().fnShoot3()")
 		defer func() {
-			if _panic := recover(); _panic != nil {
-				msg := time.Now().Local().Format("2006-01-02 15:04:05.000 fnShoot3\n")
-				msg += "\tNetClient.makeShooting().fnShoot3()\n"
-				msg0 := fmt.Sprintf("%v\n", _panic)
-				msg1 := ""
-				for _, _msg := range strings.Split(msg0, "\n") {
-					if _msg == "" {
-						continue
-					}
-					msg1 += "\t" + _msg + "\n"
-				}
-				msg += msg1
-				// _mt.Println(msg)
-				err = fmt.Errorf("%v", msg)
-				msg1 = "" // Сброс накопленной ошибки
-				for _, _msg := range списВыстрел3 {
-					if _msg == "" {
-						continue
-					}
-					msg1 += "\t" + _msg + "\n"
-				}
-				msg += msg1
-				// Выкинуть ошибку в файл
-				_ = os.MkdirAll("./errors", 0700)
-				err = os.WriteFile("./errors/attack_shoot3.html", []byte(msg), 0600)
-			}
+			_ = recover()
 		}()
 		// Получить ссылку на третий выстрел
 		var strOut string
@@ -224,7 +173,7 @@ func сделатьВыстрелы(конт ILocalCtx, lstShoot2 []string) {
 			linkShoot3 = "https://wartank.ru/" + списВыстрел3[0]
 		}
 
-		if _, err = арена.Сеть().Get(linkShoot3); err != nil {
+		if _, err = аренаБой.Сеть().Get(linkShoot3); err != nil {
 			return fmt.Errorf("ТопливоБой.makeShooting(): in Get-response shoot3, err=\n\t%w", err)
 		}
 		fuel := ангар.Топливо().Получ()

+ 38 - 183
app/lev2/arena/arena_fuel_storage/arena_fuel_storage.go

@@ -2,7 +2,6 @@
 package arena_fuel_storage
 
 import (
-	"context"
 	"log"
 	"strconv"
 	"strings"
@@ -13,60 +12,60 @@ import (
 	. "wartank/app/lev0/types"
 	"wartank/app/lev1"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
+	"wartank/app/lev2/arena/arena_fuel_storage/bf_fuel_build"
+	"wartank/app/lev2/arena/arena_fuel_storage/bf_fuel_upgrade"
 )
 
 // АренаСкладТоплива -- склад топлива
-type АренаСкладТоплива struct {
-	ИАрена
-	бот          ИБот
-	база         ИАренаБаза
-	топливо      ИСтатПарам
-	уровень      ИСтатПарам
-	продуктВремя string          // Сколько осталось времени прямо сейчас
-	кнт          context.Context // контекст шахты
-	фнОтмена     func()          // Функция отмены шахты
+type АренаБак struct {
+	ИАренаСтроение
+	конт ILocalCtx
+	бак  ИСтатПарам
 }
 
 // НовТопливо -- возвращает новой склад топлива
-func НовСкладТоплива(конт ILocalCtx, база ИАренаБаза) (*АренаСкладТоплива, error) {
-	кнт, фнОтмена := context.WithCancel(база.Контекст())
-	сам := &АренаСкладТоплива{
-		бот:      база.Бот(),
-		база:     база,
-		топливо:  lev1.НовСтатПарам("топливо"),
-		кнт:      кнт,
-		фнОтмена: фнОтмена,
-		уровень:  lev1.НовСтатПарам("уровень"), // FIXME: вынести на уровень арены
+func НовАренаБак(конт ILocalCtx) *АренаБак {
+	сам := &АренаБак{
+		конт: конт,
+		бак:  lev1.НовСтатПарам("бак"),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         база.Бот(),
+		Конт_:        конт,
 		АренаИмя_:    "Склад_топлива",
-		СтрКонтроль_: `<span class="green2">Склад топлива - `,
+		СтрКонтроль_: `<title>Склад топлива</title>`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/fuelStore",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
-	_ = ИАренаТопливо(сам)
-	return сам, nil
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
+	конт.Set("бак", сам, "Топливная база")
+	_ = ИАренаБак(сам)
+	return сам
 }
 
-func (сам *АренаСкладТоплива) Пуск() {
-	go сам.пуск()
+func (сам *АренаБак) Пуск() {
+	фнОбновить := func() { // Когда арена не построена -- ничего не вернёт
+		defer func() {
+			_ = recover()
+		}()
+		сам.Обновить()
+	}
+	фнОбновить()
+	bf_fuel_build.БакПостроить(сам.конт)
+	bf_fuel_upgrade.БакАпгрейд(сам.конт)
 }
 
 // пуск -- запускает обработку шахты
-func (сам *АренаСкладТоплива) пуск() {
+func (сам *АренаБак) пуск() {
 	time.Sleep(time.Second * 3)
 	фнРабота := func() {
 		defer time.Sleep(time.Minute * 30)
-		for !сам.уровеньОбновить() {
-		}
 		сам.ускорениеПровер()
 		сам.количествоПолучить()
 	}
 	for {
 		select {
-		case <-сам.кнт.Done():
+		case <-сам.конт.Ctx().Done():
 			return
 		case <-сам.ВремяОстат().КаналСиг():
 		default:
@@ -77,7 +76,7 @@ func (сам *АренаСкладТоплива) пуск() {
 }
 
 // Проверяет количество продукта в шахте
-func (сам *АренаСкладТоплива) количествоПолучить() {
+func (сам *АренаБак) количествоПолучить() {
 	var (
 		strOut      string
 		еслиНайдено bool
@@ -101,11 +100,11 @@ func (сам *АренаСкладТоплива) количествоПолуч
 		log.Printf("АренаСкладТоплива.количествоПолучить(): кол-во топлива (%v) не число, err=\n\t%v\n", _число, err)
 		return
 	}
-	сам.топливо.Уст(iNum)
+	сам.бак.Уст(iNum)
 }
 
 // Проверяет ускорение строительства
-func (сам *АренаСкладТоплива) ускорениеПровер() {
+func (сам *АренаБак) ускорениеПровер() {
 	списСтр := сам.Сеть().ВебВоркер().Получ("http://wartank.ru/buildings")
 	// <span class="green2">Склад топлива -
 	var (
@@ -123,51 +122,9 @@ func (сам *АренаСкладТоплива) ускорениеПровер
 	}
 }
 
-// Обновляет текущий уровень шахты (может быть не построена)
-func (сам *АренаСкладТоплива) уровеньОбновить() bool {
-	списСтр := сам.Сеть().ВебВоркер().Получ("http://wartank.ru/buildings")
-	// <span class="green2">Склад топлива -
-	var (
-		еслиНайти = false
-		стр       = ""
-	)
-	for _, стр = range списСтр {
-		if strings.Contains(стр, `<span class="green2">Склад топлива - `) {
-			еслиНайти = true
-			break
-		}
-	}
-	if !еслиНайти {
-		return false
-	}
-	// <span class="green2">Склад топлива - 0</span><br/>
-	_стр := strings.TrimPrefix(стр, `<span class="green2">Склад топлива - `)
-	_стр = strings.TrimSuffix(_стр, `</span><br/>`)
-	иУровень, ош := strconv.Atoi(_стр)
-	if ош != nil {
-		log.Printf("АренаСкладТоплива.уровеньОбновить(): строка уровня сбойная, стр=%q, ош=\n\t%v\n", стр, ош)
-		return false
-	}
-	сам.уровень.Уст(иУровень)
-	switch иУровень {
-	case 0: // склад топлива надо построить
-		for !сам.построить() {
-		}
-	default: // Может можно проапгрейдить
-		счёт := 5
-		for !сам.проапгрейдить() {
-			счёт--
-			if счёт == 0 {
-				break
-			}
-		}
-	}
-
-	return true
-}
-
+/*
 // Пытается проапгрейдить топливный склад
-func (сам *АренаСкладТоплива) проапгрейдить() bool {
+func (сам *АренаБак) проапгрейдить() bool {
 	time.Sleep(time.Millisecond * 1000)
 	var (
 		еслиНайти = false
@@ -247,111 +204,9 @@ func (сам *АренаСкладТоплива) проапгрейдить() b
 	фнКомплекс()
 	return true
 }
+*/
 
-// Строит шахту при нулевом уровне
-func (сам *АренаСкладТоплива) построить() bool {
-	time.Sleep(time.Millisecond * 1000)
-	// <td style="width:50%;padding-left:1px;"><a class="simple-but border mb5" href="building-upgrade/FuelStorage"><span><span>Построить</span></span></a></td>
-	var (
-		еслиНайти = false
-		списСтр   []string
-		стр       = ""
-	)
-	фнКупить := func() bool {
-		defer time.Sleep(time.Millisecond * 1000)
-		списСтр = сам.Сеть().ВебВоркер().Получ("https://wartank.ru/building-upgrade/FuelStorage")
-		for _, стр = range списСтр {
-			// <a class="simple-but border mb5" href="FuelStorage?71-1.ILinkListener-upgradeLink-link">
-			if strings.Contains(стр, `ILinkListener-upgradeLink-link`) {
-				еслиНайти = true
-				break
-			}
-		}
-		if !еслиНайти {
-			return true
-		}
-		// Пробуем построить шахту
-		_стр := strings.TrimPrefix(стр, "<a class=\"simple-but border mb5\" href=\"")
-		_стр = strings.TrimSuffix(_стр, "\">")
-		// https://wartank.ru/building-upgrade/FuelStorage?49-1.ILinkListener-upgradeLink-link
-		// <a class="simple-but border mb5" href="FuelStorage?50-1.ILinkListener-upgradeLink-link">
-		// https://wartank.ru/building-upgrade/FuelStorage?72-1.ILinkListener-upgradeLink-link
-		ссылка := "https://wartank.ru/building-upgrade/" + _стр
-		списСтр = сам.Сеть().ВебВоркер().Получ(ссылка)
-		// Проверить, что постройка состоялась
-		for _, стр := range списСтр {
-			if strings.Contains(стр, "ILinkListener-upgradeLink-link") {
-				log.Printf("АренаСкладТоплива.построить().фнКупить(): покупка склада топлива не прошла\n\tlink=%v\n\tстр=\n\t%v\n", ссылка, стр)
-				return false // Покупка не оплачена
-			}
-		}
-		log.Printf("+++++АренаСкладТоплива.построить().фнКупить(): покупка склада топлива прошла\n")
-		return true
-	}
-
-	фнПодтверждение := func() bool {
-		for _, стр = range списСтр {
-			// <a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../wicket/page?5-1.ILinkListener-confirmLink"><span><span>да, подтверждаю</span></span></a>
-			if strings.Contains(стр, `ILinkListener-confirmLink`) {
-				еслиНайти = true
-				break
-			}
-		}
-		if !еслиНайти {
-			return true
-		}
-		// Пробуем построить шахту
-		_стр := strings.TrimPrefix(стр, `<a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="..`)
-		_стр = strings.TrimSuffix(_стр, `"><span><span>да, подтверждаю</span></span></a>`)
-		// https://wartank.ru/wicket/page?4-1.ILinkListener-confirmLink
-		// <a class="simple-but border mb5" href="FuelStorage?50-1.ILinkListener-upgradeLink-link">
-		ссылка := "https://wartank.ru" + _стр
-		списСтр = сам.Сеть().ВебВоркер().Получ(ссылка)
-		// Проверить, что постройка состоялась
-		for _, стр := range списСтр {
-			if strings.Contains(стр, "<title>Вы сделали слишком большую паузу</title>") {
-				log.Printf("АренаСкладТоплива.построить().фнПодтверждение(): подтверждение покупка склада топлива не прошла\n\tlink=%v\n\tстр=\n\t%v\n", ссылка, стр)
-				return false // Покупка не оплачена
-			}
-		}
-		log.Printf("+++++АренаСкладТоплива.построить().фнПодтверждение(): подтверждение покупка склада топлива прошла\n")
-		return true
-	}
-
-	фнКомплекс := func() {
-		for {
-			if фнКупить() {
-				if фнПодтверждение() {
-					break
-				}
-			}
-		}
-	}
-	фнКомплекс()
-	return true
-}
-
-// Уровень -- возвращает уровень топливного склада
-func (сам *АренаСкладТоплива) Уровень() ИСтатПарам {
-	return сам.уровень
-}
-
-// Топливо -- возвращает топливо
-func (сам *АренаСкладТоплива) Топливо() ИСтатПарам {
-	return сам.топливо
-}
-
-// ПродуктСейчас -- возвращает продукт прямо сейчас
-func (сам *АренаСкладТоплива) ПродуктСейчас() ИСтатПарам {
-	return сам.топливо
-}
-
-// ПродуктИмяСейчас -- возвращает имя продукта прямо сейчас
-func (сам *АренаСкладТоплива) ПродуктИмяСейчас() string {
-	return "топливо"
-}
-
-// ПродуктВремяСейчас -- возвращает сколько осталось времени прямо сейчас
-func (сам *АренаСкладТоплива) ПродуктВремяСейчас() string {
-	return сам.продуктВремя
+// Бак -- возвращает размер бака
+func (сам *АренаБак) Бак() ИСтатПарам {
+	return сам.бак
 }

+ 87 - 0
app/lev2/arena/arena_fuel_storage/bf_fuel_build/bf_fuel_build.go

@@ -0,0 +1,87 @@
+// package bf_fuel_build -- бизнес-функция строительства базы топлива
+package bf_fuel_build
+
+import (
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	"wartank/app/lev0/cons"
+	. "wartank/app/lev0/types"
+)
+
+// БакПостроить -- постройка базы топлива
+func БакПостроить(конт ILocalCtx) {
+	бак := конт.Get("бак").Val().(ИАренаБак)
+	if бак.Состояние().Получ() != cons.РежимНеСуществует {
+		return
+	}
+	бакПостроить(конт)
+}
+
+func бакПостроить(конт ILocalCtx) {
+	база := конт.Get("база").Val().(ИАренаБаза)
+	бак := конт.Get("бак").Val().(ИАренаБак)
+	списСтр := база.Сеть().ВебВоркер().Получ("https://wartank.ru/buildings")
+	ссыльПостроить := "" // ссылка на постройку
+
+	{ // Поиск ссылки на покупку
+		// <td style="width:50%;padding-left:1px;"><a class="simple-but border mb5" href="building-upgrade/FuelStorage"><span><span>Построить</span></span></a></td>
+		for _, стр := range списСтр {
+			if strings.Contains(стр, `href="building-upgrade/FuelStorage">`) {
+				ссыльПостроить = стр
+				break
+			}
+		}
+		if ссыльПостроить == "" {
+			бак.Состояние().Уст(cons.РежимПостроено)
+			return
+		}
+		// <td style="width:50%;padding-left:1px;"><a class="simple-but border mb5" href="building-upgrade/FuelStorage"><span><span>Построить</span></span></a></td>
+		ссыльПостроить = strings.TrimPrefix(ссыльПостроить, `<td style="width:50%;padding-left:1px;"><a class="simple-but border mb5" href="`)
+		ссыльПостроить = strings.TrimSuffix(ссыльПостроить, `"><span><span>Построить</span></span></a></td>`)
+		// https://wartank.ru/building-upgrade/FuelStorage
+		ссыльПостроить = "http://wartank.ru/" + ссыльПостроить
+		списСтр = база.Сеть().ВебВоркер().Получ(ссыльПостроить)
+	}
+	ссыльПодтвердить := "" // ссылка на улучшение здания
+
+	{ // Выбор покупки
+		// <a class="simple-but border mb5" href="FuelStorage?29-1.ILinkListener-upgradeLink-link">
+		for _, стр := range списСтр {
+			if strings.Contains(стр, `ILinkListener-upgradeLink-link`) {
+				ссыльПодтвердить = стр
+				break
+			}
+		}
+		if ссыльПодтвердить == "" {
+			бак.Состояние().Уст(cons.РежимПостроено)
+			return
+		}
+		ссыльПодтвердить = strings.TrimPrefix(ссыльПодтвердить, `<a class="simple-but border mb5" href="`)
+		ссыльПодтвердить = strings.TrimSuffix(ссыльПодтвердить, `">`)
+		// https://wartank.ru/building-upgrade/FuelStorage?28-1.ILinkListener-upgradeLink-link
+		ссыльПодтвердить = "http://wartank.ru/building-upgrade/" + ссыльПодтвердить
+		списСтр = база.Сеть().ВебВоркер().Получ(ссыльПодтвердить)
+	}
+	ссыльДа := "" // подтверждение покупки
+	{             // Подтверждение покупки
+		// <a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../wicket/page?31-1.ILinkListener-confirmLink"><span><span>да, подтверждаю</span></span></a>
+		for _, стр := range списСтр {
+			if strings.Contains(стр, `confirmLink`) {
+				ссыльДа = стр
+				break
+			}
+		}
+		if ссыльДа == "" {
+			бак.Состояние().Уст(cons.РежимПостроено)
+			return
+		}
+		ссыльДа = strings.TrimPrefix(ссыльДа, `<a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../`)
+		ссыльДа = strings.TrimSuffix(ссыльДа, `"><span><span>да, подтверждаю</span></span></a>`)
+		// https://wartank.ru/wicket/page?52-1.ILinkListener-confirmLink
+		ссыльДа = "http://wartank.ru/" + ссыльДа
+		_ = база.Сеть().ВебВоркер().Получ(ссыльДа)
+		бак.Состояние().Уст(cons.РежимПостроено)
+	}
+}

+ 89 - 0
app/lev2/arena/arena_fuel_storage/bf_fuel_upgrade/bf_fuel_upgrade.go

@@ -0,0 +1,89 @@
+// package bf_fuel_upgrade -- бизнес-функция апгрейда склада топлива
+package bf_fuel_upgrade
+
+import (
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	"wartank/app/lev0/cons"
+	. "wartank/app/lev0/types"
+)
+
+// БанкАпгрейд -- повышает уровень банка
+func БакАпгрейд(конт ILocalCtx) {
+	бак := конт.Get("бак").Val().(ИАренаБак)
+	еслиПостроено := бак.Состояние().Получ() == cons.РежимПостроено
+	еслиОжидание := бак.Состояние().Получ() == cons.РежимОжидание
+	if !(еслиПостроено || еслиОжидание) {
+		return
+	}
+	бакАпгрейд(конт)
+}
+
+func бакАпгрейд(конт ILocalCtx) {
+	бак := конт.Get("бак").Val().(ИАренаБак)
+	var (
+		еслиНайти = false
+		списСтр   []string
+		стр       = ""
+	)
+	фнКупить := func() bool {
+		списСтр = бак.Сеть().ВебВоркер().Получ("https://wartank.ru/building-upgrade/FuelStorage")
+		for _, стр = range списСтр {
+			// <a class="simple-but border mb5" href="FuelStorage?5-1.ILinkListener-upgradeLink-link">
+			if strings.Contains(стр, `ILinkListener-upgradeLink-link`) {
+				еслиНайти = true
+				break
+			}
+		}
+		if !еслиНайти {
+			return true
+		}
+		// Пробуем улучшить здание
+		_стр := strings.TrimPrefix(стр, "<a class=\"simple-but border mb5\" href=\"")
+		_стр = strings.TrimSuffix(_стр, "\">")
+		// https://wartank.ru/building-upgrade/FuelStorage?4-1.ILinkListener-upgradeLink-link
+		// <a class="simple-but border mb5" href="FuelStorage?50-1.ILinkListener-upgradeLink-link">
+		ссылка := "https://wartank.ru/building-upgrade/" + _стр
+		списСтр = бак.Сеть().ВебВоркер().Получ(ссылка)
+		// Проверить, что покупка состоялась
+		for _, стр := range списСтр {
+			if strings.Contains(стр, "ILinkListener-upgradeLink-link") {
+				return false // Покупка не оплачена
+			}
+		}
+		return true
+	}
+
+	фнПодтверждение := func() {
+		for _, стр = range списСтр {
+			// <a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../wicket/page?7-1.ILinkListener-confirmLink"><span><span>да, подтверждаю</span></span></a>
+			if strings.Contains(стр, `ILinkListener-confirmLink`) {
+				еслиНайти = true
+				break
+			}
+		}
+		if !еслиНайти {
+			return
+		}
+		// Пробуем оплатить апгрейд
+		_стр := strings.TrimPrefix(стр, `<a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="..`)
+		_стр = strings.TrimSuffix(_стр, `"><span><span>да, подтверждаю</span></span></a>`)
+		// https://wartank.ru/wicket/page?6-1.ILinkListener-confirmLink
+		ссылка := "https://wartank.ru" + _стр
+		списСтр = бак.Сеть().ВебВоркер().Получ(ссылка)
+		// Проверить, что оплата состоялась
+		for _, стр := range списСтр {
+			if strings.Contains(стр, "<title>Вы сделали слишком большую паузу</title>") {
+				return // Покупка не оплачена
+			}
+		}
+		бак.Состояние().Уст(cons.РежимАпгрейдПлатный)
+	}
+
+	if !фнКупить() {
+		return
+	}
+	фнПодтверждение()
+}

+ 18 - 103
app/lev2/arena/arena_market/arena_market.go

@@ -12,34 +12,43 @@ import (
 	. "wartank/app/lev0/alias"
 	. "wartank/app/lev0/types"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
+	"wartank/app/lev2/arena/arena_market/bf_market_build"
 )
 
 // АренаРынок -- объект рынка
 type АренаРынок struct {
-	ИАрена
+	ИАренаСтроение
 	конт ILocalCtx
-	бот  ИБот
 }
 
 // НовРынок -- возвращает новый рынок
 func НовРынок(конт ILocalCtx) ИАренаРынок {
 	сам := &АренаРынок{
-		бот: конт.Get("бот").(ИБот),
+		конт: конт,
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         сам.бот,
+		Конт_:        конт,
 		АренаИмя_:    "Рынок",
 		СтрКонтроль_: `<title>Рынок</title>`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/market",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
+	конт.Set("рынок", сам, "Рынок бота")
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 	return сам
 }
 
 // Пуск -- запускает всю работу рынка в отдельном потоке
 func (сам *АренаРынок) Пуск() {
-	go сам.пуск()
+	фнОбновить := func() { // Когда арена не построена -- ничего не вернёт
+		defer func() {
+			_ = recover()
+		}()
+		сам.Обновить()
+	}
+	фнОбновить()
+	bf_market_build.РынокПостроить(сам.конт)
 }
 
 // выполняет опрос рынка базы, должен работать как горутина
@@ -56,7 +65,7 @@ func (сам *АренаРынок) пуск() {
 	}
 	for {
 		select {
-		case <-сам.бот.КонтБот().Ctx().Done():
+		case <-сам.конт.Ctx().Done():
 			return
 		default:
 			фнРабота()
@@ -109,23 +118,10 @@ func (сам *АренаРынок) уровеньОбновить() bool {
 		return false
 	}
 	сам.Уровень().Уст(иУровень)
-	switch иУровень {
-	case 0: // рынок надо построить
-		for !сам.построить() {
-		}
-	default: // Может можно проапгрейдить
-		счёт := 5
-		for !сам.проапгрейдить() {
-			счёт--
-			if счёт >= 0 {
-				break
-			}
-		}
-	}
-
 	return true
 }
 
+/*
 // Строит шахту при нулевом уровне
 func (сам *АренаРынок) построить() bool {
 	time.Sleep(time.Millisecond * 1000)
@@ -206,88 +202,7 @@ func (сам *АренаРынок) построить() bool {
 	фнКомплекс()
 	return true
 }
-
-// Пытается проапгрейдить топливный склад
-func (сам *АренаРынок) проапгрейдить() bool {
-	time.Sleep(time.Millisecond * 1000)
-	var (
-		еслиНайти = false
-		списСтр   []string
-		стр       = ""
-	)
-	фнКупить := func() bool {
-		defer time.Sleep(time.Millisecond * 1000)
-		списСтр = сам.Сеть().ВебВоркер().Получ("https://wartank.ru/building-upgrade/Market")
-		for _, стр = range списСтр {
-			// <a class="simple-but border mb5" href="Market?5-1.ILinkListener-upgradeLink-link">
-			if strings.Contains(стр, `ILinkListener-upgradeLink-link`) {
-				еслиНайти = true
-				break
-			}
-		}
-		if !еслиНайти {
-			return true
-		}
-		// Пробуем улучшить шахту
-		_стр := strings.TrimPrefix(стр, "<a class=\"simple-but border mb5\" href=\"")
-		_стр = strings.TrimSuffix(_стр, "\">")
-		// https://wartank.ru/building-upgrade/Market?4-1.ILinkListener-upgradeLink-link
-		// <a class="simple-but border mb5" href="Market?50-1.ILinkListener-upgradeLink-link">
-		ссылка := "https://wartank.ru/building-upgrade/" + _стр
-		списСтр = сам.Сеть().ВебВоркер().Получ(ссылка)
-		// Проверить, что постройка состоялась
-		for _, стр := range списСтр {
-			if strings.Contains(стр, "ILinkListener-upgradeLink-link") {
-				log.Printf("Рынок.проапгрейдить().фнКупить(): покупка рынка не прошла\n\tlink=%v\n\tстр=\n\t%v\n", ссылка, стр)
-				return false // Покупка не оплачена
-			}
-		}
-		log.Printf("+++++Рынок.проапгрейдить().фнКупить(): покупка рынка прошла\n")
-		return true
-	}
-
-	фнПодтверждение := func() bool {
-		for _, стр = range списСтр {
-			// <a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../wicket/page?7-1.ILinkListener-confirmLink"><span><span>да, подтверждаю</span></span></a>
-			if strings.Contains(стр, `ILinkListener-confirmLink`) {
-				еслиНайти = true
-				break
-			}
-		}
-		if !еслиНайти {
-			return true
-		}
-		// Пробуем построить шахту
-		_стр := strings.TrimPrefix(стр, `<a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="..`)
-		_стр = strings.TrimSuffix(_стр, `"><span><span>да, подтверждаю</span></span></a>`)
-		// https://wartank.ru/wicket/page?6-1.ILinkListener-confirmLink
-		ссылка := "https://wartank.ru" + _стр
-		списСтр = сам.Сеть().ВебВоркер().Получ(ссылка)
-		// Проверить, что постройка состоялась
-		for _, стр := range списСтр {
-			if strings.Contains(стр, "<title>Вы сделали слишком большую паузу</title>") {
-				log.Printf("Рынок.проапгрейдить().фнПодтверждение(): подтверждение покупка рынка не прошла\n\tlink=%v\n\tстр=\n\t%v\n", ссылка, стр)
-				return false // Покупка не оплачена
-			}
-		}
-		log.Printf("+++++Рынок.проапгрейдить().фнПодтверждение(): подтверждение покупка склада топлива прошла\n")
-		return true
-	}
-
-	фнКомплекс := func() {
-		count := 5
-		for count > 0 {
-			if фнКупить() {
-				if фнПодтверждение() {
-					break
-				}
-			}
-			count--
-		}
-	}
-	фнКомплекс()
-	return true
-}
+*/
 
 // Проверяет  время ожидания рынка
 func (сам *АренаРынок) проверОжидание() {

+ 86 - 0
app/lev2/arena/arena_market/bf_market_build/bf_market_build.go

@@ -0,0 +1,86 @@
+// package bf_market_build -- бизнес-функция строительства рынка
+package bf_market_build
+
+import (
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	"wartank/app/lev0/cons"
+	. "wartank/app/lev0/types"
+)
+
+// БанкПостроить -- постройка рынка
+func РынокПостроить(конт ILocalCtx) {
+	рынок := конт.Get("рынок").Val().(ИАренаРынок)
+	if рынок.Состояние().Получ() == cons.РежимНеСуществует {
+		рынокПостроить(конт)
+	}
+}
+
+func рынокПостроить(конт ILocalCtx) {
+	база := конт.Get("база").Val().(ИАренаБаза)
+	рынок := конт.Get("рынок").Val().(ИАренаРынок)
+	списСтр := база.Сеть().ВебВоркер().Получ("https://wartank.ru/buildings")
+	ссыльПостроить := "" // ссылка на постройку
+
+	{ // Поиск ссылки на покупку
+		// <td style="width:50%;padding-left:1px;"><a class="simple-but border mb5" href="building-upgrade/Market"><span><span>Построить</span></span></a></td>
+		for _, стр := range списСтр {
+			if strings.Contains(стр, `href="building-upgrade/Market">`) {
+				ссыльПостроить = стр
+				break
+			}
+		}
+		if ссыльПостроить == "" {
+			рынок.Состояние().Уст(cons.РежимПостроено)
+			return
+		}
+		// <td style="width:50%;padding-left:1px;"><a class="simple-but border mb5" href="building-upgrade/Market"><span><span>Построить</span></span></a></td>
+		ссыльПостроить = strings.TrimPrefix(ссыльПостроить, `<td style="width:50%;padding-left:1px;"><a class="simple-but border mb5" href="`)
+		ссыльПостроить = strings.TrimSuffix(ссыльПостроить, `"><span><span>Построить</span></span></a></td>`)
+		// https://wartank.ru/building-upgrade/Market
+		ссыльПостроить = "http://wartank.ru/" + ссыльПостроить
+		списСтр = база.Сеть().ВебВоркер().Получ(ссыльПостроить)
+	}
+	ссыльПодтвердить := "" // ссылка на улучшение здания
+
+	{ // Выбор покупки
+		// <a class="simple-but border mb5" href="Market?29-1.ILinkListener-upgradeLink-link">
+		for _, стр := range списСтр {
+			if strings.Contains(стр, `ILinkListener-upgradeLink-link`) {
+				ссыльПодтвердить = стр
+				break
+			}
+		}
+		if ссыльПодтвердить == "" {
+			рынок.Состояние().Уст(cons.РежимПостроено)
+			return
+		}
+		ссыльПодтвердить = strings.TrimPrefix(ссыльПодтвердить, `<a class="simple-but border mb5" href="`)
+		ссыльПодтвердить = strings.TrimSuffix(ссыльПодтвердить, `">`)
+		// https://wartank.ru/building-upgrade/Market?28-1.ILinkListener-upgradeLink-link
+		ссыльПодтвердить = "http://wartank.ru/building-upgrade/" + ссыльПодтвердить
+		списСтр = база.Сеть().ВебВоркер().Получ(ссыльПодтвердить)
+	}
+	ссыльДа := "" // подтверждение покупки
+	{             // Подтверждение покупки
+		// <a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../wicket/page?195-1.ILinkListener-confirmLink"><span><span>да, подтверждаю</span></span></a>
+		for _, стр := range списСтр {
+			if strings.Contains(стр, `ILinkListener-confirmLink"><span><span>да, подтверждаю<`) {
+				ссыльДа = стр
+				break
+			}
+		}
+		if ссыльДа == "" {
+			рынок.Состояние().Уст(cons.РежимПостроено)
+			return
+		}
+		ссыльДа = strings.TrimPrefix(ссыльДа, `<a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../`)
+		ссыльДа = strings.TrimSuffix(ссыльДа, `"><span><span>да, подтверждаю</span></span></a>`)
+		// https://wartank.ru/wicket/page?195-1.ILinkListener-confirmLink
+		ссыльДа = "http://wartank.ru/" + ссыльДа
+		_ = база.Сеть().ВебВоркер().Получ(ссыльДа)
+		рынок.Состояние().Уст(cons.РежимПостроено)
+	}
+}

+ 8 - 10
app/lev2/arena/arena_masters/arena_masters.go

@@ -5,12 +5,12 @@ import (
 	"time"
 
 	. "gitp78su.ipnodns.ru/svi/kern"
-	. "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
 
 	. "wartank/app/lev0/alias"
 	. "wartank/app/lev0/types"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
 )
 
 /*
@@ -21,28 +21,26 @@ import (
 
 // БитваМастеров -- объект битвы мастеров
 type БитваМастеров struct {
-	ИАрена
-	бот ИБот
-	лог ILogBuf
+	ИАренаСтроение
+	конт ILocalCtx
+	лог  ILogBuf
 }
 
 // НовБитваМастеров -- возвращает новый *BatMas
-func НовБитваМастеров(бот ИБот) *БитваМастеров {
+func НовБитваМастеров(конт ILocalCtx) *БитваМастеров {
 	лог := NewLogBuf()
 	лог.Info("НовБитваМастеров()\n")
-	Hassert(бот != nil, "НовБитваМастеров(): ИБот == nil\n")
 	сам := &БитваМастеров{
-		бот: бот,
 		лог: лог,
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         бот,
+		Конт_:        конт,
 		АренаИмя_:    "Битва мастеров",
 		СтрКонтроль_: `/> Битва мастеров <`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/pvp",
 	}
-	сам.ИАрена = arena.НовАрена(бот.КонтБот(), аренаКонфиг)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 	return сам
 }
 
@@ -53,7 +51,7 @@ func (сам *БитваМастеров) пуск() {
 		for {
 			time.Sleep(time.Second * 10)
 			select {
-			case <-сам.бот.КонтБот().Ctx().Done():
+			case <-сам.конт.Ctx().Done():
 				return
 			case <-сам.ВремяОстат().КаналСиг():
 				if !сам.goBatMas() { // Проверка на начало сражения

+ 42 - 0
app/lev2/arena/arena_medal/arena_medal.go

@@ -0,0 +1,42 @@
+// package arena_medal -- арена получения медалей
+package arena_medal
+
+import (
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_medal/bf_medal_find"
+)
+
+// Банк -- объект арены медалей в миссиях
+type АренаМедаль struct {
+	ИАрена
+	конт ILocalCtx
+}
+
+// НовБанк -- возвращает новую арену медалей в миссиях
+func НовАренаМедали(конт ILocalCtx) *АренаМедаль {
+
+	сам := &АренаМедаль{
+		конт: конт,
+	}
+	аренаКонфиг := arena.АренаКонфиг{
+		Конт_:        конт,
+		АренаИмя_:    "Банк",
+		СтрКонтроль_: `<title>Медали</title>`,
+		ФнПуск_:      сам.пуск,
+		СтрУрл_:      "https://wartank.ru/medals/current",
+	}
+	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
+	конт.Set("арена_медали", сам, "Арена получения медалей")
+	return сам
+}
+
+func (сам *АренаМедаль) Пуск() {
+	сам.Обновить()
+	bf_medal_find.МедалиНайти(сам.конт)
+}
+
+// запускает банк в опрос
+func (сам *АренаМедаль) пуск() {}

+ 45 - 0
app/lev2/arena/arena_medal/bf_medal_find/bf_medal_find.go

@@ -0,0 +1,45 @@
+// package bf_medal_find -- поиск медалей
+
+package bf_medal_find
+
+import (
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	. "wartank/app/lev0/types"
+)
+
+// СлаваНайти -- ищет медали бота
+func МедалиНайти(конт ILocalCtx) {
+	медали := конт.Get("арена_медали").Val().(ИАрена)
+	// Найти строку с упоминанием оставшегося времени конвоя
+	lstStr := медали.СписПолучить()
+	var (
+		стрМедаль   string
+		еслиНайдено bool
+	)
+	if len(lstStr) == 0 {
+		медали.Обновить()
+		lstStr = медали.СписПолучить()
+	}
+	// <a class="simple-but border" href="current?128-1.ILinkListener-currentMedal-takeAwardLink"><span><span>Получить медаль</span></span></a>
+	for _, val := range lstStr {
+		if strings.Contains(val, `-currentMedal-takeAwardLink"><span><span>Получить медаль</span></span></a>`) {
+			стрМедаль = val
+			еслиНайдено = true
+			break
+		}
+	}
+	if !еслиНайдено { // Может не быть, если нет медали
+		return
+	}
+	// Вырезаем ссылку  на медаль
+	// <a class="simple-but border" href="current?128-1.ILinkListener-currentMedal-takeAwardLink"><span><span>Получить медаль</span></span></a>
+	стрМедаль = strings.TrimPrefix(стрМедаль, `<a class="simple-but border" href="`)
+	стрМедаль = strings.TrimSuffix(стрМедаль, `"><span><span>Получить медаль</span></span></a>`)
+	// https://wartank.ru/medals/current?137-1.ILinkListener-currentMedal-takeAwardLink
+	// https://wartank.ru/medals/current?169-1.ILinkListener-currentMedal-takeAwardLink
+	ссыль := "https://wartank.ru/medals/" + стрМедаль
+	_ = медали.Сеть().ВебВоркер().Получ(ссыль)
+}

+ 94 - 98
app/lev2/arena/arena_mine/arena_mine.go

@@ -9,58 +9,69 @@ import (
 
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
 
-	. "wartank/app/lev0/alias"
+	// . "wartank/app/lev0/alias"
 	"wartank/app/lev0/cons"
 	. "wartank/app/lev0/types"
 	"wartank/app/lev1"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
+	"wartank/app/lev2/arena/arena_mine/bf_mine_accelerate"
+	"wartank/app/lev2/arena/arena_mine/bf_mine_build"
+	"wartank/app/lev2/arena/arena_mine/bf_mine_time_work"
 )
 
 // АренаШахта -- объект шахты на базе
 type АренаШахта struct {
-	ИАрена
-	лог          ИВебЛог
-	бот          ИБот
-	база         ИАренаБаза
-	руда         ИСтатПарам
-	железо       ИСтатПарам
-	сталь        ИСтатПарам
-	свинец       ИСтатПарам
-	уровень      ИСтатПарам
-	продукт      ИСтатПарам // делается прямо сейчас
-	продуктВремя string     // Сколько осталось времени прямо сейчас
-	кнт          ILocalCtx
+	ИАренаСтроение
+	лог    ИВебЛог
+	база   ИАренаБаза
+	руда   ИСтатПарам
+	железо ИСтатПарам
+	сталь  ИСтатПарам
+	свинец ИСтатПарам
+	конт   ILocalCtx
 }
 
 // НовШахта -- возвращает новый *Mine
-func НовШахта(конт ILocalCtx) ИАренаШахта {
+func НовШахта(конт ILocalCtx) *АренаШахта {
 	сам := &АренаШахта{
-		бот:     конт.Get("бот").Val().(ИБот),
-		база:    конт.Get("база").Val().(ИАренаБаза),
-		руда:    lev1.НовСтатПарам("руда"),
-		железо:  lev1.НовСтатПарам("железо"),
-		сталь:   lev1.НовСтатПарам("сталь"),
-		свинец:  lev1.НовСтатПарам("свинец"),
-		продукт: lev1.НовСтатПарам("кол-во"),
-		уровень: lev1.НовСтатПарам("уровень"),
-		кнт:     конт,
+		конт:   конт,
+		база:   конт.Get("база").Val().(ИАренаБаза),
+		руда:   lev1.НовСтатПарам("руда"),
+		железо: lev1.НовСтатПарам("железо"),
+		сталь:  lev1.НовСтатПарам("сталь"),
+		свинец: lev1.НовСтатПарам("свинец"),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         сам.бот,
+		Конт_:        сам.конт,
 		АренаИмя_:    "Шахта",
 		СтрКонтроль_: `<span class="green2">Руда</span><br/>`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/production/Mine",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 	сам.лог = сам.ВебЛог()
 	go сам.пуск()
 	сам.лог.ОтклВывод()
-	сам.лог.Добавить("Шахта.НовШахта(): бот=%q\n", сам.бот.Имя())
+	сам.лог.Добавить("Шахта.НовШахта(): бот=%q\n", конт.Get("бот").Val().(ИБот).Имя())
 	конт.Set("шахта", сам, "Шахта бота")
+	_ = ИАренаШахта(сам)
 	return сам
 }
 
+func (сам *АренаШахта) Пуск() {
+	фнОбновить := func() { // Когда арена не построена -- ничего не вернёт
+		defer func() {
+			_ = recover()
+		}()
+		сам.Обновить()
+	}
+	фнОбновить()
+	bf_mine_build.ШахтаПостроить(сам.конт)
+	bf_mine_accelerate.ШахтаУскорить(сам.конт)
+	bf_mine_time_work.ШахтаРаботаВремя(сам.конт)
+}
+
 // пуск -- запускает обработку шахты
 func (сам *АренаШахта) пуск() {
 	фнРабота := func() {
@@ -69,7 +80,7 @@ func (сам *АренаШахта) пуск() {
 				time.Sleep(time.Second * 5)
 			}
 		}()
-		сам.лог.Добавить("Шахта.пуск().фнРабота(): бот=%q\n", сам.бот.Имя())
+		сам.лог.Добавить("Шахта.пуск().фнРабота()")
 		{ // Построить
 			счёт := 5
 			for счёт > 0 {
@@ -108,7 +119,7 @@ func (сам *АренаШахта) пуск() {
 	}
 	for {
 		select {
-		case <-сам.кнт.Ctx().Done():
+		case <-сам.конт.Ctx().Done():
 			return
 		case <-сам.ВремяОстат().КаналСиг():
 			фнРабота()
@@ -122,7 +133,6 @@ func (сам *АренаШахта) пуск() {
 func (сам *АренаШахта) количествоПолучить() (bool, error) {
 	сам.лог.Добавить("Шахта.количествоПолучить()\n")
 	var (
-		ind         int
 		strOut      string
 		еслиНайдено bool
 		режим       string
@@ -135,7 +145,7 @@ func (сам *АренаШахта) количествоПолучить() (bool
 		Время (+8 строк):
 		<td class="vam"><div class="nwr pr5 gray1"><img class="rico vm" src="/images/icons/ore.png?2" alt="ore"/>&nbsp;1</div></td>
 	*/
-	for ind, strOut = range lstMine {
+	for _, strOut = range lstMine {
 		// Руда текущее
 		if strings.Contains(strOut, `src="/images/icons/ore.png?2" alt="ore"`) {
 			// <td class="vam"><div class="nwr pr5 gray1"><img class="rico vm" src="/images/icons/ore.png?2" alt="ore"/>&nbsp;1</div></td>
@@ -178,8 +188,8 @@ func (сам *АренаШахта) количествоПолучить() (bool
 			сам.лог.Добавить("ОШИБКА Шахта.количествоПолучить(): кол-во руды (%v) не число, err=\n\t%v\n", _число, err)
 			return false, fmt.Errorf("")
 		}
-		сам.продукт.Уст(iNum)
-		сам.продукт.ИмяУст("руда")
+		сам.ПродуктСейчас().Уст(iNum)
+		сам.ПродуктСейчас().ИмяУст("руда")
 		сам.лог.Добавить("Шахта.количествоПолучить(): кол-во руды = %v\n", iNum)
 	case "железо":
 		_число := strings.TrimPrefix(strOut, `<td class="vam"><div class="nwr pr5 gray1"><img class="rico vm" src="/images/icons/iron.png?2" alt="iron"/>&nbsp;`)
@@ -189,8 +199,8 @@ func (сам *АренаШахта) количествоПолучить() (bool
 			сам.лог.Добавить("ОШИБКА Шахта.количествоПолучить(): кол-во железа (%v) не число, err=\n\t%v\n", _число, err)
 			return false, fmt.Errorf("")
 		}
-		сам.продукт.Уст(iNum)
-		сам.продукт.ИмяУст("железо")
+		сам.ПродуктСейчас().Уст(iNum)
+		сам.ПродуктСейчас().ИмяУст("железо")
 		сам.лог.Добавить("Шахта.количествоПолучить(): кол-во железа = %v\n", iNum)
 	case "сталь":
 		_число := strings.TrimPrefix(strOut, `<td class="vam"><div class="nwr pr5 gray1"><img class="rico vm" src="/images/icons/steel.png?2" alt="steel"/>&nbsp;`)
@@ -200,8 +210,8 @@ func (сам *АренаШахта) количествоПолучить() (bool
 			сам.лог.Добавить("ОШИБКА Шахта.количествоПолучить(): кол-во стали (%v) не число, err=\n\t%v\n", _число, err)
 			return false, fmt.Errorf("")
 		}
-		сам.продукт.Уст(iNum)
-		сам.продукт.ИмяУст("сталь")
+		сам.ПродуктСейчас().Уст(iNum)
+		сам.ПродуктСейчас().ИмяУст("сталь")
 		сам.лог.Добавить("Шахта.количествоПолучить(): кол-во стали = %v\n", iNum)
 	case "свинец":
 		_число := strings.TrimPrefix(strOut, `<td class="vam"><div class="nwr pr5 gray1"><img class="rico vm" src="/images/icons/plumbum.png?2" alt="plumbum"/>&nbsp;`)
@@ -211,21 +221,13 @@ func (сам *АренаШахта) количествоПолучить() (bool
 			сам.лог.Добавить("ОШИБКА Шахта.количествоПолучить(): кол-во свинца (%v) не число, err=\n\t%v\n", _число, err)
 			return false, fmt.Errorf("")
 		}
-		сам.продукт.Уст(iNum)
-		сам.продукт.ИмяУст("свинец")
+		сам.ПродуктСейчас().Уст(iNum)
+		сам.ПродуктСейчас().ИмяУст("свинец")
 		сам.лог.Добавить("Шахта.количествоПолучить(): кол-во свинца = %v\n", iNum)
 	default:
 		сам.лог.Добавить("Шахта.количествоПолучить(): неизвестный режим (%v)\n", режим)
 		return false, fmt.Errorf("")
 	}
-	// <td><div class="value-block lh1"><span><span>00:00:34</span></span></div></td>
-	strTime := lstMine[ind+3]
-	// <td><div class="value-block lh1"><span><span>00:19:53</span></span></div></td>
-	strTime = strings.TrimPrefix(strTime, `<td><div class="value-block lh1"><span><span>`)
-	strTime = strings.TrimSuffix(strTime, `</span></span></div></td>`)
-	сам.продуктВремя = strTime
-	сам.ОбратВремяУст(АВремя(strTime))
-	сам.лог.Добавить("Шахта.количествоПолучить(): время=%q\n", strTime)
 	return true, nil
 }
 
@@ -255,7 +257,14 @@ func (сам *АренаШахта) шахтаЗабрать() bool {
 	lstBase1 := сам.Сеть().ВебВоркер().Получ(ссылка)
 	сам.СтрОбновить(lstBase1)
 	сам.лог.Добавить("Шахта.шахтаЗабрать(): ОК\n")
-	сам.АренаСостояние().Уст(cons.РежимОжидание)
+	сост := сам.Состояние().Получ()
+	if сост == cons.РежимНеСуществует {
+		сам.Состояние().Уст(cons.РежимПостроено)
+	}
+	if сам.Состояние().Получ() == cons.РежимРабота {
+		сам.Состояние().Уст(cons.РежимЗабрать)
+	}
+	сам.Состояние().Уст(cons.РежимОжидание)
 	return true
 }
 
@@ -281,11 +290,6 @@ func (сам *АренаШахта) шахтаЗабрать() bool {
 // 	сам.лог.Добавить("Шахта.ускорениеПровер(): надо\n")
 // }
 
-// Уровень -- возвращает уровень шахты
-func (сам *АренаШахта) Уровень() ИСтатПарам {
-	return сам.уровень
-}
-
 // Обновляет текущий уровень шахты (может быть не построена)
 func (сам *АренаШахта) уровеньОбновить() bool {
 	сам.лог.Добавить("Шахта.уровеньОбновить()\n")
@@ -312,13 +316,18 @@ func (сам *АренаШахта) уровеньОбновить() bool {
 		сам.лог.Добавить("ОШИБКА Шахта.уровеньОбновить(): строка уровня сбойная, стр=%q, ош=\n\t%v\n", стр, ош)
 		return false
 	}
-	сам.уровень.Уст(иУровень)
+	сам.Уровень().Уст(иУровень)
 	сам.лог.Добавить("Шахта.уровеньОбновить(): уровень=%v\n", иУровень)
 	return true
 }
 
 // Сделать -- вызывается с базы, если она обнаружила, что пора сделать продукцию
 func (сам *АренаШахта) Сделать() {
+	еслиПостроено := сам.Состояние().Получ() == cons.РежимПостроено
+	еслиОжидание := сам.Состояние().Получ() == cons.РежимОжидание
+	if !(еслиПостроено || еслиОжидание) {
+		return
+	}
 	сам.Сеть().Обновить()
 	if err := сам.выбратьМеталл(); err != nil {
 		сам.лог.Добавить("ERRO Шахта.Сделать(): при выборе продукции, err=\n\t%v\n", err)
@@ -341,7 +350,7 @@ func (сам *АренаШахта) Сделать() {
 	default:
 		сам.лог.Добавить("ERRO Шахта.Сделать(): неизвестный режим производства, режим=%q\n", продукт)
 	}
-	сам.АренаСостояние().Уст(cons.РежимРабота)
+	сам.Состояние().Уст(cons.РежимРабота)
 }
 
 // Свинец -- возвращает объект свинца
@@ -364,18 +373,6 @@ func (сам *АренаШахта) Руда() ИСтатПарам {
 	return сам.руда
 }
 
-// ПродуктКолСейчас -- возвращает производимый продукта
-func (сам *АренаШахта) ПродуктСейчас() ИСтатПарам {
-	return сам.продукт
-}
-
-// ПродуктВремяСейчас -- сколько осталось времени до производства продукта
-func (сам *АренаШахта) ПродуктВремяСейчас() string {
-	// сам.количествоПолучить()
-	return сам.продуктВремя
-	// return сам.Секция.ВремяОпрос().Стр()
-}
-
 // Выбирает продукцию по возможности произвести и её количеству
 func (сам *АренаШахта) выбратьМеталл() error {
 	var (
@@ -436,7 +433,6 @@ func (сам *АренаШахта) выбратьМеталл() error {
 			сам.ПродуктСейчас().ИмяУст("свинец")
 		}
 	}
-
 	return nil
 }
 
@@ -449,9 +445,9 @@ func (сам *АренаШахта) рудаСделать() bool {
 		return false
 	}
 	var (
-		инд         int
-		стрВых      string
-		strTime     string
+		инд    int
+		стрВых string
+		// strTime     string
 		strLink     string
 		strNum      string
 		еслиНайдено bool
@@ -459,7 +455,7 @@ func (сам *АренаШахта) рудаСделать() bool {
 	for инд, стрВых = range lstMine {
 		if strings.Contains(стрВых, `<span class="green2">Руда</span><br/>`) { // <span class="green2">Руда</span><br/>
 			strNum = lstMine[инд+1]
-			strTime = lstMine[инд+3]
+			// strTime = lstMine[инд+3]
 			strLink = lstMine[инд+10]
 			еслиНайдено = true
 			break
@@ -489,7 +485,7 @@ func (сам *АренаШахта) рудаСделать() bool {
 		}
 	}
 	// сам.СтрОбновить(lstMine)
-	сам.ОбратВремяУст(АВремя(strTime))
+	//сам.ОбратВремяУст(АВремя(strTime))
 	lstNum := strings.Split(strNum, `Кол-во: <span class="green2">`)
 	strNum = lstNum[1]
 	lstNum = strings.Split(strNum, `</span><br/>`)
@@ -499,18 +495,18 @@ func (сам *АренаШахта) рудаСделать() bool {
 		// log._rintf("ERRO Шахта.сделатьРуду(): кол-во(%v) не число, err=\n\t%v\n", strNum, err)
 		return false
 	}
-	сам.продукт.Уст(iNum)
-	сам.продукт.ИмяУст("руда")
+	сам.ПродуктСейчас().Уст(iNum)
+	сам.ПродуктСейчас().ИмяУст("руда")
 	return true
 }
 
 // Создаёт железо
 func (сам *АренаШахта) железоСделать() bool {
 	var (
-		lstMine     = сам.СписПолучить()
-		ind         int
-		strOut      string
-		strTime     string
+		lstMine = сам.СписПолучить()
+		ind     int
+		strOut  string
+		// strTime     string
 		strLink     string
 		strNum      string
 		еслиНайдено bool
@@ -520,7 +516,7 @@ func (сам *АренаШахта) железоСделать() bool {
 			// <span class="green2">Железо</span><br/>
 			strNum = lstMine[ind+1]
 			// Кол-во: <span class="green2">1</span><br/>
-			strTime = lstMine[ind+3]
+			// strTime = lstMine[ind+3]
 			// <a class="simple-but border" href="Mine?4-1.ILinkListener-productions-1-production-startProduceLink"><span><span>Начать производство</span></span></a>
 			strLink = lstMine[ind+10]
 			еслиНайдено = true
@@ -547,7 +543,7 @@ func (сам *АренаШахта) железоСделать() bool {
 		}
 	}
 	сам.СтрОбновить(lstMine)
-	сам.ОбратВремяУст(АВремя(strTime))
+	// сам.ОбратВремяУст(АВремя(strTime))
 	lstNum := strings.Split(strNum, `Кол-во: <span class="green2">`)
 	strNum = lstNum[1]
 	lstNum = strings.Split(strNum, `</span><br/>`)
@@ -557,18 +553,18 @@ func (сам *АренаШахта) железоСделать() bool {
 		// log._rintf("ERRO MineNet.makeFerrum(): кол-во(%v) не число, err=\n\t%v\n", strNum, err)
 		return false
 	}
-	сам.продукт.Уст(iNum)
-	сам.продукт.ИмяУст("железо")
+	сам.ПродуктСейчас().Уст(iNum)
+	сам.ПродуктСейчас().ИмяУст("железо")
 	return true
 }
 
 // Создаёт сталь
 func (сам *АренаШахта) стальСделать() bool {
 	var (
-		lstMine     = сам.СписПолучить()
-		ind         int
-		strOut      string
-		strTime     string
+		lstMine = сам.СписПолучить()
+		ind     int
+		strOut  string
+		// strTime     string
 		strLink     string
 		strNum      string
 		еслиНайдено bool
@@ -576,7 +572,7 @@ func (сам *АренаШахта) стальСделать() bool {
 	for ind, strOut = range lstMine {
 		if strings.Contains(strOut, `<span class="green2">Сталь</span><br/>`) {
 			strNum = lstMine[ind+1]
-			strTime = lstMine[ind+3]
+			// strTime = lstMine[ind+3]
 			strLink = lstMine[ind+10]
 			еслиНайдено = true
 			break
@@ -602,7 +598,7 @@ func (сам *АренаШахта) стальСделать() bool {
 		}
 	}
 	сам.СтрОбновить(lstMine)
-	сам.ОбратВремяУст(АВремя(strTime))
+	// сам.ОбратВремяУст(АВремя(strTime))
 	lstNum := strings.Split(strNum, `Кол-во: <span class="green2">`)
 	strNum = lstNum[1]
 	lstNum = strings.Split(strNum, `</span><br/>`)
@@ -612,18 +608,18 @@ func (сам *АренаШахта) стальСделать() bool {
 		// log._rintf("ERRO MineNet.makeSteel(): кол-во(%v) не число, err=\n\t%v\n", strNum, err)
 		return false
 	}
-	сам.продукт.Уст(iNum)
-	сам.продукт.ИмяУст("сталь")
+	сам.ПродуктСейчас().Уст(iNum)
+	сам.ПродуктСейчас().ИмяУст("сталь")
 	return true
 }
 
 // Создаёт свинец
 func (сам *АренаШахта) свинецСделать() bool {
 	var (
-		lstMine     = сам.СписПолучить()
-		ind         int
-		strOut      string
-		strTime     string
+		lstMine = сам.СписПолучить()
+		ind     int
+		strOut  string
+		// strTime     string
 		strLink     string
 		strNum      string
 		еслиНайдено bool
@@ -631,7 +627,7 @@ func (сам *АренаШахта) свинецСделать() bool {
 	for ind, strOut = range lstMine {
 		if strings.Contains(strOut, `<span class="green2">Свинец</span><br/>`) {
 			strNum = lstMine[ind+1]
-			strTime = lstMine[ind+3]
+			// strTime = lstMine[ind+3]
 			strLink = lstMine[ind+10]
 			еслиНайдено = true
 			break
@@ -657,7 +653,7 @@ func (сам *АренаШахта) свинецСделать() bool {
 		}
 	}
 	сам.СтрОбновить(lstMine)
-	сам.ОбратВремяУст(АВремя(strTime))
+	// сам.ОбратВремяУст(АВремя(strTime))
 	lstNum := strings.Split(strNum, `Кол-во: <span class="green2">`)
 	strNum = lstNum[1]
 	lstNum = strings.Split(strNum, `</span><br/>`)
@@ -667,7 +663,7 @@ func (сам *АренаШахта) свинецСделать() bool {
 		// log._rintf("ERRO Шахта.сделатьСвинец(): кол-во(%v) не число, err=\n\t%v\n", strNum, err)
 		return false
 	}
-	сам.продукт.Уст(iNum)
-	сам.продукт.ИмяУст("свинец")
+	сам.ПродуктСейчас().Уст(iNum)
+	сам.ПродуктСейчас().ИмяУст("свинец")
 	return true
 }

+ 5 - 5
app/lev0/bfunc/bf_mine_accelerate/bf_mine_accelerate.go → app/lev2/arena/arena_mine/bf_mine_accelerate/bf_mine_accelerate.go

@@ -14,8 +14,8 @@ import (
 // ШахтаУскорить -- пробует ускорить строительство шахты или апгрейда
 func ШахтаУскорить(конт ILocalCtx) {
 	шахта := конт.Get("шахта").Val().(ИАренаШахта)
-	еслиАпгрейд := шахта.АренаСостояние().Получ() == cons.РежимАпгрейд
-	еслиПлатный := шахта.АренаСостояние().Получ() == cons.РежимАпгрейдПлатный
+	еслиАпгрейд := шахта.Состояние().Получ() == cons.РежимАпгрейд
+	еслиПлатный := шахта.Состояние().Получ() == cons.РежимАпгрейдПлатный
 	if !(еслиАпгрейд || еслиПлатный) {
 		return
 	}
@@ -89,14 +89,14 @@ func шахтаАпгрейд(конт ILocalCtx) {
 		switch {
 		case ош == nil && еслиОк: // покупка апгрейда шахты прошла
 			if фнПодтверждение() {
-				шахта.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+				шахта.Состояние().Уст(cons.РежимАпгрейдПлатный)
 				return
 			}
 		case ош == nil && !еслиОк: // покупка шахты не нужна
-			шахта.АренаСостояние().Уст(cons.РежимОжидание)
+			шахта.Состояние().Уст(cons.РежимОжидание)
 			return
 		case ош != nil: // ошибка при работе с сетью
-			шахта.АренаСостояние().Уст(cons.РежимАпгрейд)
+			шахта.Состояние().Уст(cons.РежимАпгрейд)
 			return
 		}
 	}

+ 4 - 4
app/lev0/bfunc/bf_mine_build/bf_mine_build.go → app/lev2/arena/arena_mine/bf_mine_build/bf_mine_build.go

@@ -13,7 +13,7 @@ import (
 // ШахтаПостроить -- постройка шахты
 func ШахтаПостроить(конт ILocalCtx) {
 	шахта := конт.Get("шахта").Val().(ИАренаШахта)
-	if шахта.АренаСостояние().Получ() == cons.РежимНеСуществует {
+	if шахта.Состояние().Получ() == cons.РежимНеСуществует {
 		шахтаПостроить(конт)
 	}
 }
@@ -34,7 +34,7 @@ func шахтаПостроить(конт ILocalCtx) {
 		}
 	}
 	if !еслиНайти {
-		шахта.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+		шахта.Состояние().Уст(cons.РежимПостроено)
 		return
 	}
 	// Пробуем построить шахту
@@ -51,7 +51,7 @@ func шахтаПостроить(конт ILocalCtx) {
 		}
 	}
 	if !еслиНайти {
-		шахта.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+		шахта.Состояние().Уст(cons.РежимПостроено)
 		return
 	}
 	_стр = strings.TrimPrefix(стр, "<a class=\"simple-but border mb5\" href=\"")
@@ -59,5 +59,5 @@ func шахтаПостроить(конт ILocalCtx) {
 	// http://wartank.ru/building-upgrade/Mine?16-1.ILinkListener-upgradeLink-link
 	ссылка = "https://wartank.ru/building-upgrade/" + _стр
 	_ = база.Сеть().ВебВоркер().Получ(ссылка)
-	шахта.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+	шахта.Состояние().Уст(cons.РежимПостроено)
 }

+ 52 - 0
app/lev2/arena/arena_mine/bf_mine_time_work/bf_mine_time_work.go

@@ -0,0 +1,52 @@
+// package bf_mine_time -- вычисляет оставшееся время производства
+package bf_mine_time_work
+
+import (
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	. "wartank/app/lev0/alias"
+	"wartank/app/lev0/cons"
+	. "wartank/app/lev0/types"
+)
+
+// ШахтаРаботаВремя -- выясняет оставшееся время
+func ШахтаРаботаВремя(конт ILocalCtx) {
+	шахта := конт.Get("шахта").Val().(ИАренаШахта)
+	if шахта.Состояние().Получ() != cons.РежимРабота {
+		return
+	}
+	база := конт.Get("база").Val().(ИАренаБаза)
+	lstMine := база.СписПолучить()
+	if len(lstMine) == 0 {
+		база.ОбновитьПринуд()
+		lstMine = база.СписПолучить()
+	}
+	var (
+		ind    int
+		str    string
+		isFind bool
+	)
+	// <span class="green2">Шахта - 1</span><br/>
+	for ind, str = range lstMine {
+		if strings.Contains(str, `<span class="green2">Шахта - `) {
+			isFind = true
+			break
+		}
+	}
+	Hassert(isFind, "ШахтаВремя(): строка времени не найдена")
+
+	// <td><div class="value-block lh1"><span><span>00:00:34</span></span></div></td>
+	strTime := lstMine[ind+11]
+	if !strings.Contains(strTime, ":"){ // Уже время производства закончилось
+		шахта.Состояние().Уст(cons.РежимОжидание)
+		return
+	}
+	// <td><div class="value-block lh1"><span><span>00:19:53</span></span></div></td>
+	strTime = strings.TrimPrefix(strTime, `<td><div class="value-block lh1"><span><span>`)
+	strTime = strings.TrimSuffix(strTime, `</span></span></div></td>`)
+	шахта.ОбратВремяУст(АВремя(strTime))
+	шахта.ВебЛог().Добавить("Шахта.количествоПолучить(): время=%q\n", strTime)
+}

+ 1 - 1
app/lev2/arena/arena_missions/arena_missions.go

@@ -25,7 +25,7 @@ func НовМиссии(конт ILocalCtx) *АренаМиссии {
 		бот:  конт.Get("бот").Val().(ИБот),
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         сам.бот,
+		Конт_:        конт,
 		АренаИмя_:    "Миссии",
 		СтрКонтроль_: "<title>Миссии</title>",
 		ФнПуск_:      сам.пуск,

+ 38 - 151
app/lev2/arena/arena_polygon/arena_polygon.go

@@ -1,7 +1,6 @@
 package arena_polygon
 
 import (
-	"log"
 	"strconv"
 	"strings"
 	"time"
@@ -12,8 +11,13 @@ import (
 	. "wartank/app/lev0/alias"
 	"wartank/app/lev0/cons"
 	. "wartank/app/lev0/types"
-	"wartank/app/lev1"
 	"wartank/app/lev2/arena"
+	"wartank/app/lev2/arena/arena_build"
+	"wartank/app/lev2/arena/arena_polygon/bf_polygon_activate"
+	"wartank/app/lev2/arena/arena_polygon/bf_polygon_build"
+	"wartank/app/lev2/arena/arena_polygon/bf_polygon_level"
+	"wartank/app/lev2/arena/arena_polygon/bf_polygon_upgrade"
+	"wartank/app/lev2/arena/arena_polygon/bf_polygon_upgrade_fast"
 )
 
 /*
@@ -31,12 +35,10 @@ const (
 
 // АренаПолигон -- объект полигона на базе
 type АренаПолигон struct {
-	ИАрена
-	бот      ИБот
+	ИАренаСтроение
+	конт     ILocalCtx
 	танкСтат ИТанкСтат
-	продукт  ИСтатПарам
 	лог      ILogBuf
-	конт     ILocalCtx
 }
 
 // НовПолигон -- возвращает новый *Polygon
@@ -45,39 +47,43 @@ func НовПолигон(конт ILocalCtx) *АренаПолигон {
 	бот := конт.Get("бот").Val().(ИБот)
 	лог.Info("НовПолигон(): бот=%s\n", бот.Имя())
 	сам := &АренаПолигон{
-		бот:      бот,
 		танкСтат: бот.Стата(),
-		продукт:  lev1.НовСтатПарам("что-то"),
 		лог:      лог,
 		конт:     конт,
 	}
 	аренаКонфиг := arena.АренаКонфиг{
-		Бот_:         бот,
+		Конт_:        конт,
 		АренаИмя_:    "Полигон",
 		СтрКонтроль_: `<title>Полигон</title>`,
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/polygon",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 	конт.Set("полигон", сам, "Полигон бота")
 	_ = ИАренаПолигон(сам)
 	return сам
 }
 
-// ПродуктСейчас -- продукт, что именно сейчас производится на полигоне
-func (сам *АренаПолигон) ПродуктСейчас() ИСтатПарам {
-	return сам.продукт
-}
-
-// ПродуктВремяСейчас -- сколько осталось времени до обновы полигона
-func (сам *АренаПолигон) ПродуктВремяСейчас() string {
-	return сам.ВремяОстат().String()
-}
-
 const (
 	стрАпгрейд = "апгрейд"
 )
 
+func (сам *АренаПолигон) Пуск() {
+	фнОбновить := func() { // Когда арена не построена -- ничего не вернёт
+		defer func() {
+			_ = recover()
+		}()
+		сам.Обновить()
+	}
+	фнОбновить()
+	bf_polygon_build.ПолигонПостроить(сам.конт)
+	bf_polygon_upgrade.ПолигонАпгрейд(сам.конт)
+	bf_polygon_upgrade_fast.ПолигонАпгрейдБесплатно(сам.конт)
+	bf_polygon_activate.ПолигонАктивировать(сам.конт)
+	bf_polygon_level.ПолигонУровень(сам.конт)
+	bf_polygon_upgrade.ПолигонАпгрейд(сам.конт)
+}
+
 // выполняет опрос полигона базы.
 func (сам *АренаПолигон) пуск() {
 	сам.ОбратВремяУст("02")
@@ -92,26 +98,13 @@ func (сам *АренаПолигон) пуск() {
 				}
 			}
 		}()
-		if сам.построитьПровер() { // Можно ли посторить?
-			if сам.построить() {
-				return
-			}
-		}
 		сам.проверитьУскорение()
 		сам.усилениеПровер()
 		сам.времяОбнов()
 
-		if сам.продукт.Имя() == стрАпгрейд {
+		if сам.ПродуктСейчас().Имя() == стрАпгрейд {
 			сам.ВремяОстат().Уст("00:10:00")
 		}
-		счёт := 5
-		for счёт > 0 {
-			if сам.уровеньПолучить() {
-				break
-			}
-			счёт--
-		}
-		log.Printf("Полигон.пуск(): бот=%q, цикл завершён\n", сам.бот.Имя())
 	}
 	for {
 		select {
@@ -125,96 +118,6 @@ func (сам *АренаПолигон) пуск() {
 	}
 }
 
-// Построить
-func (сам *АренаПолигон) построить() bool {
-	списСтр := сам.Сеть().ВебВоркер().Получ("https://wartank.ru/building-upgrade/Polygon")
-	ссылка0 := "" // ссылка на постройку
-
-	{ // Поиск ссылки на покупку
-		for _, стр := range списСтр {
-			if strings.Contains(стр, `ILinkListener-upgradeLink-link`) {
-				ссылка0 = стр
-				break
-			}
-		}
-		if ссылка0 == "" {
-			return false
-		}
-		// <a class="simple-but border mb5" href="Polygon?9-1.ILinkListener-upgradeLink-link">
-		ссылка0 = strings.TrimPrefix(ссылка0, `<a class="simple-but border mb5" href="`)
-		ссылка0 = strings.TrimSuffix(ссылка0, `">`)
-		// https://wartank.ru/building-upgrade/Polygon?40-1.ILinkListener-upgradeLink-link
-		ссылка0 = "http://wartank.ru/building-upgrade/" + ссылка0
-		списСтр = сам.Сеть().ВебВоркер().Получ(ссылка0)
-	}
-	ссылка1 := "" // ссылка на улучшение здания
-
-	{ // Выбор покупки
-		// <a class="simple-but border mb5" href="Polygon?49-1.ILinkListener-upgradeLink-link">
-		for _, стр := range списСтр {
-			if strings.Contains(стр, `ILinkListener-upgradeLink-link`) {
-				ссылка1 = стр
-				break
-			}
-		}
-		if ссылка1 == "" {
-			return false
-		}
-		ссылка1 = strings.TrimPrefix(ссылка1, `<a class="simple-but border mb5" href="`)
-		ссылка1 = strings.TrimSuffix(ссылка1, `">`)
-		// https://wartank.ru/building-upgrade/Polygon?48-1.ILinkListener-upgradeLink-link
-		ссылка1 = "http://wartank.ru/building-upgrade/" + ссылка1
-		списСтр = сам.Сеть().ВебВоркер().Получ(ссылка1)
-	}
-	ссылка2 := "" // подтверждение покупки
-	{             // Подтверждение покупки
-		// <a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../wicket/page?53-1.ILinkListener-confirmLink"><span><span>да, подтверждаю</span></span></a>
-		for _, стр := range списСтр {
-			if strings.Contains(стр, `confirmLink`) {
-				ссылка2 = стр
-				break
-			}
-		}
-		if ссылка2 == "" {
-			return false
-		}
-		ссылка2 = strings.TrimPrefix(ссылка2, `<a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../`)
-		ссылка2 = strings.TrimSuffix(ссылка2, `">`)
-		// https://wartank.ru/wicket/page?52-1.ILinkListener-confirmLink
-		ссылка2 = "http://wartank.ru/wicket/" + ссылка2
-		//ссылка2="https://wartank.ru/wicket/page?25-1.ILinkListener-confirmLink"
-		_ = сам.Сеть().ВебВоркер().Получ(ссылка2)
-	}
-	return true
-}
-
-// Проверяет уровень полигона
-func (сам *АренаПолигон) уровеньПолучить() bool {
-	var (
-		стрВых      = ""
-		еслиНайдено bool
-	)
-	lstBase := сам.Сеть().ВебВоркер().Получ("https://wartank.ru/buildings")
-	// <span class="green2">Полигон - 5</span><br/>
-	for _, стрВых = range lstBase {
-		if strings.Contains(стрВых, `<span class="green2">Полигон - `) {
-			еслиНайдено = true
-			break
-		}
-	}
-	if !еслиНайдено {
-		return false
-	}
-	стрУровень := strings.TrimPrefix(стрВых, `<span class="green2">Полигон - `)
-	стрУровень = strings.TrimSuffix(стрУровень, `</span><br/>`)
-	цУров, ош := strconv.Atoi(стрУровень)
-	if ош != nil {
-		return false
-	}
-	сам.Уровень().Уст(цУров)
-	return true
-}
-
 // Проверяет на ускорение апгрейда полигона
 func (сам *АренаПолигон) проверитьУскорение() bool {
 	var (
@@ -227,9 +130,9 @@ func (сам *АренаПолигон) проверитьУскорение() b
 		if strings.Contains(strOut, `Ускорить за`) {
 			сам.ОбратВремяУст(времОжидПлат)
 			сам.ПродуктСейчас().ИмяУст("пусто")
-			сам.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
-			сам.продукт.Уст(-1)
-			сам.продукт.ИмяУст(стрАпгрейд)
+			сам.Состояние().Уст(cons.РежимАпгрейдПлатный)
+			сам.ПродуктСейчас().Уст(-1)
+			сам.ПродуктСейчас().ИмяУст(стрАпгрейд)
 			return true
 		}
 	}
@@ -248,9 +151,9 @@ func (сам *АренаПолигон) проверитьУскорение() b
 		if !strings.Contains(strOut, `>Ускорение<`) {
 			return false
 		}
-		сам.АренаСостояние().Уст(cons.РежимАпгрейд)
-		сам.продукт.ИмяУст(стрАпгрейд)
-		сам.продукт.Уст(-1)
+		сам.Состояние().Уст(cons.РежимАпгрейд)
+		сам.ПродуктСейчас().ИмяУст(стрАпгрейд)
+		сам.ПродуктСейчас().Уст(-1)
 		lstLink := strings.Split(strOut, `<td style="width:50%;padding-left:1px;"><a class="simple-but border" href="`)
 		strLink := lstLink[1]
 		lstLink = strings.Split(strLink, `"><span><span>Ускорение</span></span></a>`)
@@ -263,31 +166,15 @@ func (сам *АренаПолигон) проверитьУскорение() b
 		// sound.ArsenalForce()
 		сам.СтрОбновить(lstBase)
 		сам.ПродуктСейчас().ИмяУст("пусто")
-		сам.АренаСостояние().Уст(cons.РежимАпгрейд)
-		сам.продукт.Уст(-1)
+		сам.Состояние().Уст(cons.РежимАпгрейд)
+		сам.ПродуктСейчас().Уст(-1)
 		сам.ОбратВремяУст(времОжидБесплат)
 	}
 	// Все проверки прошли -- это просто работа
-	сам.АренаСостояние().Уст(cons.РежимРабота)
+	сам.Состояние().Уст(cons.РежимРабота)
 	return true
 }
 
-// Проверяет необходимость постройки полигона
-func (сам *АренаПолигон) построитьПровер() bool {
-	// https://wartank.ru/building-upgrade/Polygon
-	списПолигон := сам.Сеть().ВебВоркер().Получ("https://wartank.ru/building-upgrade/Polygon")
-	стрСсылка := ""
-	еслиНайти := false
-	// <a class="simple-but border mb5" href="Polygon?66-1.ILinkListener-upgradeLink-link">
-	for _, стрСсылка = range списПолигон {
-		if strings.Contains(стрСсылка, `href="Polygon?`) {
-			еслиНайти = true
-			break
-		}
-	}
-	return еслиНайти
-}
-
 // Обновляет оставшееся время полигона
 //
 //	Этот объект сам описывает своё время
@@ -364,6 +251,6 @@ func (сам *АренаПолигон) усилениеПровер() {
 		return
 	}
 	сам.танкСтат.ФорсажОбнов(форсажИмя, iForce)
-	сам.продукт.ИмяУст("усиление-" + форсажИмя)
-	сам.продукт.Уст(iForce)
+	сам.ПродуктСейчас().ИмяУст("усиление-" + форсажИмя)
+	сам.ПродуктСейчас().Уст(iForce)
 }

+ 1 - 1
app/lev0/bfunc/bf_polygon_activate/bf_polygon_activate.go → app/lev2/arena/arena_polygon/bf_polygon_activate/bf_polygon_activate.go

@@ -28,7 +28,7 @@ func ПолигонАктивировать(конт ILocalCtx) {
 		return
 	}
 	полигон := полигон_.Val().(ИАренаПолигон)
-	еслиОжидание := полигон.АренаСостояние().Получ() == cons.РежимОжидание
+	еслиОжидание := полигон.Состояние().Получ() == cons.РежимОжидание
 	if !еслиОжидание {
 		return
 	}

+ 5 - 5
app/lev0/bfunc/bf_polygon_build/bf_polygon_build.go → app/lev2/arena/arena_polygon/bf_polygon_build/bf_polygon_build.go

@@ -13,7 +13,7 @@ import (
 // ПолигонПостроить -- постройка полигон
 func ПолигонПостроить(конт ILocalCtx) {
 	полигон := конт.Get("полигон").Val().(ИАренаПолигон)
-	if полигон.АренаСостояние().Получ() == cons.РежимНеСуществует {
+	if полигон.Состояние().Получ() == cons.РежимНеСуществует {
 		полигонПостроить(конт)
 	}
 }
@@ -33,7 +33,7 @@ func полигонПостроить(конт ILocalCtx) {
 			}
 		}
 		if ссыльПостроить == "" {
-			полигон.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+			полигон.Состояние().Уст(cons.РежимПостроено)
 			return
 		}
 		// <td style="width:50%;padding-left:1px;"><a class="simple-but border mb5" href="building-upgrade/Polygon"><span><span>Построить</span></span></a></td>
@@ -54,7 +54,7 @@ func полигонПостроить(конт ILocalCtx) {
 			}
 		}
 		if ссыльПодтвердить == "" {
-			полигон.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+			полигон.Состояние().Уст(cons.РежимПостроено)
 			return
 		}
 		ссыльПодтвердить = strings.TrimPrefix(ссыльПодтвердить, `<a class="simple-but border mb5" href="`)
@@ -73,7 +73,7 @@ func полигонПостроить(конт ILocalCtx) {
 			}
 		}
 		if ссыльДа == "" {
-			полигон.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+			полигон.Состояние().Уст(cons.РежимПостроено)
 			return
 		}
 		ссыльДа = strings.TrimPrefix(ссыльДа, `<a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../`)
@@ -81,6 +81,6 @@ func полигонПостроить(конт ILocalCtx) {
 		// https://wartank.ru/wicket/page?52-1.ILinkListener-confirmLink
 		ссыльДа = "http://wartank.ru/" + ссыльДа
 		_ = база.Сеть().ВебВоркер().Получ(ссыльДа)
-		полигон.АренаСостояние().Уст(cons.РежимАпгрейдПлатный)
+		полигон.Состояние().Уст(cons.РежимПостроено)
 	}
 }

+ 45 - 0
app/lev2/arena/arena_polygon/bf_polygon_level/bf_polygon_level.go

@@ -0,0 +1,45 @@
+// package bf_polygon_level -- бизнес-функция, ищет уровень полигона
+package bf_polygon_level
+
+import (
+	"strconv"
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	"wartank/app/lev0/cons"
+	. "wartank/app/lev0/types"
+)
+
+// ПолигонУровень -- бизнес-функция, ищет уровень полигона
+func ПолигонУровень(конт ILocalCtx) {
+	полигон := конт.Get("полигон").Val().(ИАренаПолигон)
+	if полигон.Состояние().Получ() == cons.РежимНеСуществует {
+		полигон.Уровень().Уст(0)
+		return
+	}
+	var (
+		стрВых      = ""
+		еслиНайдено bool
+	)
+	база := конт.Get("база").Val().(ИАренаБаза)
+	lstBase := база.СписПолучить()
+	if len(lstBase) == 0 {
+		база.Обновить()
+		lstBase = база.СписПолучить()
+	}
+	// <span class="green2">Полигон - 5</span><br/>
+	for _, стрВых = range lstBase {
+		if strings.Contains(стрВых, `<span class="green2">Полигон - `) {
+			еслиНайдено = true
+			break
+		}
+	}
+	Hassert(еслиНайдено, "ПолигонУровень(): не найдена контрольная строка")
+	стрУровень := strings.TrimPrefix(стрВых, `<span class="green2">Полигон - `)
+	стрУровень = strings.TrimSuffix(стрУровень, `</span><br/>`)
+	цУров, ош := strconv.Atoi(стрУровень)
+	Hassert(ош == nil, "ПолигонУровень(): уровень(%v) не число, ош=\n\t", стрУровень, ош)
+	полигон.Уровень().Уст(цУров)
+}

+ 89 - 0
app/lev2/arena/arena_polygon/bf_polygon_upgrade/bf_polygon_upgrade.go

@@ -0,0 +1,89 @@
+// package bf_polygon_upgrade -- бизнес-функция апгрейда полигона
+package bf_polygon_upgrade
+
+import (
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	"wartank/app/lev0/cons"
+	. "wartank/app/lev0/types"
+)
+
+// ПолигонАпгрейд -- повышает уровень полигона
+func ПолигонАпгрейд(конт ILocalCtx) {
+	полигон := конт.Get("полигон").Val().(ИАренаПолигон)
+	еслиПостроено := полигон.Состояние().Получ() == cons.РежимПостроено
+	еслиОжидание := полигон.Состояние().Получ() == cons.РежимОжидание
+	if !(еслиПостроено || еслиОжидание) {
+		return
+	}
+	полигонАпгрейд(конт)
+}
+
+func полигонАпгрейд(конт ILocalCtx) {
+	полигон := конт.Get("полигон").Val().(ИАренаПолигон)
+	var (
+		еслиНайти = false
+		списСтр   []string
+		стр       = ""
+	)
+	фнКупить := func() bool {
+		списСтр = полигон.Сеть().ВебВоркер().Получ("https://wartank.ru/building-upgrade/Polygon")
+		for _, стр = range списСтр {
+			// <a class="simple-but border mb5" href="Market?5-1.ILinkListener-upgradeLink-link">
+			if strings.Contains(стр, `ILinkListener-upgradeLink-link`) {
+				еслиНайти = true
+				break
+			}
+		}
+		if !еслиНайти {
+			return true
+		}
+		// Пробуем улучшить полигон
+		_стр := strings.TrimPrefix(стр, "<a class=\"simple-but border mb5\" href=\"")
+		_стр = strings.TrimSuffix(_стр, "\">")
+		// https://wartank.ru/building-upgrade/Polygon?4-1.ILinkListener-upgradeLink-link
+		// <a class="simple-but border mb5" href="Polygon?50-1.ILinkListener-upgradeLink-link">
+		ссылка := "https://wartank.ru/building-upgrade/" + _стр
+		списСтр = полигон.Сеть().ВебВоркер().Получ(ссылка)
+		// Проверить, что постройка состоялась
+		for _, стр := range списСтр {
+			if strings.Contains(стр, "ILinkListener-upgradeLink-link") {
+				return false // Покупка не оплачена
+			}
+		}
+		return true
+	}
+
+	фнПодтверждение := func() {
+		for _, стр = range списСтр {
+			// <a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="../wicket/page?7-1.ILinkListener-confirmLink"><span><span>да, подтверждаю</span></span></a>
+			if strings.Contains(стр, `ILinkListener-confirmLink`) {
+				еслиНайти = true
+				break
+			}
+		}
+		if !еслиНайти {
+			return
+		}
+		// Пробуем подтвердить оплату
+		_стр := strings.TrimPrefix(стр, `<a class="simple-but border w50 mXa mb10" w:id="confirmLink" href="..`)
+		_стр = strings.TrimSuffix(_стр, `"><span><span>да, подтверждаю</span></span></a>`)
+		// https://wartank.ru/wicket/page?6-1.ILinkListener-confirmLink
+		ссылка := "https://wartank.ru" + _стр
+		списСтр = полигон.Сеть().ВебВоркер().Получ(ссылка)
+		// Проверить, что оплата состоялась
+		for _, стр := range списСтр {
+			if strings.Contains(стр, "<title>Вы сделали слишком большую паузу</title>") {
+				return // Покупка не оплачена
+			}
+		}
+		полигон.Состояние().Уст(cons.РежимАпгрейдПлатный)
+	}
+
+	if !фнКупить() {
+		return
+	}
+	фнПодтверждение()
+}

+ 51 - 0
app/lev2/arena/arena_polygon/bf_polygon_upgrade_fast/bf_polygon_upgrade_fast.go

@@ -0,0 +1,51 @@
+// package bf_polygon_upgrade_fast -- бизнес-функция бесплатного апгрейда полигона
+package bf_polygon_upgrade_fast
+
+import (
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	"wartank/app/lev0/cons"
+	. "wartank/app/lev0/types"
+)
+
+// БанкАпгрейд -- ускоряет повышение уровня полигона бесплатно
+func ПолигонАпгрейдБесплатно(конт ILocalCtx) {
+	полигон := конт.Get("полигон").Val().(ИАренаПолигон)
+	еслиПлатно := полигон.Состояние().Получ() == cons.РежимАпгрейдПлатный
+	еслиПостроено := полигон.Состояние().Получ() == cons.РежимПостроено
+	if !(еслиПлатно || еслиПостроено) {
+		return
+	}
+	полигонАпгрейдБесплатно(конт)
+}
+
+// https://wartank.ru/buildings?16-1.ILinkListener-buildings-2-building-rootBlock-actionPanel-freeBoostLink
+func полигонАпгрейдБесплатно(конт ILocalCtx) {
+	полигон := конт.Get("полигон").Val().(ИАренаПолигон)
+	var (
+		стрСсылка   = ""
+		еслиНайдено = false
+	)
+	списБанк := полигон.Сеть().ВебВоркер().Получ("https://wartank.ru/buildings")
+	// <td style="width:50%;padding-left:1px;"><a class="simple-but border" href="buildings?61-1.ILinkListener-buildings-0-building-rootBlock-actionPanel-freeBoostLink"><span><span>Ускорение</span></span></a>
+	for _, стрСсылка = range списБанк {
+		if strings.Contains(стрСсылка, `building-rootBlock-actionPanel-freeBoostLink"><span><span>Ускорение<`) {
+			еслиНайдено = true
+			break
+		}
+	}
+	if !еслиНайдено {
+		return
+	}
+	lstLink := strings.Split(стрСсылка, `<td style="width:50%;padding-left:1px;"><a class="simple-but border" href="`)
+	_ссылка := lstLink[1]
+	_ссылка = strings.TrimSuffix(_ссылка, `"><span><span>Ускорение</span></span></a>`)
+	// https://wartank.ru/buildings?61-1.ILinkListener-buildings-0-building-rootBlock-actionPanel-freeBoostLink
+	ссылка := "https://wartank.ru/" + _ссылка
+	_ = полигон.Сеть().ВебВоркер().Получ(ссылка)
+	if полигон.Состояние().Получ() == cons.РежимПостроено {
+		полигон.Состояние().Уст(cons.РежимАпгрейдПлатный)
+	}
+}

+ 0 - 142
app/lev2/arena/down_time/down_time.go

@@ -1,142 +0,0 @@
-// package down_time -- счётчик обратного времени в мсек
-package down_time
-
-import (
-	"context"
-	"sync"
-	"time"
-
-	. "gitp78su.ipnodns.ru/svi/kern"
-	. "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
-	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
-
-	. "wartank/app/lev0/alias"
-	. "wartank/app/lev0/types"
-	"wartank/app/lev1/product/parser_time"
-)
-
-const (
-	спатьИнтервал = time.Millisecond * 1000 // Малый интервал сна в 100 мсек
-)
-
-// ВремОбрат -- счётчик обратного времени для игровой зоны (анга, база, битва и т.п.)
-type ВремОбрат struct {
-	сцена ИАренаКонтекст // Сцена, которой принадлежит отсчёт времени
-
-	остатПарсер ИПарсерВремя // Парсер значения (мсек)
-
-	текущ        ISafeInt  // Фактическое значение счётчика в мсек
-	лимит        ISafeInt  // Целевое время срабатывания в мсек
-	еслиРаботает ISafeBool // Признак работы
-
-	канВызов chan int // Канал для отправки сигналов (для верхнего уровня)
-
-	кнт      context.Context // Контекст для счётчика времени
-	фнОтмена func()          // Функция отмены контекста для счётчика времени
-
-	блок sync.RWMutex
-}
-
-// НовВремОбрат -- возвращает новый *CountTime
-func НовВремОбрат(сцена ИАренаКонтекст, время АМилСек) *ВремОбрат {
-	Hassert(сцена != nil, "НовВремОбрат(): ИСцена == nil")
-	кнт, фнОтмена := context.WithCancel(сцена.Контекст())
-	сам := &ВремОбрат{
-		сцена:        сцена,
-		текущ:        NewSafeInt(),
-		канВызов:     make(chan int, 2),
-		еслиРаботает: NewSafeBool(),
-		остатПарсер:  parser_time.НовПарсерВремя(),
-		лимит:        NewSafeInt(),
-		кнт:          кнт,
-		фнОтмена:     фнОтмена,
-	}
-	мСек := АМилСек(time.Now().UTC().UnixMilli()) + время
-	сам.лимит.Set(int(мСек))
-	сам.еслиРаботает.Set()
-	go сам.пуск()
-	go сам.закрыть()
-	_ = ИВремяОстат(сам)
-	return сам
-}
-
-// ПолучМилСек -- возвращает оставшееся хранимое время остатка
-func (сам *ВремОбрат) ПолучМилСек() АМилСек {
-	return сам.остатПарсер.ПолучМилСек()
-}
-
-// Запускает тикер для интервалов сна (через каждые 1000 мСек)
-func (сам *ВремОбрат) пуск() {
-	defer close(сам.канВызов)
-	фнЖдать := func() {
-		time.Sleep(спатьИнтервал)
-		timeNow := time.Now().UTC().UnixMilli()
-		цТекущ := сам.текущ.Get()
-		цТекущ -= 1000
-		if цТекущ < 0 {
-			цТекущ = 0
-		}
-		сам.текущ.Set(цТекущ)
-		if сам.лимит.Get() > int(timeNow) || цТекущ > 0 {
-			return
-		}
-		сам.канВызов <- 1
-		сам.лимит.Set(int(timeNow) + int(сам.остатПарсер.ПолучМилСек()))
-	}
-	for {
-		select {
-		case <-сам.кнт.Done(): // Отмена контекста тикера (а может и сцены, может и бота)
-			return
-		default:
-			фнЖдать()
-		}
-	}
-}
-
-// Сброс -- сбрасывает оставшееся время в ноль
-func (сам *ВремОбрат) Сброс() {
-	сам.остатПарсер.Сброс()
-	сам.текущ.Reset()
-	сам.лимит.Reset()
-}
-
-// Стоп -- останавливает работу счётчика
-func (сам *ВремОбрат) Стоп() {
-	сам.фнОтмена()
-}
-
-// Уст -- устанавливает число оставшихся сек
-func (сам *ВремОбрат) Уст(время АВремя) {
-	сам.блок.Lock()
-	defer сам.блок.Unlock()
-	сам.остатПарсер.Уст(время)
-	_val := сам.остатПарсер.ПолучМилСек()
-	сам.текущ.Set(int(_val))
-	val := int(time.Now().UTC().UnixMilli()) + сам.текущ.Get()
-	сам.лимит.Set(val)
-}
-
-// String -- возвращает строковое представление оставшихся сек
-func (сам *ВремОбрат) String() string {
-	сам.блок.RLock()
-	defer сам.блок.RUnlock()
-	цОстат := сам.текущ.Get()
-	остат := time.Millisecond * time.Duration(цОстат)
-	стрВрем := остат.String()
-	return стрВрем
-}
-
-// КаналСиг -- возвращает канал чтения тиков
-func (сам *ВремОбрат) КаналСиг() <-chan int {
-	return сам.канВызов
-}
-
-func (сам *ВремОбрат) закрыть() {
-	<-сам.кнт.Done()
-	сам.блок.Lock()
-	defer сам.блок.Unlock()
-	if !сам.еслиРаботает.Get() {
-		return
-	}
-	сам.еслиРаботает.Set()
-}

+ 27 - 5
app/lev2/lev2.go

@@ -10,12 +10,34 @@ import (
 	"wartank/app/lev2/arena/arena_bank"
 	"wartank/app/lev2/arena/arena_base"
 	"wartank/app/lev2/arena/arena_convoy"
-	"wartank/app/lev2/arena/arena_fuel"
+	"wartank/app/lev2/arena/arena_fuel_duel"
+	"wartank/app/lev2/arena/arena_fuel_storage"
+	"wartank/app/lev2/arena/arena_market"
+	"wartank/app/lev2/arena/arena_medal"
 	"wartank/app/lev2/arena/arena_mine"
 	"wartank/app/lev2/arena/arena_missions"
 	"wartank/app/lev2/arena/arena_polygon"
 )
 
+// НовАренаРынок -- возвращает новую арену рынка
+func НовАренаРынок(конт ILocalCtx)ИАрена{
+	рынок:=arena_market.НовРынок(конт)
+	return рынок
+}
+
+
+// НовАренаМедаль -- возвращает новую арену медалей
+func НовАренаМедали(конт ILocalCtx)ИАрена{
+	медали:=arena_medal.НовАренаМедали(конт)
+	return медали
+}
+
+// НовБойТопливо -- возвращает новую арену боя за топливо
+func НовБойТопливо(конт ILocalCtx) ИАренаСтроение {
+	арена := arena_fuel_duel.НовАренаТопливоДуэль(конт)
+	return арена
+}
+
 // НовКонвой -- возвращает новый конвой
 func НовКонвой(конт ILocalCtx) ИАренаКонвой {
 	конвой := arena_convoy.НовКонвой(конт)
@@ -34,10 +56,10 @@ func НовМиссииПростые(конт ILocalCtx) ИАренаМисси
 	return миссии
 }
 
-// НовАренаТопливо -- возвращает арену боя за топливо
-func НовАренаТопливо(конт ILocalCtx) ИАрена {
-	арена := arena_fuel.НовАренаТопливо(конт)
-	return арена
+// НовАренаБак -- возвращает арену база топлива
+func НовАренаБак(конт ILocalCtx) ИАренаБак {
+	бак := arena_fuel_storage.НовАренаБак(конт)
+	return бак
 }
 
 // НовАнгар -- возвращает новый ангар

+ 35 - 33
app/lev3/bot/bot.go

@@ -12,22 +12,11 @@ import (
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
 
 	. "wartank/app/lev0/alias"
-	"wartank/app/lev0/bfunc/bf_ammo_make"
-	"wartank/app/lev0/bfunc/bf_ammo_stat"
-	"wartank/app/lev0/bfunc/bf_arsenal_build"
-	"wartank/app/lev0/bfunc/bf_bank_build"
-	"wartank/app/lev0/bfunc/bf_fuel_attack"
-	"wartank/app/lev0/bfunc/bf_fuel_find"
-	"wartank/app/lev0/bfunc/bf_glory_make"
+
 	"wartank/app/lev0/bfunc/bf_gold_find"
-	"wartank/app/lev0/bfunc/bf_mine_accelerate"
-	"wartank/app/lev0/bfunc/bf_mine_build"
 	"wartank/app/lev0/bfunc/bf_mission_simple"
-	"wartank/app/lev0/bfunc/bf_polygon_activate"
-	"wartank/app/lev0/bfunc/bf_polygon_build"
 	"wartank/app/lev0/bfunc/bf_silver_find"
 	"wartank/app/lev0/bfunc/bf_silver_get"
-	"wartank/app/lev0/bfunc/bf_silver_prod"
 	"wartank/app/lev0/bfunc/bf_tank_stat"
 	. "wartank/app/lev0/types"
 	"wartank/app/lev2"
@@ -44,6 +33,16 @@ type Бот struct {
 	errFinal       error // Финальная ошибка работы, если была
 	сеть           ИБотСеть
 	ангар          ИАренаАнгар
+	база           ИАренаБаза
+	шахта          ИАренаШахта
+	арсенал        ИАренаАрсенал
+	полигон        ИАренаПолигон
+	банк           ИАренаБанк
+	бак            ИАренаБак
+	бойТопливо     ИАренаСтроение
+	аренаМедаль    ИАрена
+	рынок          ИАренаРынок
+	конвой         ИАренаКонвой
 	еслиРаботает   ISafeBool
 	еслиАвтозапуск ISafeBoolReact
 	лог            ILogBuf
@@ -122,20 +121,24 @@ func создатьЯдроВарБот(конфиг *bot_config.БотКонф
 	сам.еслиАвтозапуск.Add(сам.Имя(), сам.автозапускИзм)
 	сам.конт.Set("бот", сам, "создание ядра")
 	сам.конт.Set("приложение", приложение, "Приложение WarBot")
+	сам.конт.Set("бот_имя", сам.конфиг.Логин_, "Имя бота")
 	сам.сеть = bot_net.НовБотСеть(сам.конт)
 	if сам.конфиг.ЕслиАвтозапуск_ {
 		сам.еслиАвтозапуск.Set()
 		сам.Пуск()
 	}
 	сам.ангар = lev2.НовАнгар(сам.конт)
-	_ = lev2.НовКонвой(сам.конт)
-	_ = lev2.НовБанк(сам.конт)
+	сам.рынок = lev2.НовАренаРынок(сам.конт)
+	сам.аренаМедаль = lev2.НовАренаМедали(сам.конт)
+	сам.конвой = lev2.НовКонвой(сам.конт)
+	сам.банк = lev2.НовБанк(сам.конт)
 	_ = lev2.НовМиссииПростые(сам.конт)
-	_ = lev2.НовПолигон(сам.конт)
-	_ = lev2.НовБаза(сам.конт)
-	_ = lev2.НовШахта(сам.конт)
-	_ = lev2.НовАрсенал(сам.конт)
-	_ = lev2.НовАренаТопливо(сам.конт)
+	сам.полигон = lev2.НовПолигон(сам.конт)
+	сам.база = lev2.НовБаза(сам.конт)
+	сам.шахта = lev2.НовШахта(сам.конт)
+	сам.арсенал = lev2.НовАрсенал(сам.конт)
+	сам.бак = lev2.НовАренаБак(сам.конт)
+	сам.бойТопливо = lev2.НовБойТопливо(сам.конт)
 	return сам
 }
 
@@ -176,29 +179,28 @@ func (сам *Бот) пуск() {
 			return
 		default:
 			time.Sleep(time.Second * 5)
-			сам.ангар.Обновить()
-
-			bf_mine_build.ШахтаПостроить(сам.конт)
-			bf_mine_accelerate.ШахтаУскорить(сам.конт)
+			сам.ангар.Пуск()
 
-			bf_polygon_build.ПолигонПостроить(сам.конт)
+			сам.база.Обновить()
 
-			bf_bank_build.БанкПостроить(сам.конт)
+			сам.шахта.Пуск()
 
-			bf_arsenal_build.АрсеналПостроить(сам.конт)
+			сам.арсенал.Пуск()
+			сам.полигон.Пуск()
+			сам.банк.Пуск()
+			сам.бак.Пуск()
+			сам.бойТопливо.Пуск()
+			сам.аренаМедаль.Пуск()
+			сам.рынок.Пуск()
+			сам.конвой.Пуск()
 
 			bf_gold_find.ЗолотоНайти(сам.конт)
 			bf_silver_find.СереброНайти(сам.конт)
-			bf_fuel_find.ТопливоНайти(сам.конт)
-			bf_fuel_attack.ТопливоАтаковать(сам.конт)
+
 			bf_mission_simple.МиссииПростыеЗабрать(сам.конт)
 			bf_silver_get.СереброЗабрать(сам.конт)
-			bf_silver_prod.СереброПроизводить(сам.конт)
-			bf_polygon_activate.ПолигонАктивировать(сам.конт)
+
 			bf_tank_stat.ТанкСтатПолучить(сам.конт)
-			bf_ammo_stat.СнарядыСтат(сам.конт)
-			bf_ammo_make.СнарядыСделать(сам.конт)
-			bf_glory_make.СлаваБой(сам.конт)
 		}
 	}
 }

+ 3 - 3
app/lev3/serv_web/web_api/web_api.go

@@ -282,7 +282,7 @@ func (сам *ВебАпи) арсеналРежим(кнт *fiber.Ctx) error {
 		return кнт.SendString("[Режим: нет такого бота]")
 	}
 	арсенал := бот.КонтБот().Get("арсенал").Val().(ИАренаАрсенал)
-	сценаРежим := арсенал.АренаСостояние()
+	сценаРежим := арсенал.Состояние()
 	стрРежим := fmt.Sprint(сценаРежим.Получ())
 	if стрРежим == "" {
 		return кнт.SendString("[Режим: пустой режим]")
@@ -448,7 +448,7 @@ func (сам *ВебАпи) полигонРежим(кнт *fiber.Ctx) error {
 		return кнт.SendString("[Режим: нет такого бота]")
 	}
 	полигон := бот.КонтБот().Get("полигон").Val().(ИАренаПолигон)
-	сценаРежим := полигон.АренаСостояние()
+	сценаРежим := полигон.Состояние()
 	стрРежим := fmt.Sprint(сценаРежим.Получ())
 	if стрРежим == "" {
 		return кнт.SendString("[Режим: пустой режим]")
@@ -590,7 +590,7 @@ func (сам *ВебАпи) шахтаРежим(кнт *fiber.Ctx) error {
 		return кнт.SendString("[Режим: нет такого бота]")
 	}
 	шахта := бот.КонтБот().Get("шахта").Val().(ИАренаШахта)
-	сценаРежим := шахта.АренаСостояние()
+	сценаРежим := шахта.Состояние()
 	стрРежим := fmt.Sprint(сценаРежим.Получ())
 	if стрРежим == "" {
 		return кнт.SendString("[Режим: пустой режим]")

+ 4 - 3
app/lev3/serv_web/web_gui/page_bot_show/bot_show.tmpl.html

@@ -23,7 +23,7 @@
             {.attack} {.броня}<br><br> {.точность} {.прочность}
         </div>
         <div class="card-footer border-success">
-            {.мощь}
+            {.block_power}
         </div>
     </div>
 
@@ -38,8 +38,9 @@
             {.шахта_уровень} {.шахта_режим}<br><br>
             {.шахта_сделать_кол} {.шахта_сделать_назв}
         </div>
-        <div class="card-footer border-success" hx-post="/api/bot/{.id}/mine/back_time" hx-trigger="every 5s">
-            Время: {.шахта_сделать_время}</div>
+        <div class="card-footer border-success">
+            {.шахта_сделать_время}
+        </div>
         <button type="button" class="btn btn-secondary" hx-post="/api/bot/{.id}/mine/log" hx-trigger="click"
             hx-target="#mine_log">Лог</button>
         <div class="text" id="mine_log"></div>

+ 1 - 1
app/lev3/serv_web/web_gui/page_bot_show/btn_gold/btn_gold.go

@@ -38,7 +38,7 @@ func (sf *BtnGold) Html() string {
 
 func (sf *BtnGold) click(dict map[string]string) string {
 	bot := sf.dictBot.Get(dict)
-	if bot==nil{
+	if bot == nil {
 		sf.Btn.Text().Set("Золото: не бота")
 		return sf.Btn.Html()
 	}

+ 1 - 1
app/lev3/serv_web/web_gui/page_bot_show/btn_mine_mode/btn_mine_mode.go

@@ -43,7 +43,7 @@ func (sf *BtnMineMode) click(dict map[string]string) string {
 		return sf.Btn.Html()
 	}
 	шахта := bot.КонтБот().Get("шахта").Val().(ИАренаШахта)
-	strOut := fmt.Sprintf("[Режим: %v]", шахта.АренаСостояние().Получ())
+	strOut := fmt.Sprintf("[Режим: %v]", шахта.Состояние().Получ())
 	sf.Btn.Text().Set(strOut)
 	return sf.Btn.Html()
 }

+ 49 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_mine_time/btn_mine_time.go

@@ -0,0 +1,49 @@
+package btn_mine_time
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnMineTime struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnMineTime() *BtnMineTime {
+	ctx := GetKernelCtx()
+	sf := &BtnMineTime{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Время:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnMineTime) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnMineTime) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot == nil {
+		sf.Btn.Text().Set("Время: не бота")
+		return sf.Btn.Html()
+	}
+	шахта := bot.КонтБот().Get("шахта").Val().(ИАренаШахта)
+	strOut := fmt.Sprintf("[Время: %v]", шахта.ПродуктВремяСейчас().Get())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 1 - 1
app/lev3/serv_web/web_gui/page_bot_show/btn_power/btn_power.go

@@ -39,7 +39,7 @@ func (sf *BtnPower) Html() string {
 func (sf *BtnPower) click(dict map[string]string) string {
 	bot := sf.dictBot.Get(dict)
 	if bot == nil {
-		sf.Btn.Text().Set("Мощь: не бота")
+		sf.Btn.Text().Set("[Мощь: нет бота]")
 		return sf.Btn.Html()
 	}
 	strOut := fmt.Sprintf("[Мощь: %v]", bot.Стата().Мощь().ЗначСтр())

+ 1 - 2
app/lev3/serv_web/web_gui/page_bot_show/btn_silver/btn_silver.go

@@ -26,7 +26,6 @@ func NewBtnSilver() *BtnSilver {
 		ctx:     ctx,
 		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
 		dictBot: dict_bot.GetDictBot(),
-
 	}
 	sf.Btn = wui.NewWuiButton("Серебро:", sf.click)
 	sf.Btn.Hx().Trigger().Set("every 5s")
@@ -40,7 +39,7 @@ func (sf *BtnSilver) Html() string {
 
 func (sf *BtnSilver) click(dict map[string]string) string {
 	bot := sf.dictBot.Get(dict)
-	if bot==nil{
+	if bot == nil {
 		sf.Btn.Text().Set("Серебро: не бота")
 		return sf.Btn.Html()
 	}

+ 14 - 10
app/lev3/serv_web/web_gui/page_bot_show/page_bot_show.go

@@ -24,6 +24,7 @@ import (
 	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_mine_count"
 	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_mine_level"
 	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_mine_mode"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_mine_time"
 	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_mine_type"
 	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_power"
 	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_silver"
@@ -44,9 +45,10 @@ type СтраницаБотПоказать struct {
 	кнпПрочность    *btn_hard.BtnHard
 	кнпМощь         *btn_power.BtnPower
 	кнпШахтаУровень *btn_mine_level.BtnMineLevel
-	кнпШахтаРежим    *btn_mine_mode.BtnMineMode
+	кнпШахтаРежим   *btn_mine_mode.BtnMineMode
 	кнпШахтаПродукт *btn_mine_count.BtnMineCount
-	кнпШахтаТип *btn_mine_type.BtnMineType
+	кнпШахтаТип     *btn_mine_type.BtnMineType
+	кнпШахтаВремя   *btn_mine_time.BtnMineTime
 }
 
 //go:embed bot_show.tmpl.html
@@ -70,9 +72,10 @@ func НовСтраницаБотПоказать(конт IKernelCtx) *Стра
 		кнпПрочность:    btn_hard.NewBtnHard(),
 		кнпМощь:         btn_power.NewBtnPower(),
 		кнпШахтаУровень: btn_mine_level.NewBtnMineLevel(),
-		кнпШахтаРежим: btn_mine_mode.NewBtnMineMode(),
+		кнпШахтаРежим:   btn_mine_mode.NewBtnMineMode(),
 		кнпШахтаПродукт: btn_mine_count.NewBtnMineCount(),
-		кнпШахтаТип: btn_mine_type.NewBtnMineType(),
+		кнпШахтаТип:     btn_mine_type.NewBtnMineType(),
+		кнпШахтаВремя:   btn_mine_time.NewBtnMineTime(),
 	}
 	файбер := конт.Get("fiberApp").Val().(*fiber.App)
 	файбер.Post("/gui/bot/:id/show", сам.гетБотПоказ)
@@ -122,25 +125,26 @@ func (сам *СтраницаБотПоказать) гетБотПоказ(к
 		сам.рендер.Доб("{.броня}", сам.кнпБроня.Html())
 		сам.рендер.Доб("{.точность}", сам.кнпТочность.Html())
 		сам.рендер.Доб("{.прочность}", сам.кнпПрочность.Html())
-		сам.рендер.Доб("{.мощь}", сам.кнпМощь.Html())
+		мощь := сам.кнпМощь.Html()
+		сам.рендер.Доб("{.block_power}", мощь)
 	}
 	{ // Шахта
 		сам.кнпШахтаУровень.Btn.Hx().Vals().Set("id", иНомер)
 		сам.кнпШахтаРежим.Btn.Hx().Vals().Set("id", иНомер)
 		сам.кнпШахтаПродукт.Btn.Hx().Vals().Set("id", иНомер)
 		сам.кнпШахтаТип.Btn.Hx().Vals().Set("id", иНомер)
+		сам.кнпШахтаВремя.Btn.Hx().Vals().Set("id", иНомер)
 
-		шахта := бот.КонтБот().Get("шахта").Val().(ИАренаШахта)
 		сам.рендер.Доб("{.шахта_уровень}", сам.кнпШахтаУровень.Html())
 		сам.рендер.Доб("{.шахта_режим}", сам.кнпШахтаРежим.Html())
 		сам.рендер.Доб("{.шахта_сделать_кол}", сам.кнпШахтаПродукт.Html())
 		сам.рендер.Доб("{.шахта_сделать_назв}", сам.кнпШахтаТип.Html())
-		сам.рендер.Доб("{.шахта_сделать_время}", шахта.ПродуктВремяСейчас())
+		сам.рендер.Доб("{.шахта_сделать_время}", сам.кнпШахтаВремя.Html())
 	}
 	{ // Полигон
 		полигон := бот.КонтБот().Get("полигон").Val().(ИАренаПолигон)
 		сам.рендер.Доб("полигон_уровень", полигон.Уровень().ЗначСтр())
-		сам.рендер.Доб("полигон_режим", полигон.АренаСостояние().Получ())
+		сам.рендер.Доб("полигон_режим", полигон.Состояние().Получ())
 		сам.рендер.Доб("полигон_сделать_кол", полигон.ПродуктСейчас().ЗначСтр())
 		сам.рендер.Доб("полигон_сделать_назв", полигон.ПродуктСейчас().Имя())
 		сам.рендер.Доб("полигон_сделать_время", полигон.ПродуктВремяСейчас())
@@ -149,12 +153,12 @@ func (сам *СтраницаБотПоказать) гетБотПоказ(к
 		арс := бот.КонтБот().Get("арсенал").Val().(ИАренаАрсенал)
 		сам.рендер.Доб("оружейная_уровень", арс.Уровень().ЗначСтр())
 		сам.рендер.Доб("оружейная_работа", арс.ПродуктСейчас().Имя())
-		сам.рендер.Доб("оружейная_режим", арс.АренаСостояние().Получ())
+		сам.рендер.Доб("оружейная_режим", арс.Состояние().Получ())
 		сам.рендер.Доб("оружейная_кумул", арс.Кумулятивы().ЗначСтр())
 		сам.рендер.Доб("оружейная_бронебойки", арс.Бронебойки().ЗначСтр())
 		сам.рендер.Доб("оружейная_фугасы", арс.Фугасы().ЗначСтр())
 		сам.рендер.Доб("оружейная_ремки", арс.Ремки().ЗначСтр())
-		сам.рендер.Доб("оружейная_время", арс.ВремяОстат().String())
+		сам.рендер.Доб("оружейная_время", арс.ПродуктВремяСейчас().Get())
 		сам.рендер.Доб("мощь", бот.Стата().Мощь().ЗначСтр())
 		сам.рендер.Доб("мощь", бот.Стата().Мощь().ЗначСтр())
 	}