health.go 10 KB

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