| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- 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
- )
- // <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>`) {
- isFind = true
- break
- }
- }
- if !isFind {
- return
- }
- strOut = lstBattle[ind]
- // <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)
- sf.isEnd.Уст()
- sf.CancelBattle()
- return
- }
- strTime := lstTime[1]
- lstTime = strings.Split(strTime, ` секунд</span></span></a>`)
- 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
- )
- // <span>Ремкомплект</span>
- // <a href="pve?19-14.ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>Ремкомплект</span></span></a>
- for ind, strOut = range lstBattleOn {
- if strings.Contains(strOut, `<span>Ремкомплект</span>`) {
- isFindRepair = true
- break
- }
- }
- if !isFindRepair {
- return
- }
- strOut = lstBattleOn[ind]
- // <a href="pve?6-26.ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>Ремкомплект</span></span></a>
- lstLink := strings.Split(strOut, `<a href="`)
- strLink := lstLink[1]
- lstLink = strings.Split(strLink, `" class="simple-but blue"><span><span>Ремкомплект</span></span></a>`)
- 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, `<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 {
- 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
- }
- }
|