package shot
import (
"context"
"fmt"
// "log"
"strconv"
"strings"
"time"
"wartank/server/serv_bots/warbot/angar/division/divwar/divwaron/shot/damage"
"wartank/server/serv_bots/warbot/angar/division/divwar/divwaron/shottime"
// "wartank/internal/components/sound"
"wartank/pkg/alias"
"wartank/pkg/components/safebool"
"wartank/pkg/types"
)
/*
Исходник предоставляет выстрел со свойствами:
- время до выстрела
- длительность перезарядки
Первый параметр постоянно изменяется (после выстрела восстанавливается)
Второй параметр меняется медленно (в зависимости от количества очков после выстрела)
*/
// Shot -- объект выстрела
type Shot struct {
types.IDivWarOn // FIXME:
recharge *shottime.ShotTime // Сколько времени нужно для полной перезарядки
damage *damage.Damage // Урон от выстрела с памятью
damageSum alias.ADamage // Суммарный урон
isEnd *safebool.SafeBool // Признак конца сражения
login string // Логин для поиска контрольных строк
chTick chan int // Тик для выстрела
ctxEnd context.Context // Признак окончания сражения
}
// NewShot -- возвращает новый *Shot
func NewShot(divWar types.IDivWarOn, login string) (*Shot, error) {
{ // Предусловия
if divWar == nil {
return nil, fmt.Errorf("NewShot(): battle is nil")
}
if login == "" {
return nil, fmt.Errorf("NewShot(): login is empty")
}
}
sf := &Shot{
IDivWarOn: divWar,
ctxEnd: divWar.Ctx(),
recharge: shottime.NewShotTime(),
damage: damage.NewDamage(),
isEnd: divWar.IsEnd(),
login: login,
chTick: make(chan int, 2),
}
// Установить время перезарядки
sf.recharge.Set(6800)
go sf.makeTick()
go sf.run()
return sf, nil
}
// Генерирует тики, когда можно стрелять
func (sf *Shot) makeTick() {
defer func() {
sf.isEnd.Set()
close(sf.chTick)
sf.CancelBattle()
// log._rintf("Shot.makeTick(): сражение завершёно\n")
}()
countMasking := 0
for {
select {
case <-sf.ctxEnd.Done():
return
default:
if sf.isEnd.Get() { // Битва закончилась
return
}
switch sf.Masking().Get() { // Проверить запрет на стрельбу при слабом здоровье
case true:
// log._rintf("WARN Shot.run(): запрет на выстрел\n")
countMasking++
if countMasking >= 200 {
return
}
sf.Manevr()
time.Sleep(time.Millisecond * 500)
continue
default:
countMasking = 0
}
sf.chTick <- 1 // Здесь же первый выстрел
recharge := sf.recharge.Get()
// log._rintf("INFO Shot.run() перезарядка=%v msec\n", recharge)
// Если идёт перезарядка -- постепенно обнуляем время ожидания
time.Sleep(time.Millisecond * time.Duration(recharge))
}
}
}
// Цикл выстрела (в отдельном потоке)
func (sf *Shot) run() {
for {
select {
case <-sf.ctxEnd.Done():
return
case <-sf.chTick:
// Стрелять можно, стандартное ожидание
sf.shot()
sf.findDamage()
}
}
}
// Обновляет возможность выстрела (~)
//
// Вызывается из отдельного потока
func (sf *Shot) shot() {
if err := sf.Net().UpdateLst("Битва дивизий+ выстрел"); err != nil { // Проверка на непосредственно битву
// закончилась 00:00:07 назад
// log._rintf("ERRO Shot.shot(): при обновлении lstBattleOn, err=\n\t%v\n", err)
sf.isEnd.Set()
sf.CancelBattle()
return
}
var (
strOut string
lstBattle = sf.GetLst()
isFind bool
err error
)
// ОБЫЧНЫЕ
for _, strOut = range lstBattle {
if strings.Contains(strOut, `-currentControl-attackRegularShellLink`) {
isFind = true
break
}
}
if !isFind {
// log._rintf("WARN Shot.shot(): не найдены ссылка на выстрел\n")
sf.isEnd.Set()
sf.CancelBattle()
return
}
lstLink := strings.Split(strOut, `ОБЫЧНЫЕ`)
strLink = "http://wartank.ru/" + lstLink[0]
lstBattle, err = sf.Net().Get(strLink)
if err != nil {
// log._rintf("ERRO Shot.shot(): при исполнении GET-команды выстрела обычным снарядом, err=\n\t%v\n", err)
sf.isEnd.Set()
sf.CancelBattle()
return
}
if err = sf.Update(lstBattle); err != nil {
// log._rintf("ERRO Shot.shot(): при обновлении lstBattle, err=\n\t%v\n", err)
sf.isEnd.Set()
sf.CancelBattle()
return
}
// sound.Shot()
sf.Manevr()
}
// Ищет урон от выстрела
func (sf *Shot) findDamage() {
var (
ind = 0
isFind = false
lstShot = sf.GetLst()
strOut string
)
for ind, strOut = range lstShot {
// prospero tank
if strings.Contains(strOut, ``+sf.login+``) {
ind += 2
strOut = lstShot[ind]
isFind = true
break
}
}
if !isFind { // Возможно был рикошет или манёвр
// log._rintf("WARN Shot.findDamage(): не найден урон от выстрела, strOut=%q, ind=%v\n", strOut, ind)
return
}
lstDamage := strings.Split(strOut, ` на `)
if len(lstDamage) == 1 {
return
}
strDamage := lstDamage[1]
iDamage, err := strconv.Atoi(strDamage)
if err != nil {
// log._rintf("ERRO Shot.findDamage(): damage(%v) не число, err=\n\t%v\n", strDamage, err)
return
}
if iDamage <= 0 { // Кривой урон от выстрела
sf.recharge.Dec5()
// log._rintf("WARN Shot.findDamage(): ошибка в значении урона(%v)\n", iDamage)
iDamage = 0
}
sf.damageSum += alias.ADamage(iDamage)
// log._rintf("INFO Shot.Damage(): damageSum=%v\n", sf.damageSum)
if iDamage < 70 {
sf.damage.Set(alias.ADamage(iDamage))
sf.recharge.Inc210()
}
// log._rintf("INFO Shot.findDamage(): выстрел=+%v, урон=%v", iDamage, sf.damageSum)
if iDamage == 0 {
return
}
sf.setDamage(alias.ADamage(iDamage))
}
// setDamage -- обновляет время перезарядки в зависимости от произведённого урона
func (sf *Shot) setDamage(val alias.ADamage) {
sf.damage.Set(val)
switch sf.damage.Result() {
case "none":
sf.recharge.Dec5()
case "up":
sf.recharge.Dec30()
case "down":
sf.recharge.Inc210()
}
}
// IsEnd -- возвращает объект разрешения стрельбы
func (sf *Shot) IsEnd() *safebool.SafeBool {
return sf.isEnd
}