feat: base

Signed-off-by: Avior <github@avior.me>
This commit is contained in:
2025-07-15 00:46:55 +02:00
commit bcfc8fbc81
20 changed files with 498 additions and 0 deletions

4
.editorconfig Normal file
View File

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

13
.gitattributes vendored Normal file
View File

@@ -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

3
.gitignore vendored Normal file
View File

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

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: 994 B

37
icon.svg.import Normal file
View File

@@ -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

20
project.godot Normal file
View File

@@ -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"

View File

@@ -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

View File

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

View File

@@ -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"

View File

@@ -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"

View File

@@ -0,0 +1 @@
uid://5pqr71r8dv5q

View File

@@ -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"

View File

@@ -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"

35
scenes/entrypoint.tscn Normal file
View File

@@ -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)

View File

@@ -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

View File

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

View File

@@ -0,0 +1,5 @@
extends Resource
class_name Save
@export var money: int
@export var day: int

View File

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

View File

@@ -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}

View File

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