shot.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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. if err := сам.Сеть().Обновить(); err != nil { // Проверка на непосредственно битву
  115. // <span>закончилась 00:00:07 назад</span>
  116. // log._rintf("ERRO Shot.shot(): при обновлении lstBattleOn, err=\n\t%v\n", err)
  117. сам.isEnd.Уст()
  118. сам.CancelBattle()
  119. return
  120. }
  121. var (
  122. strOut string
  123. lstBattle = сам.СписПолучить()
  124. еслиНайдено bool
  125. err error
  126. )
  127. // <a href="pve?6-26.ILinkListener-currentControl-attackRegularShellLink" class="simple-but gray"><span><span>ОБЫЧНЫЕ</span></span></a>
  128. for _, strOut = range lstBattle {
  129. if strings.Contains(strOut, `-currentControl-attackRegularShellLink`) {
  130. еслиНайдено = true
  131. break
  132. }
  133. }
  134. if !еслиНайдено {
  135. // log._rintf("WARN Shot.shot(): не найдены ссылка на выстрел\n")
  136. сам.isEnd.Уст()
  137. сам.CancelBattle()
  138. return
  139. }
  140. lstLink := strings.Split(strOut, `<a href="`)
  141. strLink := lstLink[1]
  142. lstLink = strings.Split(strLink, `" class="simple-but gray"><span><span>ОБЫЧНЫЕ</span></span></a>`)
  143. strLink = "https://wartank.ru/" + lstLink[0]
  144. lstBattle, err = сам.Сеть().Get(strLink)
  145. if err != nil {
  146. // log._rintf("ERRO Shot.shot(): при исполнении GET-команды выстрела обычным снарядом, err=\n\t%v\n", err)
  147. сам.isEnd.Уст()
  148. сам.CancelBattle()
  149. return
  150. }
  151. if err = сам.СтрОбновить(lstBattle); err != nil {
  152. // log._rintf("ERRO Shot.shot(): при обновлении lstBattle, err=\n\t%v\n", err)
  153. сам.isEnd.Уст()
  154. сам.CancelBattle()
  155. return
  156. }
  157. // sound.Shot()
  158. сам.Манёвр()
  159. }
  160. // Ищет урон от выстрела
  161. func (сам *Shot) findDamage() {
  162. var (
  163. ind int
  164. еслиНайдено bool
  165. lstShot = сам.СписПолучить()
  166. strOut string
  167. )
  168. for ind, strOut = range lstShot {
  169. // <span class="yellow1 td_u">prospero tank</span>
  170. if strings.Contains(strOut, `<span class="yellow1 td_u">`+сам.login+`</span>`) {
  171. ind += 2
  172. strOut = lstShot[ind]
  173. еслиНайдено = true
  174. break
  175. }
  176. }
  177. if !еслиНайдено { // Возможно был рикошет или манёвр
  178. // log._rintf("WARN Shot.findDamage(): не найден урон от выстрела, strOut=%q, ind=%v\n", strOut, ind)
  179. return
  180. }
  181. lstDamage := strings.Split(strOut, ` на <span class="red1">`)
  182. if len(lstDamage) == 1 {
  183. return
  184. }
  185. strDamage := lstDamage[1]
  186. iDamage, err := strconv.Atoi(strDamage)
  187. if err != nil {
  188. // log._rintf("ERRO Shot.findDamage(): damage(%v) не число, err=\n\t%v\n", strDamage, err)
  189. return
  190. }
  191. if iDamage <= 0 { // Кривой урон от выстрела
  192. сам.recharge.Dec5()
  193. // log._rintf("WARN Shot.findDamage(): ошибка в значении урона(%v)\n", iDamage)
  194. iDamage = 0
  195. }
  196. сам.damageSum += alias.Урон(iDamage)
  197. // log._rintf("INFO Shot.Damage(): damageSum=%v\n", сам.damageSum)
  198. if iDamage < 70 {
  199. сам.damage.Set(alias.Урон(iDamage))
  200. сам.recharge.Inc210()
  201. }
  202. // log._rintf("INFO Shot.findDamage(): выстрел=+%v, урон=%v", iDamage, сам.damageSum)
  203. if iDamage == 0 {
  204. return
  205. }
  206. сам.setDamage(alias.Урон(iDamage))
  207. }
  208. // setDamage -- обновляет время перезарядки в зависимости от произведённого урона
  209. func (сам *Shot) setDamage(val alias.Урон) {
  210. сам.damage.Set(val)
  211. switch сам.damage.Result() {
  212. case "none":
  213. сам.recharge.Dec5()
  214. case "up":
  215. сам.recharge.Dec30()
  216. case "down":
  217. сам.recharge.Inc210()
  218. }
  219. }
  220. // IsEnd -- возвращает объект разрешения стрельбы
  221. func (сам *Shot) IsEnd() *safe_bool.БезопБул {
  222. return сам.isEnd
  223. }