mulgogi 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mulgogi-0.1.0/.gitignore +218 -0
- mulgogi-0.1.0/PKG-INFO +57 -0
- mulgogi-0.1.0/README.md +33 -0
- mulgogi-0.1.0/mulgogi/__init__.py +0 -0
- mulgogi-0.1.0/mulgogi/__main__.py +3 -0
- mulgogi-0.1.0/mulgogi/main.py +289 -0
- mulgogi-0.1.0/pyproject.toml +38 -0
- mulgogi-0.1.0/requirements.txt +1 -0
- mulgogi-0.1.0/scripts/generate-homebrew-formula.py +30 -0
mulgogi-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
mulgogi-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mulgogi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A fishing game in your terminal
|
|
5
|
+
Project-URL: Homepage, https://github.com/justart-dev/mulgogi
|
|
6
|
+
Project-URL: Repository, https://github.com/justart-dev/mulgogi
|
|
7
|
+
Project-URL: Issues, https://github.com/justart-dev/mulgogi/issues
|
|
8
|
+
Author-email: justart-dev <justart-dev@users.noreply.github.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: cli,fishing,game,terminal,tui
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Games/Entertainment
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: textual>=0.70.0
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# mulgogi 🎣
|
|
26
|
+
|
|
27
|
+
A fishing game in your terminal.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install mulgogi
|
|
31
|
+
mulgogi
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Controls
|
|
35
|
+
|
|
36
|
+
- `←` `→`: adjust rod angle (cosmetic for now)
|
|
37
|
+
- `Space`: cast / hook / reel
|
|
38
|
+
- `q`: quit
|
|
39
|
+
|
|
40
|
+
## Development
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/justart-dev/mulgogi.git
|
|
44
|
+
cd mulgogi
|
|
45
|
+
python -m venv .venv
|
|
46
|
+
source .venv/bin/activate
|
|
47
|
+
pip install -e .
|
|
48
|
+
mulgogi
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Homebrew (macOS/Linux)
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
brew tap justart-dev/mulgogi
|
|
55
|
+
brew install mulgogi
|
|
56
|
+
mulgogi
|
|
57
|
+
```
|
mulgogi-0.1.0/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# mulgogi 🎣
|
|
2
|
+
|
|
3
|
+
A fishing game in your terminal.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install mulgogi
|
|
7
|
+
mulgogi
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Controls
|
|
11
|
+
|
|
12
|
+
- `←` `→`: adjust rod angle (cosmetic for now)
|
|
13
|
+
- `Space`: cast / hook / reel
|
|
14
|
+
- `q`: quit
|
|
15
|
+
|
|
16
|
+
## Development
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
git clone https://github.com/justart-dev/mulgogi.git
|
|
20
|
+
cd mulgogi
|
|
21
|
+
python -m venv .venv
|
|
22
|
+
source .venv/bin/activate
|
|
23
|
+
pip install -e .
|
|
24
|
+
mulgogi
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Homebrew (macOS/Linux)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
brew tap justart-dev/mulgogi
|
|
31
|
+
brew install mulgogi
|
|
32
|
+
mulgogi
|
|
33
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import random
|
|
3
|
+
import time
|
|
4
|
+
import argparse
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from textual.app import App, ComposeResult
|
|
8
|
+
from textual.containers import Center, Middle, Vertical
|
|
9
|
+
from textual.reactive import reactive
|
|
10
|
+
from textual.widgets import Footer, Header, Static
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class Fish:
|
|
15
|
+
name: str
|
|
16
|
+
ascii: str
|
|
17
|
+
min_weight: float
|
|
18
|
+
max_weight: float
|
|
19
|
+
difficulty: int # 1-5
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
FISH_POOL = [
|
|
23
|
+
Fish("붕어", " <\\><(_)>\n<))))><", 0.3, 1.2, 1),
|
|
24
|
+
Fish("잉어", " __\n__/ o\\__\n \____/", 1.0, 4.0, 2),
|
|
25
|
+
Fish("송어", " _ __\n>(___(_)>\n ~~~~~~~", 0.8, 2.5, 2),
|
|
26
|
+
Fish("메기", " __\n ( \\___\n \_____)>", 1.5, 6.0, 3),
|
|
27
|
+
Fish("황금잉어", " \*/\n*<(o o)>*\n /| |\\", 2.0, 5.0, 4),
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SplashWidget(Static):
|
|
32
|
+
"""물결 애니메이션 위젯"""
|
|
33
|
+
|
|
34
|
+
frame = reactive(0)
|
|
35
|
+
|
|
36
|
+
def __init__(self, **kwargs):
|
|
37
|
+
super().__init__("", **kwargs)
|
|
38
|
+
self._timer = None
|
|
39
|
+
self._running = False
|
|
40
|
+
|
|
41
|
+
def on_mount(self):
|
|
42
|
+
self._running = True
|
|
43
|
+
self._timer = self.set_interval(0.15, self._tick)
|
|
44
|
+
|
|
45
|
+
def _tick(self):
|
|
46
|
+
if not self._running:
|
|
47
|
+
return
|
|
48
|
+
self.frame += 1
|
|
49
|
+
waves = ["~", "-", "≈", "∿", "~"]
|
|
50
|
+
lines = []
|
|
51
|
+
for row in range(5):
|
|
52
|
+
line = ""
|
|
53
|
+
for col in range(40):
|
|
54
|
+
idx = (self.frame + row * 3 + col) % len(waves)
|
|
55
|
+
line += waves[idx]
|
|
56
|
+
lines.append(line)
|
|
57
|
+
self.update("\n".join(lines))
|
|
58
|
+
|
|
59
|
+
def stop(self):
|
|
60
|
+
self._running = False
|
|
61
|
+
if self._timer:
|
|
62
|
+
self._timer.stop()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class AngleWidget(Static):
|
|
66
|
+
"""낚싯대 각도 표시 (장식용)"""
|
|
67
|
+
|
|
68
|
+
angle = reactive(45)
|
|
69
|
+
|
|
70
|
+
def render(self):
|
|
71
|
+
rad = self.angle * 3.14159 / 180
|
|
72
|
+
length = 10
|
|
73
|
+
dx = int(round(length * 0.7))
|
|
74
|
+
dy = int(round(length * 0.7))
|
|
75
|
+
# 각도를 라인으로 그리기
|
|
76
|
+
x, y = 15, 5
|
|
77
|
+
end_x = x + int(round(length * 0.7))
|
|
78
|
+
end_y = y - int(round(length * 0.7))
|
|
79
|
+
grid = [[" " for _ in range(40)] for _ in range(8)]
|
|
80
|
+
# 대각선
|
|
81
|
+
for i in range(length):
|
|
82
|
+
px = x + int(round(i * 0.7))
|
|
83
|
+
py = y - int(round(i * 0.7))
|
|
84
|
+
if 0 <= px < 40 and 0 <= py < 8:
|
|
85
|
+
grid[py][px] = "/" if i < length - 1 else "@"
|
|
86
|
+
# 물고기 잡는 사람
|
|
87
|
+
grid[5][12] = "O"
|
|
88
|
+
lines = ["".join(row) for row in grid]
|
|
89
|
+
lines_joined = "\n".join(lines)
|
|
90
|
+
return f"{lines_joined}\n\n각도: {self.angle}° (장식용)"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class ReelWidget(Static):
|
|
94
|
+
"""릴 미니게임 게이지"""
|
|
95
|
+
|
|
96
|
+
position = reactive(0.0)
|
|
97
|
+
direction = reactive(1)
|
|
98
|
+
|
|
99
|
+
def render(self):
|
|
100
|
+
bar_width = 30
|
|
101
|
+
pos = int(self.position * bar_width)
|
|
102
|
+
pos = max(0, min(bar_width - 1, pos))
|
|
103
|
+
bar = ["-"] * bar_width
|
|
104
|
+
bar[pos] = "O"
|
|
105
|
+
return f"타이밍! Space를 눌러 멈추세요\n[{''.join(bar)}]"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class MulgogiApp(App):
|
|
109
|
+
"""mulgogi 메인 앱"""
|
|
110
|
+
|
|
111
|
+
CSS = """
|
|
112
|
+
Screen { align: center middle; }
|
|
113
|
+
#main { width: 80; height: auto; border: solid green; padding: 1 2; }
|
|
114
|
+
Static { text-align: center; }
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
BINDINGS = [
|
|
118
|
+
("q", "quit", "종료"),
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
def __init__(self):
|
|
122
|
+
super().__init__()
|
|
123
|
+
self.state = "menu"
|
|
124
|
+
self.angle = 45
|
|
125
|
+
self.bite_timer = None
|
|
126
|
+
self.reel_timer = None
|
|
127
|
+
self.reel_pos = 0.0
|
|
128
|
+
self.reel_dir = 1
|
|
129
|
+
self.reel_speed = 0.06
|
|
130
|
+
self.catch_result = None
|
|
131
|
+
|
|
132
|
+
def compose(self) -> ComposeResult:
|
|
133
|
+
yield Header(show_clock=True)
|
|
134
|
+
with Vertical(id="main"):
|
|
135
|
+
yield Static("mulgogi 🎣", id="title")
|
|
136
|
+
yield Static("방향키로 각도 조절, Space로 캐스트\nq를 누륵 종료", id="info")
|
|
137
|
+
yield AngleWidget(id="angle")
|
|
138
|
+
yield SplashWidget(id="splash")
|
|
139
|
+
yield ReelWidget(id="reel")
|
|
140
|
+
yield Static("", id="result")
|
|
141
|
+
yield Footer()
|
|
142
|
+
|
|
143
|
+
def on_mount(self):
|
|
144
|
+
self.query_one("#splash", SplashWidget).display = False
|
|
145
|
+
self.query_one("#reel", ReelWidget).display = False
|
|
146
|
+
self.query_one("#result", Static).update(
|
|
147
|
+
"[b]mulgogi[/b]\n터미널에서 즐기는 낚시 게임\n\n"
|
|
148
|
+
"[green]Space[/green]: 캐스트 / 입질 / 릴\n"
|
|
149
|
+
"[green]← →[/green]: 각도 조절 (장식용)\n"
|
|
150
|
+
"[green]q[/green]: 종료"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def on_key(self, event):
|
|
154
|
+
key = event.key
|
|
155
|
+
|
|
156
|
+
if key == "q":
|
|
157
|
+
self.exit()
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
if self.state == "menu":
|
|
161
|
+
if key == "left":
|
|
162
|
+
self.angle = max(10, self.angle - 5)
|
|
163
|
+
self.query_one("#angle", AngleWidget).angle = self.angle
|
|
164
|
+
elif key == "right":
|
|
165
|
+
self.angle = min(80, self.angle + 5)
|
|
166
|
+
self.query_one("#angle", AngleWidget).angle = self.angle
|
|
167
|
+
elif key == "space":
|
|
168
|
+
self.start_cast()
|
|
169
|
+
|
|
170
|
+
elif self.state == "waiting":
|
|
171
|
+
if key == "space":
|
|
172
|
+
# 입질 전에는 무시하거나 패널티
|
|
173
|
+
pass
|
|
174
|
+
|
|
175
|
+
elif self.state == "biting":
|
|
176
|
+
if key == "space":
|
|
177
|
+
self.start_reel()
|
|
178
|
+
|
|
179
|
+
elif self.state == "reeling":
|
|
180
|
+
if key == "space":
|
|
181
|
+
self.stop_reel()
|
|
182
|
+
|
|
183
|
+
elif self.state == "result":
|
|
184
|
+
if key == "space":
|
|
185
|
+
self.reset_to_menu()
|
|
186
|
+
|
|
187
|
+
def start_cast(self):
|
|
188
|
+
self.state = "waiting"
|
|
189
|
+
self.query_one("#info", Static).update("물고기가 물기를 기다리는 중...")
|
|
190
|
+
self.query_one("#angle", AngleWidget).display = False
|
|
191
|
+
self.query_one("#splash", SplashWidget).display = True
|
|
192
|
+
self.query_one("#result", Static).update("")
|
|
193
|
+
# 랜덤 시간 후 입질
|
|
194
|
+
delay = random.uniform(2.0, 5.0)
|
|
195
|
+
self.bite_timer = self.set_timer(delay, self.trigger_bite)
|
|
196
|
+
|
|
197
|
+
def trigger_bite(self):
|
|
198
|
+
if self.state != "waiting":
|
|
199
|
+
return
|
|
200
|
+
self.state = "biting"
|
|
201
|
+
self.query_one("#info", Static).update("[bold red]!! 입질 !!\nSpace를 눌러 릴을 걸어라![/]")
|
|
202
|
+
# 1.5초 안에 못 누른 놓치기
|
|
203
|
+
self.bite_timer = self.set_timer(1.5, self.miss_bite)
|
|
204
|
+
|
|
205
|
+
def miss_bite(self):
|
|
206
|
+
if self.state != "biting":
|
|
207
|
+
return
|
|
208
|
+
self.state = "result"
|
|
209
|
+
self.query_one("#splash", SplashWidget).stop()
|
|
210
|
+
self.query_one("#splash", SplashWidget).display = False
|
|
211
|
+
self.query_one("#info", Static).update("입질을 놓쳤다...")
|
|
212
|
+
self.query_one("#result", Static).update("미끼만 날아갔습니다.\nSpace를 눌러 계속")
|
|
213
|
+
|
|
214
|
+
def start_reel(self):
|
|
215
|
+
if self.bite_timer:
|
|
216
|
+
self.bite_timer.stop()
|
|
217
|
+
self.state = "reeling"
|
|
218
|
+
self.query_one("#info", Static).update("[bold cyan]릴을 감는 중... Space로 멈춰![/]")
|
|
219
|
+
self.reel_pos = 0.0
|
|
220
|
+
self.reel_dir = 1
|
|
221
|
+
self.reel_speed = random.uniform(0.04, 0.10)
|
|
222
|
+
self.reel_timer = self.set_interval(0.05, self.update_reel)
|
|
223
|
+
|
|
224
|
+
def update_reel(self):
|
|
225
|
+
self.reel_pos += self.reel_dir * self.reel_speed
|
|
226
|
+
if self.reel_pos >= 1.0:
|
|
227
|
+
self.reel_pos = 1.0
|
|
228
|
+
self.reel_dir = -1
|
|
229
|
+
elif self.reel_pos <= 0.0:
|
|
230
|
+
self.reel_pos = 0.0
|
|
231
|
+
self.reel_dir = 1
|
|
232
|
+
widget = self.query_one("#reel", ReelWidget)
|
|
233
|
+
widget.position = self.reel_pos
|
|
234
|
+
widget.display = True
|
|
235
|
+
|
|
236
|
+
def stop_reel(self):
|
|
237
|
+
if self.reel_timer:
|
|
238
|
+
self.reel_timer.stop()
|
|
239
|
+
self.query_one("#reel", ReelWidget).display = False
|
|
240
|
+
self.query_one("#splash", SplashWidget).stop()
|
|
241
|
+
self.query_one("#splash", SplashWidget).display = False
|
|
242
|
+
|
|
243
|
+
# 중앙 근처(0.4~0.6)면 성공
|
|
244
|
+
if 0.4 <= self.reel_pos <= 0.6:
|
|
245
|
+
self.catch_fish()
|
|
246
|
+
else:
|
|
247
|
+
self.lose_fish()
|
|
248
|
+
|
|
249
|
+
def catch_fish(self):
|
|
250
|
+
self.state = "result"
|
|
251
|
+
fish = random.choice(FISH_POOL)
|
|
252
|
+
weight = round(random.uniform(fish.min_weight, fish.max_weight), 2)
|
|
253
|
+
self.query_one("#info", Static).update(f"[bold green]{fish.name}을(를) 잡았다![/]")
|
|
254
|
+
self.query_one("#result", Static).update(
|
|
255
|
+
f"{fish.ascii}\n\n"
|
|
256
|
+
f"무게: {weight}kg\n"
|
|
257
|
+
f"난이도: {'★' * fish.difficulty}{'☆' * (5 - fish.difficulty)}\n\n"
|
|
258
|
+
f"Space를 눌러 계속"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
def lose_fish(self):
|
|
262
|
+
self.state = "result"
|
|
263
|
+
self.query_one("#info", Static).update("[bold red]물고기가 도망갔다...[/]")
|
|
264
|
+
self.query_one("#result", Static).update("타이밍이 안 맞았습니다.\nSpace를 눌러 계속")
|
|
265
|
+
|
|
266
|
+
def reset_to_menu(self):
|
|
267
|
+
self.state = "menu"
|
|
268
|
+
self.query_one("#angle", AngleWidget).display = True
|
|
269
|
+
self.query_one("#splash", SplashWidget).display = False
|
|
270
|
+
self.query_one("#reel", ReelWidget).display = False
|
|
271
|
+
self.query_one("#info", Static).update("방향키로 각도 조절, Space로 캐스트\nq를 누륵 종료")
|
|
272
|
+
self.query_one("#result", Static).update(
|
|
273
|
+
"[b]mulgogi[/b]\n터미널에서 즐기는 낚시 게임\n\n"
|
|
274
|
+
"[green]Space[/green]: 캐스트 / 입질 / 릴\n"
|
|
275
|
+
"[green]← →[/green]: 각도 조절 (장식용)\n"
|
|
276
|
+
"[green]q[/green]: 종료"
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def main():
|
|
281
|
+
parser = argparse.ArgumentParser(prog="mulgogi", description="A fishing game in your terminal")
|
|
282
|
+
parser.add_argument("--version", action="version", version="%(prog)s 0.1.0")
|
|
283
|
+
parser.parse_args()
|
|
284
|
+
app = MulgogiApp()
|
|
285
|
+
app.run()
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
if __name__ == "__main__":
|
|
289
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mulgogi"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A fishing game in your terminal"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "justart-dev", email = "justart-dev@users.noreply.github.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["cli", "game", "fishing", "terminal", "tui"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: End Users/Desktop",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Topic :: Games/Entertainment",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"textual>=0.70.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
mulgogi = "mulgogi.main:main"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/justart-dev/mulgogi"
|
|
37
|
+
Repository = "https://github.com/justart-dev/mulgogi"
|
|
38
|
+
Issues = "https://github.com/justart-dev/mulgogi/issues"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
textual>=0.70.0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PyPI에 mulgogi를 올린 후 실행하여 Homebrew formula의 resource 블록을 생성합니다.
|
|
4
|
+
|
|
5
|
+
pip install homebrew-pypi-poet
|
|
6
|
+
python scripts/generate-homebrew-formula.py
|
|
7
|
+
|
|
8
|
+
출력된 내용을 homebrew-mulgogi/Formula/mulgogi.rb의 resource 영역에 붙여넣으세요.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main():
|
|
16
|
+
try:
|
|
17
|
+
output = subprocess.check_output(["poet", "mulgogi"], text=True)
|
|
18
|
+
except FileNotFoundError:
|
|
19
|
+
print("homebrew-pypi-poet가 설치되지 않았습니다.", file=sys.stderr)
|
|
20
|
+
print(" pip install homebrew-pypi-poet", file=sys.stderr)
|
|
21
|
+
sys.exit(1)
|
|
22
|
+
except subprocess.CalledProcessError as e:
|
|
23
|
+
print(f"실패: {e}", file=sys.stderr)
|
|
24
|
+
sys.exit(1)
|
|
25
|
+
|
|
26
|
+
print(output)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if __name__ == "__main__":
|
|
30
|
+
main()
|