바탕화면 바닷속 방치형 위젯 첫 커밋
화면 아래 가장자리에 가로로 깔리는 Godot 4.7 위젯. 거품을 모아 바다를 꾸미고, 꾸밀수록 거품이 더 빨리 모인다. - 도킹 창: 투명·테두리 없음, 화면 폭 전체, 위/아래 가장자리 스냅, 높이 프리셋, 클릭 통과(모서리로 복귀), DPI 배율 보정 - 3/4 부감 격자: 구역당 9x6, 전체 45x6 = 270칸. 바닥은 위에서, 물체는 서 있는 모습으로 그리고 Y 정렬로 앞뒤를 가린다 - 구역 5개(얕은 바다 -> 열수구): 앞 구역을 꾸며야 다음이 열린다 - 칸이 곧 한계인 경제: 배치/이동/치우기(절반 환불), 여러 칸짜리 장식 - 저장: 30초 자동 저장, 백업 한 세대, 8시간 상한 오프라인 보상, 격자 이전 형식 저장본 자동 이전 - 스모크 테스트 60여 개 (test.ps1) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
class_name Korean
|
||||
## 한글 조사 처리. 앞 글자의 받침 유무에 따라 을/를, 이/가, 은/는이 갈린다.
|
||||
## 구역 이름을 문장에 끼워 넣을 때 "켈프 숲를" 같은 어색한 말이 나오지 않게 한다.
|
||||
|
||||
const HANGUL_FIRST := 0xAC00
|
||||
const HANGUL_LAST := 0xD7A3
|
||||
|
||||
|
||||
## 마지막 글자에 받침이 있는지. 한글 음절이 아니면 없는 것으로 친다.
|
||||
static func has_final(word: String) -> bool:
|
||||
if word.is_empty():
|
||||
return false
|
||||
var c := word.unicode_at(word.length() - 1)
|
||||
if c < HANGUL_FIRST or c > HANGUL_LAST:
|
||||
return false
|
||||
return (c - HANGUL_FIRST) % 28 != 0
|
||||
|
||||
|
||||
static func josa(word: String, with_final: String, without_final: String) -> String:
|
||||
return word + (with_final if has_final(word) else without_final)
|
||||
|
||||
|
||||
static func eul(word: String) -> String:
|
||||
return josa(word, "을", "를")
|
||||
|
||||
|
||||
static func i(word: String) -> String:
|
||||
return josa(word, "이", "가")
|
||||
|
||||
|
||||
static func eun(word: String) -> String:
|
||||
return josa(word, "은", "는")
|
||||
@@ -0,0 +1 @@
|
||||
uid://bo2o0ge2vbhp1
|
||||
@@ -0,0 +1,40 @@
|
||||
class_name NumberFormat
|
||||
## 방치형 게임 특유의 큰 수를 짧게 표기한다. 1234 -> "1.23K"
|
||||
|
||||
const SUFFIXES := ["", "K", "M", "B", "T", "aa", "ab", "ac", "ad", "ae", "af", "ag"]
|
||||
|
||||
|
||||
static func short(value: float) -> String:
|
||||
if value < 0.0:
|
||||
return "-" + short(-value)
|
||||
if value < 1000.0:
|
||||
# 1 미만은 소수점을 보여줘야 초당 생산량이 0으로 보이지 않는다.
|
||||
if value < 10.0 and value != floor(value):
|
||||
return "%.1f" % value
|
||||
return "%d" % int(value)
|
||||
|
||||
var tier := 0
|
||||
var v := value
|
||||
while v >= 1000.0 and tier < SUFFIXES.size() - 1:
|
||||
v /= 1000.0
|
||||
tier += 1
|
||||
|
||||
if v < 10.0:
|
||||
return "%.2f%s" % [v, SUFFIXES[tier]]
|
||||
if v < 100.0:
|
||||
return "%.1f%s" % [v, SUFFIXES[tier]]
|
||||
return "%d%s" % [int(v), SUFFIXES[tier]]
|
||||
|
||||
|
||||
## 경과 시간을 "3시간 12분" 형태로.
|
||||
static func duration(seconds: int) -> String:
|
||||
if seconds < 60:
|
||||
return "%d초" % seconds
|
||||
var m := seconds / 60
|
||||
if m < 60:
|
||||
return "%d분" % m
|
||||
var h := m / 60
|
||||
var rem := m % 60
|
||||
if h < 24:
|
||||
return "%d시간 %d분" % [h, rem] if rem > 0 else "%d시간" % h
|
||||
return "%d일 %d시간" % [h / 24, h % 24]
|
||||
@@ -0,0 +1 @@
|
||||
uid://crfj2c2detfxk
|
||||
@@ -0,0 +1,64 @@
|
||||
class_name UIStyle
|
||||
## 위젯 UI의 색과 스타일박스를 한곳에서 정의한다.
|
||||
## 테마 리소스(.tres) 대신 코드로 두어, 값 하나 바꾸면 전체가 따라오게 했다.
|
||||
|
||||
const INK := Color(0.96, 0.99, 1.0)
|
||||
const INK_DIM := Color(0.78, 0.88, 0.94)
|
||||
const ACCENT := Color(0.62, 0.92, 1.0)
|
||||
const GOLD := Color(1.0, 0.85, 0.45)
|
||||
const PANEL_BG := Color(0.03, 0.10, 0.18, 0.82)
|
||||
const ROW_BG := Color(1, 1, 1, 0.06)
|
||||
const ROW_BG_HOVER := Color(1, 1, 1, 0.13)
|
||||
const ROW_BG_DISABLED := Color(0, 0, 0, 0.18)
|
||||
|
||||
|
||||
static func panel(bg: Color = PANEL_BG, radius: int = 14) -> StyleBoxFlat:
|
||||
var sb := StyleBoxFlat.new()
|
||||
sb.bg_color = bg
|
||||
sb.set_corner_radius_all(radius)
|
||||
sb.content_margin_left = 12
|
||||
sb.content_margin_right = 12
|
||||
sb.content_margin_top = 10
|
||||
sb.content_margin_bottom = 10
|
||||
return sb
|
||||
|
||||
|
||||
static func row(bg: Color, radius: int = 10) -> StyleBoxFlat:
|
||||
var sb := StyleBoxFlat.new()
|
||||
sb.bg_color = bg
|
||||
sb.set_corner_radius_all(radius)
|
||||
sb.content_margin_left = 10
|
||||
sb.content_margin_right = 10
|
||||
sb.content_margin_top = 8
|
||||
sb.content_margin_bottom = 8
|
||||
return sb
|
||||
|
||||
|
||||
## 버튼의 네 가지 상태 스타일을 한 번에 입힌다.
|
||||
static func style_button(b: Button, radius: int = 10) -> void:
|
||||
b.add_theme_stylebox_override("normal", row(ROW_BG, radius))
|
||||
b.add_theme_stylebox_override("hover", row(ROW_BG_HOVER, radius))
|
||||
b.add_theme_stylebox_override("pressed", row(ROW_BG_HOVER, radius))
|
||||
b.add_theme_stylebox_override("disabled", row(ROW_BG_DISABLED, radius))
|
||||
b.add_theme_stylebox_override("focus", StyleBoxEmpty.new())
|
||||
b.add_theme_color_override("font_color", INK)
|
||||
b.add_theme_color_override("font_hover_color", INK)
|
||||
b.add_theme_color_override("font_pressed_color", ACCENT)
|
||||
b.add_theme_color_override("font_disabled_color", Color(0.6, 0.66, 0.72, 0.7))
|
||||
|
||||
|
||||
static func label(l: Label, size: int, color: Color = INK) -> void:
|
||||
l.add_theme_font_size_override("font_size", size)
|
||||
l.add_theme_color_override("font_color", color)
|
||||
# 물 위에서도 글씨가 읽히도록 옅은 외곽선을 준다.
|
||||
l.add_theme_color_override("font_shadow_color", Color(0, 0.05, 0.1, 0.55))
|
||||
l.add_theme_constant_override("shadow_offset_x", 0)
|
||||
l.add_theme_constant_override("shadow_offset_y", 1)
|
||||
l.add_theme_constant_override("shadow_outline_size", 2)
|
||||
|
||||
|
||||
## 잠긴 구역을 덮는 어두운 베일.
|
||||
static func veil(alpha: float) -> StyleBoxFlat:
|
||||
var sb := StyleBoxFlat.new()
|
||||
sb.bg_color = Color(0.01, 0.03, 0.07, alpha)
|
||||
return sb
|
||||
@@ -0,0 +1 @@
|
||||
uid://b7lu7lfy32jd8
|
||||
Reference in New Issue
Block a user