First commit

This commit is contained in:
Michele Rossi
2026-03-08 10:38:30 +01:00
commit bd11d2c037
27 changed files with 707 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
---
name: developing-godot-gdscript
description: Builds and refactors Godot 4 projects with idiomatic GDScript, scene composition, signals, and autoload patterns. Use when implementing gameplay, UI, tools, or architecture changes in Godot/GDScript repositories.
---
# Developing Godot With GDScript
Implements production-friendly Godot 4 changes using typed GDScript, scene composition, and low-coupling architecture.
## Use This Skill When
- Adding or refactoring gameplay systems, UI flows, or content pipelines in Godot.
- Debugging node lifecycle, signals, resources, or autoload behavior.
- Improving maintainability, style consistency, or data flow in GDScript code.
## Core Rules
1. Prefer composition over deep inheritance: split reusable behavior into scenes/resources/scripts with focused responsibilities.
2. Choose scenes for reusable node trees and editor-authored structure; choose scripts/resources for logic and data that do not need node hierarchy.
3. Use autoloads only for truly global state/services (save systems, global config, cross-scene coordinators), not as default dependency injection.
4. Favor signals over direct cross-tree mutation to reduce coupling between gameplay, UI, and services.
5. Use typed GDScript where possible: annotate variables, params, and return types for safer refactors.
## GDScript Implementation Checklist
1. Keep naming idiomatic: `snake_case` for vars/functions/signals, `PascalCase` for `class_name`, `UPPER_SNAKE_CASE` for constants.
2. Use `@export` for editor-facing configuration and `@onready` only for scene-dependent node lookups.
3. Respect lifecycle intent: `_init` for construction data, `_enter_tree` for tree attachment concerns, `_ready` for node wiring, `_process`/`_physics_process` for frame loops.
4. Distinguish frame loops clearly: use `_physics_process` for deterministic physics movement and `_process` for visual or non-physics updates.
5. Use `match` for clear branching on enums/states and keep functions short with early returns.
## Data, Loading, And Error Handling
1. Guard all file access (`FileAccess.open`) and JSON parsing before indexing into parsed values.
2. Validate dynamic data types (`is Dictionary`, `is Array`) before field access to avoid runtime surprises.
3. Use `preload` for stable, frequently used compile-time resources; use `load` for optional or dynamic runtime assets.
4. Prefer `Resource`/`RefCounted` for plain data models when node features are unnecessary.
5. Use `assert`/`push_error` for invalid states that should fail loudly during development.
## Delivery Workflow
1. Read `project.godot` first to identify autoloads, rendering/physics settings, and project conventions.
2. Inspect relevant `.tscn` + `.gd` pairs together before editing to preserve scene-script contracts.
3. Implement minimal, targeted changes that keep public APIs/signals stable unless change is requested.
4. Run headless validation and script checks for touched scripts; run project smoke test when feasible.
5. Summarize changed files, behavior impact, and any follow-up risks.
## Validation Commands
Use project-provided commands when available. Typical Godot checks:
```bash
"$GODOT_BIN" --headless --path . --quit
"$GODOT_BIN" --headless --check-only --script res://path/to/changed_script.gd
```
## References
- https://docs.godotengine.org/en/stable/tutorials/best_practices/index.html
- https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html

4
.editorconfig Normal file
View File

@@ -0,0 +1,4 @@
root = true
[*]
charset = utf-8

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
# Godot 4+ specific ignores
.godot/
/android/

22
AGENTS.md Normal file
View File

