Files
uarep-ctl/scenes/работа/работа.gd

653 lines
27 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class_name work extends Panel
## Основной скрипт для режима "Работа".
@export var Bip: PackedScene ## Индикатор излучения цели.
@export var col_grey: Color ## Второй цвет для фона.
@export var col_red: Color ## Первый цвет для фона.
@export var drag_scale_band: float ## Масштаб изменения параметра от движения мыши для строба по радиальному размеру.
@export var drag_scale_width: float ## Масштаб изменения параметра от движения мыши для строба по угловому размеру.
@export var freq_high: float ## Верхняя рабочая частота, МГЦ.
@export var freq_low: float ## Нижняя рабочая частота, МГЦ.
@export var radius_center: Vector2 ## Координаты центра диаграммы пеленг-частота.
@export var radius_internal: float ## Внутренний радиус диаграммы пеленг-частота.
@export var radius_outter: float ## Внешний радиус диаграммы пеленг-частота.
@export var strob_max_band: float ## Максимальная ширина строба по углу.
@export var strob_max_width: float ## Максимальная ширина строба по углу.
@export var strob_min_band: float ## Минимальная ширина строба по частоте.
@export var strob_min_width: float ## Минимальная ширина строба по углу.
@export var window_0_id: int ## Идетификатор окна. Присваивается godot от 0 до количества созданных окон.
const SVK_EMIT = 1 ## Код состояния выполнения помехи - выполняется
var drag_button: MouseButton ## Кнопка мыши нажатая при перетаскивании.
var drag_fsm: DragFSM = DragFSM.IDLE ## Текущее состояние перетаскивания.
var btn_select: BtnSel = BtnSel.THREAT ## Текущее состояние кнопки выбора цели.
var drag_pos_begin: Vector2 ## Точка начала перетаскивания.
var max_zoom: int ## Максимальный уровень детализации.
var min_zoom: int ## Минимальный уровень детализации.
var shift_pressed: bool ## Кнопка SHIFT нажата.
var strob_band_begin: float ## Ширина строба в момент начала перетаскивания.
var strob_dir_begin: float ## Направление антенны в момент начала перетаскивания.
var strob_width_begin: float ## Ширина строба по углу в момент начала перетаскивания.
var view_mode: MapViewMode ## Режим отображения карты
var thrs_dict: Dictionary ## Ссылка на словарь целей
var gos_flag = true ## Флаг работать по своим целям
var color_ecm: Color
var color_select: Color
var color_emit: Color
var color_emit_sel: Color
var color_closed: Color
var color_closed_emit: Color
var course = 0.0 ## Курс корабля
signal drag_begin ## Вызывается в момент начала перетаскивания.
signal drag_continue ## Вызывается во время продолжения перетаскивания.
signal drag_end ## Вызывается в момент завершения перетаскивания.
## Размеры карты для разных режимов РТО и РЛС соответственно
const map_sizes = [
Rect2i(360, 360, 400, 400),
Rect2i(82, 82, 955, 955)]
var fs_band = prd.FS_BAND_RTO ## Текущий режим блоков ФС по частоте
var fs_selected: Dictionary ## Словарь выбранных секторов
var fs_active: Dictionary ## Словарь секторов назначенных на подавление
var fs_emit: Dictionary ## Словарь секторов назначенных на подавление
var fs_closed: Dictionary ## Словарь для секторов запрета
var fs_failed: Array ## Индикация сломанных секторов (модули ФС). AntBandState.ANT_BAND_READY - исправен, AntBandState.ANT_BAND_FAILURE - сломан.
## Состояние перетаскивания.
enum DragFSM {
DRAG, ## В процессе.
IDLE, ## Не активно, ожидание.
OFF ## Выключено.
}
## Режим выбора
enum BtnSel {
SECTOR, ## Выбор сектора.
THREAT, ## Выбор цели.
FS ## Выбор стробом.
}
## Режим отображения карты
enum MapViewMode {
RTO = 0, ## Режим приоритет РТО (малая карта, широкие секторы)
RLS = 1 ## Режим приоритет РЛС (большая карта, узкие секторы)
}
enum AntBandState {
ANT_BAND_READY = 1,
ANT_BAND_FAILED = 0
}
const VAL_TO_EVENT = {
false: 'manual_control', ## Выбор целей и установка помех вручную.
true: 'full_auto' } ## Выбор целей и установка помех автоматически.
## Обработчик выбора цели.
func on_threat_selected(th, ecm_btns): on_rto_threat_sel(th.id, ecm_btns)
## Встроенный обратный вызов.
func on_enter_tree() -> void: call_deferred('on_button_strobe_pressed')
func clear_all_klaster(): $canvas.clear_all_seczap()
func on_user_panning(): $btn_center.button_pressed = false
func on_settings_changed():
gos_flag = ProjectSettings.get_setting('application/config/Включить работу по своим целям', true)
color_emit = ProjectSettings.get_setting('application/interfer/emit_color', Color(0.58, 0.47, 0.51, 1.0))
color_ecm = ProjectSettings.get_setting('application/interfer/interfer_color', Color(0.58, 0.47, 0.51, 1.0))
color_select = ProjectSettings.get_setting('application/interfer/select_color', Color(1.0, 1.0, 1.0, 1.0))
color_closed = ProjectSettings.get_setting('application/interfer/closed_sectors', Color(0.0, 0.0, 0.0, 1.0))
color_emit_sel = color_select
color_closed_emit = color_closed
color_select *= Color(1.0, 1.0, 1.0, 0.0)
color_ecm *= Color(1.0, 1.0, 1.0, 0.0)
color_emit_sel *= Color(1.0, 1.0, 1.0, 0.2)
color_closed *= Color(1.0, 1.0, 1.0, 0.0)
color_closed_emit *= Color(1.0, 1.0, 1.0, 1.0)
## Обработчик сигнала изменения списка целей.
func on_threats_resized(ths: Dictionary):
count_threats(ths)
thrs_dict = ths
## Обработчик нажатия на кнопку вид.
func on_button_view_toggled(toggled: bool):
signaller.emit_signal('map_mode_changed', int(toggled))
fs_band = prd.FS_BAND_RLS if toggled else prd.FS_BAND_RTO
var s = map_sizes[int(toggled)]
$tilemap.size = s.size
$tilemap.position = s.position
view_mode = MapViewMode.RLS if toggled else MapViewMode.RTO
if view_mode != MapViewMode.RTO:
fs_selected.clear()
## Встроенный обратный вызов.
func _enter_tree() -> void:
var timer1 = Timer.new()
timer1.name = 'timer1'
add_child(timer1)
timer1.one_shot = true
timer1.connect('timeout', Callable(self, 'on_enter_tree'))
timer1.start(1.0)
## Встроенный обратный вызов.
func _exit_tree() -> void:
var timer1 = get_node('timer1')
remove_child(timer1)
timer1.queue_free()
func init_fs_failed():
var sectors_count = $canvas.fs_default_colors.size()
fs_failed.resize(sectors_count)
for i in fs_failed.size():
fs_failed[i] = AntBandState.ANT_BAND_READY
## Производит инициализацию.
func _ready():
ProjectSettings.connect('settings_changed', on_settings_changed)
on_settings_changed()
init_fs_failed()
min_zoom = ProjectSettings.get_setting('application/config/map_minimum_zoom', 5)
max_zoom = ProjectSettings.get_setting('application/config/map_maximum_zoom', 19)
drag_fsm = DragFSM.OFF
var btns_select: Array = get_tree().get_nodes_in_group('группа-режим-выбора')
btns_select.any(func(btn): btn.button_connect('pressed', Callable(self, 'on_button_select').bind(btn, btns_select)))
var nodes: Array = get_tree().get_nodes_in_group('группа-режим-помехи')
nodes.any(func(btn): btn.button_connect('pressed', Callable(self, 'on_button_pressed').bind(btn, nodes)))
$btn_close.button_connect('pressed', Callable(self, 'on_btn_close').bind($btn_close))
$btn_work.button_connect('pressed', Callable(self, 'on_btn_work').bind($btn_work))
threats.connect('threat_new', Callable(self, 'on_threat_new'))
threats.connect('threat_lost', Callable(self, 'on_threat_lost'))
threats.connect('threats_resized', Callable(self, 'on_threats_resized'))
threats.connect('threat_update', Callable(self, 'on_threat_update'))
signaller.connect('rto_threat_sel', Callable(self, 'on_rto_threat_sel').bind(nodes))
signaller.connect('bip_pressed', Callable(self, 'on_bip_pressed').bind(nodes))
signaller.connect('rto_threat_unsel', Callable(self, 'on_rto_threat_unsel').bind(nodes))
signaller.connect('threat_selected', Callable(self, 'on_threat_selected').bind(nodes))
signaller.connect('interfer_selected', Callable(self, 'on_interfer_selected').bind(nodes))
signaller.connect('interfer_new', Callable(self, 'on_interfer_new'))
signaller.connect('on_prog_ecm_off', Callable(self, 'on_interfer_new'))
signaller.connect('interfer_off_all', Callable(self, 'on_interfer_off_all'))
signaller.connect('interfer_prinuditelno', Callable(self, 'on_interfer_prinuditelno').bind(nodes))
signaller.connect('fs_selected', Callable(self, 'on_fs_selected'))
signaller.connect('fs_update_color', Callable(self, 'set_fs_color'))
interfer.connect('interfer_update', Callable(self, 'on_interfer_new'))
signaller.connect('interfer_prinuditelno', Callable(self, 'on_chk_interfer_prinuditelno'))
signaller.connect('fs_update_state', Callable(self, 'on_fs_update_state'))
signaller.connect('map_user_panning', Callable(self, 'on_user_panning'))
signaller.connect('update_coordinates_map', Callable(self, 'on_update_coordinates_label'))
signaller.connect('update_scale_map', Callable(self, 'on_update_scale_label'))
signaller.connect('sector_klaster', Callable(self, 'sector_klaster'))
signaller.connect('clear_klaster', Callable(self, 'clear_all_klaster'))
signaller.connect('th_aoa_update', Callable(self, 'on_th_aoa_update'))
var unit_nav = network.get_unit_instance('навигация')
unit_nav.connect('data_received', on_navi_data_received)
on_btn_center_toggled(true)
on_btn_activate_toggled(true)
func sector_klaster(sector_data):
if sector_data['draw']:
var rect: Rect2 = sector_data['rect']
var color: Color = sector_data['color']
var id: int = sector_data['index']
$canvas.add_seczap(id, Vector4(rect.position.x, rect.position.y, rect.size.x, rect.size.y))
$canvas.set_seczap_color(id, color)
func on_navi_data_received(data: Dictionary):
var k = data.get('k', 0.0)
$canvas.set_canvas_rotation(k)
course = k
func on_chk_interfer_prinuditelno(_interfer_sectors):
$chk_auto.set_pressed_no_signal(false)
on_value_changed(false)
func on_value_changed(val: bool):
signaller.emit_signal(VAL_TO_EVENT[val])
func get_all_nodes(root):
var nodes = []
nodes.append(root)
for child in root.get_children():
nodes.append_array(get_all_nodes(child))
return nodes
## Производит отображение цели на диаграмму.
func map_threat_to_bip(th: threats.Threat, bip: Control) -> bool:
var freq = th.freq
var sector_key = null
var sector_width = 50.0
for key in prd.FS_FREQ:
var freq_range = prd.FS_FREQ[key]
var _num_values = (freq_range[1] - freq_range[0]) / sector_width
if freq_range[0] <= freq and freq <= freq_range[1]:
sector_key = key
break
if sector_key != null:
radius_internal = fs_band[sector_key][0]
radius_outter = fs_band[sector_key][1]
freq_low = prd.FS_FREQ[sector_key][0]
freq_high = prd.FS_FREQ[sector_key][1]
bip.position = tools.pos_calc(
th.freq,
th.aoa,
radius_center,
radius_internal,
radius_outter,
freq_low,
freq_high) - bip.size / 2
return sector_key != null
## Обработчик сигнала обновления цели.
func on_threat_update(th: threats.Threat):
var bip = get_node('%d' % th.id)
bip.update(th.tick_rx)
bip.set_bip_color(Color.CORAL if th.proto else Color.WHITE)
bip.visible = map_threat_to_bip(th, bip)
## Обработчик выбора помехи для цели.
func on_interfer_new(ecm):
if not interfer.thrs.has(ecm.ispp): return
var th_id = interfer.thrs[ecm.ispp]
var bip = get_node('%d' % th_id)
if bip != null:
if ecm.krp!= interfer.INTERFER_OFF:
bip.set_track_visible(true)
if ecm.svk == 1:
bip.set_ecm_visible(true)
else: bip.set_ecm_visible(false)
else:
bip.set_ecm_visible(false)
bip.set_track_visible(false)
else:
interfer.thrs.erase(ecm.ispp)
update_fs_colors()
## Обработчик выключения всех помех.
func on_interfer_off_all():
var bips: Array = get_children().filter(func(item): return item.name.is_valid_int())
for bip in bips:
bip.set_ecm_visible(false)
bip.set_track_visible(false)
fs_active.clear()
fs_emit.clear()
update_fs_colors()
## Обработчик сигнала обнаружения цели.
func on_threat_new(th) -> void:
var bip = Bip.instantiate()
bip.name = '%d' % th.id
map_threat_to_bip(th, bip)
add_child(bip)
## Обработчик сигнала потери цели.
func on_threat_lost(th) -> void:
var bip = get_node('%d' % th.id)
remove_child(bip)
bip.free()
## обработчик нажатия на цель на экране.
func on_bip_pressed(th_id, _ecm_btns):
if btn_select == BtnSel.THREAT:
signaller.emit_signal('rto_threat_sel', th_id)
## Обработчик выбора РТО цели.
func on_rto_threat_sel(th_id, ecm_btns):
var bips: Array = get_children().filter(func(item): return item.name.is_valid_int())
for item in bips:
if th_id == item.name.to_int():
item.set_sel_visible(true)
unsel_fs_all(false)
else:
item.set_sel_visible(false)
var th = thrs_dict[th_id]
var krp = interfer.get_krp(th)
for btn in ecm_btns:
set_btns_disabled(false, btn)
var btn_meta = btn.get_meta('rfi_name')
if krp == btn_meta:
btn.set_rec_visible(true)
else:
btn.set_rec_visible(false)
## Обработчик снятия выбора РТО цели.
func on_rto_threat_unsel(th_id, ecm_btns):
var bips: Array = get_children().filter(func(item): return item.name.is_valid_int())
for item in bips:
if th_id == item.name.to_int():
item.set_sel_visible(false)
break
set_ecm_btns_state(true)
for btn in ecm_btns:
btn.set_rec_visible(false)
## Обработчик нажатия кнопки "Строб". Управляет отображением строба.
func on_button_strobe_pressed() -> void:
drag_fsm = DragFSM.IDLE if $btns_select/btn_strob.is_pressed() else DragFSM.OFF
$canvas.set_strob_visible((drag_fsm == DragFSM.IDLE) and (view_mode == MapViewMode.RTO))
## Поведение радиокнопок в группе. Управляет состоянием кнопок выбора целей.
func on_button_select(btn_this, btn_others) -> void:
for btn_other in btn_others: btn_other.set_pressed(btn_this == btn_other)
btn_select = btn_this.get_meta('state')
on_button_strobe_pressed()
signaller.emit_signal('rto_threat_unsel_all')
unsel_fs_all(true)
set_ecm_btns_state(true)
## Поведение радиокнопок в группе. Управляет состоянием кнопок в радиогруппе.
func on_button_pressed(btn_this, btn_others) -> void:
$chk_auto.set_pressed_no_signal(false)
on_value_changed(false)
var fs_arr: Array = []
for btn_other in btn_others: btn_other.set_pressed(btn_this == btn_other)
var interfer_name: String = btn_this.get_meta('rfi_name')
for key in fs_selected.keys():
fs_arr.append(prd.get_fs_param(key))
signaller.emit_signal('interfer_init', interfer_name, fs_arr, interfer.POW_DEFAULT)
## Управляет состоянием кнопок в радиогруппе при выборе помехи.
func on_interfer_selected(ecm, btns) -> void:
var ecm_name: String
if ecm != null:
ecm_name = ecm.krp
else:
ecm_name = interfer.INTERFER_OFF
for btn in btns:
set_btns_disabled(false, btn)
btn.get_meta('rfi_name')
if ecm_name == btn.get_meta('rfi_name'):
btn.set_pressed(true)
else:
btn.set_pressed(false)
## Возвращает ширину строба по направлению.
func get_strob_width(pos: Vector2, prev: float) -> float:
var strob_dir_width: float = prev + (drag_pos_begin.y - pos.y) * drag_scale_width
strob_dir_width = tools.clip_value(strob_dir_width, strob_min_width, strob_max_width)
return strob_dir_width
## Возвращает ширину строба по частоте.
func get_strob_band(pos: Vector2, prev: float) -> float:
var strob_dir_band: float = prev - (drag_pos_begin.y - pos.y) * drag_scale_band
strob_dir_band = tools.clip_value(strob_dir_band, strob_min_band, strob_max_band)
return strob_dir_band
## Устанавливает центр строба по частоте и направлению.
func set_strob(pos: Vector2) -> void:
var radp: Vector2 = tools.map_dec_to_rad(radius_center, pos)
if radp.y < $canvas.get_radius_outter():
$canvas.set_strob_dir(radp.x)
$canvas.set_strob_radius(radp.y)
## Обработка движения мыши для управления стробом. Вызывается во время движения мыши.
func _on_drag_continue(event) -> void:
if not $canvas.get_strob_visible(): return
if drag_button == MOUSE_BUTTON_RIGHT:
if shift_pressed:
var strob_width: float = get_strob_width(event.position, strob_width_begin)
$canvas.set_strob_width(strob_width)
else:
var strob_band: float = get_strob_band(event.position, strob_band_begin)
$canvas.set_strob_band(strob_band)
elif drag_button == MOUSE_BUTTON_LEFT:
set_strob(event.position)
## Обработка движения мыши для управления стробом. Вызывается при нажатии мыши.
func _on_drag_begin(event) -> void:
if not $canvas.get_strob_visible(): return
drag_pos_begin = event.position
if drag_button == MOUSE_BUTTON_LEFT:
set_strob(event.position)
elif drag_button == MOUSE_BUTTON_RIGHT:
strob_width_begin = $canvas.get_strob_width()
strob_band_begin = $canvas.get_strob_band()
## Обработчик событий ввода. Вызывается godot.
func _input(event) -> void:
if not is_visible_in_tree(): return
if (event is InputEventMouseMotion) and (view_mode == MapViewMode.RTO):
if drag_fsm == DragFSM.DRAG:
emit_signal('drag_continue', event)
elif event is InputEventMouseButton:
drag_button = event.button_index
if event.pressed:
if drag_fsm == DragFSM.IDLE:
drag_fsm = DragFSM.DRAG
emit_signal('drag_begin', event)
else:
if btn_select == BtnSel.FS:
sel_fs_sector(event)
drag_fsm = DragFSM.IDLE
emit_signal('drag_end', event)
elif event is InputEventKey:
if event.physical_keycode == KEY_SHIFT:
shift_pressed = event.pressed
## Обработчик нажатия по сектору формирователя сигналов.
func sel_fs_sector(event):
if view_mode != MapViewMode.RTO: return
if (event.button_index == MOUSE_BUTTON_LEFT) or (event.button_index == MOUSE_BUTTON_RIGHT):
var center: Vector2 = $tilemap.position
var center_offset: Vector2 = ProjectSettings.get_setting('application/interfer/center_offset', Vector2i(0, 25))
var angle: float = event.position.angle_to_point(center + center_offset) * 180/PI - 45.0 - course
if angle < 0:
angle += 360
var distance: float = event.position.distance_to(center + center_offset)
var sector: int
var band: int
var has_band: bool = false
for sec in prd.FS_SECTORS.values():
if angle >= sec[0] and angle <= sec[1]:
sector = prd.FS_SECTORS.find_key(sec)
for diap in fs_band.values():
if distance >= diap[0] and distance <= diap[1]:
band = fs_band.find_key(diap)
has_band = true
if not has_band:
return
var fs_index = band * 4 + sector
if event.button_index == MOUSE_BUTTON_LEFT:
fs_selected[fs_index] = null
else:
fs_selected.erase(fs_index)
update_fs_colors()
signaller.emit_signal('rto_threat_unsel_all')
var btn_state = len(fs_selected) == 0
set_ecm_btns_state(btn_state)
func set_fs_color(ecms):
fs_active.clear()
fs_emit.clear()
for ecm in ecms.values():
if not prd.fs_caps_id.has(ecm.nmfs):
push_warning('нет такого модуля ФС: %d, но имеется: %s' % [ecm.nmfs, prd.fs_caps_id.keys()])
continue
if ecm.krp != interfer.INTERFER_OFF:
fs_active[prd.fs_caps_id[ecm.nmfs]] = null
if ecm.svk == SVK_EMIT:
fs_emit[prd.fs_caps_id[ecm.nmfs]] = null
update_fs_colors()
func update_fs_colors():
var fs_colors = $canvas.fs_default_colors.duplicate()
for key in fs_active.keys():
fs_colors[key] = color_ecm
for key in fs_emit.keys():
fs_colors[key] = color_emit
for key in fs_closed:
if key in fs_emit:
fs_colors[key] = color_closed_emit
else:
fs_colors[key] = color_closed
for key in fs_selected.keys():
if key in fs_emit:
fs_colors[key] = color_emit_sel
else:
fs_colors[key] = color_select
$canvas.set_band_colors(fs_colors)
$canvas.set_band_failures(fs_failed)
if fs_emit.size():
if $btn_activate.pressed:
$btn_activate.self_modulate = Color.RED
else:
$btn_activate.self_modulate = Color.WHITE
else:
$btn_activate.self_modulate = Color.WHITE
func unsel_fs_all(need_btns_off: bool):
fs_selected.clear()
update_fs_colors()
if need_btns_off:
set_ecm_btns_state(true)
func set_ecm_btns_state(value: bool):
var btns: Array = get_tree().get_nodes_in_group('группа-режим-помехи')
for btn in btns:
btn.set_pressed(false)
set_btns_disabled(value, btn)
func on_fs_selected(ecm):
var fs_index_arr: Array = []
prd.get_fs_index(ecm, fs_index_arr)
fs_selected.clear()
if view_mode != MapViewMode.RTO: return
for item in fs_index_arr:
fs_selected[item] = null
update_fs_colors()
func set_btns_disabled(value: bool, btn):
var external_ctl = ProjectSettings.get_setting('application/config/Внешнее управление', false)
if external_ctl:
btn.set_pressed(false)
btn.set_disabled(true)
else:
btn.set_disabled(value)
func on_interfer_prinuditelno (prenuditelno_sectors, btns):
for key in prenuditelno_sectors:
var sectors = []
for sector in prenuditelno_sectors[key]:
sectors.append(prd.get_fs_param(sector))
signaller.emit_signal('interfer_init', key, sectors, interfer.POW_DEFAULT)
update_fs_colors()
for btn in btns:
btn.set_pressed(false)
set_btns_disabled(true, btn)
## Вызывается при получении информации о состоянии модулей ФС от КАПС РПБ.
func on_fs_update_state(fs_dict: Dictionary):
for key in fs_dict.keys():
fs_failed[key] = fs_dict[key].isg
func on_btn_center_toggled(toggled_on: bool) -> void:
$btn_center.tooltip_text = 'Центрирует карту на текущие координаты (%s)' % ('включено' if toggled_on else 'отключено')
signaller.emit_signal('map_centered', toggled_on)
func on_btn_activate_toggled(toggled_on: bool) -> void:
$btn_activate.tooltip_text = 'Включает подавление (%s)' % ('включено' if toggled_on else 'отключено')
signaller.emit_signal('emit_changed', toggled_on)
if not toggled_on:
$btn_activate.self_modulate = Color.WHITE
func on_update_coordinates_label(coordinates_label) -> void:
$coordinates_map.text = str(coordinates_label)
func on_update_scale_label(scale_label) -> void:
$scale_map.text = str(scale_label)
func on_btn_close(btn):
await get_tree().create_timer(0.2).timeout
btn.pressed = false
fs_closed.merge(fs_selected)
signaller.emit_signal('update_fs_closed', fs_closed)
func on_btn_work(btn):
await get_tree().create_timer(0.2).timeout
btn.pressed = false
fs_closed.clear()
for fs_index in prd.FS_PRD.keys():
if fs_index in fs_selected: continue
fs_closed[fs_index] = null
signaller.emit_signal('update_fs_closed', fs_closed)
func _on_btn_all_work_pressed() -> void:
fs_closed.clear()
signaller.emit_signal('update_fs_closed', fs_closed)
func on_th_aoa_update(ths):
count_threats(ths)
func count_threats(ths):
$count_all_pad/count_all.text = '%02d' % ths.size()
var c: int = 0
if gos_flag:
c = ths.values().filter(func(th: threats.Threat): return th.proto != '').size()
else:
c = ths.values().filter(func(th: threats.Threat): return ((th.proto != '') and th.proto not in interfer.gos_protocols)).size()
$count_danger_pad/count_danger.text = '%02d' % c