shot.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. package shot
  2. import (
  3. "strconv"
  4. "strings"
  5. "time"
  6. . "wartank/app/lev0/types"
  7. "wartank/app/lev1/shot/damage"
  8. "wartank/app/lev1/shot/shot_time"
  9. // "wartank/internal/components/sound"
  10. "wartank/app/lev0/alias"
  11. . "gitp78su.ipnodns.ru/svi/kern"
  12. . "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
  13. . "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
  14. )
  15. /*
  16. Исходник предоставляет выстрел со свойствами:
  17. - время до выстрела
  18. - длительность перезарядки
  19. Первый параметр постоянно изменяется (после выстрела восстанавливается)
  20. Второй параметр меняется медленно (в зависимости от количества очков после выстрела)
  21. */
  22. // выстрел -- объект выстрела
  23. type выстрел struct {
  24. ИСражениеПроцесс // FIXME:
  25. перезарядка *shot_time.ShotTime // Сколько времени нужно для полной перезарядки
  26. урон *damage.Damage // Урон от выстрела с памятью
  27. уронВсего alias.АУрон // Суммарный урон
  28. isEnd ISafeBool // Признак конца сражения
  29. еслиБлок ISafeBool // Признак блокировки выстрела
  30. логин string // Логин для поиска контрольных строк
  31. chTick chan int // Тик для выстрела
  32. }
  33. // НовВыстрел -- возвращает новый выстрел
  34. func НовВыстрел(проц ИСражениеПроцесс) ИВыстрел {
  35. Hassert(проц != nil, "НовВыстрел(): ИСражениеПроцесс == nil")
  36. логинТанк := проц.Бот().Имя()
  37. сам := &выстрел{
  38. ИСражениеПроцесс: проц,
  39. перезарядка: shot_time.NewShotTime(),
  40. урон: damage.NewDamage(),
  41. еслиБлок: NewSafeBool(),
  42. isEnd: проц.ЕслиКонец(),
  43. логин: логинТанк,
  44. chTick: make(chan int, 2),
  45. }
  46. // Установить время перезарядки
  47. сам.перезарядка.Set(6800)
  48. go сам.makeTick()
  49. go сам.run()
  50. return сам
  51. }
  52. // БлокУст -- установка блокировки выстрела
  53. func (сам *выстрел) БлокУст() {
  54. сам.еслиБлок.Set()
  55. }
  56. // БлокСброс -- сброс блокировки выстрела
  57. func (сам *выстрел) БлокСброс() {
  58. сам.еслиБлок.Reset()
  59. }
  60. // ЕслиБлок -- возвращает признак блокировки выстрела
  61. func (сам *выстрел) ЕслиБлок() bool {
  62. return сам.еслиБлок.Get()
  63. }
  64. // Генерирует тики, когда можно стрелять
  65. func (сам *выстрел) makeTick() {
  66. defer func() {
  67. сам.isEnd.Set()
  68. close(сам.chTick)
  69. сам.Отменить()
  70. // log._rintf("Shot.makeTick(): сражение завершёно\n")
  71. }()
  72. countMasking := 0
  73. for {
  74. select {
  75. case <-сам.Контекст().Done():
  76. return
  77. default:
  78. if сам.isEnd.Get() { // Битва закончилась
  79. return
  80. }
  81. switch сам.еслиБлок.Get() { // Проверить запрет на стрельбу при слабом здоровье
  82. case true:
  83. // log._rintf("WARN Shot.run(): запрет на выстрел\n")
  84. countMasking++
  85. if countMasking >= 200 {
  86. return
  87. }
  88. сам.Манёвр()
  89. time.Sleep(time.Millisecond * 500)
  90. continue
  91. default:
  92. countMasking = 0
  93. }
  94. сам.chTick <- 1 // Здесь же первый выстрел
  95. recharge := сам.перезарядка.Get()
  96. // log._rintf("INFO Shot.run() перезарядка=%v msec\n", recharge)
  97. // Если идёт перезарядка -- постепенно обнуляем время ожидания
  98. time.Sleep(time.Millisecond * time.Duration(recharge))
  99. }
  100. }
  101. }
  102. // Цикл выстрела (в отдельном потоке)
  103. func (сам *выстрел) run() {
  104. for {
  105. select {
  106. case <-сам.Контекст().Done():
  107. return
  108. case <-сам.chTick:
  109. // Стрелять можно, стандартное ожидание
  110. сам.shot()
  111. сам.findDamage()
  112. }
  113. }
  114. }
  115. // Обновляет возможность выстрела (~)
  116. //
  117. // Вызывается из отдельного потока
  118. func (сам *выстрел) shot() {
  119. сам.Обновить()
  120. var (
  121. strOut string
  122. lstBattle = сам.СписПолучить()
  123. еслиНайдено bool
  124. )
  125. // <a href="pve?6-26.ILinkListener-currentControl-attackRegularShellLink" class="simple-but gray"><span><span>ОБЫЧНЫЕ</span></span></a>
  126. for _, strOut = range lstBattle {
  127. if strings.Contains(strOut, `-currentControl-attackRegularShellLink`) {
  128. еслиНайдено = true
  129. break
  130. }
  131. }
  132. if !еслиНайдено {
  133. // log._rintf("WARN Shot.shot(): не найдены ссылка на выстрел\n")
  134. сам.isEnd.Set()
  135. сам.Бот().КонтБот().Cancel()
  136. return
  137. }
  138. strLink := strings.TrimPrefix(strOut, `<a href="`)
  139. strLink = strings.TrimSuffix(strLink, `" class="simple-but gray"><span><span>ОБЫЧНЫЕ</span></span></a>`)
  140. strLink = "https://wartank.ru/" + strLink
  141. res := сам.Сеть().Get(strLink)
  142. if res.IsErr() {
  143. // log._rintf("ERRO Shot.shot(): при исполнении GET-команды выстрела обычным снарядом, err=\n\t%v\n", err)
  144. сам.isEnd.Set()
  145. сам.Бот().КонтБот().Cancel()
  146. return
  147. }
  148. lstBattle = res.Unwrap()
  149. сам.СтрОбновить(lstBattle)
  150. // sound.Shot()
  151. сам.Манёвр()
  152. }
  153. // Ищет урон от выстрела
  154. func (сам *выстрел) findDamage() {
  155. var (
  156. ind int
  157. еслиНайдено bool
  158. lstShot = сам.СписПолучить()
  159. strOut string
  160. )
  161. for ind, strOut = range lstShot {
  162. // <span class="yellow1 td_u">prospero tank</span>
  163. if strings.Contains(strOut, `<span class="yellow1 td_u">`+сам.логин+`</span>`) {
  164. ind += 2
  165. strOut = lstShot[ind]
  166. еслиНайдено = true
  167. break
  168. }
  169. }
  170. if !еслиНайдено { // Возможно был рикошет или манёвр
  171. // log._rintf("WARN Shot.findDamage(): не найден урон от выстрела, strOut=%q, ind=%v\n", strOut, ind)
  172. return
  173. }
  174. lstDamage := strings.Split(strOut, ` на <span class="red1">`)
  175. if len(lstDamage) == 1 {
  176. return
  177. }
  178. strDamage := lstDamage[1]
  179. iDamage, err := strconv.Atoi(strDamage)
  180. if err != nil {
  181. // log._rintf("ERRO Shot.findDamage(): damage(%v) не число, err=\n\t%v\n", strDamage, err)
  182. return
  183. }
  184. if iDamage <= 0 { // Кривой урон от выстрела
  185. сам.перезарядка.Dec5()
  186. // log._rintf("WARN Shot.findDamage(): ошибка в значении урона(%v)\n", iDamage)
  187. iDamage = 0
  188. }
  189. сам.уронВсего += alias.АУрон(iDamage)
  190. // log._rintf("INFO Shot.Damage(): damageSum=%v\n", сам.damageSum)
  191. if iDamage < 70 {
  192. сам.урон.Set(alias.АУрон(iDamage))
  193. сам.перезарядка.Inc210()
  194. }
  195. // log._rintf("INFO Shot.findDamage(): выстрел=+%v, урон=%v", iDamage, сам.damageSum)
  196. if iDamage == 0 {
  197. return
  198. }
  199. сам.setDamage(alias.АУрон(iDamage))
  200. }
  201. // setDamage -- обновляет время перезарядки в зависимости от произведённого урона
  202. func (сам *выстрел) setDamage(val alias.АУрон) {
  203. сам.урон.Set(val)
  204. switch сам.урон.Result() {
  205. case "none":
  206. сам.перезарядка.Dec5()
  207. case "up":
  208. сам.перезарядка.Dec30()
  209. case "down":
  210. сам.перезарядка.Inc210()
  211. }
  212. }
  213. // IsEnd -- возвращает объект разрешения стрельбы
  214. func (сам *выстрел) IsEnd() ISafeBool {
  215. return сам.isEnd
  216. }