@@ -0,0 +1,22 @@
# AGENTS.md
Godot 4.6 idle-game prototype; code is GDScript + `.tscn` scenes + JSON data at repo root.
## Build, Lint, And Test Commands
Use `GODOT_BIN` for your local editor binary (e.g. `godot`, `godot4`, or full path).
Run project: `"$GODOT_BIN" --path .`
Headless smoke/lint parse: `"$GODOT_BIN" --headless --path . --quit`
Script syntax check (single script): `"$GODOT_BIN" --headless --check-only --script res://big_number.gd`
Tests: no test framework is currently committed (`tests/` and `addons/gut` are absent).
Single-test command: N/A right now; if GUT is added, use `"$GODOT_BIN" --headless --path . -s res://addons/gut/gut_cmdln.gd -gtest=res://tests/<file>.gd`.
## Architecture And Structure
`project.godot` defines autoload singletons: `GameState` (runtime currency state + save/load) and `CurrencyGeneratorDatabase` (loads `generator_data.json`).
`big_number.gd` (`class_name BigNumber`) is the internal numeric API for huge-value math, comparisons, formatting, and serialization.
`big_number_museum.tscn` + `big_number_museum.gd` is the current main playable scene/prototype UI.
`currency_generator.gd`, `currency_label.gd`, and `big_number_progress_bar.gd` are UI/gameplay adapters that react to `GameState` signals.
Persistence is local JSON only: `user://idle_save.json`; there is no external DB/server/backend.
## Code Style And Conventions
Prefer typed GDScript (`var x: Type`, `func f() -> void`) and keep shared models/components as `class_name` scripts.
Naming: `snake_case` for vars/functions/signals, `PascalCase` for classes, `UPPER_SNAKE_CASE` for constants.
Follow existing Godot style: tabs/consistent indentation, early returns, and short functions grouped by purpose.
When reading files/JSON, always guard `FileAccess.open(...)` and validate parsed types before indexing.
Use signals (already in `GameState`) for UI updates instead of polling or cross-node direct mutation.
No Cursor/Claude/Windsurf/Cline/Goose/Copilot rule files were found in this repository, so this file is the canonical agent guidance.

227
big_number.gd Normal file
View File

