health.go 12 KB

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