extends Control const OWNIVERSE_ITEM = preload("uid://d3xjfxwe3w16v") const SPEED_TEXTURE_NAME_TEMPLATE = "res://ui/topmenu/time&speed/time%s.png" const ItemDescription = preload("res://world/owniverse/item_description.gd") var description_targets: Dictionary = {} var description_samples: Dictionary = {} var description_rates: Dictionary = {} var description_elapsed: float = 0.0 const UI_UPDATE_INTERVAL: float = 0.1 var ui_elapsed: float = 0.0 var ui_pages: Vector2i = Vector2i(-1, -1) var closing: bool = false const PAGE_COUNT = 5 const RESOURCE_PAGE = 1 const PAGE_WIDTH = 1080 const DRAG_TOLERANCE = 360 const SWIPE_DURATION: float = 0.4 @onready var stage_page_scroller: ScrollContainer = $StageRows/PageScroller @onready var item_pages: Array[Control] = [ $StageRows/PageScroller/HBoxContainer/ForcePage/Forces, $StageRows/PageScroller/HBoxContainer/ResourcePage/HBoxContainer/Resources, $StageRows/PageScroller/HBoxContainer/StarPage/Stars, $StageRows/PageScroller/HBoxContainer/StructurePage/Structures, $StageRows/PageScroller/HBoxContainer/LifePage/Planets/Life, $StageRows/PageScroller/HBoxContainer/LifePage/Planets/LivingPlanet, $StageRows/PageScroller/HBoxContainer/LifePage/Planets/Civ0, $StageRows/PageScroller/HBoxContainer/LifePage/Planets/Civ1, $StageRows/PageScroller/HBoxContainer/LifePage/Planets/Civ2, $StageRows/PageScroller/HBoxContainer/LifePage/Planets/Civ3, $StageRows/PageScroller/HBoxContainer/LifePage/Planets/Civ4 ] @onready var page_actions: Dictionary[int, OwniverseAction] = { 2: $StageRows/PageScroller/HBoxContainer/StarPage/Actions, 3: $StageRows/PageScroller/HBoxContainer/StructurePage/Actions, } @onready var g_item_descriptions: Array = [ $StageRows/PageScroller/HBoxContainer/ForcePage/Description, $StageRows/PageScroller/HBoxContainer/ResourcePage/Description, $StageRows/PageScroller/HBoxContainer/StarPage/Description, $StageRows/PageScroller/HBoxContainer/StructurePage/Description, $StageRows/PageScroller/HBoxContainer/LifePage/Description ] @onready var date_time: Label = $StageRows/TopMenu/InfoPanel/DateTime @onready var g_speed_icons: Array = [ $StageRows/TopMenu/InfoPanel/Speed/Speed1, $StageRows/TopMenu/InfoPanel/Speed/Speed2, $StageRows/TopMenu/InfoPanel/Speed/Speed3, $StageRows/TopMenu/InfoPanel/Speed/Speed4, $StageRows/TopMenu/InfoPanel/Speed/Speed5, $StageRows/TopMenu/InfoPanel/Speed/Speed6, $StageRows/TopMenu/InfoPanel/Speed/Speed7, $StageRows/TopMenu/InfoPanel/Speed/Speed8 ] @onready var g_time_minus: TextureButton = $StageRows/TopMenu/TimeMinus @onready var g_time_plus: TextureButton = $StageRows/TopMenu/TimePlus var g_tween: Tween = null var g_dragged_start: Vector2 = Vector2.ZERO var g_scroll_start: int = 0 #var g_current_page: int = 0 var g_last_scroll_updated: int = 0 var f_check_swipe: bool = false var g_swipe_time_delta: float = 0 var g_counters: Dictionary[String, Counter] = { "swipe": Counter.new(), } var g_active_items: Dictionary = {} var pending_page: int = -1 var owniverse:Owniverse = Owniverse.new() var owniverse_items: Dictionary[String, OwniverseItem] = {} var locked_items: Array = [] func set_datetime(value: float): # Simulation time advances in tenths of a year; displaying five decimal # places only adds zeroes and suggests precision the simulation does not # have. var text: String = Global.format_number(value, 1) if value != 0.0 else "0.0" if date_time.text != text: date_time.text = text #region Item manipulations: update and visibility func set_owniverse_item_value() -> void: for item_id in owniverse.items_to_update: if owniverse_items.has(item_id): var item: OwniverseItem = owniverse_items[item_id] if item.is_visible_in_tree() and item.get_global_rect().intersects(stage_page_scroller.get_global_rect()): item.set_label(owniverse.items_to_update[item_id]) func _refresh_ui(force: bool = false) -> void: if force or owniverse.item_amounts_dirty: owniverse.refresh_item_amounts(true) set_owniverse_item_value() set_datetime(owniverse.state.time.year) _set_active_page_actions() ui_elapsed = 0.0 func _update_ui(delta: float) -> void: ui_elapsed += delta var position_in_pages: float = float(stage_page_scroller.scroll_horizontal) / PAGE_WIDTH var pages := Vector2i(floori(position_in_pages), ceili(position_in_pages)) if pages != ui_pages or ui_elapsed >= UI_UPDATE_INTERVAL: ui_pages = pages _refresh_ui() #endregion func _unlock_items(): if locked_items.size() == 0: return var _new_locked_items = [] var display_totals: Dictionary = owniverse.get_item_amounts(true) for _item_id in locked_items: #print(_item_id ' | ', _entity.unlock) var _flag_to_unlock = true for _elem in owniverse.entities[_item_id].unlock: if maxf(owniverse.state.stats.total_amount.get(_elem, 0.0), display_totals.get(_elem, 0.0)) < owniverse.entities[_item_id].unlock[_elem]: _flag_to_unlock = false break if _flag_to_unlock: var item_id: String = owniverse.entities[_item_id].count_as if item_id == "": item_id = _item_id if owniverse_items.has(item_id): owniverse_items[item_id].set_visible(true) else: _new_locked_items.append(_item_id) locked_items = _new_locked_items func _on_indicator_pressed(indicator_name: String, page: int = 0): _set_description(indicator_name, page) func _set_item_description(item: OwniverseItem) -> void: var category: String = "Entity" if owniverse.entities.has(item.item_id) else "Force" var id: String = item.item_id if category == "Entity" else item.item_name.capitalize() var page: int = clampi(item.item_page, 0, 4) if description_targets.get(page, {}).get("id", "") != id: description_samples.erase(id) description_rates.erase(id) description_targets[page] = {"id": id, "category": category} _refresh_descriptions() func _set_description(item_name: String, page: int) -> void: description_targets[page] = {"id": item_name, "category": "Indicator"} _refresh_descriptions() func _refresh_descriptions() -> void: for page in description_targets: var target: Dictionary = description_targets[page] var label: RichTextLabel = g_item_descriptions[page] label.bbcode_enabled = true label.text = ItemDescription.render(owniverse, Global, target.id, target.category, description_rates.get(target.id, 0.0)) func _update_descriptions(delta: float) -> void: description_elapsed += delta if description_elapsed < 0.25: return for page in description_targets: var id: String = description_targets[page].id var total: float = ItemDescription.quantities(owniverse, id).total description_rates[id] = (total - description_samples[id]) / description_elapsed if description_samples.has(id) else 0.0 description_samples[id] = total description_elapsed = 0.0 _refresh_descriptions() func _control_page(item: OwniverseItem) -> void: # TODO переписать это ↓ if !g_active_items.has(item.item_page): g_active_items[item.item_page] = null var current_item: OwniverseItem = g_active_items[item.item_page] if current_item == item: return g_active_items[item.item_page] = item if current_item != null and current_item.item_page == item.item_page: current_item.set_selection(false) item.set_selection(true) # subpages if item.item_sub_page != -1: item_pages[item.item_sub_page].visible = true if current_item == null \ or current_item.item_sub_page == -1 \ or current_item.item_sub_page == item.item_sub_page: return item_pages[current_item.item_sub_page].visible = false func _init_owniverse_items() -> bool: owniverse_items = {} for item_id in Global.OwniverseItems: var item_props: Dictionary = Global.OwniverseItems[item_id] var item: OwniverseItem = OWNIVERSE_ITEM.instantiate() if !item_props.has("page"): printerr("Item `", item_id, "` has no `page` key.") return false var page: int = int(item_props.page) item_pages[page].add_child(item) item.init_button(item_props) item.item_activated.connect(item_activated) owniverse_items[item_id] = item return false func item_activated(item: OwniverseItem) -> void: _control_page(item) _set_item_description(item) if item.item_id == "S" or item.item_id == "H": var _value = owniverse.produce_hs(item.item_id, 100_000) _unlock_items() _refresh_ui(true) _refresh_descriptions() func _check_counters(delta: float) -> Array: var actions: Array = [] for k in g_counters: var cnt: Counter = g_counters[k] if not cnt.is_active: continue cnt.value += delta if cnt.value >= cnt.sec: actions.append(k) cnt.value = 0 cnt.is_active = false return actions #region Swipe and page func _do_actions(actions: Array) -> void: for action: String in actions: match action: "swipe": _swipe_to_nearest_page() func _swipe_to_nearest_page() -> void: if stage_page_scroller.scroll_horizontal % PAGE_WIDTH == 0: return var new_page: int = round(1.0 * stage_page_scroller.scroll_horizontal / PAGE_WIDTH) if g_tween != null: if g_tween.is_running(): return g_tween = stage_page_scroller.get_tree().create_tween() g_tween.tween_property(stage_page_scroller, "scroll_horizontal", new_page * PAGE_WIDTH, SWIPE_DURATION).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT) func _input(event: InputEvent) -> void: if event is InputEventScreenDrag: stage_page_scroller.scroll_horizontal = clamp( stage_page_scroller.scroll_horizontal - PAGE_COUNT * event.screen_relative.x, 0, PAGE_WIDTH * (PAGE_COUNT - 1)) g_counters.swipe.is_active = true g_counters.swipe.value = 0 elif event is InputEventPanGesture: g_counters.swipe.is_active = true g_counters.swipe.value = 0 elif event is InputEventScreenTouch: pass func _set_active_page_actions() -> void: for pa in page_actions: if !g_active_items.has(pa): continue if not page_actions[pa].is_visible_in_tree() or not page_actions[pa].get_global_rect().intersects(stage_page_scroller.get_global_rect()): continue var item_id: String = g_active_items[pa].item_id var entity: OwniverseEntity = owniverse.entities[item_id] #var amount = owniverse.get_available_to_produce(item_id) #var amount = owniverse.get_available_to_produce(entity) var amount = owniverse.calculate_max_producible(entity) #print(item_id, " : ", amount) if item_id == "SMBH": var preview: Callable = func(requested: float) -> float: return owniverse.calculate_max_producible(entity, requested) page_actions[pa].set_actions(item_id, amount, preview) else: page_actions[pa].set_actions(item_id, amount) func _page_action_pressed(page_id: int, action_amount: float) -> void: if !g_active_items.has(page_id): return var item_id: String = g_active_items[page_id].item_id if action_amount > 0: owniverse.produce(item_id, action_amount) _unlock_items() _refresh_ui(true) _refresh_descriptions() #endregion func _ready() -> void: get_tree().auto_accept_quit = false owniverse.defer_item_updates = true var inited_result = init_enitities() locked_items = inited_result.locked_items load_data() _init_owniverse_items() owniverse.refresh_item_amounts() _change_speed(0) set_datetime(owniverse.state.time.year) set_owniverse_item_value() _unlock_items() _refresh_ui(true) g_counters.swipe.sec = 0.2 for pa in page_actions: page_actions[pa].action_pressed.connect(_page_action_pressed) page_actions[pa].page_id = pa func _process(delta: float) -> void: owniverse.evolute(delta) _update_descriptions(delta) _update_ui(delta) #owniverse.cycle(delta) #owniverse.cycle_legacy(year_delta) var actions = _check_counters(delta) _do_actions(actions) func _change_speed(delta: int) -> void: owniverse.speed = owniverse.speed + delta var speed: int = owniverse.speed var ind: int = 1 for icon in g_speed_icons: if ind <= speed: icon.visible = true else: icon.visible = false ind += 1 if speed == 1: g_time_minus.texture_normal = load(SPEED_TEXTURE_NAME_TEMPLATE % "x") g_time_plus.texture_normal = load(SPEED_TEXTURE_NAME_TEMPLATE % "2") elif speed == Global.SPEED_LIMIT: g_time_minus.texture_normal = load(SPEED_TEXTURE_NAME_TEMPLATE % str(Global.SPEED_LIMIT - 1)) g_time_plus.texture_normal = load(SPEED_TEXTURE_NAME_TEMPLATE % "x") else: g_time_minus.texture_normal = load(SPEED_TEXTURE_NAME_TEMPLATE % str(speed - 1)) g_time_plus.texture_normal = load(SPEED_TEXTURE_NAME_TEMPLATE % str(speed + 1)) func _on_topmenu_button_pressed(_ButtonName) -> void: print("Debug → ") #, owniverse.state) for id in owniverse.state.batches: var s = owniverse.state.batches[id].size() if s != 0: print(id, " → ", s) #, owniverse.state.batches[id]) if id == 'HG': print(owniverse.state.batches[id]) #var s: float = 1E15 #var z = 1.0 #for i in range(30): #print(i, " | ", z, " | ", " | ", s - z) #z *= 10 print("-------") func _on_unlock_and_save_timeout() -> void: save_data() _unlock_items() #region Init save load routines func load_data() -> bool: var game_data: Dictionary = SaveManager.load_data("owniverse", "qwerty") if game_data.is_empty(): if SaveManager.last_error == "": pending_page = RESOURCE_PAGE _restore_page.call_deferred() return false var restored: Dictionary = game_data.state # Current definitions are initialized first; reject incompatible saves before publishing. for id in owniverse.state.amount: if not restored.amount.has(id) or not restored.stats.total_amount.has(id): SaveManager.blocked_saves["owniverse"] = true push_error("Save is missing the current entity: " + id) return false owniverse.state = restored owniverse.new_state = restored.duplicate(true) owniverse.initialize_life_batches() owniverse.refresh_item_amounts() pending_page = clampi(int(game_data.get("ui", {}).get("current_page", 0)), 0, PAGE_COUNT - 1) _restore_page.call_deferred() return true func _restore_page() -> void: # Wait until containers have calculated the scrollable width. await get_tree().process_frame if pending_page >= 0: stage_page_scroller.scroll_horizontal = pending_page * PAGE_WIDTH pending_page = -1 _refresh_ui(true) func save_data() -> void: var page: int = pending_page if page < 0: page = clampi(roundi(float(stage_page_scroller.scroll_horizontal) / PAGE_WIDTH), 0, PAGE_COUNT - 1) SaveManager.request_save("owniverse", "qwerty", { "state": owniverse.state, "ui": {"current_page": page}, }) func _notification(what: int) -> void: if not is_node_ready() or closing: return if what == NOTIFICATION_WM_CLOSE_REQUEST: _close_after_save() elif what == NOTIFICATION_APPLICATION_PAUSED: save_data() # Mobile platforms can suspend all processing immediately after this event. SaveManager.flush_saves() func _close_after_save() -> void: closing = true set_process(false) set_process_input(false) mouse_filter = Control.MOUSE_FILTER_IGNORE # Stop GUI actions too while the final snapshot is being written. process_mode = Node.PROCESS_MODE_DISABLED save_data() if SaveManager.is_saving(): await SaveManager.saves_drained get_tree().quit() func init_enitities() -> Dictionary: var _result := { 'locked_items': [], } for entity_id in Global.OwniverseEntities: var entity = OwniverseEntity.new(Global.OwniverseEntities[entity_id]) owniverse.entities[entity_id] = entity owniverse.state.stats.total_amount[entity_id] = 0 owniverse.state.amount[entity_id] = 0 if entity.lifespan > 0: owniverse.state.batches[entity.id] = [] owniverse.add_entity_to_batch(entity_id, 0, 0) if entity.group_as != "": owniverse.state.amount[entity.group_as] = 0 owniverse.state.stats.total_amount[entity.group_as] = 0 if entity.unlock != {}: owniverse.locked_entities.append(entity) _result.locked_items.append(entity.id) if entity.product_per_year_type != "": owniverse.auto_production.append(entity) # groups if entity.group_as != "": if !owniverse.entity_groups.has(entity.group_as): owniverse.entity_groups[entity.group_as] = [] owniverse.entity_groups[entity.group_as].append(entity_id) if entity.event_group != "": if !owniverse.entity_event_groups.has(entity.event_group): owniverse.entity_event_groups[entity.event_group] = [] owniverse.entity_event_groups[entity.event_group].append(entity_id) return _result #endregion