package health import ( "context" "fmt" // "log" "strconv" "strings" "time" "wartank/server/serv_bots/warbot/angar/division/divwar/divwaron/health/healthtime" "wartank/server/serv_bots/warbot/angar/division/divwar/divwaron/health/repairtime" // "wartank/internal/components/sound" "wartank/pkg/components/safe_bool" "wartank/pkg/types" ) /* Контролирует состояние здоровья танка */ // Health -- контроль здоровья танка type Health struct { types.ИДивизияВойнаДействие // FIXME: fnCancel func() temp *healthtime.HealthTime // Изменяемое здоровье танка full *healthtime.HealthTime // Полное здоровье танка isRepair *safe_bool.БезопБул // Необходимость восстановления repairTime *repairtime.RepairTime // Время до восстановления isEnd *safe_bool.БезопБул // Ссылка на признак конца сражения login string // Для поиска контрольных строк chTick chan int // Канал для ровной отправки тиков deltaOld int // Старая дельта потери здоровья countLow int ctxBattle context.Context // Конекст сражения } // NewHealth -- возвращает новый *Health func NewHealth(divwar types.ИДивизияВойнаДействие, isEnd *safe_bool.БезопБул, login string) (*Health, error) { { // Предусловия if divwar == nil { return nil, fmt.Errorf("NewHealth(): battle is nil") } if isEnd == nil { return nil, fmt.Errorf("NewHealth(): isEnd is nil") } if login == "" { return nil, fmt.Errorf("NewHealth(): login is empty") } } sf := &Health{ ИДивизияВойнаДействие: divwar, fnCancel: divwar.CancelBattle, ctxBattle: divwar.Ctx(), temp: healthtime.NewHealthTime(), full: healthtime.NewHealthTime(), isRepair: safe_bool.НовБезопБул(), repairTime: repairtime.NewRepairTime(), isEnd: divwar.ЕслиКонец(), login: login, chTick: make(chan int, 2), } go sf.makeTik() go sf.run() return sf, nil } // Отправляе ттики с заданным равным интервалом func (sf *Health) makeTik() { defer func() { sf.CancelBattle() close(sf.chTick) // log._rintf("Health.makeTick(): сражение завершёно\n") }() count := 0 repairTime := 0 for { select { case <-sf.ctxBattle.Done(): return default: if sf.IsDeath() { return } if sf.repairTime.Get() == repairTime { count++ } else { repairTime = sf.repairTime.Get() count = 0 } if count > 90 { return } } sf.chTick <- 1 time.Sleep(time.Second * 1) sf.repairTime.Dec() } } // Главный цикл обработки здоровья в сражении func (sf *Health) run() { for { select { case <-sf.ctxBattle.Done(): sf.isEnd.Уст() return case <-sf.chTick: if err := sf.findHealth(); err != nil { // Найти свой здоровье // log._rintf("ERRO Health.run(): при попытке найти здоровье, err=\n\t%v\n", err) } sf.findRepairTime() if sf.ВыстрелБлок().Получ() { if sf.isRepair.Получ() { go sf.repair() } continue } if sf.isRepair.Получ() { go sf.repair() } } } } // Full -- возвращает объект полного здоровья танка func (sf *Health) Full() int { return sf.full.Get() } // IsDeath -- возвращает признак мертвичины танка func (sf *Health) IsDeath() bool { if sf.isEnd.Получ() { sf.fnCancel() return true } lstBattle := sf.СписПолучить() for _, strOut := range lstBattle { if strings.Contains(strOut, `>Ваш танк подбит.`) { // log._rintf("INFO Health.repair(): танк подбит\n") sf.temp.Set(0) sf.isEnd.Уст() sf.CancelBattle() return true } } return sf.isEnd.Получ() } // Ищет время восстановления ремки func (sf *Health) findRepairTime() { defer func() { if sf.repairTime.IsReady() { return } if sf.repairTime.IsChange() { // log._rintf("INFO Health.findRepair(): до ремки=%v\n", sf.repairTime.Get()) } }() if sf.repairTime.IsReady() { return } var ( strOut string lstBattle = sf.СписПолучить() isFind bool ind int ) // 12 секунд // for ind, strOut = range lstBattle { if !strings.Contains(strOut, `ILinkListener-currentControl-repairLink`) { continue } if strings.Contains(strOut, ` секунд`) { isFind = true break } } if !isFind { return } strOut = lstBattle[ind] // 12 секунд lstTime := strings.Split(strOut, `ILinkListener-currentControl-repairLink" class="simple-but blue">`) if len(lstTime) < 2 { // log._rintf("ERRO Health.findRepair(): при попытке получить ссылку на ремонт, strOut=\n%v\n", strOut) sf.isEnd.Уст() sf.CancelBattle() return } strTime := lstTime[1] lstTime = strings.Split(strTime, ` секунд`) strTime = lstTime[0] if err := sf.repairTime.Set(strTime); err != nil { // log._rintf("ERRO Health.findRepair(): при установке времени восстановления ремки, err=\n\t%v\n", err) } } // Восстановливает здоровье (~) func (sf *Health) repair() { var ( strOut string lstBattleOn = sf.СписПолучить() isFindRepair bool ind int ) // Ремкомплект // Ремкомплект for ind, strOut = range lstBattleOn { if strings.Contains(strOut, `Ремкомплект`) { isFindRepair = true break } } if !isFindRepair { return } strOut = lstBattleOn[ind] // Ремкомплект lstLink := strings.Split(strOut, `Ремкомплект`) strLink = "https://wartank.ru/" + lstLink[0] lstBattleOn, err := sf.Сеть().Get(strLink) if err != nil { // log._rintf("ERRO Health.repair(): при выполнении GET-команды ремонта, err=\n\t%v\n", err) sf.isEnd.Уст() sf.CancelBattle() return } if err = sf.СтрОбновить(lstBattleOn); err != nil { // log._rintf("ERRO Health.repair(): при обновлении lstBattle, err=\n\t%v\n", err) sf.isEnd.Уст() sf.CancelBattle() return } // sound.Repair() // log._rintf("INFO Health.repair(): здоровье восстановлено\n") } // Ищет своё здоровье (~) func (sf *Health) findHealth() error { var ( ind int strOut string isFind bool lstBattle = sf.СписПолучить() ) if len(lstBattle) == 0 { // Принудительно обновим сражение if err := sf.Сеть().Обновить(); err != nil { sf.isEnd.Уст() sf.fnCancel() return fmt.Errorf("Health.findHealth(): после принудительного обновления lsBattleOn, err=\n\t%w", err) } } for ind, strOut = range lstBattle { if strings.Contains(strOut, `alt="`+sf.login+`"`) { isFind = true break } } if !isFind { // Свой танк не найден sf.isEnd.Уст() sf.fnCancel() return fmt.Errorf("Health.findHealth(): своё здоровье не найдено") } // Свой танк найден, ищем здоровье ind += 11 strOut = lstBattle[ind] lstHealth := strings.Split(strOut, `
`) strHealth := lstHealth[1] lstHealth = strings.Split(strHealth, `
`) strHealth = lstHealth[0] iHealth, err := strconv.Atoi(strHealth) if err != nil { sf.isEnd.Уст() sf.CancelBattle() return fmt.Errorf("Health.findHealth(): здоровье(%v) не число, err=%w", strHealth, err) } sf.setHealth(iHealth) return nil } // setHealth -- устанавливает текущее здоровье func (sf *Health) setHealth(val int) { if val < 0 { // log._rintf("WARN Health.setHealth(): кривое значение здоровья танка(%v)\n", val) val = 0 } if val > sf.full.Get() { // log._rintf("WARN Health.setHealth(): кривое текущее здоровье, %v/%v\n", val, sf.full.Get()) sf.full.Set(val) sf.temp.Set(val) sf.deltaOld = 0 sf.ВыстрелБлок().Сброс() sf.isRepair.Сброс() return } delta := sf.temp.Get() - val if delta > 0 { // Дельта будет больше нуля, если только if delta != sf.deltaOld { // log._rintf("INFO Health.setHealth(): потеря здоровья=%v/%v\n", -delta, val) sf.deltaOld = delta sf.temp.Set(val) } } switch { case sf.isEnd.Получ(): sf.temp.Set(0) sf.isEnd.Уст() sf.CancelBattle() return case val == 0: sf.temp.Set(0) sf.isEnd.Уст() sf.CancelBattle() return case val <= 500: // Запретить стрельбу sf.ВыстрелБлок().Уст() // Установить запрет стрельбы пока слабое здоровье sf.isRepair.Уст() // log._rintf("WARN Health.setHealth(): низкий уровень здоровья(%v)\n", val) sf.Манёвр() case val > 500: // Разрешить стрельбы sf.ВыстрелБлок().Сброс() sf.isRepair.Сброс() if delta > sf.full.Get()*4/10 { // Проверить на критичность падения здоровья на 40% // log._rintf("WARN Health.setHealth(): большая разовая потеря здоровья(%v)\n", delta) sf.Манёвр() sf.isRepair.Уст() return } } isMask := sf.ВыстрелБлок().Получ() switch isMask { case true: sf.countLow++ if sf.countLow >= 200 { sf.isEnd.Уст() sf.CancelBattle() return } default: sf.countLow = 0 } if val == sf.full.Get() { sf.temp.Set(val) sf.isRepair.Сброс() sf.ВыстрелБлок().Сброс() sf.countLow = 0 } }