shot.go 7.5 KB

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