Skip to content

Commit 1b08956

Browse files
authored
Mobile controls (#262)
* added readme pt-pt * added url/querystring deps and fix chat pos/scale url and querystring were missing in node_modules. chat scale option wasn't implemented and chat input was on top instead of bottom. * added bot version text field and guiScale for small screens text field to choose bot version. gui scale changes on small screens (slider takes no effect then). Removed unused images. * added mobile controls * fixed bot and chat * mobile controls only appear on mobile or if forced * lint fix
1 parent 8cbc95b commit 1b08956

File tree

6 files changed

+284
-30
lines changed

6 files changed

+284
-30
lines changed

lib/chat.js

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,40 @@ class ChatBox extends LitElement {
127127
`
128128
}
129129

130+
constructor () {
131+
super()
132+
this.chatHistoryPos = 0
133+
this.chatHistory = []
134+
}
135+
136+
enableChat (isCommand) {
137+
const chat = this.shadowRoot.querySelector('#chat')
138+
const chatInput = this.shadowRoot.querySelector('#chatinput')
139+
140+
// Set inChat value
141+
this.inChat = true
142+
// Exit the pointer lock
143+
document.exitPointerLock()
144+
// Show chat input
145+
chatInput.style.display = 'block'
146+
// Show extended chat history
147+
chat.style.maxHeight = 'var(--chatHeight)'
148+
chat.scrollTop = chat.scrollHeight // Stay bottom of the list
149+
if (isCommand) { // handle commands
150+
chatInput.value = '/'
151+
}
152+
// Focus element
153+
chatInput.focus()
154+
this.chatHistoryPos = this.chatHistory.length
155+
document.querySelector('#hud').shadowRoot.querySelector('#chat').shadowRoot.querySelectorAll('.chat-message').forEach(e => e.classList.add('chat-message-chat-opened'))
156+
}
157+
130158
init (client, renderer) {
131159
this.inChat = false
132160
const chat = this.shadowRoot.querySelector('#chat')
133161
const gameMenu = document.getElementById('pause-screen')
134162
const chatInput = this.shadowRoot.querySelector('#chatinput')
135163

136-
const chatHistory = []
137-
let chatHistoryPos = 0
138-
139164
renderer.domElement.requestPointerLock = renderer.domElement.requestPointerLock ||
140165
renderer.domElement.mozRequestPointerLock ||
141166
renderer.domElement.webkitRequestPointerLock
@@ -153,11 +178,11 @@ class ChatBox extends LitElement {
153178
if (e.code === 'Escape') {
154179
disableChat()
155180
} else if (e.keyCode === 38) {
156-
if (chatHistoryPos === 0) return
157-
chatInput.value = chatHistory[--chatHistoryPos] !== undefined ? chatHistory[chatHistoryPos] : ''
181+
if (this.chatHistoryPos === 0) return
182+
chatInput.value = this.chatHistory[--this.chatHistoryPos] !== undefined ? this.chatHistory[this.chatHistoryPos] : ''
158183
} else if (e.keyCode === 40) {
159-
if (chatHistoryPos === chatHistory.length) return
160-
chatInput.value = chatHistory[++chatHistoryPos] !== undefined ? chatHistory[chatHistoryPos] : ''
184+
if (this.chatHistoryPos === this.chatHistory.length) return
185+
chatInput.value = this.chatHistory[++this.chatHistoryPos] !== undefined ? this.chatHistory[this.chatHistoryPos] : ''
161186
}
162187
})
163188

@@ -172,10 +197,10 @@ class ChatBox extends LitElement {
172197
if (e.code === km.key) {
173198
switch (km.defaultKey) {
174199
case 'KeyT':
175-
setTimeout(() => enableChat(false), 0)
200+
setTimeout(() => this.enableChat(false), 0)
176201
break
177202
case 'Slash':
178-
setTimeout(() => enableChat(true), 0)
203+
setTimeout(() => this.enableChat(true), 0)
179204
break
180205
}
181206
}
@@ -187,7 +212,7 @@ class ChatBox extends LitElement {
187212
if (!self.inChat) return
188213
e.stopPropagation()
189214
if (e.code === 'Enter') {
190-
chatHistory.push(chatInput.value)
215+
this.chatHistory.push(chatInput.value)
191216
client.write('chat', { message: chatInput.value })
192217
disableChat()
193218
}
@@ -204,24 +229,6 @@ class ChatBox extends LitElement {
204229
hideChat();
205230
}
206231
}); */
207-
function enableChat (isCommand) {
208-
// Set inChat value
209-
self.inChat = true
210-
// Exit the pointer lock
211-
document.exitPointerLock()
212-
// Show chat input
213-
chatInput.style.display = 'block'
214-
// Show extended chat history
215-
chat.style.maxHeight = 'calc(var(--chatHeight) * var(--chatScale))'
216-
chat.scrollTop = chat.scrollHeight // Stay bottom of the list
217-
if (isCommand) { // handle commands
218-
chatInput.value = '/'
219-
}
220-
// Focus element
221-
chatInput.focus()
222-
chatHistoryPos = chatHistory.length
223-
document.querySelector('#hud').shadowRoot.querySelector('#chat').shadowRoot.querySelectorAll('.chat-message').forEach(e => e.classList.add('chat-message-chat-opened'))
224-
}
225232

226233
function disableChat () {
227234
self.inChat = false

lib/menus/components/common.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ const commonCss = css`
4242
}
4343
`
4444

45+
/** @returns {boolean} */
46+
function isMobile () {
47+
const m = require('ismobilejs').default()
48+
return m.any
49+
}
50+
4551
/**
4652
* @param {string} url
4753
*/
@@ -60,6 +66,7 @@ function displayScreen (prev, next) {
6066

6167
export {
6268
commonCss,
69+
isMobile,
6370
openURL,
6471
displayScreen
6572
}

lib/menus/hud.js

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const { LitElement, html, css } = require('lit')
2+
const { isMobile } = require('./components/common')
23

34
class Hud extends LitElement {
45
static get styles () {
@@ -55,9 +56,142 @@ class Hud extends LitElement {
5556
background-size: 256px;
5657
background-position-y: -69px;
5758
}
59+
60+
.mobile-top-btns {
61+
display: none;
62+
flex-direction: row;
63+
position: absolute;
64+
top: 0;
65+
left: 50%;
66+
transform: translate(-50%);
67+
gap: 0 1px;
68+
z-index: 20;
69+
}
70+
71+
.pause-btn,
72+
.chat-btn {
73+
border: none;
74+
outline: none;
75+
width: 18px;
76+
height: 18px;
77+
background-image: url('extra-textures/gui.png');
78+
background-size: 256px;
79+
background-position-x: -200px;
80+
background-position-y: -64px;
81+
}
82+
83+
.chat-btn {
84+
background-position-y: -82px;
85+
}
86+
87+
.mobile-control-forward,
88+
.mobile-control-back,
89+
.mobile-control-left,
90+
.mobile-control-sneak,
91+
.mobile-control-jump,
92+
.mobile-control-right {
93+
position: absolute;
94+
width: 22px;
95+
height: 22px;
96+
background-image: url('extra-textures/gui.png');
97+
background-size: 256px;
98+
border: none;
99+
outline: none;
100+
}
101+
102+
.mobile-control-forward:active,
103+
.mobile-control-back:active,
104+
.mobile-control-sneak:active,
105+
.mobile-control-left:active,
106+
.mobile-control-jump:active,
107+
.mobile-control-right:active {
108+
filter: brightness(85%);
109+
}
110+
111+
.mobile-control-forward {
112+
top: 0;
113+
left: 50%;
114+
transform: translate(-50%);
115+
background-position: -2px -109px;
116+
}
117+
118+
.mobile-control-back {
119+
bottom: 0;
120+
left: 50%;
121+
transform: translate(-50%);
122+
background-position: -54px -109px;
123+
}
124+
125+
.mobile-control-left {
126+
top: 50%;
127+
left: 0;
128+
transform: translate(0, -50%);
129+
background-position: -28px -109px;
130+
}
131+
132+
.mobile-control-right {
133+
top: 50%;
134+
right: 0;
135+
transform: translate(0, -50%);
136+
background-position: -80px -109px;
137+
}
138+
139+
.mobile-control-jump {
140+
position: relative;
141+
width: 18px;
142+
height: 18px;
143+
background-position: -108px -111px;
144+
}
145+
146+
.mobile-control-sneak {
147+
width: 18px;
148+
height: 18px;
149+
top: 50%;
150+
left: 50%;
151+
transform: translate(-50%, -50%);
152+
background-position: -218px -82px;
153+
}
154+
.mobile-control-sneak.is-down {
155+
background-position: -218px -64px;
156+
}
157+
158+
.mobile-controls-left {
159+
display: none;
160+
position: absolute;
161+
left: 8px;
162+
bottom: 0;
163+
width: 70px;
164+
height: 70px;
165+
z-index: 30;
166+
opacity: 0.65;
167+
transform-origin: bottom left;
168+
transform: scale(1.5);
169+
}
170+
171+
.mobile-controls-right {
172+
display: none;
173+
flex-direction: column;
174+
place-items: center;
175+
place-content: center;
176+
position: absolute;
177+
right: 20px;
178+
bottom: 0;
179+
width: 22px;
180+
height: 70px;
181+
z-index: 30;
182+
opacity: 0.65;
183+
transform-origin: bottom right;
184+
transform: scale(1.5);
185+
}
58186
`
59187
}
60188

