shot.go 7.9 KB

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