ticker
This commit is contained in:
parent
c5846aba7b
commit
074bb21ffd
18 changed files with 99 additions and 51 deletions
|
@ -21,6 +21,7 @@ GuiEventBus="*res://scripts/gui/gui_event_bus.gd"
|
||||||
RuntimePlayerData="*res://scripts/autoloads/runtime_player_data.gd"
|
RuntimePlayerData="*res://scripts/autoloads/runtime_player_data.gd"
|
||||||
Registry="*res://scripts/autoloads/registry.gd"
|
Registry="*res://scripts/autoloads/registry.gd"
|
||||||
BeltManager="*res://scripts/autoloads/belt_manager.gd"
|
BeltManager="*res://scripts/autoloads/belt_manager.gd"
|
||||||
|
Ticker="*res://scripts/autoloads/ticker.gd"
|
||||||
|
|
||||||
[display]
|
[display]
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,12 @@
|
||||||
resource_local_to_scene = true
|
resource_local_to_scene = true
|
||||||
script = ExtResource("3_ruvuk")
|
script = ExtResource("3_ruvuk")
|
||||||
capacity = 4
|
capacity = 4
|
||||||
progress_speed = 10.0
|
|
||||||
pop_treshold = 0.95
|
pop_treshold = 0.95
|
||||||
internal_array = Array[ExtResource("2_54w8r")]([Object(RefCounted,"script":ExtResource("2_54w8r"),"held_item":null,"amount":0,"filter":null)
|
internal_array = Array[ExtResource("2_54w8r")]([Object(RefCounted,"script":ExtResource("2_54w8r"),"held_item":null,"amount":0,"filter":null,"updated":null)
|
||||||
, Object(RefCounted,"script":ExtResource("2_54w8r"),"held_item":null,"amount":0,"filter":null)
|
, Object(RefCounted,"script":ExtResource("2_54w8r"),"held_item":null,"amount":0,"filter":null,"updated":null)
|
||||||
, Object(RefCounted,"script":ExtResource("2_54w8r"),"held_item":null,"amount":0,"filter":null)
|
, Object(RefCounted,"script":ExtResource("2_54w8r"),"held_item":null,"amount":0,"filter":null,"updated":null)
|
||||||
, Object(RefCounted,"script":ExtResource("2_54w8r"),"held_item":null,"amount":0,"filter":null)
|
, Object(RefCounted,"script":ExtResource("2_54w8r"),"held_item":null,"amount":0,"filter":null,"updated":null)
|
||||||
])
|
])
|
||||||
progress_array = Array[float]([0.0, 0.0, 0.0, 0.0])
|
|
||||||
metadata/_custom_type_script = "uid://v0hkuo3gda1b"
|
metadata/_custom_type_script = "uid://v0hkuo3gda1b"
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id="AtlasTexture_54w8r"]
|
[sub_resource type="AtlasTexture" id="AtlasTexture_54w8r"]
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
resource_local_to_scene = true
|
resource_local_to_scene = true
|
||||||
script = ExtResource("3_2ulpw")
|
script = ExtResource("3_2ulpw")
|
||||||
capacity = 1
|
capacity = 1
|
||||||
|
upper_array = Array[ExtResource("2_lbove")]([Object(RefCounted,"script":ExtResource("2_lbove"),"held_item":null,"amount":0,"filter":null)
|
||||||
|
])
|
||||||
|
down_array = Array[ExtResource("2_lbove")]([Object(RefCounted,"script":ExtResource("2_lbove"),"held_item":null,"amount":0,"filter":null)
|
||||||
|
])
|
||||||
metadata/_custom_type_script = "uid://dlt3mbu6hk572"
|
metadata/_custom_type_script = "uid://dlt3mbu6hk572"
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id="AtlasTexture_0dkfh"]
|
[sub_resource type="AtlasTexture" id="AtlasTexture_0dkfh"]
|
||||||
|
|
|
@ -5,4 +5,6 @@ extends Node
|
||||||
var sync_time : float
|
var sync_time : float
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
sync_time = fposmod(sync_time+delta,1)
|
sync_time += delta
|
||||||
|
if sync_time > 1:
|
||||||
|
sync_time -= 1
|
||||||
|
|
16
scripts/autoloads/ticker.gd
Normal file
16
scripts/autoloads/ticker.gd
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
extends Timer
|
||||||
|
|
||||||
|
const tics_per_second :float = 32.0
|
||||||
|
signal tick(current_tick: int)
|
||||||
|
|
||||||
|
var current_tick: int
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
start(1.0/tics_per_second)
|
||||||
|
timeout.connect(make_tick)
|
||||||
|
|
||||||
|
func make_tick():
|
||||||
|
current_tick += 1
|
||||||
|
if current_tick >= tics_per_second:
|
||||||
|
current_tick -= int(tics_per_second)
|
||||||
|
tick.emit(current_tick)
|
1
scripts/autoloads/ticker.gd.uid
Normal file
1
scripts/autoloads/ticker.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://cl3bqbr52mwfl
|
|
@ -14,12 +14,10 @@ class_name BeltInventory
|
||||||
get:
|
get:
|
||||||
return capacity
|
return capacity
|
||||||
|
|
||||||
@export var progress_speed : float = 1.0
|
|
||||||
@export var pop_treshold : float = 0.99
|
@export var pop_treshold : float = 0.99
|
||||||
|
|
||||||
## :3
|
## :3
|
||||||
@export_storage var internal_array : Array[InventorySlot] = []
|
@export_storage var internal_array : Array[InventorySlot] = []
|
||||||
@export_storage var progress_array : Array[float] = []
|
|
||||||
|
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
super()
|
super()
|
||||||
|
@ -27,7 +25,6 @@ func _init() -> void:
|
||||||
|
|
||||||
func deferred_init():
|
func deferred_init():
|
||||||
internal_array.resize(capacity)
|
internal_array.resize(capacity)
|
||||||
progress_array.resize(capacity)
|
|
||||||
for i in range(capacity):
|
for i in range(capacity):
|
||||||
internal_array[i] = InventorySlot.new()
|
internal_array[i] = InventorySlot.new()
|
||||||
|
|
||||||
|
@ -74,30 +71,21 @@ func can_add(_item : Item = null, context : InventoryContext = null) -> bool:
|
||||||
func pop() -> Stack:
|
func pop() -> Stack:
|
||||||
if internal_array[capacity-1].amount == 0:
|
if internal_array[capacity-1].amount == 0:
|
||||||
return null
|
return null
|
||||||
if progress_array[capacity-1] < pop_treshold:
|
|
||||||
return null
|
|
||||||
stack_taken.emit(internal_array[capacity-1], capacity-1)
|
stack_taken.emit(internal_array[capacity-1], capacity-1)
|
||||||
return internal_array[capacity-1].extract()
|
return internal_array[capacity-1].extract()
|
||||||
|
|
||||||
func peek() -> Stack:
|
func peek() -> Stack:
|
||||||
if internal_array[capacity-1].amount == 0:
|
if internal_array[capacity-1].amount == 0:
|
||||||
return null
|
return null
|
||||||
if progress_array[capacity-1] < pop_treshold:
|
|
||||||
return null
|
|
||||||
return internal_array[capacity-1].as_stack()
|
return internal_array[capacity-1].as_stack()
|
||||||
|
|
||||||
func advance(delta : float) -> void:
|
func advance() -> void:
|
||||||
var progress_flag : bool = false
|
for i in range(capacity-1,0,-1):
|
||||||
for i in range(capacity):
|
if internal_array[i].amount == 0 and internal_array[0].updated == false:
|
||||||
if internal_array[i].amount == 0:
|
var extracted_stack = internal_array[i-1].extract()
|
||||||
continue
|
internal_array[i].merge_stack(extracted_stack)
|
||||||
if progress_array[i] >= 1:
|
if extracted_stack != null and extracted_stack.is_valid():
|
||||||
progress_flag = true
|
internal_array[i-1].merge_stack(extracted_stack)
|
||||||
continue
|
|
||||||
progress_array[i] += delta * progress_speed
|
|
||||||
|
|
||||||
if progress_flag:
|
|
||||||
sort()
|
|
||||||
|
|
||||||
## Tries to take certain item from inventory. Returns null if no item found
|
## Tries to take certain item from inventory. Returns null if no item found
|
||||||
func take(item: Item,amount: int) -> Stack:
|
func take(item: Item,amount: int) -> Stack:
|
||||||
|
@ -108,12 +96,6 @@ func take(item: Item,amount: int) -> Stack:
|
||||||
stack_taken.emit(extracted,found)
|
stack_taken.emit(extracted,found)
|
||||||
return extracted
|
return extracted
|
||||||
|
|
||||||
func sort() -> void:
|
func refresh() -> void:
|
||||||
for i in range(capacity-1,0,-1):
|
for i in range(capacity):
|
||||||
if progress_array[i-1] >= 1.0 and internal_array[i].amount == 0:
|
internal_array[i].updated = false
|
||||||
progress_array[i-1] = 0
|
|
||||||
progress_array[i] = 0
|
|
||||||
var extracted_stack = internal_array[i-1].extract()
|
|
||||||
internal_array[i].merge_stack(extracted_stack)
|
|
||||||
if extracted_stack.is_valid():
|
|
||||||
internal_array[i-1].merge_stack(extracted_stack)
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ func _init() -> void:
|
||||||
resize.call_deferred()
|
resize.call_deferred()
|
||||||
|
|
||||||
func add(stack : Stack, _context: InventoryContext = null) -> Stack:
|
func add(stack : Stack, _context: InventoryContext = null) -> Stack:
|
||||||
|
if stack == null:
|
||||||
|
return stack
|
||||||
for i in range(input_capacity):
|
for i in range(input_capacity):
|
||||||
if input_array[i].can_be_merged(stack.held_item):
|
if input_array[i].can_be_merged(stack.held_item):
|
||||||
stack_added.emit(stack,i)
|
stack_added.emit(stack,i)
|
||||||
|
@ -69,3 +71,7 @@ func find(item : Item) -> int:
|
||||||
if output_slot.held_item == item:
|
if output_slot.held_item == item:
|
||||||
return 0
|
return 0
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
func refresh() -> void:
|
||||||
|
for i in range(input_capacity):
|
||||||
|
input_array[i].updated = false
|
||||||
|
|
|
@ -33,6 +33,9 @@ func take(item : Item,amount: int) -> Stack
|
||||||
@abstract
|
@abstract
|
||||||
func find(item : Item) -> int
|
func find(item : Item) -> int
|
||||||
|
|
||||||
|
@abstract
|
||||||
|
func refresh() -> void
|
||||||
|
|
||||||
## Does inventory have certain item?
|
## Does inventory have certain item?
|
||||||
func has(item : Item) -> bool:
|
func has(item : Item) -> bool:
|
||||||
return find(item) != -1
|
return find(item) != -1
|
||||||
|
|
|
@ -30,12 +30,15 @@ class_name InventorySlot
|
||||||
return amount
|
return amount
|
||||||
|
|
||||||
@export_storage var filter : Comparable
|
@export_storage var filter : Comparable
|
||||||
|
@export_storage var updated : bool
|
||||||
|
|
||||||
## Get some amount of items from slot. Returns null if slot is empty
|
## Get some amount of items from slot. Returns null if slot is empty
|
||||||
func extract_stack(extract_amount: int) -> Stack:
|
func extract_stack(extract_amount: int) -> Stack:
|
||||||
if amount == 0:
|
if amount == 0 or updated:
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
updated = true
|
||||||
|
|
||||||
var return_stack : Stack
|
var return_stack : Stack
|
||||||
if extract_amount > amount:
|
if extract_amount > amount:
|
||||||
return_stack = Stack.new(held_item,amount)
|
return_stack = Stack.new(held_item,amount)
|
||||||
|
@ -48,9 +51,11 @@ func extract_stack(extract_amount: int) -> Stack:
|
||||||
|
|
||||||
## Extract entire stack from slot. Returns null if slot is empty
|
## Extract entire stack from slot. Returns null if slot is empty
|
||||||
func extract() -> Stack:
|
func extract() -> Stack:
|
||||||
if amount == 0:
|
if amount == 0 or updated:
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
updated = true
|
||||||
|
|
||||||
var return_stack : Stack = Stack.new(held_item,amount)
|
var return_stack : Stack = Stack.new(held_item,amount)
|
||||||
self.amount = 0
|
self.amount = 0
|
||||||
|
|
||||||
|
@ -65,6 +70,8 @@ func as_stack() -> Stack:
|
||||||
return return_stack
|
return return_stack
|
||||||
|
|
||||||
func can_be_merged(item : Item) -> bool:
|
func can_be_merged(item : Item) -> bool:
|
||||||
|
if updated:
|
||||||
|
return false
|
||||||
if filter != null:
|
if filter != null:
|
||||||
return filter.is_equal(item)
|
return filter.is_equal(item)
|
||||||
if amount <= 0:
|
if amount <= 0:
|
||||||
|
@ -73,12 +80,14 @@ func can_be_merged(item : Item) -> bool:
|
||||||
|
|
||||||
## Add provided stack's amount to this amount. Returns null if stack is empty or items arent matching
|
## Add provided stack's amount to this amount. Returns null if stack is empty or items arent matching
|
||||||
func merge_stack(stack: Stack) -> bool:
|
func merge_stack(stack: Stack) -> bool:
|
||||||
if stack == null or stack.is_valid() == false:
|
if stack == null or stack.is_valid() == false or updated:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if filter and filter.is_equal(stack.held_item) == false:
|
if filter and filter.is_equal(stack.held_item) == false:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
updated = true
|
||||||
|
|
||||||
if held_item == null:
|
if held_item == null:
|
||||||
held_item = stack.held_item
|
held_item = stack.held_item
|
||||||
amount = stack.amount
|
amount = stack.amount
|
||||||
|
|
|
@ -29,7 +29,7 @@ func deferred_init():
|
||||||
down_array[i] = InventorySlot.new()
|
down_array[i] = InventorySlot.new()
|
||||||
|
|
||||||
func add(stack : Stack, context: InventoryContext = null) -> Stack:
|
func add(stack : Stack, context: InventoryContext = null) -> Stack:
|
||||||
if context == null:
|
if context == null or stack == null:
|
||||||
return stack
|
return stack
|
||||||
if context.position == context.target.global_position:
|
if context.position == context.target.global_position:
|
||||||
return add_to_array(stack,upper_array,context)
|
return add_to_array(stack,upper_array,context)
|
||||||
|
@ -72,3 +72,8 @@ func take(item : Item,amount: int) -> Stack:
|
||||||
## Finds first entry of item. Returns -1 if no item found
|
## Finds first entry of item. Returns -1 if no item found
|
||||||
func find(item : Item) -> int:
|
func find(item : Item) -> int:
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
func refresh() -> void:
|
||||||
|
for i in range(capacity):
|
||||||
|
upper_array[i].updated = false
|
||||||
|
down_array[i].updated = false
|
||||||
|
|
|
@ -87,3 +87,7 @@ func take(item: Item,amount: int) -> Stack:
|
||||||
var extracted = internal_array[found].extract_stack(amount)
|
var extracted = internal_array[found].extract_stack(amount)
|
||||||
stack_taken.emit(extracted,found)
|
stack_taken.emit(extracted,found)
|
||||||
return extracted
|
return extracted
|
||||||
|
|
||||||
|
func refresh() -> void:
|
||||||
|
for i in range(capacity):
|
||||||
|
internal_array[i].updated = false
|
||||||
|
|
|
@ -27,3 +27,6 @@ func take(item : Item,amount: int) -> Stack:
|
||||||
## Finds first entry of item. Returns -1 if no item found
|
## Finds first entry of item. Returns -1 if no item found
|
||||||
func find(item : Item) -> int:
|
func find(item : Item) -> int:
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
func refresh():
|
||||||
|
pass
|
||||||
|
|
|
@ -4,3 +4,9 @@ extends Node2D
|
||||||
class_name StructureBehaviour
|
class_name StructureBehaviour
|
||||||
|
|
||||||
@onready var structure_parent : Structure = get_parent()
|
@onready var structure_parent : Structure = get_parent()
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
Ticker.tick.connect(_tick)
|
||||||
|
|
||||||
|
@abstract
|
||||||
|
func _tick(current_tick : int) -> void
|
||||||
|
|
|
@ -15,9 +15,10 @@ func switch_recipe(recipe: Recipe) -> void:
|
||||||
for i in range(len(selected_recipe.ingridients)):
|
for i in range(len(selected_recipe.ingridients)):
|
||||||
inventory.input_array[i].filter = selected_recipe.ingridients[i].item
|
inventory.input_array[i].filter = selected_recipe.ingridients[i].item
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _tick(_current_tick : int) -> void:
|
||||||
if inventory.output_slot.amount <= 0:
|
if inventory.output_slot.amount <= 0:
|
||||||
return
|
return
|
||||||
|
inventory.refresh()
|
||||||
var output : Structure = get_output_structure()
|
var output : Structure = get_output_structure()
|
||||||
if output == null:
|
if output == null:
|
||||||
return
|
return
|
||||||
|
|
|
@ -8,28 +8,28 @@ func _draw() -> void:
|
||||||
var calculated_position = calculate_position(i) - inventory.internal_array[i].held_item.preview.get_size()/2.0
|
var calculated_position = calculate_position(i) - inventory.internal_array[i].held_item.preview.get_size()/2.0
|
||||||
draw_texture(inventory.internal_array[i].held_item.preview,calculated_position)
|
draw_texture(inventory.internal_array[i].held_item.preview,calculated_position)
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _tick(current_tick: int) -> void:
|
||||||
inventory.advance(delta)
|
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
if current_tick % 8 != 0:
|
||||||
|
return
|
||||||
|
inventory.refresh()
|
||||||
|
inventory.advance()
|
||||||
var next : Structure = get_next()
|
var next : Structure = get_next()
|
||||||
try_transfer(next)
|
try_transfer(next)
|
||||||
|
|
||||||
func calculate_position(index: int) -> Vector2:
|
func calculate_position(index: int) -> Vector2:
|
||||||
var indexed_part = 16.0 / inventory.capacity
|
var indexed_part = 16.0 / inventory.capacity
|
||||||
|
|
||||||
return -structure_parent.direction_vector()*8 + structure_parent.direction_vector() * indexed_part * (index + inventory.progress_array[index])
|
return -structure_parent.direction_vector()*8 + structure_parent.direction_vector() * indexed_part * index
|
||||||
|
|
||||||
func get_next() -> Structure:
|
func get_next() -> Structure:
|
||||||
return structure_parent.get_relative(structure_parent.direction_vector())
|
return structure_parent.get_relative(structure_parent.direction_vector())
|
||||||
|
|
||||||
func try_transfer(structure : Structure) -> void:
|
func try_transfer(structure : Structure) -> void:
|
||||||
if structure == null or inventory.internal_array[inventory.capacity-1].amount == 0 or inventory.progress_array[inventory.capacity-1] < inventory.pop_treshold:
|
if structure == null or inventory.internal_array[inventory.capacity-1].amount == 0:
|
||||||
return
|
return
|
||||||
var last_slot = inventory.internal_array[inventory.capacity-1]
|
var last_slot = inventory.internal_array[inventory.capacity-1]
|
||||||
var transfer_context : InventoryContext = InventoryContext.new(structure_parent,structure,to_global(structure_parent.direction_vector()))
|
var transfer_context : InventoryContext = InventoryContext.new(structure_parent,structure,to_global(structure_parent.direction_vector()))
|
||||||
if structure.inventory.can_add(last_slot.held_item,transfer_context) == false:
|
if structure.inventory.can_add(last_slot.held_item,transfer_context) == false:
|
||||||
return
|
return
|
||||||
last_slot.merge_stack(structure.inventory.add(last_slot.extract(),transfer_context))
|
last_slot.merge_stack(structure.inventory.add(last_slot.extract(),transfer_context))
|
||||||
|
|
||||||
if last_slot.amount == 0:
|
|
||||||
inventory.sort()
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ const inp1 := preload("res://generic/items/dbg_input1.tres")
|
||||||
const inp2 := preload("res://generic/items/dbg_input2.tres")
|
const inp2 := preload("res://generic/items/dbg_input2.tres")
|
||||||
const out := preload("res://generic/items/dbg_output.tres")
|
const out := preload("res://generic/items/dbg_output.tres")
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _tick(_current_tick: int) -> void:
|
||||||
try_add(Vector2.UP,Stack.new(inp1,1))
|
try_add(Vector2.UP,Stack.new(inp1,1))
|
||||||
try_add(Vector2.DOWN,Stack.new(inp2,1))
|
try_add(Vector2.DOWN,Stack.new(inp2,1))
|
||||||
try_add(Vector2.RIGHT,Stack.new(out,1))
|
try_add(Vector2.RIGHT,Stack.new(out,1))
|
||||||
|
|
|
@ -10,16 +10,20 @@ func get_down_next() -> Vector2:
|
||||||
var base = get_upper_next()
|
var base = get_upper_next()
|
||||||
return base + base.rotated(PI/2).abs()
|
return base + base.rotated(PI/2).abs()
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _tick(current_tick: int) -> void:
|
||||||
|
if current_tick % 8 != 0:
|
||||||
|
return
|
||||||
|
inventory.refresh()
|
||||||
if has_non_empty(inventory.upper_array) == false and has_non_empty(inventory.down_array) == false:
|
if has_non_empty(inventory.upper_array) == false and has_non_empty(inventory.down_array) == false:
|
||||||
return
|
return
|
||||||
var transfer_upper = try_transfer(get_upper_next(),false)
|
var transfer_upper = try_transfer(get_upper_next(),false)
|
||||||
var transfer_down = try_transfer(get_down_next(),true)
|
var transfer_down = try_transfer(get_down_next(),true)
|
||||||
if transfer_upper or transfer_down or (not transfer_down and not transfer_upper):
|
if transfer_upper or transfer_down or not (transfer_down or transfer_upper):
|
||||||
split = not split
|
inverse_split()
|
||||||
|
|
||||||
func try_transfer(point:Vector2,down:bool) -> bool:
|
func try_transfer(point:Vector2,down:bool) -> bool:
|
||||||
var next :Structure = structure_parent.get_relative(point)
|
var next :Structure = structure_parent.get_relative(point)
|
||||||
|
|
||||||
var array : Array[InventorySlot] = inventory.down_array if split != down else inventory.upper_array
|
var array : Array[InventorySlot] = inventory.down_array if split != down else inventory.upper_array
|
||||||
if next == null:
|
if next == null:
|
||||||
return false
|
return false
|
||||||
|
@ -33,6 +37,9 @@ func try_transfer(point:Vector2,down:bool) -> bool:
|
||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
func inverse_split() -> void:
|
||||||
|
split = not split
|
||||||
|
|
||||||
func has_non_empty(array:Array[InventorySlot]) -> bool:
|
func has_non_empty(array:Array[InventorySlot]) -> bool:
|
||||||
for i in range(inventory.capacity):
|
for i in range(inventory.capacity):
|
||||||
if array[i].amount > 0:
|
if array[i].amount > 0:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue