Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions app_entry.gd
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ func _on_save_slot_selected(slot_index: int) -> void:
_on_normal_requested()
return
if character_select != null:
_configure_character_select_for_slot(slot)
character_select.visible = true

func _on_new_slot_requested(slot_index: int) -> void:
Expand All @@ -118,12 +119,24 @@ func _on_new_slot_requested(slot_index: int) -> void:
opening_prologue.open()
return
if character_select != null:
_configure_character_select_for_slot(SaveManager.get_active_slot() if SaveManager != null else {})
character_select.visible = true

func _on_opening_prologue_finished() -> void:
if character_select != null:
_configure_character_select_for_slot(SaveManager.get_active_slot() if SaveManager != null else {})
character_select.visible = true

func _configure_character_select_for_slot(slot: Dictionary) -> void:
if character_select == null or not character_select.has_method("set_disabled_family_ids"):
return
var disabled: Array[String] = []
for raw_id in String(slot.get("crowned_families", "")).split(",", false):
var family_id := raw_id.strip_edges()
if not family_id.is_empty() and not disabled.has(family_id):
disabled.append(family_id)
character_select.set_disabled_family_ids(disabled)

func _input(event: InputEvent) -> void:
if not (event is InputEventKey) or not event.pressed or event.echo:
return
Expand Down Expand Up @@ -158,6 +171,9 @@ func _on_character_selected(character_id: StringName) -> void:
selected_character_id = character_id
if character_select != null:
character_select.visible = false
if play_mode_select != null and play_mode_select.has_method("open"):
play_mode_select.open(character_id)
return
_on_normal_requested()


Expand Down
35 changes: 31 additions & 4 deletions export_presets.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="build/windows/InfinityKingdom.exe"
patches=PackedStringArray()
patch_delta_encoding=false
patch_delta_compression_level_zstd=19
patch_delta_min_reduction=0.1
patch_delta_include_filters="*"
patch_delta_exclude_filters=""
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
Expand All @@ -21,9 +28,10 @@ custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=0
binary_format/embed_pck=true
binary_format/architecture="x86_64"
texture_format/s3tc_bptc=true
texture_format/etc2_astc=false
shader_baker/enabled=false
binary_format/architecture="x86_64"
codesign/enable=false
codesign/timestamp=true
codesign/timestamp_server_url=""
Expand All @@ -39,15 +47,23 @@ application/product_version="1.0.0"
application/company_name="Es777777"
application/product_name="Infinity Kingdom"
application/file_description="Infinity Kingdom"
application/copyright="Copyright (c) 2026 Es777777"
application/copyright="Copyright (c) 2026 xinxiangchen"
application/trademarks=""
application/export_angle=0
application/export_d3d12=0
application/d3d12_agility_sdk_multiarch=true
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"
ssh_remote_deploy/extra_args_ssh=""
ssh_remote_deploy/extra_args_scp=""
ssh_remote_deploy/run_script="#!/usr/bin/env bash\nexport DISPLAY=:0\nunzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"\n\"{temp_dir}/{exe_name}\" {cmd_args}"
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash\nkill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")\nrm -rf \"{temp_dir}\""
ssh_remote_deploy/run_script="#!/usr/bin/env bash
export DISPLAY=:0
unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"
\"{temp_dir}/{exe_name}\" {cmd_args}"
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")
rm -rf \"{temp_dir}\""

[preset.1]

Expand All @@ -60,8 +76,15 @@ export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="build/web/index.html"
patches=PackedStringArray()
patch_delta_encoding=false
patch_delta_compression_level_zstd=19
patch_delta_min_reduction=0.1
patch_delta_include_filters="*"
patch_delta_exclude_filters=""
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
Expand All @@ -71,6 +94,7 @@ script_export_mode=2
custom_template/debug=""
custom_template/release=""
variant/extensions_support=false
variant/thread_support=false
vram_texture_compression/for_desktop=true
vram_texture_compression/for_mobile=false
html/export_icon=true
Expand All @@ -80,10 +104,13 @@ html/canvas_resize_policy=2
html/focus_canvas_on_start=true
html/experimental_virtual_keyboard=false
progressive_web_app/enabled=false
progressive_web_app/ensure_cross_origin_isolation_headers=true
progressive_web_app/offline_page=""
progressive_web_app/display=1
progressive_web_app/orientation=0
progressive_web_app/icon_144x144=""
progressive_web_app/icon_180x180=""
progressive_web_app/icon_512x512=""
progressive_web_app/background_color=Color(0, 0, 0, 1)
threads/emscripten_pool_size=8
threads/godot_pool_size=4
1 change: 1 addition & 0 deletions project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ config/icon="res://icon.svg"
[autoload]

