Skip to content

Commit 721e530

Browse files
authored
Merge pull request #47 from yuchanns/feat/initial-touch-support
feat(core): support touch
2 parents 19facc9 + 146eef9 commit 721e530

File tree

6 files changed

+258
-3
lines changed

6 files changed

+258
-3
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ The [rules](./rules/Deep_Future_Rules_1_6.pdf) include tables and guidelines for
2222

2323
## How to Play
2424

25+
### Play Online
26+
27+
Click the "Play Online" button above to play the game in your browser.
28+
29+
### Play Locally
30+
2531
- Download [soluna](https://github.com/cloudwu/soluna/releases/tag/nightly) (the game engine runtime).
2632
- Clone the game repository.
2733
- (Optional) For Linux users with Simplified Chinese, install the font `WenQuanYi Micro Hei`.
@@ -40,6 +46,7 @@ soluna.exe main.game
4046
Feedback is welcome. Please include your save file when reporting issues. The save file can be found at:
4147

4248
- Windows: `%USERPROFILE%\Documents\My Games\deepfuture\save.txt`
43-
- macOS | Linux: `~/.local/share/deepfuture/save.txt`
49+
- macOS & Linux: `~/.local/share/deepfuture/save.txt`
50+
- Web: Browser Local Storage
4451

4552
To share your thoughts about the game, please use Discussions.

README.zh-CN.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222

2323
## 如何游玩
2424

25+
### 在线体验
26+
27+
点击上方“在线体验”按钮,即可在浏览器中游玩游戏。
28+
29+
### 本地体验
30+
2531
- 下载 [soluna](https://github.com/cloudwu/soluna/releases/tag/nightly)(游戏引擎运行时框架)。
2632
- 克隆本游戏仓库。
2733
- (可选)Linux 用户如需简体中文,请安装字体 `WenQuanYi Micro Hei`
@@ -40,6 +46,7 @@ soluna.exe main.game
4046
游戏正在持续开发与改进中,欢迎参与测试并提供反馈。遇到问题请带上存档文件提交 issue,存档文件可在以下位置找到:
4147

4248
- Windows: `%USERPROFILE%\Documents\My Games\deepfuture\save.txt`
43-
- macOS | Linux: `~/.local/share/deepfuture/save.txt`
49+
- macOS & Linux: `~/.local/share/deepfuture/save.txt`
50+
- Web: 浏览器 Local Storage
4451

4552
关于游戏内容的交流,请在 Discussions 中发帖。

core/mouse.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ function mouse.focus_region()
143143
return focus.region
144144
end
145145

146+
function mouse.focus_object()
147+
return focus.object
148+
end
149+
146150
function mouse.scroll(delta)
147151
mouse.z = mouse.z + delta
148152
end

core/touch.lua

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
local mouse = require "core.mouse"
2+
3+
local TOUCH_LONG_PRESS_FRAMES <const> = 18
4+
local TOUCH_MOVE_THRESHOLD2 <const> = 12 * 12
5+
-- Regions that require a confirmation tap.
6+
local DOUBLE_TAP_REGIONS <const> = {
7+
hand = true,
8+
neutral = true,
9+
homeworld = true,
10+
colony = true,
11+
discard = true,
12+
deck = true,
13+
card = true,
14+
}
15+
16+
local REGION_CONFIRM_DOUBLE <const> = {
17+
deck = true,
18+
}
19+
20+
local touch = {}
21+
22+
local current_frame = 0
23+
local pending_clear_focus
24+
25+
local function dist2(x1, y1, x2, y2)
26+
local dx = x1 - x2
27+
local dy = y1 - y2
28+
return dx * dx + dy * dy
29+
end
30+
31+
local state = {
32+
active = false,
33+
pressing = false,
34+
double_candidate = false,
35+
require_double = false,
36+
moved = false,
37+
start_frame = 0,
38+
start_x = 0,
39+
start_y = 0,
40+
x = 0,
41+
y = 0,
42+
}
43+
44+
local last_tap = {
45+
require_double = false,
46+
object = nil,
47+
region = nil,
48+
x = 0,
49+
y = 0,
50+
}
51+
52+
local function clear_last_tap()
53+
last_tap.require_double = false
54+
last_tap.object = nil
55+
last_tap.region = nil
56+
last_tap.x = 0
57+
last_tap.y = 0
58+
end
59+
60+
local function store_last_tap(focus_object, region, x, y)
61+
if focus_object then
62+
last_tap.require_double = true
63+
last_tap.object = focus_object
64+
last_tap.region = region
65+
last_tap.x = x
66+
last_tap.y = y
67+
return true
68+
end
69+
if region and REGION_CONFIRM_DOUBLE[region] then
70+
last_tap.require_double = true
71+
last_tap.object = nil
72+
last_tap.region = region
73+
last_tap.x = x
74+
last_tap.y = y
75+
return true
76+
end
77+
clear_last_tap()
78+
end
79+
80+
local function reset_state()
81+
state.active = false
82+
state.pressing = false
83+
state.double_candidate = false
84+
state.require_double = false
85+
state.moved = false
86+
state.start_frame = 0
87+
state.start_x = 0
88+
state.start_y = 0
89+
state.x = 0
90+
state.y = 0
91+
end
92+
93+
local function apply_press()
94+
if state.pressing then
95+
return
96+
end
97+
mouse.mouse_button("left", true)
98+
state.pressing = true
99+
state.double_candidate = false
100+
clear_last_tap()
101+
end
102+
103+
function touch.begin(x, y)
104+
mouse.mouse_move(x, y)
105+
state.active = true
106+
state.pressing = false
107+
state.moved = false
108+
state.start_frame = current_frame
109+
state.start_x = x
110+
state.start_y = y
111+
state.x = x
112+
state.y = y
113+
state.double_candidate = false
114+
state.require_double = false
115+
local region = mouse.focus_region()
116+
if region and DOUBLE_TAP_REGIONS[region] then
117+
state.require_double = true
118+
end
119+
if last_tap.require_double then
120+
local focus_object = mouse.focus_object()
121+
local same_object = focus_object and focus_object == last_tap.object
122+
local same_region = (focus_object == nil and last_tap.object == nil and region ~= nil and REGION_CONFIRM_DOUBLE[region] and region == last_tap.region)
123+
if same_object or same_region then
124+
state.double_candidate = true
125+
else
126+
clear_last_tap()
127+
end
128+
end
129+
end
130+
131+
function touch.moved(x, y)
132+
mouse.mouse_move(x, y)
133+
state.x = x
134+
state.y = y
135+
if not state.active then
136+
return
137+
end
138+
if not state.moved and dist2(x, y, state.start_x, state.start_y) > TOUCH_MOVE_THRESHOLD2 then
139+
state.moved = true
140+
state.double_candidate = false
141+
end
142+
end
143+
144+
function touch.ended(x, y)
145+
mouse.mouse_move(x, y)
146+
state.x = x
147+
state.y = y
148+
local region = mouse.focus_region()
149+
if state.pressing then
150+
mouse.mouse_button("left", false)
151+
clear_last_tap()
152+
reset_state()
153+
return
154+
end
155+
if state.active then
156+
if not state.moved then
157+
if state.require_double then
158+
if state.double_candidate then
159+
mouse.mouse_button("left", true)
160+
mouse.mouse_button("left", false)
161+
clear_last_tap()
162+
pending_clear_focus = true
163+
else
164+
local focus_object = mouse.focus_object()
165+
if not store_last_tap(focus_object, region, state.x, state.y) then
166+
reset_state()
167+
return
168+
end
169+
end
170+
else
171+
mouse.mouse_button("left", true)
172+
mouse.mouse_button("left", false)
173+
clear_last_tap()
174+
end
175+
else
176+
clear_last_tap()
177+
end
178+
end
179+
reset_state()
180+
end
181+
182+
function touch.update(frame)
183+
current_frame = frame
184+
if pending_clear_focus then
185+
mouse.set_focus(nil, nil)
186+
pending_clear_focus = nil
187+
end
188+
if not state.active then
189+
return
190+
end
191+
local region = mouse.focus_region()
192+
local need_double = region and DOUBLE_TAP_REGIONS[region] or false
193+
state.require_double = need_double
194+
if need_double then
195+
local candidate = false
196+
if last_tap.require_double then
197+
local focus_object = mouse.focus_object()
198+
if focus_object and focus_object == last_tap.object then
199+
candidate = true
200+
elseif focus_object == nil and last_tap.object == nil and REGION_CONFIRM_DOUBLE[region] and region == last_tap.region then
201+
candidate = true
202+
end
203+
end
204+
state.double_candidate = candidate
205+
else
206+
state.double_candidate = false
207+
end
208+
if state.pressing or state.moved then
209+
return
210+
end
211+
if frame - state.start_frame >= TOUCH_LONG_PRESS_FRAMES then
212+
apply_press()
213+
end
214+
end
215+
216+
return touch

main.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ local loadsave = require "core.loadsave"
1515
local track = require "gameplay.track"
1616
local vbutton = require "visual.button"
1717
local mouse = require "core.mouse"
18+
local touch = require "core.touch"
1819
local keyboard = require "core.keyboard"
1920
local text = require "soluna.text"
2021
local setting =require "core.setting"
@@ -155,10 +156,23 @@ function callback.mouse_scroll(x, y)
155156
mouse.scroll(x)
156157
end
157158

159+
function callback.touch_begin(x, y)
160+
touch.begin(x, y)
161+
end
162+
163+
function callback.touch_end(x, y)
164+
touch.ended(x, y)
165+
end
166+
167+
function callback.touch_moved(x, y)
168+
touch.moved(x, y)
169+
end
170+
158171
function callback.frame(count)
159172
local x, y = mouse.sync(count)
160173
vdesktop.set_mouse(x, y)
161174
flow.update()
175+
touch.update(count)
162176
-- todo : don't flush card here
163177
vdesktop.card_count(card.count "draw", card.count "discard", card.seen())
164178
map.update()

visual/region.lua

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,14 @@ local function draw_card(obj)
190190
end
191191

192192
local function test_card(obj, mx, my)
193-
return obj._move == nil and vcard.test(mx, my, obj.x, obj.y, obj.scale)
193+
if obj._move then
194+
return
195+
end
196+
local x, y, scale = obj.x, obj.y, obj.scale
197+
if obj._focus_time then
198+
x, y, scale = focus_args(obj)
199+
end
200+
return vcard.test(mx, my, x, y, scale)
194201
end
195202

196203
function region:transfer(card, new_region)

0 commit comments

Comments
 (0)