Przeglądaj źródła

SVI Возврат сражения

SVI 1 rok temu
rodzic
commit
b718de8696

+ 28 - 2
app/lev0/bfunc/bf_mission_simple/bf_missin_simple.go

@@ -67,7 +67,20 @@ func сражениеЗащита(конт ILocalCtx) {
 		strOut      string
 		еслиНайдено bool
 	)
-	арена.Обновить()
+	фнЗабрать := func() (isOk bool) { // Здесь бывают задержки из-за того, что эта часть работает неправильно
+		isOk = true
+		defer func() {
+			if _panic := recover(); _panic != nil {
+				isOk = false
+			}
+		}()
+		арена.Обновить()
+		return isOk
+	}
+	if !фнЗабрать() {
+		return
+	}
+
 	списМиссия := арена.СписПолучить()
 	// <a class="simple-but border" href="?23-1.ILinkListener-missions-cc-0-c-awardLink"><span><span>Получить награду</span></span></a>
 	for _, strOut = range списМиссия {
@@ -137,7 +150,20 @@ func сражениеНаступление(конт ILocalCtx) {
 		lstMissions = арена.СписПолучить()
 	)
 	if len(lstMissions) == 0 {
-		арена.Обновить()
+		фнЗабрать := func() (isOk bool) { // Здесь бывают задержки из-за того, что эта часть работает неправильно
+			isOk = true
+			defer func() {
+				if _panic := recover(); _panic != nil {
+					isOk = false
+				}
+			}()
+			арена.Обновить()
+			return isOk
+		}
+		if !фнЗабрать() {
+			return
+		}
+
 		lstMissions = арена.СписПолучить()
 	}
 	for ind, strOut = range lstMissions {

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

@@ -10,7 +10,7 @@ import (
 
 // ИДивизияВойнаДействие -- интерфейс к непосредственному сражению
 type ИСражениеПроцесс interface {
-	ИАрена
+	ИАренаСтроение
 	// Манёвр -- выполняет манёвр по требованию
 	Манёвр() ИМанёвр
 	// ЕслиКонец -- признак окончания битвы дивизий

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

@@ -4,4 +4,8 @@ package types
 type ИВыстрел interface {
 	// ЕслиБлок -- если установлена блокировка на выстрел
 	ЕслиБлок() bool
+	// БлокСброс -- сбросить блокировку выстрела
+	БлокСброс()
+	// БлокУст -- установить блокировку выстрела
+	БлокУст()
 }

+ 129 - 183
app/lev1/health/health.go

@@ -1,9 +1,7 @@
 package health
 
 import (
-
-	// "log"
-	// "strconv"
+	"strconv"
 	"strings"
 	"time"
 
@@ -23,18 +21,16 @@ import (
 
 // Здоровье -- контроль здоровья танка
 type Здоровье struct {
-	ИСражениеПроцесс // FIXME:
-	fnCancel         func()
-	temp             *health_time.HealthTime // Изменяемое здоровье танка
-	full             *health_time.HealthTime // Полное здоровье танка
-	isRepair         ISafeBool               // Необходимость восстановления
-	repairTime       *repair_time.RepairTime // Время до восстановления
+	ИСражениеПроцесс                         // FIXME:
+	здоровьеСейчас   *health_time.HealthTime // Изменяемое здоровье танка
+	здоровьеПолное   *health_time.HealthTime // Полное здоровье танка
+	еслиНадо         ISafeBool               // Необходимость восстановления
+	отсчётАптечка    *repair_time.RepairTime // Время до восстановления
 	isEnd            ISafeBool               // Ссылка на признак конца сражения
-	login            string                  // Для поиска контрольных строк
+	логин            string                  // Для поиска контрольных строк
 	chTick           chan int                // Канал для ровной отправки тиков
 	// deltaOld                    int                    // Старая дельта потери здоровья
 	// countLow                    int
-	ctxBattle ILocalCtx // Контекст сражения
 }
 
 // НовЗдоровье -- возвращает новый *Health
@@ -43,14 +39,12 @@ func НовЗдоровье(проц ИСражениеПроцесс) *Здор
 	логин := проц.Бот().Имя()
 	сам := &Здоровье{
 		ИСражениеПроцесс: проц,
-		fnCancel:         проц.Отменить,
-		ctxBattle:        проц.Бот().КонтБот(),
-		temp:             health_time.NewHealthTime(),
-		full:             health_time.NewHealthTime(),
-		isRepair:         NewSafeBool(),
-		repairTime:       repair_time.NewRepairTime(),
+		здоровьеСейчас:   health_time.NewHealthTime(),
+		здоровьеПолное:   health_time.NewHealthTime(),
+		еслиНадо:         NewSafeBool(),
+		отсчётАптечка:    repair_time.NewRepairTime(),
 		isEnd:            проц.ЕслиКонец(),
-		login:            логин,
+		логин:            логин,
 		chTick:           make(chan int, 2),
 	}
 	go сам.makeTik()
@@ -61,33 +55,35 @@ func НовЗдоровье(проц ИСражениеПроцесс) *Здор
 // Отправляет тики с заданным равным интервалом
 func (сам *Здоровье) makeTik() {
 	defer func() {
-		сам.fnCancel()
 		close(сам.chTick)
-		// log._rintf("Health.makeTick(): сражение завершёно\n")
+		сам.Отменить()
 	}()
-	count := 0
-	repairTime := 0
+	лимитАптечка := 0 // Предел времени ожидания
+	отсчётАптечка := 0
 	for {
 		select {
-		case <-сам.ctxBattle.Ctx().Done():
+		case <-сам.Контекст().Done():
 			return
 		default:
 			if сам.ЕслиУбит() {
 				return
 			}
-			if сам.repairTime.Get() == repairTime {
-				count++
+			if сам.отсчётАптечка.Получ() == отсчётАптечка {
+				лимитАптечка++
 			} else {
-				repairTime = сам.repairTime.Get()
-				count = 0
+				отсчётАптечка = сам.отсчётАптечка.Получ()
+				лимитАптечка = 0
+			}
+			if сам.отсчётАптечка.IsReady() {
+				лимитАптечка = 0
 			}
-			if count > 90 {
+			if лимитАптечка > 90 {
 				return
 			}
 		}
 		сам.chTick <- 1
 		time.Sleep(time.Second * 1)
-		сам.repairTime.Dec()
+		сам.отсчётАптечка.Dec()
 	}
 }
 
@@ -95,45 +91,36 @@ func (сам *Здоровье) makeTik() {
 func (сам *Здоровье) run() {
 	for {
 		select {
-		case <-сам.ctxBattle.Ctx().Done():
+		case <-сам.Контекст().Done():
 			сам.isEnd.Set()
 			return
 		case <-сам.chTick:
-			// if err := сам.findHealth(); err != nil { // Найти свой здоровье
-			// 	// log._rintf("ERRO Health.run(): при попытке найти здоровье, err=\n\t%v\n", err)
-			// }
-			сам.findRepairTime()
-			if сам.Выстрел().ЕслиБлок() {
-				if сам.isRepair.Get() {
-					go сам.repair()
-				}
-				continue
-			}
-			if сам.isRepair.Get() {
+			сам.здоровьеНайти()
+			сам.найтиВремяВосстановления()
+			if сам.еслиНадо.Get() {
 				go сам.repair()
 			}
 		}
 	}
 }
 
-// Full -- возвращает объект полного здоровья танка
-func (сам *Здоровье) Full() int {
-	return сам.full.Get()
+// Полное -- возвращает объект полного здоровья танка
+func (сам *Здоровье) Полное() int {
+	return сам.здоровьеПолное.Get()
 }
 
 // ЕслиУбит -- возвращает признак мертвичины танка
 func (сам *Здоровье) ЕслиУбит() bool {
 	if сам.isEnd.Get() {
-		сам.fnCancel()
+		сам.Отменить()
 		return true
 	}
 	lstBattle := сам.СписПолучить()
 	for _, strOut := range lstBattle {
 		if strings.Contains(strOut, `>Ваш танк подбит.`) {
-			// log._rintf("INFO Health.repair(): танк подбит\n")
-			сам.temp.Set(0)
+			сам.здоровьеСейчас.Set(0)
 			сам.isEnd.Set()
-			сам.fnCancel()
+			сам.Отменить()
 			return true
 		}
 	}
@@ -141,16 +128,8 @@ func (сам *Здоровье) ЕслиУбит() bool {
 }
 
 // Ищет время восстановления ремки
-func (сам *Здоровье) findRepairTime() {
-	defer func() {
-		if сам.repairTime.IsReady() {
-			return
-		}
-		// if сам.repairTime.IsChange() {
-		// log._rintf("INFO Health.findRepair(): до ремки=%v\n", сам.repairTime.Get())
-		// }
-	}()
-	if сам.repairTime.IsReady() {
+func (сам *Здоровье) найтиВремяВосстановления() {
+	if сам.отсчётАптечка.IsReady() {
 		return
 	}
 	var (
@@ -162,10 +141,7 @@ func (сам *Здоровье) findRepairTime() {
 	// <a href="pve?19-14.ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>12 секунд</span></span></a>
 	//
 	for ind, strOut = range lstBattle {
-		if !strings.Contains(strOut, `ILinkListener-currentControl-repairLink`) {
-			continue
-		}
-		if strings.Contains(strOut, ` секунд</span></span></a>`) {
+		if strings.Contains(strOut, `ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>`) {
 			еслиНайдено = true
 			break
 		}
@@ -177,17 +153,17 @@ func (сам *Здоровье) findRepairTime() {
 	// <a href="pve?19-14.ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>12 секунд</span></span></a>
 	lstTime := strings.Split(strOut, `ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>`)
 	if len(lstTime) < 2 {
-		// log._rintf("ERRO Health.findRepair(): при попытке получить ссылку на ремонт, strOut=\n%v\n", strOut)
 		сам.isEnd.Set()
-		сам.fnCancel()
+		сам.Отменить()
+		return
+	}
+	strTime := lstTime[1]
+	strTime = strings.TrimSuffix(strTime, ` секунд</span></span></a>`)
+	if err := сам.отсчётАптечка.Уст(strTime); err != nil {
+		сам.isEnd.Set()
+		сам.Отменить()
 		return
 	}
-	// strTime := lstTime[1]
-	// lstTime = strings.Split(strTime, ` секунд</span></span></a>`)
-	// strTime = lstTime[0]
-	// if err := сам.repairTime.Set(strTime); err != nil {
-	// 	 log._rintf("ERRO Health.findRepair(): при установке времени восстановления ремки, err=\n\t%v\n", err)
-	// }
 }
 
 // Восстанавливает здоровье (~)
@@ -219,7 +195,7 @@ func (сам *Здоровье) repair() {
 	if err != nil {
 		// log._rintf("ERRO Health.repair(): при выполнении GET-команды ремонта, err=\n\t%v\n", err)
 		сам.isEnd.Set()
-		сам.fnCancel()
+		сам.Отменить()
 		return
 	}
 	сам.СтрОбновить(lstBattleOn)
@@ -227,117 +203,87 @@ func (сам *Здоровье) repair() {
 }
 
 // Ищет своё здоровье (~)
-// func (сам *Health) findHealth() error {
-// 	var (
-// 		ind         int
-// 		strOut      string
-// 		еслиНайдено bool
-// 		lstBattle   = сам.СписПолучить()
-// 	)
-// 	if len(lstBattle) == 0 { // Принудительно обновим сражение
-// 		if err := сам.Сеть().Обновить(); err != nil {
-// 			сам.isEnd.Уст()
-// 			сам.fnCancel()
-// 			return fmt.Errorf("Health.findHealth(): после принудительного обновления lsBattleOn, err=\n\t%w", err)
-// 		}
-// 	}
-// 	for ind, strOut = range lstBattle {
-// 		if strings.Contains(strOut, `alt="`+сам.login+`"`) {
-// 			еслиНайдено = true
-// 			break
-// 		}
-// 	}
-// 	if !еслиНайдено { // Свой танк не найден
-// 		сам.isEnd.Уст()
-// 		сам.fnCancel()
-// 		return fmt.Errorf("Health.findHealth(): своё здоровье не найдено")
-// 	}
-// 	// Свой танк найден, ищем здоровье
-// 	ind += 11
-// 	strOut = lstBattle[ind]
-// 	lstHealth := strings.Split(strOut, `<div class="value-block lh1"><span><span>`)
-// 	strHealth := lstHealth[1]
-// 	lstHealth = strings.Split(strHealth, `</span></span></div>`)
-// 	strHealth = lstHealth[0]
-// 	iHealth, err := strconv.Atoi(strHealth)
-// 	if err != nil {
-// 		сам.isEnd.Уст()
-// 		сам.CancelBattle()
-// 		return fmt.Errorf("Health.findHealth(): здоровье(%v) не число, err=%w", strHealth, err)
-// 	}
-// 	сам.setHealth(iHealth)
-// 	return nil
-// }
-
-// setHealth -- устанавливает текущее здоровье
-// func (сам *Health) setHealth(val int) {
-// 	if val < 0 {
-// 		// log._rintf("WARN Health.setHealth(): кривое значение здоровья танка(%v)\n", val)
-// 		val = 0
-// 	}
-
-// 	if val > сам.full.Get() {
-// 		// log._rintf("WARN Health.setHealth(): кривое текущее здоровье, %v/%v\n", val, сам.full.Get())
-// 		сам.full.Set(val)
-// 		сам.temp.Set(val)
-// 		// сам.deltaOld = 0
-// 		сам.ВыстрелБлок().Сброс()
-// 		сам.isRepair.Сброс()
-// 		return
-// 	}
-
-// 	delta := сам.temp.Get() - val
-// 	// if delta > 0 { // Дельта будет больше нуля, если только
-// 	// 	if delta != сам.deltaOld {
-// 	// 		// log._rintf("INFO Health.setHealth(): потеря здоровья=%v/%v\n", -delta, val)
-// 	// 		сам.deltaOld = delta
-// 	// 		сам.temp.Set(val)
-// 	// 	}
-// 	// }
+func (сам *Здоровье) здоровьеНайти() {
+	var (
+		ind         int
+		strOut      string
+		еслиНайдено bool
+		lstBattle   = сам.СписПолучить()
+	)
+	if len(lstBattle) == 0 { // Принудительно обновим сражение
+		сам.Обновить()
+		lstBattle = сам.СписПолучить()
+	}
+	// <div class="small bold green1 sh_b mb10 mt5">Половина коня</div>
+	for ind, strOut = range lstBattle {
+		if strings.Contains(strOut, `<div class="small bold green1 sh_b mb10 mt5">`+сам.логин+`"`) {
+			еслиНайдено = true
+			break
+		}
+	}
+	if !еслиНайдено { // Свой танк не найден
+		сам.isEnd.Set()
+		сам.Отменить()
+		return
+	}
+	// Свой танк найден, ищем здоровье
+	// <div class="value-block lh1"><span><span>500</span></span></div>
+	ind += 11
+	strOut = lstBattle[ind]
+	strHealth := strings.TrimPrefix(strOut, `<div class="value-block lh1"><span><span>`)
+	strHealth = strings.TrimSuffix(strHealth, `</span></span></div>`)
+	iHealth, err := strconv.Atoi(strHealth)
+	if err != nil {
+		сам.isEnd.Set()
+		сам.Отменить()
+		return
+	}
+	сам.здоровьеУстановить(iHealth)
+}
 
-// 	switch {
-// 	case сам.isEnd.Получ():
-// 		сам.temp.Set(0)
-// 		сам.isEnd.Уст()
-// 		сам.CancelBattle()
-// 		return
-// 	case val == 0:
-// 		сам.temp.Set(0)
-// 		сам.isEnd.Уст()
-// 		сам.CancelBattle()
-// 		return
-// 	case val <= 500: // Запретить стрельбу
-// 		сам.ВыстрелБлок().Уст() // Установить запрет стрельбы пока слабое здоровье
-// 		сам.isRepair.Уст()
-// 		// log._rintf("WARN Health.setHealth(): низкий уровень здоровья(%v)\n", val)
-// 		сам.Манёвр()
-// 	case val > 500: // Разрешить стрельбы
-// 		сам.ВыстрелБлок().Сброс()
-// 		сам.isRepair.Сброс()
-// 		if delta > сам.full.Get()*4/10 { // Проверить на критичность падения здоровья на 40%
-// 			// log._rintf("WARN Health.setHealth(): большая разовая потеря здоровья(%v)\n", delta)
-// 			сам.Манёвр()
-// 			сам.isRepair.Уст()
-// 			return
-// 		}
-// 	}
+// здоровьеУстановить -- устанавливает текущее здоровье
+func (сам *Здоровье) здоровьеУстановить(здоровье int) {
+	if здоровье < 0 {
+		// log._rintf("WARN Health.здоровьеУстановить(): кривое значение здоровья танка(%v)\n", val)
+		здоровье = 0
+	}
+	дельта := сам.здоровьеСейчас.Get() - здоровье
+	if дельта < 0 { // Такое может быть, если было лечение
+		дельта = 0
+	}
+	if здоровье >= сам.здоровьеПолное.Get() {
+		// log._rintf("WARN Health.здоровьеУстановить(): кривое текущее здоровье, %v/%v\n", val, сам.full.Get())
+		сам.здоровьеПолное.Set(здоровье)
+		сам.здоровьеСейчас.Set(здоровье)
+		// сам.deltaOld = 0
+		сам.Выстрел().БлокСброс()
+		сам.еслиНадо.Reset()
+		return
+	}
 
-// 	isMask := сам.ВыстрелБлок().Получ()
-// 	switch isMask {
-// 	case true:
-// 		// сам.countLow++
-// 		// if сам.countLow >= 200 {
-// 		// 	сам.isEnd.Уст()
-// 		// 	сам.CancelBattle()
-// 		// 	return
-// 		// }
-// 	default:
-// 		// сам.countLow = 0
-// 	}
-// 	if val == сам.full.Get() {
-// 		сам.temp.Set(val)
-// 		сам.isRepair.Сброс()
-// 		сам.ВыстрелБлок().Сброс()
-// 		// сам.countLow = 0
-// 	}
-// }
+	switch {
+	case сам.isEnd.Get(): // Конец сражения
+		сам.здоровьеСейчас.Set(0)
+		сам.isEnd.Set()
+		сам.Отменить()
+		return
+	case здоровье <= 0: // Убит
+		сам.здоровьеСейчас.Set(0)
+		сам.isEnd.Set()
+		сам.Отменить()
+		return
+	case здоровье <= 500: // Запретить стрельбу
+		сам.Выстрел().БлокУст() // Установить запрет стрельбы пока слабое здоровье
+		сам.еслиНадо.Set()
+		сам.Манёвр().УстНадо()
+	case здоровье > 500: // Разрешить стрельбу
+		сам.Выстрел().БлокСброс()
+		сам.еслиНадо.Reset()
+		if дельта > сам.здоровьеПолное.Get()*4/10 { // Проверить на критичность падения здоровья на 40%
+			// log._rintf("WARN Health.здоровьеУстановить(): большая разовая потеря здоровья(%v)\n", delta)
+			сам.Манёвр().УстНадо()
+			сам.еслиНадо.Set()
+			return
+		}
+	}
+}

+ 5 - 5
app/lev1/manevr/manevr.go

@@ -45,7 +45,7 @@ func НовМанёвр(проц ИСражениеПроцесс) ИМанёв
 		manevrTime:       repair_time.NewRepairTime(),
 		chTick:           make(chan int, 1),
 	}
-	_ = сам.manevrTime.Set("0") // При запуске боя есть возможность маневрировать
+	_ = сам.manevrTime.Уст("0") // При запуске боя есть возможность маневрировать
 	go сам.makeTick()
 	go сам.run()
 	return сам
@@ -72,7 +72,7 @@ func (сам *манёвр) makeTick() {
 		case <-сам.ctxEnd.Done():
 			return
 		default:
-			if сам.manevrTime.Get() == 0 {
+			if сам.manevrTime.Получ() == 0 {
 				сам.chTick <- 1
 			}
 
@@ -109,7 +109,7 @@ func (сам *манёвр) findManevrTime() {
 	}
 	if !еслиНайдено { // Или манёвр успел восстановиться, или конец сражения
 		if strings.Contains(стрВых, `<span>Маневр</span>`) {
-			_ = сам.manevrTime.Set("0")
+			_ = сам.manevrTime.Уст("0")
 			time.Sleep(time.Second * 1)
 			return
 		}
@@ -137,7 +137,7 @@ func (сам *манёвр) findManevrTime() {
 		strTime := lstTime[1]
 		lstTime = strings.Split(strTime, ` секунд</span></span></a>`)
 		strTime = lstTime[0]
-		if err := сам.manevrTime.Set(strTime); err != nil {
+		if err := сам.manevrTime.Уст(strTime); err != nil {
 			сам.лог.Err("манёвр.findManevrTime(): при обновлении времени ожидания манёвра, ош=\n\t%v", err)
 			сам.еслиГотов.Reset()
 			time.Sleep(time.Second * 1)
@@ -145,7 +145,7 @@ func (сам *манёвр) findManevrTime() {
 		}
 	}
 	сам.еслиГотов.Set()
-	сам.лог.Info("манёвр.findManevrTime(): до манёвра, время=%v", сам.manevrTime.Get())
+	сам.лог.Info("манёвр.findManevrTime(): до манёвра, время=%v", сам.manevrTime.Получ())
 }
 
 // Выполнить -- принудительный манёвр по требованию

+ 6 - 6
app/lev1/repair_time/repair_time.go

@@ -22,22 +22,22 @@ func NewRepairTime() *RepairTime {
 	return &RepairTime{}
 }
 
-// Get -- возвращает хранимое значение времени
-func (сам *RepairTime) Get() int {
+// Получ -- возвращает хранимое значение времени
+func (сам *RepairTime) Получ() int {
 	сам.block.RLock()
 	defer сам.block.RUnlock()
 	return сам.val
 }
 
-// GetOld -- возвращает хранимое старое значение времени
-func (сам *RepairTime) GetOld() int {
+// Старое -- возвращает хранимое старое значение времени
+func (сам *RepairTime) Старое() int {
 	сам.block.RLock()
 	defer сам.block.RUnlock()
 	return сам.valOld
 }
 
-// Set -- устанавливает хранимое время восстановления ремки
-func (сам *RepairTime) Set(val string) error {
+// Уст -- устанавливает хранимое время восстановления ремки
+func (сам *RepairTime) Уст(val string) error {
 	сам.block.Lock()
 	defer сам.block.Unlock()
 	iVal, err := strconv.Atoi(val)

+ 35 - 31
app/lev1/shot/shot.go

@@ -1,9 +1,6 @@
 package shot
 
 import (
-	"context"
-
-	// "log"
 	"strconv"
 	"strings"
 	"time"
@@ -32,14 +29,13 @@ import (
 // выстрел -- объект выстрела
 type выстрел struct {
 	ИСражениеПроцесс                     // FIXME:
-	recharge         *shot_time.ShotTime // Сколько времени нужно для полной перезарядки
-	damage           *damage.Damage      // Урон от выстрела с памятью
-	damageSum        alias.АУрон         // Суммарный урон
+	перезарядка      *shot_time.ShotTime // Сколько времени нужно для полной перезарядки
+	урон             *damage.Damage      // Урон от выстрела с памятью
+	уронВсего        alias.АУрон         // Суммарный урон
 	isEnd            ISafeBool           // Признак конца сражения
 	еслиБлок         ISafeBool           // Признак блокировки выстрела
-	login            string              // Логин для поиска контрольных строк
+	логин            string              // Логин для поиска контрольных строк
 	chTick           chan int            // Тик для выстрела
-	ctxEnd           context.Context     // Признак окончания сражения
 }
 
 // НовВыстрел -- возвращает новый выстрел
@@ -48,21 +44,30 @@ func НовВыстрел(проц ИСражениеПроцесс) ИВыст
 	логинТанк := проц.Бот().Имя()
 	сам := &выстрел{
 		ИСражениеПроцесс: проц,
-		ctxEnd:           проц.Контекст(),
-		recharge:         shot_time.NewShotTime(),
-		damage:           damage.NewDamage(),
+		перезарядка:      shot_time.NewShotTime(),
+		урон:             damage.NewDamage(),
 		еслиБлок:         NewSafeBool(),
 		isEnd:            проц.ЕслиКонец(),
-		login:            логинТанк,
+		логин:            логинТанк,
 		chTick:           make(chan int, 2),
 	}
 	// Установить время перезарядки
-	сам.recharge.Set(6800)
+	сам.перезарядка.Set(6800)
 	go сам.makeTick()
 	go сам.run()
 	return сам
 }
 
+// БлокУст -- установка блокировки выстрела
+func (сам *выстрел) БлокУст() {
+	сам.еслиБлок.Set()
+}
+
+// БлокСброс -- сброс блокировки выстрела
+func (сам *выстрел) БлокСброс() {
+	сам.еслиБлок.Reset()
+}
+
 // ЕслиБлок -- возвращает признак блокировки выстрела
 func (сам *выстрел) ЕслиБлок() bool {
 	return сам.еслиБлок.Get()
@@ -73,13 +78,13 @@ func (сам *выстрел) makeTick() {
 	defer func() {
 		сам.isEnd.Set()
 		close(сам.chTick)
-		сам.Бот().КонтБот().Cancel()
+		сам.Отменить()
 		// log._rintf("Shot.makeTick(): сражение завершёно\n")
 	}()
 	countMasking := 0
 	for {
 		select {
-		case <-сам.ctxEnd.Done():
+		case <-сам.Контекст().Done():
 			return
 		default:
 			if сам.isEnd.Get() { // Битва закончилась
@@ -99,7 +104,7 @@ func (сам *выстрел) makeTick() {
 				countMasking = 0
 			}
 			сам.chTick <- 1 // Здесь же первый выстрел
-			recharge := сам.recharge.Get()
+			recharge := сам.перезарядка.Get()
 			// log._rintf("INFO Shot.run() перезарядка=%v msec\n", recharge)
 			// Если идёт перезарядка -- постепенно обнуляем время ожидания
 			time.Sleep(time.Millisecond * time.Duration(recharge))
@@ -111,7 +116,7 @@ func (сам *выстрел) makeTick() {
 func (сам *выстрел) run() {
 	for {
 		select {
-		case <-сам.ctxEnd.Done():
+		case <-сам.Контекст().Done():
 			return
 		case <-сам.chTick:
 			// Стрелять можно, стандартное ожидание
@@ -145,10 +150,9 @@ func (сам *выстрел) shot() {
 		сам.Бот().КонтБот().Cancel()
 		return
 	}
-	lstLink := strings.Split(strOut, `<a href="`)
-	strLink := lstLink[1]
-	lstLink = strings.Split(strLink, `" class="simple-but gray"><span><span>ОБЫЧНЫЕ</span></span></a>`)
-	strLink = "https://wartank.ru/" + lstLink[0]
+	strLink := strings.TrimPrefix(strOut, `<a href="`)
+	strLink = strings.TrimSuffix(strLink, `" class="simple-but gray"><span><span>ОБЫЧНЫЕ</span></span></a>`)
+	strLink = "https://wartank.ru/" + strLink
 	lstBattle, err = сам.Сеть().Get(strLink)
 	if err != nil {
 		// log._rintf("ERRO Shot.shot(): при исполнении GET-команды выстрела обычным снарядом, err=\n\t%v\n", err)
@@ -172,7 +176,7 @@ func (сам *выстрел) findDamage() {
 
 	for ind, strOut = range lstShot {
 		// <span class="yellow1 td_u">prospero tank</span>
-		if strings.Contains(strOut, `<span class="yellow1 td_u">`+сам.login+`</span>`) {
+		if strings.Contains(strOut, `<span class="yellow1 td_u">`+сам.логин+`</span>`) {
 			ind += 2
 			strOut = lstShot[ind]
 			еслиНайдено = true
@@ -194,15 +198,15 @@ func (сам *выстрел) findDamage() {
 		return
 	}
 	if iDamage <= 0 { // Кривой урон от выстрела
-		сам.recharge.Dec5()
+		сам.перезарядка.Dec5()
 		// log._rintf("WARN Shot.findDamage(): ошибка в значении урона(%v)\n", iDamage)
 		iDamage = 0
 	}
-	сам.damageSum += alias.АУрон(iDamage)
+	сам.уронВсего += alias.АУрон(iDamage)
 	// log._rintf("INFO Shot.Damage(): damageSum=%v\n", сам.damageSum)
 	if iDamage < 70 {
-		сам.damage.Set(alias.АУрон(iDamage))
-		сам.recharge.Inc210()
+		сам.урон.Set(alias.АУрон(iDamage))
+		сам.перезарядка.Inc210()
 	}
 	// log._rintf("INFO Shot.findDamage(): выстрел=+%v, урон=%v", iDamage, сам.damageSum)
 	if iDamage == 0 {
@@ -213,14 +217,14 @@ func (сам *выстрел) findDamage() {
 
 // setDamage -- обновляет время перезарядки в зависимости от произведённого урона
 func (сам *выстрел) setDamage(val alias.АУрон) {
-	сам.damage.Set(val)
-	switch сам.damage.Result() {
+	сам.урон.Set(val)
+	switch сам.урон.Result() {
 	case "none":
-		сам.recharge.Dec5()
+		сам.перезарядка.Dec5()
 	case "up":
-		сам.recharge.Dec30()
+		сам.перезарядка.Dec30()
 	case "down":
-		сам.recharge.Inc210()
+		сам.перезарядка.Inc210()
 	}
 }
 

+ 13 - 35
app/lev2/arena/arena_battle/arena_battle.go

@@ -2,30 +2,24 @@
 package arena_battle
 
 import (
-	"time"
-
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
 
 	. "wartank/app/lev0/types"
 	"wartank/app/lev2/arena"
-	"wartank/app/lev2/arena/arena_battle/battle_register"
-	"wartank/app/lev2/arena/arena_battle/battle_wait"
-	"wartank/app/lev2/arena/arena_battle/battle_worker"
+	"wartank/app/lev2/arena/arena_battle/bf_battle_make"
+	"wartank/app/lev2/arena/arena_battle/bf_battle_register"
+	"wartank/app/lev2/arena/arena_battle/bf_battle_wait"
+	"wartank/app/lev2/arena/arena_build"
 )
 
 // АренаСражение -- объект сражения
 type АренаСражение struct {
-	ИАрена
+	ИАренаСтроение
 	конт   ILocalCtx
 	клиент ИХттпВоркер
-
-	регистрация *battle_register.СхваткаРегистрация // Регистратор на сражение
-	ожидание    *battle_wait.СхваткаОжидание        // Ожидатель начала сражения
-	действие    *battle_worker.СхваткаИсполнитель   // Исполнитель сражения
-
 }
 
-// НовСражение -- возвращает новый *Battle
+// НовСражение -- возвращает новую арену сражения PVE
 func НовСражение(конт ILocalCtx) *АренаСражение {
 	бот := конт.Get("бот").Val().(ИБот)
 	сам := &АренаСражение{
@@ -35,39 +29,23 @@ func НовСражение(конт ILocalCtx) *АренаСражение {
 	аренаКонфиг := arena.АренаКонфиг{
 		Конт_:        конт,
 		АренаИмя_:    "Арена сражения",
-		СтрКонтроль_: "<span>до начала ",
+		СтрКонтроль_: "<title>Сражения</title>",
 		ФнПуск_:      сам.пуск,
 		СтрУрл_:      "https://wartank.ru/pve",
 	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
-	сам.регистрация = battle_register.НовСражениеРегистрация(конт)
-	сам.ожидание = battle_wait.НовСражениеОжидание(конт)
-	сам.действие = battle_worker.НовСражениеИсполнитель(конт)
+	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
 
-	// сам.shotTimeFull.Set(8000) // 8000 msec
+	конт.Set("pve", сам, "Сражение с ботами")
 	return сам
 }
 
 func (сам *АренаСражение) Пуск() {
-	go сам.пуск()
+	сам.Обновить()
+	bf_battle_register.СражениеРегистрация(сам.конт)
+	bf_battle_wait.СражениеОжидать(сам.конт)
+	bf_battle_make.СражениеВыполнить(сам.конт)
 }
 
 // запускает в работу сражение
 func (сам *АренаСражение) пуск() {
-	for {
-		select {
-		case <-сам.конт.Ctx().Done():
-			return
-		default:
-			сам.регистрация.Зарегистрироваться()
-			сам.ожидание.Ожидать()
-			сам.действие.Пуск()
-			time.Sleep(time.Second * 2) // Пауза между циклами, чтобы сервер не долбить запросами
-		}
-	}
-}
-
-// ЕслиНачало -- возвращает признак начала сражения (для браузера)
-func (сам *АренаСражение) ЕслиНачало() ИСтатПарам {
-	return сам.действие.Тревога()
 }

+ 0 - 72
app/lev2/arena/arena_battle/battle_register/battle_register.go

@@ -1,72 +0,0 @@
-// package battle_register -- регистрирует танк в битве
-package battle_register
-
-import (
-	"log"
-	"strings"
-	"time"
-
-	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
-
-	. "wartank/app/lev0/types"
-	"wartank/app/lev2/arena"
-)
-
-// СражениеРегистрация -- регистрирует танк к началу атаки
-type СхваткаРегистрация struct {
-	ИАрена
-	конт         ILocalCtx
-	счётРегистер int // Счётчик регистраций на сражение
-}
-
-// НовСражениеРегистрация -- возвращает новый ожидатель битвы
-func НовСражениеРегистрация(конт ILocalCtx) *СхваткаРегистрация {
-	сам := &СхваткаРегистрация{
-		конт:         конт,
-		счётРегистер: 10_000,
-	}
-	аренаКонфиг := arena.АренаКонфиг{
-		Конт_:        конт,
-		АренаИмя_:    "Сражение",
-		СтрКонтроль_: `<title>Сражения</title>`,
-		ФнПуск_:      сам.пуск,
-		СтрУрл_:      "https://wartank.ru/pve",
-	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
-	return сам
-}
-
-// Запускает в работу
-func (сам *СхваткаРегистрация) пуск() {
-
-}
-
-// Зарегистрироваться -- регистрирует танк на сражение
-func (сам *СхваткаРегистрация) Зарегистрироваться() {
-	// Найдено приглашение на участие
-	// https://wartank.ru/pve?{count}-1.ILinkListener-currentOverview-apply
-	фнГеис := func() []string {
-		стрСсылка := "https://wartank.ru/pve?0-1.ILinkListener-currentOverview-apply"
-		var стрКонтроль string //  "https://wartank.ru/pve?0-1.ILinkListener-currentOverview-apply"
-		for {
-			time.Sleep(time.Second * 1)
-			лстСражение, err := сам.Сеть().Get(стрСсылка)
-			if err != nil {
-				log.Printf("ERRO СражениеРегистрация.Зарегистрироваться(): при выполнении GET-команды на подъём в атаку, err=\n\t%v\n", err)
-			}
-			if len(лстСражение) < 113 {
-				continue
-			}
-			стрКонтроль = лстСражение[113]
-			if !strings.Contains(стрКонтроль, "ILinkListener-currentOverview-apply") {
-				return лстСражение
-			}
-			log.Printf("СражениеРегистрация.Зарегистрироваться(): регистрация не прошла\n")
-			стрСсылка = strings.TrimPrefix(стрКонтроль, `<a class="simple-but border" href="`)
-			стрСсылка = strings.TrimSuffix(стрСсылка, `.ILinkListener-currentOverview-apply"><span><span>Взвод, подъем! В атаку!</span></span></a>`)
-			стрСсылка = "https://wartank.ru/" + стрСсылка + ".ILinkListener-currentOverview-apply"
-		}
-	}
-
-	сам.СтрОбновить(фнГеис())
-}

+ 0 - 110
app/lev2/arena/arena_battle/battle_wait/battle_wait.go

@@ -1,110 +0,0 @@
-// package battle_wait -- заставляет ожидать начало битвы
-package battle_wait
-
-import (
-	"strings"
-	"time"
-
-	. "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"
-)
-
-// СражениеОжидание -- ожидатель начала битвы
-type СхваткаОжидание struct {
-	ИАренаСтроение
-	конт ILocalCtx
-}
-
-// НовСражениеОжидание -- возвращает новый ожидатель битвы
-func НовСражениеОжидание(конт ILocalCtx) *СхваткаОжидание {
-	сам := &СхваткаОжидание{
-		конт: конт,
-	}
-	аренаКонфиг := arena.АренаКонфиг{
-		Конт_:        конт,
-		АренаИмя_:    "Ожидание сражения",
-		СтрКонтроль_: `<title>Сражения</title>`,
-		ФнПуск_:      сам.пуск,
-		СтрУрл_:      "https://wartank.ru/pve",
-	}
-	сам.ИАренаСтроение = arena_build.НовАренаСтроение(конт, аренаКонфиг)
-	return сам
-}
-
-func (сам *СхваткаОжидание) пуск() {
-
-}
-
-// Ожидать -- ожидает начало сражения
-func (сам *СхваткаОжидание) Ожидать() {
-
-	// Зайти в цикл ожидания сражения
-	for {
-		// countTime := сам.ВремяОпрос().Получ()
-		// if countTime > 0 {
-		// 	time.Sleep(time.Millisecond * 500)
-		// 	// log.Printf("BattleWait.Wait(): countTime=%v\n", сам.CountDown().String())
-		// 	continue
-		// }
-		стрВрем := сам.ждать()
-		if стрВрем == "" {
-			return
-		}
-		лстВрем := strings.Split(стрВрем, ":")
-		стрЧас := лстВрем[0]
-		if стрЧас > "00" {
-			time.Sleep(time.Hour * 1)
-			continue
-		}
-		стрМин := лстВрем[1]
-		if стрМин > "10" {
-			time.Sleep(time.Minute * 10)
-			continue
-		}
-		if стрМин > "01" {
-			time.Sleep(time.Minute * 1)
-			continue
-		}
-		if "00:00:05" < стрВрем && стрВрем < "00:00:59" {
-			time.Sleep(time.Second * 5)
-			continue
-		}
-		time.Sleep(time.Second * 1)
-	}
-}
-
-// Ждёт пока время не обнулится
-func (сам *СхваткаОжидание) ждать() string {
-	сам.Обновить()
-	var (
-		strOut      string
-		lstBattle   = сам.СписПолучить()
-		еслиНайдено bool
-	)
-	for _, strOut = range lstBattle {
-		if strings.Contains(strOut, `<span>до начала `) {
-			еслиНайдено = true
-			break
-		}
-		// if strings.Contains(strOut, `>ОБЫЧНЫЕ<`) { // Это уже битва
-		// 	if len(сам.chBattle) == 0 {
-		// 		сам.chBattle <- 1
-		// 	}
-		// 	return
-		// }
-	}
-	if !еслиНайдено { // Сражение уже идёт
-		return ""
-	}
-	// Найдена строка ожидания начала сражения
-	lstTime := strings.Split(strOut, `<span>до начала `)
-	strTime := lstTime[1]
-	lstTime = strings.Split(strTime, ` (`)
-	strTime = lstTime[0]
-	сам.ОбратВремяУст(АВремя(strTime))
-	return strTime
-}

+ 0 - 59
app/lev2/arena/arena_battle/battle_worker/battle_worker.go

@@ -1,59 +0,0 @@
-// package battle_worker -- исполнение битвы
-package battle_worker
-
-import (
-	"time"
-
-	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
-
-	. "wartank/app/lev0/types"
-	"wartank/app/lev1"
-	"wartank/app/lev2/arena"
-	"wartank/app/lev2/arena/arena_battle/battle_worker/battle_worker"
-	"wartank/app/lev2/arena/arena_battle/battle_worker/battle_worker/battle_sound"
-)
-
-// СражениеДействие -- исполнение битвы
-type СхваткаИсполнитель struct {
-	ИАрена
-	конт ILocalCtx
-
-	еслиНачало ИСтатПарам
-
-	// Непосредственное сражение
-	действие ИСражениеПроцесс
-
-	sound *battle_sound.BattleSound // Однопоточное проигрывание звука
-}
-
-// НовСражениеДействие -- возвращает новый исполнитель битвы
-func НовСражениеИсполнитель(конт ILocalCtx) *СхваткаИсполнитель {
-	сам := &СхваткаИсполнитель{
-		конт:       конт,
-		еслиНачало: lev1.НовСтатПарам("тревога"),
-		sound:      battle_sound.NewBattleSound(),
-	}
-	аренаКонфиг := arena.АренаКонфиг{
-		Конт_:        конт,
-		АренаИмя_:    "Ход сражения",
-		СтрКонтроль_: `<title>Сражения</title>`,
-		ФнПуск_:      сам.пуск,
-		СтрУрл_:      "https://wartank.ru/pve",
-	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
-	return сам
-}
-
-// выполняет битву
-func (сам *СхваткаИсполнитель) пуск() {
-	сам.действие = battle_worker.НовСражениеДействие(сам.конт) // IBattleOn (онлайн)
-	сам.sound.Play()
-	time.Sleep(time.Second * 10) // Задержка для звука на странице
-	<-сам.действие.Контекст().Done()
-	// log._rintf("Battle.runBaton(): сражение завершено\n")
-}
-
-// Тревога -- возвращает признак начала сражения (для браузера)
-func (сам *СхваткаИсполнитель) Тревога() ИСтатПарам {
-	return сам.еслиНачало
-}

+ 0 - 0
app/lev2/arena/arena_battle/battle_worker/battle_worker/battle_sound/battle_sound.go → app/lev2/arena/arena_battle/bf_battle_make/battle_worker/battle_sound/battle_sound.go


+ 5 - 26
app/lev2/arena/arena_battle/battle_worker/battle_worker/battle_worker.go → app/lev2/arena/arena_battle/bf_battle_make/battle_worker/battle_worker.go

@@ -11,7 +11,6 @@ import (
 	"wartank/app/lev1/health"
 	"wartank/app/lev1/manevr"
 	"wartank/app/lev1/shot"
-	"wartank/app/lev2/arena"
 )
 
 /*
@@ -20,7 +19,7 @@ import (
 
 // СражениеДействие -- непосредственно танкует в сражении
 type СражениеДействие struct {
-	ИАрена
+	ИАренаСтроение
 	конт       ILocalCtx
 	кнт        context.Context // Контекст сражения
 	фнОтменить func()          // Функция отмены сражения
@@ -43,15 +42,10 @@ func НовСражениеДействие(конт ILocalCtx) ИСражени
 		логин:      конт.Get("бот_имя").Val().(string),
 		еслиКонец:  NewSafeBool(),
 	}
-	аренаКонфиг := arena.АренаКонфиг{
-		Конт_:        конт,
-		АренаИмя_:    "Исполнитель сражения",
-		СтрКонтроль_: `<title>Сражения</title>`,
-		ФнПуск_:      сам.пуск,
-		СтрУрл_:      "https://wartank.ru/pve",
-	}
-	сам.ИАрена = arena.НовАрена(конт, аренаКонфиг)
-	go сам.пуск()
+	сам.ИАренаСтроение = конт.Get("pve").Val().(ИАренаСтроение)
+	сам.выстрел = shot.НовВыстрел(сам) // Объект выстрела
+	сам.здоровье = health.НовЗдоровье(сам)
+	сам.манёвр = manevr.НовМанёвр(сам)
 	return сам
 }
 
@@ -65,21 +59,6 @@ func (сам *СражениеДействие) ЕслиКонец() ISafeBool {
 	return сам.еслиКонец
 }
 
-// запускает сражение
-func (сам *СражениеДействие) пуск() {
-	defer func() {
-		сам.фнОтменить()
-		// log._rintf("BattleOn.run(): сражение завершено\n")
-	}()
-	{ // Подготовка к сражению
-		сам.выстрел = shot.НовВыстрел(сам) // Объект выстрела
-		сам.здоровье = health.НовЗдоровье(сам)
-		сам.манёвр = manevr.НовМанёвр(сам)
-	}
-	// Рабочий цикл сражения
-	<-сам.кнт.Done()
-}
-
 func (сам *СражениеДействие) МанёврНадоУст() {
 	if сам.манёвр == nil {
 		return

+ 0 - 0
app/lev2/arena/arena_battle/battle_worker/battleon/battleon.go → app/lev2/arena/arena_battle/bf_battle_make/battleon/battleon.go


+ 61 - 0
app/lev2/arena/arena_battle/bf_battle_make/bf_battle_make.go

@@ -0,0 +1,61 @@
+// package battle_worker -- бизнес-функция исполнения сражения
+package bf_battle_make
+
+import (
+	"time"
+
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	"wartank/app/lev0/cons"
+	. "wartank/app/lev0/types"
+	"wartank/app/lev2/arena/arena_battle/bf_battle_make/battle_worker"
+	"wartank/app/lev2/arena/arena_battle/bf_battle_make/battle_worker/battle_sound"
+)
+
+// СражениеДействие -- исполнение битвы
+// type СхваткаИсполнитель struct {
+// 	ИАрена
+// 	конт ILocalCtx
+
+// 	еслиНачало ИСтатПарам
+
+// 	// Непосредственное сражение
+// 	действие ИСражениеПроцесс
+
+// 	sound *battle_sound.BattleSound // Однопоточное проигрывание звука
+// }
+
+// НовСражениеДействие -- возвращает новый исполнитель битвы
+func СражениеВыполнить(конт ILocalCtx) {
+	сражение := конт.Get("pve").Val().(ИАренаСтроение)
+	/*
+		Здесь процесс хитрый.
+		Сражения следуют с некоторым интервалом.
+
+		При запуске бота будет состояние не создано.
+		После регистрации -- платный апгрейд
+		При обратном отсчёте --- ожидание
+		При идущей битве -- работа.
+		После окончания -- забрать и переход в ожидание.
+
+		Целевое состояние здесь -- платный апгрейд (из не существует, построено, ожидание -- запрещено)
+	*/
+	if сражение.Состояние().Получ()!=cons.РежимОжидание {
+		return
+	}
+	пуск(конт)
+}
+
+// выполняет битву
+func пуск(конт ILocalCtx) {
+	действие := battle_worker.НовСражениеДействие(конт) // IBattleOn (онлайн)
+	звук:=battle_sound.NewBattleSound()
+	звук.Play()
+	time.Sleep(time.Second * 10) // Задержка для звука на странице
+	<-действие.Контекст().Done()
+}
+
+// Тревога -- возвращает признак начала сражения (для браузера)
+// func Тревога() ИСтатПарам {
+// 	return сам.еслиНачало
+// }

+ 75 - 0
app/lev2/arena/arena_battle/bf_battle_register/bf_battle_register.go

@@ -0,0 +1,75 @@
+// package battle_register -- бизнес-функция регистрации танк в битве
+package bf_battle_register
+
+import (
+	"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("pve").Val().(ИАренаСтроение)
+	/*
+		Здесь процесс хитрый.
+		Битвы следуют с некоторым интервалом.
+
+		При запуске бота будет состояние не создано.
+		После регистрации -- платный апгрейд
+		При обратном отсчёте --- ожидание
+		При идущей битве -- работа.
+		После окончания -- забрать и переход в ожидание.
+
+		Целевое состояние здесь -- платный апгрейд (из не существует, построено, ожидание -- запрещено)
+	*/
+	if битва.Состояние().Получ() == cons.РежимНеСуществует { // БотоФерма только запущена
+		битва.Состояние().Уст(cons.РежимПостроено)
+	}
+	еслиПостроено := битва.Состояние().Получ() == cons.РежимПостроено
+	еслиОжидание := битва.Состояние().Получ() == cons.РежимОжидание // Уже были битвы
+	if !(еслиОжидание || еслиПостроено) {
+		return
+	}
+	регистрация(конт)
+}
+
+func регистрация(конт ILocalCtx) {
+	битва := конт.Get("pve").Val().(ИАренаСтроение)
+	// Найдено приглашение на участие
+	// https://wartank.ru/pve?{count}-1.ILinkListener-currentOverview-apply
+	var (
+		лстСражение = битва.СписПолучить()
+		err         error
+	)
+	фнГеис := func() []string {
+		if len(лстСражение) == 0 { // Уже обратный отсчёт
+			битва.Обновить()
+			лстСражение = битва.СписПолучить()
+		}
+		if len(лстСражение) < 113 { // Уже обратный отсчёт
+			битва.Состояние().Уст(cons.РежимОжидание)
+			return лстСражение
+		}
+		// "https://wartank.ru/pve?0-1.ILinkListener-currentOverview-apply"
+		стрКонтроль := лстСражение[113]
+		if !strings.Contains(стрКонтроль, "ILinkListener-currentOverview-apply") {
+			битва.Состояние().Уст(cons.РежимОжидание)
+			return лстСражение
+		}
+		// <a class="simple-but border" href="pve?8-1.ILinkListener-currentOverview-apply"><span><span>Взвод, подъем! В атаку!</span></span></a>
+		стрСсылка := strings.TrimPrefix(стрКонтроль, `<a class="simple-but border" href="`)
+		стрСсылка = strings.TrimSuffix(стрСсылка, `"><span><span>Взвод, подъем! В атаку!</span></span></a>`)
+		стрСсылка = "https://wartank.ru/" + стрСсылка
+		// https://wartank.ru/pve?9-1.ILinkListener-currentOverview-apply
+		лстСражение, err = битва.Сеть().Get(стрСсылка)
+		Hassert(err == nil, "регистрация(): при регистрации на сражение, ош=\n\t%v", err)
+		битва.Состояние().Уст(cons.РежимАпгрейдПлатный)
+		return лстСражение
+	}
+
+	битва.СтрОбновить(фнГеис())
+}

+ 61 - 0
app/lev2/arena/arena_battle/bf_battle_wait/bf_battle_wait.go

@@ -0,0 +1,61 @@
+// package bf_battle_wait -- заставляет ожидать начало битвы
+package bf_battle_wait
+
+import (
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	. "wartank/app/lev0/alias"
+	"wartank/app/lev0/cons"
+	. "wartank/app/lev0/types"
+)
+
+// НовСражениеОжидание -- возвращает новый ожидатель битвы
+func СражениеОжидать(конт ILocalCtx) {
+	сражение := конт.Get("pve").Val().(ИАренаСтроение)
+	/*
+		Здесь процесс хитрый.
+		Сражения следуют с некоторым интервалом.
+
+		При запуске бота будет состояние не создано.
+		После регистрации -- платный апгрейд
+		При обратном отсчёте --- ожидание
+		При идущей битве -- работа.
+		После окончания -- забрать и переход в ожидание.
+
+		Целевое состояние здесь -- платный апгрейд (из не существует, построено, ожидание -- запрещено)
+	*/
+	еслиПостроено := сражение.Состояние().Получ() == cons.РежимПостроено
+	еслиАпгрейд := сражение.Состояние().Получ() == cons.РежимАпгрейдПлатный // Уже есть регистрация
+	if !(еслиАпгрейд || еслиПостроено) {
+		return
+	}
+	ждать(конт)
+}
+
+// Ждёт пока время не обнулится
+func ждать(конт ILocalCtx) {
+	сражение := конт.Get("pve").Val().(ИАренаСтроение)
+	var (
+		strOut      string
+		lstBattle   = сражение.СписПолучить()
+		еслиНайдено bool
+	)
+	// <span>до начала 00:46:42 (2 993 заявок)</span>
+	for _, strOut = range lstBattle {
+		if strings.Contains(strOut, `<span>до начала `) {
+			еслиНайдено = true
+			break
+		}
+	}
+	if !еслиНайдено { // Сражение уже идёт
+		return
+	}
+	// Найдена строка ожидания начала сражения
+	lstTime := strings.Split(strOut, `<span>до начала `)
+	strTime := lstTime[1]
+	lstTime = strings.Split(strTime, ` (`)
+	strTime = lstTime[0]
+	сражение.ОбратВремяУст(АВремя(strTime))
+}

+ 45 - 3
app/lev2/arena/arena_mine/arena_mine.go

@@ -137,7 +137,22 @@ func (сам *АренаШахта) количествоПолучить() (bool
 		еслиНайдено bool
 		режим       string
 	)
-	lstMine := сам.Сеть().ВебВоркер().Получ("https://wartank.ru/buildings")
+	var lstMine []string
+	фнЗабрать := func() (isOk bool) { // Здесь бывают задержки из-за того, что эта часть работает неправильно
+		isOk = true
+		defer func() {
+			if _panic := recover(); _panic != nil {
+				isOk = false
+			}
+		}()
+		lstMine = сам.Сеть().ВебВоркер().Получ("https://wartank.ru/buildings")
+		return isOk
+	}
+	if !фнЗабрать() {
+		return false, nil
+	}
+
+
 	/*
 		Режим (руда-1):
 		<td class="vam"><div class="nwr pr5 gray1"><img class="rico vm" src="/images/icons/ore.png?2" alt="ore"/>&nbsp;1</div></td>
@@ -237,8 +252,21 @@ func (сам *АренаШахта) шахтаЗабрать() bool {
 	var (
 		strOut      string
 		еслиНайдено bool
+		списШахта   []string
 	)
-	списШахта := сам.Сеть().ВебВоркер().Получ("https://wartank.ru/buildings")
+	фнЗабрать := func() (isOk bool) { // Здесь бывают задержки из-за того, что эта часть работает неправильно
+		isOk = true
+		defer func() {
+			if _panic := recover(); _panic != nil {
+				isOk = false
+			}
+		}()
+		списШахта = сам.Сеть().ВебВоркер().Получ("https://wartank.ru/buildings")
+		return isOk
+	}
+	if !фнЗабрать() {
+		return false
+	}
 	// <a class="simple-but border" href="buildings?35-1.ILinkListener-buildings-0-building-rootBlock-actionPanel-takeProductionLink"><span><span>Забрать</span></span></a>
 	for _, strOut = range списШахта {
 		if strings.Contains(strOut, `.ILinkListener-buildings-0-building-rootBlock-actionPanel-takeProductionLink`) {
@@ -293,7 +321,21 @@ func (сам *АренаШахта) шахтаЗабрать() bool {
 // Обновляет текущий уровень шахты (может быть не построена)
 func (сам *АренаШахта) уровеньОбновить() bool {
 	сам.лог.Добавить("Шахта.уровеньОбновить()\n")
-	списСтр := сам.Сеть().ВебВоркер().Получ("http://wartank.ru/buildings")
+	var списСтр []string
+	фнУровень := func() (isOk bool) { // Здесь бывают задержки из-за того, что эта часть работает неправильно
+		isOk = true
+		defer func() {
+			if _panic := recover(); _panic != nil {
+				isOk = false
+			}
+		}()
+		списСтр = сам.Сеть().ВебВоркер().Получ("http://wartank.ru/buildings")
+		return isOk
+	}
+	if !фнУровень() {
+		return false
+	}
+
 	// <span class="green2">Шахта - 0</span><br/>
 	var (
 		еслиНайти = false

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

@@ -47,6 +47,10 @@ func ШахтаРаботаВремя(конт ILocalCtx) {
 	// <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>`)
+	// Здесь уже может выйти время работы, нужна проверка на контрольную строку
+	if strTime==`<td style="width:50%;padding-right:1px;">`{
+		return
+	}
 	шахта.ОбратВремяУст(АВремя(strTime))
 	шахта.ВебЛог().Добавить("Шахта.количествоПолучить(): время=%q\n", strTime)
 }

+ 11 - 5
app/lev2/lev2.go

@@ -9,6 +9,7 @@ import (
 	"wartank/app/lev2/arena/arena_arsenal"
 	"wartank/app/lev2/arena/arena_bank"
 	"wartank/app/lev2/arena/arena_base"
+	"wartank/app/lev2/arena/arena_battle"
 	"wartank/app/lev2/arena/arena_convoy"
 	"wartank/app/lev2/arena/arena_fuel_duel"
 	"wartank/app/lev2/arena/arena_fuel_storage"
@@ -19,16 +20,21 @@ import (
 	"wartank/app/lev2/arena/arena_polygon"
 )
 
+// НовСражение -- возвращает новую арену сражения PVE
+func НовСражение(конт ILocalCtx) ИАренаСтроение {
+	сражение := arena_battle.НовСражение(конт)
+	return сражение
+}
+
 // НовАренаРынок -- возвращает новую арену рынка
-func НовАренаРынок(конт ILocalCtx)ИАрена{
-	рынок:=arena_market.НовРынок(конт)
+func НовАренаРынок(конт ILocalCtx) ИАрена {
+	рынок := arena_market.НовРынок(конт)
 	return рынок
 }
 
-
 // НовАренаМедаль -- возвращает новую арену медалей
-func НовАренаМедали(конт ILocalCtx)ИАрена{
-	медали:=arena_medal.НовАренаМедали(конт)
+func НовАренаМедали(конт ILocalCtx) ИАрена {
+	медали := arena_medal.НовАренаМедали(конт)
 	return медали
 }
 

+ 3 - 0
app/lev3/bot/bot.go

@@ -43,6 +43,7 @@ type Бот struct {
 	аренаМедаль    ИАрена
 	рынок          ИАренаРынок
 	конвой         ИАренаКонвой
+	сражение       ИАренаСтроение
 	еслиРаботает   ISafeBool
 	еслиАвтозапуск ISafeBoolReact
 	лог            ILogBuf
@@ -128,6 +129,7 @@ func создатьЯдроВарБот(конфиг *bot_config.БотКонф
 		сам.Пуск()
 	}
 	сам.ангар = lev2.НовАнгар(сам.конт)
+	сам.сражение = lev2.НовСражение(сам.конт)
 	сам.рынок = lev2.НовАренаРынок(сам.конт)
 	сам.аренаМедаль = lev2.НовАренаМедали(сам.конт)
 	сам.конвой = lev2.НовКонвой(сам.конт)
@@ -193,6 +195,7 @@ func (сам *Бот) пуск() {
 			сам.аренаМедаль.Пуск()
 			сам.рынок.Пуск()
 			сам.конвой.Пуск()
+			сам.сражение.Пуск()
 
 			bf_gold_find.ЗолотоНайти(сам.конт)
 			bf_silver_find.СереброНайти(сам.конт)