From bcfc8fbc8180cf49fcdfe116bdc4b6b5dd22b9e8 Mon Sep 17 00:00:00 2001 From: Avior Date: Tue, 15 Jul 2025 00:46:55 +0200 Subject: [PATCH] feat: base Signed-off-by: Avior --- .editorconfig | 4 + .gitattributes | 13 + .gitignore | 3 + icon.svg | 1 + icon.svg.import | 37 +++ project.godot | 20 ++ resources/data/boosters/booster.gd | 27 ++ resources/data/boosters/booster.gd.uid | 1 + resources/data/boosters/new_base_booster.tres | 15 + resources/data/cards/card.gd | 7 + resources/data/cards/card.gd.uid | 1 + resources/data/cards/king_slime.tres | 11 + resources/data/cards/water_slime.tres | 10 + scenes/entrypoint.tscn | 35 +++ scripts/autoload/game_state.gd | 41 +++ scripts/autoload/game_state.gd.uid | 1 + scripts/interfaces/save.gd | 5 + scripts/interfaces/save.gd.uid | 1 + scripts/utils/json_packer.gd | 264 ++++++++++++++++++ scripts/utils/json_packer.gd.uid | 1 + 20 files changed, 498 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 icon.svg create mode 100644 icon.svg.import create mode 100644 project.godot create mode 100644 resources/data/boosters/booster.gd create mode 100644 resources/data/boosters/booster.gd.uid create mode 100644 resources/data/boosters/new_base_booster.tres create mode 100644 resources/data/cards/card.gd create mode 100644 resources/data/cards/card.gd.uid create mode 100644 resources/data/cards/king_slime.tres create mode 100644 resources/data/cards/water_slime.tres create mode 100644 scenes/entrypoint.tscn create mode 100644 scripts/autoload/game_state.gd create mode 100644 scripts/autoload/game_state.gd.uid create mode 100644 scripts/interfaces/save.gd create mode 100644 scripts/interfaces/save.gd.uid create mode 100644 scripts/utils/json_packer.gd create mode 100644 scripts/utils/json_packer.gd.uid diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f28239b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +root = true + +[*] +charset = utf-8 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bfecb13 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,13 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf + +# files stored in LFS +*.glb filter=lfs diff=lfs merge=lfs -text +*.obj filter=lfs diff=lfs merge=lfs -text +*.exr filter=lfs diff=lfs merge=lfs -text +*.avi filter=lfs diff=lfs merge=lfs -text +*.ogv filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.blend filter=lfs diff=lfs merge=lfs -text +*.blend1 filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0af181c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Godot 4+ specific ignores +.godot/ +/android/ diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..9d8b7fa --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..62f0374 --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cnvhkptdeevyy" +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/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +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 diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..54cc8cc --- /dev/null +++ b/project.godot @@ -0,0 +1,20 @@ +; 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="kairo" +run/main_scene="uid://c37m0t4dwnwbn" +config/features=PackedStringArray("4.4", "Forward Plus") +config/icon="res://icon.svg" + +[autoload] + +GameState="*res://scripts/autoload/game_state.gd" diff --git a/resources/data/boosters/booster.gd b/resources/data/boosters/booster.gd new file mode 100644 index 0000000..b97f893 --- /dev/null +++ b/resources/data/boosters/booster.gd @@ -0,0 +1,27 @@ +extends Resource +class_name Booster + + + +@export var name: String +@export var counts: Dictionary = { "Common": 4, "Rare": 1 } +@export var cards: Array[Card] = [] + +func draw() -> Array[Card]: + var res: Array[Card] = [] + + # Group cards by rarity + var rarity_pool: Dictionary = {} + for card in cards: + if not rarity_pool.has(card.rarity): + rarity_pool[card.rarity] = [] + rarity_pool[card.rarity].append(card) + + # Draw based on rarity count + for rarity in counts.keys(): + var count = counts[rarity] + var pool = rarity_pool.get(rarity, []) + for i in range(min(count, pool.size())): + res.append(pool.pick_random()) + + return res diff --git a/resources/data/boosters/booster.gd.uid b/resources/data/boosters/booster.gd.uid new file mode 100644 index 0000000..65bc4ef --- /dev/null +++ b/resources/data/boosters/booster.gd.uid @@ -0,0 +1 @@ +uid://d2vr5ocvib663 diff --git a/resources/data/boosters/new_base_booster.tres b/resources/data/boosters/new_base_booster.tres new file mode 100644 index 0000000..e176680 --- /dev/null +++ b/resources/data/boosters/new_base_booster.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" script_class="Booster" load_steps=4 format=3 uid="uid://c63rkr0cudnom"] + +[ext_resource type="Script" uid="uid://5pqr71r8dv5q" path="res://resources/data/cards/card.gd" id="1_uvxrb"] +[ext_resource type="Script" uid="uid://d2vr5ocvib663" path="res://resources/data/boosters/booster.gd" id="2_1ex0h"] +[ext_resource type="Resource" uid="uid://bsto1qwayy1sk" path="res://resources/data/cards/water_slime.tres" id="2_hsyr7"] + +[resource] +script = ExtResource("2_1ex0h") +name = "Base Booster" +counts = { +"Common": 4, +"Rare": 1 +} +cards = Array[ExtResource("1_uvxrb")]([ExtResource("2_hsyr7")]) +metadata/_custom_type_script = "uid://d2vr5ocvib663" diff --git a/resources/data/cards/card.gd b/resources/data/cards/card.gd new file mode 100644 index 0000000..35a9dee --- /dev/null +++ b/resources/data/cards/card.gd @@ -0,0 +1,7 @@ +extends Resource +class_name Card + +@export var name: String +@export var hp: int +@export var damage: int +@export var rarity: String = "Common" diff --git a/resources/data/cards/card.gd.uid b/resources/data/cards/card.gd.uid new file mode 100644 index 0000000..f56688e --- /dev/null +++ b/resources/data/cards/card.gd.uid @@ -0,0 +1 @@ +uid://5pqr71r8dv5q diff --git a/resources/data/cards/king_slime.tres b/resources/data/cards/king_slime.tres new file mode 100644 index 0000000..7bd1311 --- /dev/null +++ b/resources/data/cards/king_slime.tres @@ -0,0 +1,11 @@ +[gd_resource type="Resource" script_class="Card" load_steps=2 format=3 uid="uid://r20ullywveey"] + +[ext_resource type="Script" uid="uid://5pqr71r8dv5q" path="res://resources/data/cards/card.gd" id="1_w886e"] + +[resource] +script = ExtResource("1_w886e") +name = "Water Slime" +hp = 80 +damage = 40 +rarity = "Rare" +metadata/_custom_type_script = "uid://5pqr71r8dv5q" diff --git a/resources/data/cards/water_slime.tres b/resources/data/cards/water_slime.tres new file mode 100644 index 0000000..d5f45f9 --- /dev/null +++ b/resources/data/cards/water_slime.tres @@ -0,0 +1,10 @@ +[gd_resource type="Resource" script_class="Card" load_steps=2 format=3 uid="uid://bsto1qwayy1sk"] + +[ext_resource type="Script" uid="uid://5pqr71r8dv5q" path="res://resources/data/cards/card.gd" id="1_rk1da"] + +[resource] +script = ExtResource("1_rk1da") +name = "Water Slime" +hp = 40 +damage = 10 +metadata/_custom_type_script = "uid://5pqr71r8dv5q" diff --git a/scenes/entrypoint.tscn b/scenes/entrypoint.tscn new file mode 100644 index 0000000..912c40a --- /dev/null +++ b/scenes/entrypoint.tscn @@ -0,0 +1,35 @@ +[gd_scene load_steps=4 format=3 uid="uid://c37m0t4dwnwbn"] + +[ext_resource type="Resource" uid="uid://c63rkr0cudnom" path="res://resources/data/boosters/new_base_booster.tres" id="1_ki0t7"] + +[sub_resource type="GDScript" id="GDScript_0ug6y"] +script/source = "extends Node2D + + +@export var booster: Booster + +func _ready() -> void: + print(booster.draw()) +" + +[sub_resource type="GDScript" id="GDScript_yqgyj"] +script/source = "extends Label + +func _process(delta: float) -> void: + text = str(GameState.data.money) + GameState.add_money(1000) +" + +[node name="Entrypoint" type="Node2D"] +script = SubResource("GDScript_0ug6y") +booster = ExtResource("1_ki0t7") + +[node name="Label" type="Label" parent="."] +offset_left = 504.0 +offset_top = 255.0 +offset_right = 544.0 +offset_bottom = 278.0 +script = SubResource("GDScript_yqgyj") + +[node name="Camera2D" type="Camera2D" parent="."] +position = Vector2(577, 325) diff --git a/scripts/autoload/game_state.gd b/scripts/autoload/game_state.gd new file mode 100644 index 0000000..4dcbcb3 --- /dev/null +++ b/scripts/autoload/game_state.gd @@ -0,0 +1,41 @@ +extends Node + +signal money_changed(new_money) + +var _save_dir := "user://saves" + +var data := { + "money": 1000, + "day": 1 +} + +func list_saves(): + var dir := DirAccess.open("user://saves") + var saves = dir.get_files() + var res: Array[Save] = [] + for save in saves: + res.append(Save.new()) +func add_money(amount): + data.money += amount + emit_signal("money_changed", data.money) + +var save_path = "user://save.bin" + +func save_save(): + var file = FileAccess.open(save_path, FileAccess.WRITE) + file.store_buffer(JSONPacker.encode(data)) + +func load_save(path: String): + if FileAccess.file_exists(path): + var file = FileAccess.open(path, FileAccess.READ) + data = JSONPacker.decode(file.get_buffer(file.get_length())) + return true + return false + +func _init() -> void: + load_state() + +func _notification(what): + if what == NOTIFICATION_WM_CLOSE_REQUEST: + save_state() + get_tree().quit() # default behavior diff --git a/scripts/autoload/game_state.gd.uid b/scripts/autoload/game_state.gd.uid new file mode 100644 index 0000000..366015f --- /dev/null +++ b/scripts/autoload/game_state.gd.uid @@ -0,0 +1 @@ +uid://nkr0vilsuhfk diff --git a/scripts/interfaces/save.gd b/scripts/interfaces/save.gd new file mode 100644 index 0000000..550d087 --- /dev/null +++ b/scripts/interfaces/save.gd @@ -0,0 +1,5 @@ +extends Resource +class_name Save + +@export var money: int +@export var day: int diff --git a/scripts/interfaces/save.gd.uid b/scripts/interfaces/save.gd.uid new file mode 100644 index 0000000..84bed36 --- /dev/null +++ b/scripts/interfaces/save.gd.uid @@ -0,0 +1 @@ +uid://b7g047l14kcxh diff --git a/scripts/utils/json_packer.gd b/scripts/utils/json_packer.gd new file mode 100644 index 0000000..66da604 --- /dev/null +++ b/scripts/utils/json_packer.gd @@ -0,0 +1,264 @@ +class_name JSONPacker + +enum Type { + NUM = 0x00, + LIT = 0x40, + ARR = 0x80, + BOL = 0xA0, + OBJ = 0xC0, + STR = 0xE0, + MULTI = 0x10, + INT = 0x00, + FLOAT = 0x04, + L8 = 0x00, + L16 = 0x01, + L32 = 0x02, + L64 = 0x03, + NULL = 0x00, + UNDEFINED = 0x01, + NAN = 0x02 +} + + +static func encode(value) -> PackedByteArray: + if typeof(value) == TYPE_NIL: + #print("packing NIL") + return PackedByteArray([Type.LIT | Type.NULL]) + elif typeof(value) == TYPE_BOOL: + #print("packing bool") + return PackedByteArray([Type.BOL | (1 if value else 0)]) + elif typeof(value) == TYPE_INT or typeof(value) == TYPE_FLOAT: + #print("packing int") + if typeof(value) == TYPE_FLOAT and is_nan(value): + #print("packing float") + return PackedByteArray([Type.LIT | Type.NAN]) + var encoded = _encode_number(value) + if encoded.type == 0: + return encoded.data + return _concat([PackedByteArray([Type.NUM | encoded.type]), encoded.data]) + elif typeof(value) == TYPE_STRING: + #print("packing string") + var utf8 = value.to_utf8_buffer() + var length = utf8.size() + if length > 0x0F: + var encoded = _encode_number(length) + return _concat([PackedByteArray([Type.STR | encoded.type]), encoded.data, utf8]) + return _concat([PackedByteArray([Type.STR | length]), utf8]) + elif typeof(value) == TYPE_ARRAY: + #print("packing array") + var length = value.size() + var items = [] + if length > 0x0F: + var encoded = _encode_number(length) + items.append(PackedByteArray([Type.ARR | encoded.type])) + items.append(encoded.data) + else: + items.append(PackedByteArray([Type.ARR | length])) + for v in value: + items.append(encode(v)) + + return _concat(items) + elif typeof(value) == TYPE_DICTIONARY: + #print("packing object") + var keys = value.keys() + var items = [] + if keys.size() > 0x0F: + var encoded = _encode_number(keys.size()) + items.append(PackedByteArray([Type.OBJ | encoded.type])) + items.append(encoded.bfr) + else: + items.append(PackedByteArray([Type.OBJ | keys.size()])) + for key in keys: + items.append(encode(str(key))) + items.append(encode(value[key])) + return _concat(items) + else: + push_error("Unsupported type: " + str(typeof(value))) + return PackedByteArray() + +static func _encode_number(value): + var isInt = typeof(value) == TYPE_INT + #print("compiling value") + #print(value) + if isInt and value >= 0 and value <= 0x0F: + #print("int between 0x0 & 0x0F") + return { "type": 0, "data": PackedByteArray([value]) } + + elif isInt and value >= -0x7F and value <= 0x7F: + #print("int between -07F & 0x7F") + return { + "type": Type.MULTI | Type.INT | Type.L8, + "data": PackedByteArray([value]) + } + + elif isInt and value >= -0x7FFF and value <= 0x7FFF: + #print("int between -07FFF & 0x7FFF") + var bfr = PackedByteArray() + bfr.resize(2) + bfr.encode_s16(0, value) + return { + "type": Type.MULTI | Type.INT | Type.L16, + "data": bfr + } + + elif isInt: + #print("int for other cases") + var bfr = PackedByteArray() + bfr.resize(4) + bfr.encode_s32(0, value) + return { + "type": Type.MULTI | Type.INT | Type.L32, + "data": bfr + } + + elif typeof(value) == TYPE_FLOAT: + #print("float") + var bfr = PackedByteArray() + bfr.resize(8) + bfr.encode_double(0, value) + return { + "type": Type.MULTI | Type.FLOAT | Type.L64, + "data": bfr + } + + push_error("Unsupported number type") + return { "type": 0, "data": PackedByteArray() } + +static func _concat(args: Array) -> PackedByteArray: + print("concat", args) + var result = PackedByteArray() + for arr in args: + print("res", arr) + result.append_array(arr) + return result + +static func decode(bfr: PackedByteArray) -> Variant: + return decode_with_end(bfr)["value"] + +static func decode_with_end(bfr: PackedByteArray) -> Dictionary: + var firstByte = bfr[0] + var type = firstByte & 0xE0 + var isMulti = firstByte & 0x10 + + if type == Type.LIT: + var subtype = firstByte & 0x0F + match subtype: + Type.NULL: + return {"value": null, "end": 1} + Type.UNDEFINED: + return {"value": null, "end": 1} + Type.NAN: + return {"value": float('nan'), "end": 1} + + if (firstByte & 0xC0) == 0 && !isMulti: + return {"value": firstByte, "end": 1} + elif (firstByte & 0xC0) == 0 && isMulti: + return _decode_number(firstByte, bfr) + + match type: + Type.STR: + if !isMulti: + var len = firstByte & 0x0F + return { "value": bfr.slice(1, len + 1).get_string_from_utf8(), "end": len + 1} + else: + var tmp = _decode_number(firstByte, bfr) + var len = tmp.value + var lenlen = tmp.end + return { "value": bfr.slice(lenlen, lenlen + len).get_string_from_utf8(), "end": lenlen + len} + Type.BOL: + return {"value": bool(firstByte), "end": 1} + Type.ARR: + if !isMulti: + var len = firstByte & 0x0F + var offset = 1 + var list = [] + for i in len: + var tmp = decode_with_end(bfr.slice(offset)) + var value = tmp.value + var newOffset = tmp.end + list.append(value) + offset += newOffset + return {"value": list, "end": offset} + else: + var tmp1 = _decode_number(firstByte, bfr) + var len = tmp1.value + var lenlen = tmp1.end + var offset = lenlen + var list = [] + for i in len: + var tmp = decode_with_end(bfr.slice(offset)) + var value = tmp.value + var newOffset = tmp.end + list.append(value) + offset += newOffset + return {"value": list, "end": offset} + Type.OBJ: + var dict := {} + if !isMulti: + var len = firstByte & 0x0F + var offset = 1 + for i in len: + var tmpA = decode_with_end(bfr.slice(offset)) + var key = tmpA.value + var tmpOffset = tmpA.end + var tmpB = decode_with_end(bfr.slice(offset + tmpOffset)) + var value = tmpB.value + var newOffset = tmpB.end + dict[key] = value + offset += tmpOffset + newOffset + return {"value": dict, "end": offset} + else: + var tmp = _decode_number(firstByte, bfr) + var len = tmp.value + var lenlen = tmp.end + var offset = lenlen + for i in len: + var tmpA = decode_with_end(bfr.slice(offset)) + var key = tmpA.value + var tmpOffset = tmpA.end + var tmpB = decode_with_end(bfr.slice(offset + tmpOffset)) + var value = tmpB.value + var newOffset = tmpB.end + dict[key] = value + offset += tmpOffset + newOffset + return {"value": dict, "end": offset} + _: + push_error("Unknown type: " + str(type)) + return {"value": null, "end": 0} + return {"value": null, "end": 0} + +static func _decode_float(bfr: PackedByteArray, offset: int = 0, bigEndian = false) -> float: + if bigEndian: + return bfr.decode_float(offset) + return PackedByteArray([bfr[offset+3], bfr[offset+2], bfr[offset+1], bfr[offset+0]]).decode_float(0) + +static func _decode_number(type: int, bfr: PackedByteArray) -> Dictionary: + var subtype := type & 0x0C + var len := type & 0x03 + var bytes := 1 + var value + match subtype: + Type.FLOAT: + if len == Type.L32: + #print(bfr.slice(1, 5)) + value = _decode_float(bfr, 1) + #print(value) + bytes = 4 + elif len == Type.L64: + value = bfr.decode_double(1) + bytes = 8 + Type.INT: + if len == Type.L8: + value = bfr.decode_s8(1) + elif len == Type.L16: + value = bfr.decode_s16(1) + bytes = 2 + elif len == Type.L32: + value = bfr.decode_s32(1) + bytes = 4 + #if value < 0: + #print("possible underflow") + #print(subtype) + #print(len) + #print(value) + return {"value": value, "end": bytes + 1} diff --git a/scripts/utils/json_packer.gd.uid b/scripts/utils/json_packer.gd.uid new file mode 100644 index 0000000..ed34819 --- /dev/null +++ b/scripts/utils/json_packer.gd.uid @@ -0,0 +1 @@ +uid://bxr48ok8lsqt0