189+
static get properties () {
190+
return {
191+
bot: { type: Object }
192+
}
193+
}
194+
61195
/**
62196
* @param {globalThis.THREE.Renderer} renderer
63197
* @param {import('mineflayer').Bot} bot
@@ -73,6 +207,7 @@ class Hud extends LitElement {
73207
const hotbar = this.shadowRoot.querySelector('#hotbar')
74208
const xpLabel = this.shadowRoot.querySelector('#xp-label')
75209

210+
this.bot = bot
76211
hotbar.bot = bot
77212
debugMenu.bot = bot
78213

@@ -125,10 +260,84 @@ class Hud extends LitElement {
125260
foodbar.updateHunger(bot.food)
126261
// breathbar.updateOxygen(bot.oxygenLevel ?? 20)
127262
hotbar.init()
263+
264+
if (document.getElementById('options-screen').forceMobileControls || isMobile()) {
265+
this.showMobileControls(true)
266+
} else {
267+
this.showMobileControls(false)
268+
}
269+
}
270+
271+
/** @param {boolean} bl */
272+
showMobileControls (bl) {
273+
this.shadowRoot.querySelector('#mobile-top').style.display = bl ? 'flex' : 'none'
274+
this.shadowRoot.querySelector('#mobile-left').style.display = bl ? 'block' : 'none'
275+
this.shadowRoot.querySelector('#mobile-right').style.display = bl ? 'flex' : 'none'
276+
}
277+
278+
/**
279+
* @param {string} id
280+
* @param {boolean} action
281+
*/
282+
mobileControl (e, id, action) {
283+
e.stopPropagation()
284+
this.bot.setControlState(id, action)
128285
}
129286