@@ -0,0 +1,227 @@
class_name BigNumber
extends RefCounted
## The core value representation.
## For example, 1.5e24 is represented as mantissa = 1.5, exponent = 24.
var mantissa: float
var exponent: int
## Pre-calculate this to avoid dividing logs constantly
const LOG10_E: float = 0.4342944819032518
func _init(m: float = 0.0, e: int = 0) -> void:
mantissa = m
exponent = e
_normalize()
## Adjusts the internal representation so the mantissa is always between 1.0 and 9.99...
## or exactly 0.0.
func _normalize() -> void:
if mantissa == 0.0:
exponent = 0
return
var is_negative: bool = mantissa < 0.0
var abs_m: float = abs(mantissa)
if abs_m >= 10.0 or abs_m < 1.0:
# Calculate how many powers of 10 we need to shift
# Godot's log() is base 'e', so we multiply by log10(e) to get log10(x)
var exp_diff: int = floori(log(abs_m) * LOG10_E)
abs_m /= pow(10.0, float(exp_diff))
exponent += exp_diff
mantissa = abs_m if not is_negative else -abs_m
# ==========================================
# MATH OPERATIONS
# ==========================================
func add(other: BigNumber) -> BigNumber:
if mantissa == 0: return BigNumber.new(other.mantissa, other.exponent)
if other.mantissa == 0: return BigNumber.new(mantissa, exponent)
var exp_diff: int = exponent - other.exponent
# If the difference in magnitude is massive, the smaller number is insignificant
# (15 is a safe threshold for 64-bit float precision).
if exp_diff >= 15:
return BigNumber.new(mantissa, exponent)
elif exp_diff <= -15:
return BigNumber.new(other.mantissa, other.exponent)
var new_m: float = mantissa
var new_e: int = exponent
# Scale the smaller number down to match the larger number's exponent
if exp_diff > 0:
new_m += other.mantissa / pow(10.0, float(exp_diff))
elif exp_diff < 0:
new_m = (mantissa / pow(10.0, float(-exp_diff))) + other.mantissa
new_e = other.exponent
else:
new_m += other.mantissa
return BigNumber.new(new_m, new_e)
## High-performance addition for the _process() loop.
## Modifies THIS instance instead of creating a new RefCounted object.
func add_in_place(other: BigNumber) -> void:
if other.mantissa == 0.0: return
if mantissa == 0.0:
mantissa = other.mantissa
exponent = other.exponent
return
var exp_diff: int = exponent - other.exponent
if exp_diff >= 15:
return # Other number is too small to matter
elif exp_diff <= -15:
# This number is effectively replaced by the larger other number
mantissa = other.mantissa
exponent = other.exponent
return
if exp_diff > 0:
mantissa += other.mantissa / pow(10.0, float(exp_diff))
elif exp_diff < 0:
mantissa = (mantissa / pow(10.0, float(-exp_diff))) + other.mantissa
exponent = other.exponent
else:
mantissa += other.mantissa
# Prevent floating-point drift near zero
if abs(mantissa) < 0.0000000001:
mantissa = 0.0
exponent = 0
else:
_normalize()
func subtract(other: BigNumber) -> BigNumber:
# Subtraction is just adding a negative number
var negative_other = BigNumber.new(-other.mantissa, other.exponent)
return add(negative_other)
func multiply(other: BigNumber) -> BigNumber:
var new_m: float = mantissa * other.mantissa
var new_e: int = exponent + other.exponent
return BigNumber.new(new_m, new_e)
func divide(other: BigNumber) -> BigNumber:
if other.mantissa == 0.0:
push_error("BigNumber: Division by zero!")
return BigNumber.new(0.0, 0)
var new_m: float = mantissa / other.mantissa
var new_e: int = exponent - other.exponent
return BigNumber.new(new_m, new_e)
# ==========================================
# COMPARISONS
# ==========================================
## Returns 1 if this > other, -1 if this < other, 0 if equal
func compare_to(other: BigNumber) -> int:
if mantissa == 0.0 and other.mantissa == 0.0: return 0
# Handle signs
if mantissa > 0 and other.mantissa <= 0: return 1
if mantissa < 0 and other.mantissa >= 0: return -1
# Both are same sign. Compare exponents first.
var sign_mult: int = 1 if mantissa > 0 else -1
if exponent > other.exponent: return sign_mult
if exponent < other.exponent: return -sign_mult
# Exponents are equal, compare mantissas
if mantissa > other.mantissa: return 1
if mantissa < other.mantissa: return -1
return 0
func is_greater_than(other: BigNumber) -> bool:
return compare_to(other) == 1
func is_less_than(other: BigNumber) -> bool:
return compare_to(other) == -1
func is_equal_to(other: BigNumber) -> bool:
return compare_to(other) == 0
## Calculates the progress ratio between this number and a target number.
## Returns a standard float clamped between 0.0 and 1.0 for UI progress bars.
func get_ratio(target: BigNumber) -> float:
if target.mantissa == 0.0:
return 1.0 # If the goal is 0, you've already beaten it!
if mantissa == 0.0:
return 0.0
var exp_diff: int = exponent - target.exponent
# If the target is massively larger, progress is practically 0%
if exp_diff <= -15:
return 0.0
# If current is equal or greater, progress is 100%
if exp_diff >= 15 or is_greater_than(target) or is_equal_to(target):
return 1.0
# Calculate the actual float ratio
var ratio: float = (mantissa / target.mantissa) * pow(10.0, float(exp_diff))
# Clamp it just to be perfectly safe for UI elements
return clampf(ratio, 0.0, 1.0)
# ==========================================
# UTILITIES
# ==========================================
## Creates a BigNumber from a standard float or int
static func from_float(val: float) -> BigNumber:
return BigNumber.new(val, 0)
## Outputs a UI-friendly string (e.g., "1.50e12")
func to_string_sci(decimals: int = 2) -> String:
if exponent < 3:
# For small numbers, just show the regular number
var val: float = mantissa * pow(10.0, float(exponent))
return ("%." + str(decimals) + "f") % val
var format_str: String = "%." + str(decimals) + "f"
return (format_str % mantissa) + "e" + str(exponent)
## Optional: Standard idle game suffix formatting (K, M, B, T, Qa, etc.)
func to_string_suffix(decimals: int = 2) -> String:
if exponent < 3:
return to_string_sci(decimals)
var suffixes = ["", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "Dc"]
var suffix_index: int = floori(exponent / 3.0)
if suffix_index < suffixes.size():
var display_mantissa = mantissa * pow(10.0, float(exponent % 3))
var format_str: String = "%." + str(decimals) + "f"
return (format_str % display_mantissa) + suffixes[suffix_index]
else:
# Fall back to scientific if we run out of suffixes
return to_string_sci(decimals)
# ==========================================
# SAVE & LOAD (SERIALIZATION)
# ==========================================
## Converts the BigNumber into a basic Dictionary for easy JSON saving.
func serialize() -> Dictionary:
return {
"m": mantissa,
"e": exponent
}
## A static factory method that creates a new BigNumber from loaded Dictionary data.
static func deserialize(data: Dictionary) -> BigNumber:
# Provide fallbacks (0.0 and 0) just in case the save file is corrupted
var loaded_m: float = data.get("m", 0.0)
var loaded_e: int = data.get("e", 0)
return BigNumber.new(loaded_m, loaded_e)

