health.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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. lstBattleOn, err := сам.Сеть().Get(strLink)
  183. if err != nil {
  184. // log._rintf("ERRO Health.repair(): при выполнении GET-команды ремонта, err=\n\t%v\n", err)
  185. сам.isEnd.Set()
  186. сам.Отменить()
  187. return
  188. }
  189. сам.СтрОбновить(lstBattleOn)
  190. // sound.Repair()
  191. }
  192. // Ищет своё здоровье (~)
  193. func (сам *Здоровье) здоровьеНайти() {
  194. var (
  195. ind int
  196. strOut string
  197. еслиНайдено bool
  198. lstBattle = сам.СписПолучить()
  199. )
  200. if len(lstBattle) == 0 { // Принудительно обновим сражение
  201. сам.Обновить()
  202. lstBattle = сам.СписПолучить()
  203. }
  204. // <div class="small bold green1 sh_b mb10 mt5">Половина коня</div>
  205. for ind, strOut = range lstBattle {
  206. if strings.Contains(strOut, `<div class="small bold green1 sh_b mb10 mt5">`+сам.логин+`"`) {
  207. еслиНайдено = true
  208. break
  209. }
  210. }
  211. if !еслиНайдено { // Свой танк не найден
  212. сам.isEnd.Set()
  213. сам.Отменить()
  214. return
  215. }
  216. // Свой танк найден, ищем здоровье
  217. // <div class="value-block lh1"><span><span>500</span></span></div>
  218. ind += 11
  219. strOut = lstBattle[ind]
  220. strHealth := strings.TrimPrefix(strOut, `<div class="value-block lh1"><span><span>`)
  221. strHealth = strings.TrimSuffix(strHealth, `</span></span></div>`)
  222. iHealth, err := strconv.Atoi(strHealth)
  223. if err != nil {
  224. сам.isEnd.Set()
  225. сам.Отменить()
  226. return
  227. }
  228. сам.здоровьеУстановить(iHealth)
  229. }
  230. // здоровьеУстановить -- устанавливает текущее здоровье
  231. func (сам *Здоровье) здоровьеУстановить(здоровье int) {
  232. if здоровье < 0 {
  233. // log._rintf("WARN Health.здоровьеУстановить(): кривое значение здоровья танка(%v)\n", val)
  234. здоровье = 0
  235. }
  236. дельта := сам.здоровьеСейчас.Get() - здоровье
  237. if дельта < 0 { // Такое может быть, если было лечение
  238. дельта = 0
  239. }
  240. if здоровье >= сам.здоровьеПолное.Get() {
  241. // log._rintf("WARN Health.здоровьеУстановить(): кривое текущее здоровье, %v/%v\n", val, сам.full.Get())
  242. сам.здоровьеПолное.Set(здоровье)
  243. сам.здоровьеСейчас.Set(здоровье)
  244. // сам.deltaOld = 0
  245. сам.Выстрел().БлокСброс()
  246. сам.еслиНадо.Reset()
  247. return
  248. }
  249. switch {
  250. case сам.isEnd.Get(): // Конец сражения
  251. сам.здоровьеСейчас.Set(0)
  252. сам.isEnd.Set()
  253. сам.Отменить()
  254. return
  255. case здоровье <= 0: // Убит
  256. сам.здоровьеСейчас.Set(0)
  257. сам.isEnd.Set()
  258. сам.Отменить()
  259. return
  260. case здоровье <= 500: // Запретить стрельбу
  261. сам.Выстрел().БлокУст() // Установить запрет стрельбы пока слабое здоровье
  262. сам.еслиНадо.Set()
  263. сам.Манёвр().УстНадо()
  264. case здоровье > 500: // Разрешить стрельбу
  265. сам.Выстрел().БлокСброс()
  266. сам.еслиНадо.Reset()
  267. if дельта > сам.здоровьеПолное.Get()*4/10 { // Проверить на критичность падения здоровья на 40%
  268. // log._rintf("WARN Health.здоровьеУстановить(): большая разовая потеря здоровья(%v)\n", delta)
  269. сам.Манёвр().УстНадо()
  270. сам.еслиНадо.Set()
  271. return
  272. }
  273. }
  274. }