AccessoryManager="*res://systems/accessories/accessory_manager.gd"
ConsumableManager="*res://systems/consumables/consumable_manager.gd"
RunDirector="*res://systems/run/run_director.gd"
SaveManager="*res://systems/run/save_manager.gd"
LineageDirector="*res://systems/run/lineage_director.gd"
Expand Down
162 changes: 162 additions & 0 deletions systems/consumables/consumable_manager.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
extends Node

signal inventory_changed(slots: Array)
signal consumable_used(consumable_id: String, slot_index: int)

const MAX_SLOTS := 6
const CATALOG := {
"bandage_pack": {
"name": "Bandage Pack",
"short_name": "BDG",
"summary": "Restore 22% max health.",
"icon": "res://assets/ui/consumable/bandage_pack.png",
"tint": Color(0.84, 0.74, 0.64, 1.0)
},
"healing_vial": {
"name": "Healing Vial",
"short_name": "HP",
"summary": "Restore 35% max health.",
"icon": "res://assets/ui/consumable/first_aid_bottle.png",
"tint": Color(0.62, 1.0, 0.72, 1.0)
},
"medkit": {
"name": "Medkit",
"short_name": "MED",
"summary": "Restore 55% max health.",
"icon": "res://assets/ui/consumable/medkit.png",
"tint": Color(0.95, 0.86, 0.74, 1.0)
},
"armor_patch": {
"name": "Light Armor Pack",
"short_name": "DEF",
"summary": "Restore 45% max defense.",
"icon": "res://assets/ui/consumable/light_armor_pack.png",
"tint": Color(0.52, 0.96, 0.92, 1.0)
},
"inspiration_ampoule": {
"name": "Protective Candle",
"short_name": "IN",
"summary": "Restore 45% max inspiration.",
"icon": "res://assets/ui/consumable/protective_candle.png",
"tint": Color(0.46, 0.72, 1.0, 1.0)
},
"edge_oil": {
"name": "Sharpening Oil",
"short_name": "ATK",
"summary": "Next fight: +10% attack damage.",
"icon": "res://assets/ui/consumable/sharpening_oil.png",
"tint": Color(1.0, 0.72, 0.46, 1.0)
}
}

var slots: Array[Dictionary] = []
var rng := RandomNumberGenerator.new()

func _ready() -> void:
rng.randomize()
reset_run()

func reset_run() -> void:
slots.clear()
for _index in range(MAX_SLOTS):
slots.append({})
inventory_changed.emit(get_slots())

func get_slots() -> Array[Dictionary]:
return slots.duplicate(true)

func get_catalog() -> Dictionary:
return CATALOG.duplicate(true)

func random_consumable_id() -> String:
var keys := CATALOG.keys()
if keys.is_empty():
return ""
return String(keys[rng.randi_range(0, keys.size() - 1)])

func add_consumable(consumable_id: String, amount: int = 1) -> bool:
if not CATALOG.has(consumable_id) or amount <= 0:
return false
for slot in slots:
if String(slot.get("id", "")) == consumable_id:
slot["amount"] = int(slot.get("amount", 0)) + amount
inventory_changed.emit(get_slots())
return true
for slot in slots:
if slot.is_empty():
slot["id"] = consumable_id
slot["amount"] = amount
inventory_changed.emit(get_slots())
return true
return false

func use_slot(slot_index: int, actor: Node) -> bool:
if slot_index < 0 or slot_index >= slots.size():
return false
var slot := slots[slot_index]
var consumable_id := String(slot.get("id", ""))
if consumable_id.is_empty() or not CATALOG.has(consumable_id):
return false
if not _apply_consumable(consumable_id, actor):
return false
var next_amount := int(slot.get("amount", 1)) - 1
if next_amount <= 0:
slots[slot_index] = {}
else:
slot["amount"] = next_amount
consumable_used.emit(consumable_id, slot_index)
inventory_changed.emit(get_slots())
return true

func describe(consumable_id: String) -> Dictionary:
var data: Dictionary = (CATALOG.get(consumable_id, {}) as Dictionary).duplicate(true)
data["id"] = consumable_id
return data

func _apply_consumable(consumable_id: String, actor: Node) -> bool:
if actor == null or not is_instance_valid(actor):
return false
match consumable_id:
"bandage_pack":
return _restore_ratio(actor, "hp", "max_hp", 0.22)
"healing_vial":
return _restore_ratio(actor, "hp", "max_hp", 0.35)
"medkit":
return _restore_ratio(actor, "hp", "max_hp", 0.55)
"armor_patch":
return _restore_ratio(actor, "defense", "max_defense", 0.45)
"inspiration_ampoule":
return _restore_ratio(actor, "inspiration", "max_inspiration", 0.45)
"edge_oil":
return _queue_next_fight_prep("edge_oil", "Edge Oil", {"attack_damage_pct": 0.10})
return false