1
big_number.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://be4in86mvsj0q

10
big_number_museum.gd Normal file
View File

@@ -0,0 +1,10 @@
extends Control
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

1
big_number_museum.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://coasop1lyw5rh

43
big_number_museum.tscn Normal file
View File

@@ -0,0 +1,43 @@
[gd_scene format=3 uid="uid://b11eevc8qj18p"]
[ext_resource type="Script" uid="uid://dtbxopw6ulhl8" path="res://generator/currency_generator.gd" id="1_h2bxk"]
[ext_resource type="PackedScene" uid="uid://clu4rn1tl1s8t" path="res://big_number_progress_bar.tscn" id="1_lni8d"]
[ext_resource type="Script" uid="uid://coasop1lyw5rh" path="res://big_number_museum.gd" id="1_muwei"]
[ext_resource type="Script" uid="uid://sp67wcb6s5nw" path="res://currency_label.gd" id="2_a7k0t"]
[node name="BigNumberMuseum" type="Control" unique_id=1249411984]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_muwei")
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=1937517070]
layout_mode = 0
offset_right = 264.0
offset_bottom = 111.0
[node name="Label" type="Label" parent="VBoxContainer" unique_id=1357448839]
layout_mode = 2
text = "Primary currency"
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer" unique_id=1592054467]
layout_mode = 2
theme_override_constants/separation = 126
[node name="Button" type="Button" parent="VBoxContainer/HBoxContainer" unique_id=2126082317]
layout_mode = 2
text = "Get Currency"
script = ExtResource("1_h2bxk")
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer" unique_id=1036299287]
layout_mode = 2
text = "000"
script = ExtResource("2_a7k0t")
[node name="HBoxContainerAuto" parent="VBoxContainer" unique_id=1174903091 instance=ExtResource("1_lni8d")]
layout_mode = 2
[connection signal="pressed" from="VBoxContainer/HBoxContainer/Button" to="VBoxContainer/HBoxContainer/Button" method="_on_pressed"]

View File

@@ -0,0 +1,36 @@
extends HBoxContainer
@export var currency: GameState.CurrencyType
@onready var _label_start = $LabelStart
@onready var _label_end = $LabelEnd
@onready var _progress_bar = $ProgressBar
var _start: BigNumber
var _end: BigNumber
var _current: BigNumber
func set_limits(start: BigNumber, end: BigNumber) -> void:
_start = start
_end = end
_label_start.text = _start.to_string_suffix(2)
_label_end.text = _end.to_string_suffix(2)
_progress_bar.min_value = _start.get_ratio(_end)
_progress_bar.max_value = _end.get_ratio(_start)
func _ready() -> void:
match currency:
GameState.CurrencyType.gold:
GameState.gold_changed.connect(_on_currency_changed)
_current = GameState.gold
GameState.CurrencyType.gems:
GameState.gems_changed.connect(_on_currency_changed)
_current = GameState.gems
set_limits(_current, _current.add(BigNumber.new(1, 1)))
func _on_currency_changed(currency: BigNumber) -> void:
_current = currency
_progress_bar.value = currency.get_ratio(_end)

View File

@@ -0,0 +1 @@
uid://bom3jmnv0cvon

View File

@@ -0,0 +1,23 @@
[gd_scene format=3 uid="uid://clu4rn1tl1s8t"]
[ext_resource type="Script" uid="uid://bom3jmnv0cvon" path="res://big_number_progress_bar.gd" id="1_ssa4w"]
[node name="HBoxContainer" type="HBoxContainer" unique_id=1174903091]
offset_right = 298.0
offset_bottom = 49.0
alignment = 1
script = ExtResource("1_ssa4w")
[node name="LabelStart" type="Label" parent="." unique_id=1661715778]
layout_mode = 2
text = "100
"
[node name="ProgressBar" type="ProgressBar" parent="." unique_id=577927023]
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
[node name="LabelEnd" type="Label" parent="." unique_id=440697928]
layout_mode = 2
text = "100
"

View File

@@ -0,0 +1,31 @@
extends Node
var items: Dictionary = {}
func _ready():
load_item_data()
func load_item_data():
# 1. Open the JSON file
var file = FileAccess.open("res://generator_data.json", FileAccess.READ)
var json_text = file.get_as_text()
# 2. Parse it into a Godot Dictionary
var parsed_data = JSON.parse_string(json_text)
# 3. Loop through and create Custom Resources
for key in parsed_data:
var data = CurrencyGeneratorData.new()
var item_info = parsed_data[key]
# Populate the resource
data.id = key
data.name = key
data.initial_cost = item_info["initial_cost"]
data.coefficient = item_info["coefficient"]
data.initial_time = item_info["initial_time"]
data.initial_revenue = item_info["initial_revenue"]
data.initial_productivity = item_info["initial_productivity"]
# Store it in our database dictionary
items[key] = data

View File

@@ -0,0 +1 @@
uid://cjuinqoug01v1

16
currency_label.gd Normal file
View File

@@ -0,0 +1,16 @@
class_name CurrencyLabel
extends Label
@export var currency: GameState.CurrencyType
func _ready() -> void:
match currency:
GameState.CurrencyType.gold:
GameState.gold_changed.connect(_on_currency_changed)
GameState.CurrencyType.gems:
GameState.gems_changed.connect(_on_currency_changed)
func _on_currency_changed(currency: BigNumber) -> void:
text = currency.to_string_sci(0)

1
currency_label.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://sp67wcb6s5nw

76
game_state.gd Normal file
View File

