health.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. package health
  2. import (
  3. "context"
  4. // "log"
  5. // "strconv"
  6. "strings"
  7. "time"
  8. . "wartank/app/lev0/types"
  9. "wartank/app/lev1/health/health_time"
  10. "wartank/app/lev1/repair_time"
  11. // "wartank/internal/components/sound"
  12. "wartank/kernel"
  13. . "wartank/kernel/helpers"
  14. . "wartank/kernel/kernel_types"
  15. )
  16. /*
  17. Контролирует состояние здоровья танка
  18. */
  19. // Здоровье -- контроль здоровья танка
  20. type Здоровье struct {
  21. ИСражениеПроцесс // FIXME:
  22. fnCancel func()
  23. temp *health_time.HealthTime // Изменяемое здоровье танка
  24. full *health_time.HealthTime // Полное здоровье танка
  25. isRepair ИБезопБул // Необходимость восстановления
  26. repairTime *repair_time.RepairTime // Время до восстановления
  27. isEnd ИБезопБул // Ссылка на признак конца сражения
  28. login string // Для поиска контрольных строк
  29. chTick chan int // Канал для ровной отправки тиков
  30. // deltaOld int // Старая дельта потери здоровья
  31. // countLow int
  32. ctxBattle context.Context // Контекст сражения
  33. }
  34. // НовЗдоровье -- возвращает новый *Health
  35. func НовЗдоровье(проц ИСражениеПроцесс) *Здоровье {
  36. Паника(проц!=nil, "НовЗдоровье(): ИСражениеПроцесс == nil")
  37. логин:=проц.Бот().Имя()
  38. сам := &Здоровье{
  39. ИСражениеПроцесс: проц,
  40. fnCancel: проц.Отменить,
  41. ctxBattle: проц.Бот().Контекст(),
  42. temp: health_time.NewHealthTime(),
  43. full: health_time.NewHealthTime(),
  44. isRepair: kernel.НовБезопБул(),
  45. repairTime: repair_time.NewRepairTime(),
  46. isEnd: проц.ЕслиКонец(),
  47. login: логин,
  48. chTick: make(chan int, 2),
  49. }
  50. go сам.makeTik()
  51. go сам.run()
  52. return сам
  53. }
  54. // Отправляет тики с заданным равным интервалом
  55. func (сам *Здоровье) makeTik() {
  56. defer func() {
  57. сам.Отменить()
  58. close(сам.chTick)
  59. // log._rintf("Health.makeTick(): сражение завершёно\n")
  60. }()
  61. count := 0
  62. repairTime := 0
  63. for {
  64. select {
  65. case <-сам.ctxBattle.Done():
  66. return
  67. default:
  68. if сам.IsDeath() {
  69. return
  70. }
  71. if сам.repairTime.Get() == repairTime {
  72. count++
  73. } else {
  74. repairTime = сам.repairTime.Get()
  75. count = 0
  76. }
  77. if count > 90 {
  78. return
  79. }
  80. }
  81. сам.chTick <- 1
  82. time.Sleep(time.Second * 1)
  83. сам.repairTime.Dec()
  84. }
  85. }
  86. // Главный цикл обработки здоровья в сражении
  87. func (сам *Здоровье) run() {
  88. for {
  89. select {
  90. case <-сам.ctxBattle.Done():
  91. сам.isEnd.Уст()
  92. return
  93. case <-сам.chTick:
  94. // if err := сам.findHealth(); err != nil { // Найти свой здоровье
  95. // // log._rintf("ERRO Health.run(): при попытке найти здоровье, err=\n\t%v\n", err)
  96. // }
  97. сам.findRepairTime()
  98. if сам.Выстрел().ЕслиБлок() {
  99. if сам.isRepair.Получ() {
  100. go сам.repair()
  101. }
  102. continue
  103. }
  104. if сам.isRepair.Получ() {
  105. go сам.repair()
  106. }
  107. }
  108. }
  109. }
  110. // Full -- возвращает объект полного здоровья танка
  111. func (сам *Здоровье) Full() int {
  112. return сам.full.Get()
  113. }
  114. // IsDeath -- возвращает признак мертвичины танка
  115. func (сам *Здоровье) IsDeath() bool {
  116. if сам.isEnd.Получ() {
  117. сам.fnCancel()
  118. return true
  119. }
  120. lstBattle := сам.СписПолучить()
  121. for _, strOut := range lstBattle {
  122. if strings.Contains(strOut, `>Ваш танк подбит.`) {
  123. // log._rintf("INFO Health.repair(): танк подбит\n")
  124. сам.temp.Set(0)
  125. сам.isEnd.Уст()
  126. сам.Отменить()
  127. return true
  128. }
  129. }
  130. return сам.isEnd.Получ()
  131. }
  132. // Ищет время восстановления ремки
  133. func (сам *Здоровье) findRepairTime() {
  134. defer func() {
  135. if сам.repairTime.IsReady() {
  136. return
  137. }
  138. // if сам.repairTime.IsChange() {
  139. // log._rintf("INFO Health.findRepair(): до ремки=%v\n", сам.repairTime.Get())
  140. // }
  141. }()
  142. if сам.repairTime.IsReady() {
  143. return
  144. }
  145. var (
  146. strOut string
  147. lstBattle = сам.СписПолучить()
  148. еслиНайдено bool
  149. ind int
  150. )
  151. // <a href="pve?19-14.ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>12 секунд</span></span></a>
  152. //
  153. for ind, strOut = range lstBattle {
  154. if !strings.Contains(strOut, `ILinkListener-currentControl-repairLink`) {
  155. continue
  156. }
  157. if strings.Contains(strOut, ` секунд</span></span></a>`) {
  158. еслиНайдено = true
  159. break
  160. }
  161. }
  162. if !еслиНайдено {
  163. return
  164. }
  165. strOut = lstBattle[ind]
  166. // <a href="pve?19-14.ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>12 секунд</span></span></a>
  167. lstTime := strings.Split(strOut, `ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>`)
  168. if len(lstTime) < 2 {
  169. // log._rintf("ERRO Health.findRepair(): при попытке получить ссылку на ремонт, strOut=\n%v\n", strOut)
  170. сам.isEnd.Уст()
  171. сам.Отменить()
  172. return
  173. }
  174. // strTime := lstTime[1]
  175. // lstTime = strings.Split(strTime, ` секунд</span></span></a>`)
  176. // strTime = lstTime[0]
  177. // if err := сам.repairTime.Set(strTime); err != nil {
  178. // log._rintf("ERRO Health.findRepair(): при установке времени восстановления ремки, err=\n\t%v\n", err)
  179. // }
  180. }
  181. // Восстанавливает здоровье (~)
  182. func (сам *Здоровье) repair() {
  183. var (
  184. strOut string
  185. lstBattleOn = сам.СписПолучить()
  186. еслиНайденоRepair bool
  187. ind int
  188. )
  189. // <span>Ремкомплект</span>
  190. // <a href="pve?19-14.ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>Ремкомплект</span></span></a>
  191. for ind, strOut = range lstBattleOn {
  192. if strings.Contains(strOut, `<span>Ремкомплект</span>`) {
  193. еслиНайденоRepair = true
  194. break
  195. }
  196. }
  197. if !еслиНайденоRepair {
  198. return
  199. }
  200. strOut = lstBattleOn[ind]
  201. // <a href="pve?6-26.ILinkListener-currentControl-repairLink" class="simple-but blue"><span><span>Ремкомплект</span></span></a>
  202. lstLink := strings.Split(strOut, `<a href="`)
  203. strLink := lstLink[1]
  204. lstLink = strings.Split(strLink, `" class="simple-but blue"><span><span>Ремкомплект</span></span></a>`)
  205. strLink = "https://wartank.ru/" + lstLink[0]
  206. lstBattleOn, err := сам.Сеть().Get(strLink)
  207. if err != nil {
  208. // log._rintf("ERRO Health.repair(): при выполнении GET-команды ремонта, err=\n\t%v\n", err)
  209. сам.isEnd.Уст()
  210. сам.Отменить()
  211. return
  212. }
  213. if err = сам.СтрОбновить(lstBattleOn); err != nil {
  214. // log._rintf("ERRO Health.repair(): при обновлении lstBattle, err=\n\t%v\n", err)
  215. сам.isEnd.Уст()
  216. сам.Отменить()
  217. return
  218. }
  219. // sound.Repair()
  220. // log._rintf("INFO Health.repair(): здоровье восстановлено\n")
  221. }
  222. // Ищет своё здоровье (~)
  223. // func (сам *Health) findHealth() error {
  224. // var (
  225. // ind int
  226. // strOut string
  227. // еслиНайдено bool
  228. // lstBattle = сам.СписПолучить()
  229. // )
  230. // if len(lstBattle) == 0 { // Принудительно обновим сражение
  231. // if err := сам.Сеть().Обновить(); err != nil {
  232. // сам.isEnd.Уст()
  233. // сам.fnCancel()
  234. // return fmt.Errorf("Health.findHealth(): после принудительного обновления lsBattleOn, err=\n\t%w", err)
  235. // }
  236. // }
  237. // for ind, strOut = range lstBattle {
  238. // if strings.Contains(strOut, `alt="`+сам.login+`"`) {
  239. // еслиНайдено = true
  240. // break
  241. // }
  242. // }
  243. // if !еслиНайдено { // Свой танк не найден
  244. // сам.isEnd.Уст()
  245. // сам.fnCancel()
  246. // return fmt.Errorf("Health.findHealth(): своё здоровье не найдено")
  247. // }
  248. // // Свой танк найден, ищем здоровье
  249. // ind += 11
  250. // strOut = lstBattle[ind]
  251. // lstHealth := strings.Split(strOut, `<div class="value-block lh1"><span><span>`)
  252. // strHealth := lstHealth[1]
  253. // lstHealth = strings.Split(strHealth, `</span></span></div>`)
  254. // strHealth = lstHealth[0]
  255. // iHealth, err := strconv.Atoi(strHealth)
  256. // if err != nil {
  257. // сам.isEnd.Уст()
  258. // сам.CancelBattle()
  259. // return fmt.Errorf("Health.findHealth(): здоровье(%v) не число, err=%w", strHealth, err)
  260. // }
  261. // сам.setHealth(iHealth)
  262. // return nil
  263. // }
  264. // setHealth -- устанавливает текущее здоровье
  265. // func (сам *Health) setHealth(val int) {
  266. // if val < 0 {
  267. // // log._rintf("WARN Health.setHealth(): кривое значение здоровья танка(%v)\n", val)
  268. // val = 0
  269. // }
  270. // if val > сам.full.Get() {
  271. // // log._rintf("WARN Health.setHealth(): кривое текущее здоровье, %v/%v\n", val, сам.full.Get())
  272. // сам.full.Set(val)
  273. // сам.temp.Set(val)
  274. // // сам.deltaOld = 0
  275. // сам.ВыстрелБлок().Сброс()
  276. // сам.isRepair.Сброс()
  277. // return
  278. // }
  279. // delta := сам.temp.Get() - val
  280. // // if delta > 0 { // Дельта будет больше нуля, если только
  281. // // if delta != сам.deltaOld {
  282. // // // log._rintf("INFO Health.setHealth(): потеря здоровья=%v/%v\n", -delta, val)
  283. // // сам.deltaOld = delta
  284. // // сам.temp.Set(val)
  285. // // }
  286. // // }
  287. // switch {
  288. // case сам.isEnd.Получ():
  289. // сам.temp.Set(0)
  290. // сам.isEnd.Уст()
  291. // сам.CancelBattle()
  292. // return
  293. // case val == 0:
  294. // сам.temp.Set(0)
  295. // сам.isEnd.Уст()
  296. // сам.CancelBattle()
  297. // return
  298. // case val <= 500: // Запретить стрельбу
  299. // сам.ВыстрелБлок().Уст() // Установить запрет стрельбы пока слабое здоровье
  300. // сам.isRepair.Уст()
  301. // // log._rintf("WARN Health.setHealth(): низкий уровень здоровья(%v)\n", val)
  302. // сам.Манёвр()
  303. // case val > 500: // Разрешить стрельбы
  304. // сам.ВыстрелБлок().Сброс()
  305. // сам.isRepair.Сброс()
  306. // if delta > сам.full.Get()*4/10 { // Проверить на критичность падения здоровья на 40%
  307. // // log._rintf("WARN Health.setHealth(): большая разовая потеря здоровья(%v)\n", delta)
  308. // сам.Манёвр()
  309. // сам.isRepair.Уст()
  310. // return
  311. // }
  312. // }
  313. // isMask := сам.ВыстрелБлок().Получ()
  314. // switch isMask {
  315. // case true:
  316. // // сам.countLow++
  317. // // if сам.countLow >= 200 {
  318. // // сам.isEnd.Уст()
  319. // // сам.CancelBattle()
  320. // // return
  321. // // }
  322. // default:
  323. // // сам.countLow = 0
  324. // }
  325. // if val == сам.full.Get() {
  326. // сам.temp.Set(val)
  327. // сам.isRepair.Сброс()
  328. // сам.ВыстрелБлок().Сброс()
  329. // // сам.countLow = 0
  330. // }
  331. // }