health.go 12 KB

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