@@ -0,0 +1,76 @@
extends Node
# This script is added as an Autoload named 'GameState'
# ==========================================
# SIGNALS
# ==========================================
signal gold_changed(new_amount: BigNumber)
signal gems_changed(new_amount: BigNumber)
enum CurrencyType { gold, gems }
# ==========================================
# STATE VARIABLES
# ==========================================
var gold: BigNumber = BigNumber.from_float(0.0)
var gems: BigNumber = BigNumber.from_float(0.0)
var last_save_time: int = 0 # Unix timestamp
static var file_name: String = "user://idle_save.json"
func _ready() -> void:
load_game()
# ==========================================
# STATE MODIFIERS
# ==========================================
func add_gold(amount: BigNumber) -> void:
gold.add_in_place(amount)
gold_changed.emit(gold)
func spend_gold(cost: BigNumber) -> bool:
if gold.is_greater_than(cost) or gold.is_equal_to(cost):
var negative_cost = BigNumber.new(-cost.mantissa, cost.exponent)
gold.add_in_place(negative_cost)
gold_changed.emit(gold)
return true
return false
func add_gems(amount: BigNumber) -> void:
gems.add_in_place(amount)
gems_changed.emit(gems)
func spend_gems(cost: BigNumber) -> bool:
if gems.is_greater_than(cost) or gems.is_equal_to(cost):
var negative_cost = BigNumber.new(-cost.mantissa, cost.exponent)
gems.add_in_place(negative_cost)
gems_changed.emit(gems)
return true
return false
# ==========================================
# PERSISTENCE (Save/Load)
# ==========================================
func save_game() -> void:
var save_data = {
"gold": gold.serialize(),
"gems": gems.serialize(),
"last_save_time": Time.get_unix_time_from_system()
}
var file = FileAccess.open(file_name, FileAccess.WRITE)
if file:
file.store_string(JSON.stringify(save_data))
func load_game() -> void:
if not FileAccess.file_exists(file_name):
return
var file = FileAccess.open(file_name, FileAccess.READ)
if file:
var parsed = JSON.parse_string(file.get_as_text())
if parsed is Dictionary:
gold = BigNumber.deserialize(parsed.get("gold", {}))
gems = BigNumber.deserialize(parsed.get("gems", {}))
last_save_time = parsed.get("last_save_time", Time.get_unix_time_from_system())

1
game_state.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://d2j7tvlgxr2jp

View File

@@ -0,0 +1,20 @@
class_name CurrencyGenerator
extends Node
@export var currency: GameState.CurrencyType
@export var data: CurrencyGeneratorData
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
func _on_pressed() -> void:
match currency:
GameState.CurrencyType.gold: GameState.add_gold(BigNumber.new(1, 0))
GameState.CurrencyType.gems: GameState.add_gems(BigNumber.new(1, 0))
_: print("c")
pass

View File

@@ -0,0 +1 @@
uid://dtbxopw6ulhl8

View File

@@ -0,0 +1,18 @@
class_name CurrencyGeneratorData
extends Resource
@export var id: GameState.CurrencyType
@export var name: String
@export var initial_cost: float
@export var coefficient: float
@export var initial_time: float
@export var initial_revenue: float
@export var initial_productivity: float
## Returns cost of next generator
func cost_next(owned: int) -> float:
return initial_cost * pow(coefficient, owned)
## Returns value produced
func production_total(owned: int, multipliers: float) -> float:
return (initial_productivity * owned) * multipliers

View File

@@ -0,0 +1 @@
uid://b00tqsuhxdy0d

37
generator_data.json Normal file
View File

@@ -0,0 +1,37 @@
{
"generator_1": {
"initial_cost": 10,
"coefficient": 1.15,
"initial_time": 0.6,
"initial_revenue": 1.0,
"initial_productivity": 1.67
},
"generator_2": {
"initial_cost": 60.0,
"coefficient": 1.15,
"initial_time": 3.0,
"initial_revenue": 60.0,
"initial_productivity": 20.0
},
"generator_3": {
"initial_cost": 720.0,
"coefficient": 1.14,
"initial_time": 6.0,
"initial_revenue": 540.0,
"initial_productivity": 90.0
},
"generator_4": {
"initial_cost": 8640.0,
"coefficient": 1.13,
"initial_time": 12.0,
"initial_revenue": 4320.0,
"initial_productivity": 360.0
},
"generator_5": {
"initial_cost": 103680.0,
"coefficient": 1.12,
"initial_time": 24.0,
"initial_revenue": 51840.0,
"initial_productivity": 2160.0
}
}

1
icon.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>

After

Width:  |  Height:  |  Size: 995 B

43
icon.svg.import Normal file
View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bgtt3wu43tajh"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

27
project.godot Normal file
View File

@@ -0,0 +1,27 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
[application]
config/name="Idles"
config/features=PackedStringArray("4.6", "Forward Plus")
config/icon="res://icon.svg"
[autoload]
GameState="*uid://d2j7tvlgxr2jp"
[physics]
3d/physics_engine="Jolt Physics"
[rendering]
rendering_device/driver.windows="d3d12"