130287
render () {
131288
return html`
289+
<div class="mobile-top-btns" id="mobile-top">
290+
<button class="chat-btn" @click=${(e) => {
291+
e.stopPropagation()
292+
this.shadowRoot.querySelector('#chat').enableChat(false)
293+
}}></button>
294+
<button class="pause-btn" @click=${(e) => {
295+
e.stopPropagation()
296+
document.getElementById('pause-screen').enableGameMenu()
297+
}}></button>
298+
</div>
299+
<div class="mobile-controls-left" id="mobile-left">
300+
<button
301+
class="mobile-control-forward"
302+
@touchstart=${(e) => this.mobileControl(e, 'forward', true)}
303+
@touchend=${(e) => this.mobileControl(e, 'forward', false)}
304+
@mousedown=${(e) => this.mobileControl(e, 'forward', true)}
305+
@mouseup=${(e) => this.mobileControl(e, 'forward', false)}
306+
></button>
307+
<button
308+
class="mobile-control-back"
309+
@touchstart=${(e) => this.mobileControl(e, 'back', true)}
310+
@touchend=${(e) => this.mobileControl(e, 'back', false)}
311+
@mousedown=${(e) => this.mobileControl(e, 'back', true)}
312+
@mouseup=${(e) => this.mobileControl(e, 'back', false)}
313+
></button>
314+
<button class="mobile-control-left"
315+
@touchstart=${(e) => this.mobileControl(e, 'right', true)}
316+
@touchend=${(e) => this.mobileControl(e, 'right', false)}
317+
@mousedown=${(e) => this.mobileControl(e, 'right', true)}
318+
@mouseup=${(e) => this.mobileControl(e, 'right', false)}
319+
></button>
320+
<button class="mobile-control-right"
321+
@touchstart=${(e) => this.mobileControl(e, 'left', true)}
322+
@touchend=${(e) => this.mobileControl(e, 'left', false)}
323+
@mousedown=${(e) => this.mobileControl(e, 'left', true)}
324+
@mouseup=${(e) => this.mobileControl(e, 'left', false)}
325+
></button>
326+
<button class="mobile-control-sneak" @dblclick=${(e) => {
327+
e.stopPropagation()
328+
const b = e.target.classList.toggle('is-down')
329+
this.bot.setControlState('sneak', b)
330+
}}></button>
331+
</div>
332+
<div class="mobile-controls-right" id="mobile-right">
333+
<button class="mobile-control-jump"
334+
@touchstart=${(e) => this.mobileControl(e, 'jump', true)}
335+
@touchend=${(e) => this.mobileControl(e, 'jump', false)}
336+
@mousedown=${(e) => this.mobileControl(e, 'jump', true)}
337+
@mouseup=${(e) => this.mobileControl(e, 'jump', false)}
338+
></button>
339+
</div>
340+
132341
<pmui-debug-overlay id="debug-overlay"></pmui-debug-overlay>
133342
<pmui-playerlist-overlay id="playerlist-overlay"></pmui-playerlist-overlay>
134343
<div class="crosshair"></div>

0 commit comments

Comments
 (0)