func _restore_ratio(actor: Node, current_field: String, max_field: String, ratio: float) -> bool:
if not _has_property(actor, current_field) or not _has_property(actor, max_field):
return false
var max_value := float(actor.get(max_field))
if max_value <= 0.0:
return false
actor.set(current_field, clampf(float(actor.get(current_field)) + max_value * ratio, 0.0, max_value))
if actor.has_method("emit_stat_signals"):
actor.emit_stat_signals()
return true

func _queue_next_fight_prep(consumable_id: String, title: String, effects: Dictionary) -> bool:
if RunDirector == null:
return false
RunDirector.set_pending_encounter_prep({
"choice_id": consumable_id,
"title": title,
"summary": "Consumable prep for the next map.",
"temporary_effects": effects
})
return true

func _has_property(actor: Node, field: String) -> bool:
if actor == null:
return false
for property in actor.get_property_list():
if String(property.get("name", "")) == field:
return true
return false
1 change: 1 addition & 0 deletions systems/consumables/consumable_manager.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://b48vhargq8s1o
24 changes: 24 additions & 0 deletions systems/pickups/run_pickup.gd
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ var age: float = 0.0
var lifetime: float = 8.0
var drift_phase: float = 0.0
var collected_flag: bool = false
var icon_texture: Texture2D = null

func setup(pickup_kind: String, pickup_amount: float, options: Dictionary = {}) -> void:
name = "RunPickup"
kind = pickup_kind
amount = maxf(pickup_amount, 0.0)
tint = options.get("tint", _default_tint(kind))
var icon_path := String(options.get("icon", ""))
icon_texture = load(icon_path) as Texture2D if not icon_path.is_empty() else null
var launch_speed := float(options.get("launch_speed", randf_range(34.0, 62.0)))
var launch_angle := float(options.get("launch_angle", randf() * TAU))
velocity = Vector2.RIGHT.rotated(launch_angle) * launch_speed
Expand Down Expand Up @@ -59,6 +62,15 @@ func _draw() -> void:
var outer_radius := 7.0 * scale_pulse
var inner_radius := 4.0 * scale_pulse
draw_circle(center, outer_radius + 2.0, Color(0.02, 0.03, 0.05, 0.52))
if icon_texture != null:
var icon_size := Vector2(28.0, 28.0)
if kind == "accessory":
icon_size = Vector2(26.0, 26.0)
elif kind == "consumable":
icon_size = Vector2(30.0, 30.0)
draw_texture_rect(icon_texture, Rect2(center - icon_size * 0.5, icon_size), false, Color.WHITE)
draw_arc(center, maxf(icon_size.x, icon_size.y) * 0.5 + 1.0, 0.0, TAU, 20, Color(1.0, 1.0, 1.0, 0.44), 1.2)
return
match kind:
"gold":
var diamond := PackedVector2Array([
Expand All @@ -84,6 +96,14 @@ func _draw() -> void:
draw_colored_polygon(shield, tint)
draw_rect(Rect2(center + Vector2(-1.2, -4.0), Vector2(2.4, 8.0)), Color.WHITE, true)
draw_rect(Rect2(center + Vector2(-4.0, -1.2), Vector2(8.0, 2.4)), Color.WHITE, true)
"consumable":
draw_rect(Rect2(center + Vector2(-outer_radius * 0.55, -outer_radius), Vector2(outer_radius * 1.1, outer_radius * 1.8)), tint, true)
draw_rect(Rect2(center + Vector2(-outer_radius * 0.32, -outer_radius * 1.24), Vector2(outer_radius * 0.64, outer_radius * 0.34)), Color(0.92, 0.96, 1.0, 1.0), true)
draw_rect(Rect2(center + Vector2(-1.1, -4.0), Vector2(2.2, 8.0)), Color.WHITE, true)
draw_rect(Rect2(center + Vector2(-4.0, -1.1), Vector2(8.0, 2.2)), Color.WHITE, true)
"accessory":
draw_arc(center, outer_radius * 0.75, 0.0, TAU, 24, tint, 2.8)
draw_circle(center + Vector2(0.0, -outer_radius * 0.82), inner_radius * 0.62, Color(1.0, 0.96, 0.72, 1.0))
_:
draw_circle(center, outer_radius, tint)
draw_circle(center + Vector2(0.0, -1.0), inner_radius, Color(0.94, 1.0, 0.94, 0.72))
Expand All @@ -105,5 +125,9 @@ func _default_tint(pickup_kind: String) -> Color:
return Color(0.52, 0.96, 0.92, 1.0)
"heal":
return Color(0.58, 1.0, 0.72, 1.0)
"consumable":
return Color(0.80, 0.90, 1.0, 1.0)
"accessory":
return Color(1.0, 0.88, 0.52, 1.0)
_:
return Color(1.0, 0.88, 0.42, 1.0)
Loading
Loading