mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-22 04:56:15 -04:00
Automatic deploy 29000794
This commit is contained in:
parent
80fa8e6329
commit
54512644ab
10 changed files with 876 additions and 730 deletions
|
@ -27,7 +27,8 @@ There's also an easy mode for kids (slower ball) and a color-blind mode (no colo
|
||||||
|
|
||||||
## Doing
|
## Doing
|
||||||
- publish on Fdroid
|
- publish on Fdroid
|
||||||
|
- enable gif export of gameplay capture
|
||||||
|
- enable export of gameplay capture in webview
|
||||||
## Perk ideas
|
## Perk ideas
|
||||||
- wrap left / right
|
- wrap left / right
|
||||||
- n% of the broken bricks respawn when the ball touches the puck
|
- n% of the broken bricks respawn when the ball touches the puck
|
||||||
|
@ -67,7 +68,6 @@ There's also an easy mode for kids (slower ball) and a color-blind mode (no colo
|
||||||
- show total score on end screen (score added to total)
|
- show total score on end screen (score added to total)
|
||||||
- show stats on end screen compared to other runs
|
- show stats on end screen compared to other runs
|
||||||
- handle back bouton in menu
|
- handle back bouton in menu
|
||||||
- Make a small mp4/gif of game which can be shown on gameover and shared. https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder
|
|
||||||
- mouvement relatif du puck
|
- mouvement relatif du puck
|
||||||
- balls should collide with each other
|
- balls should collide with each other
|
||||||
- when game resumes near bottom, be unvulnerable for .5s ? , once per level
|
- when game resumes near bottom, be unvulnerable for .5s ? , once per level
|
||||||
|
|
|
@ -11,8 +11,8 @@ android {
|
||||||
applicationId = "me.lecaro.breakout"
|
applicationId = "me.lecaro.breakout"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 28999986
|
versionCode = 29000794
|
||||||
versionName = "28999986"
|
versionName = "29000794"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
useSupportLibrary = true
|
useSupportLibrary = true
|
||||||
|
|
|
@ -107,6 +107,7 @@ function play() {
|
||||||
if (audioContext) {
|
if (audioContext) {
|
||||||
audioContext.resume()
|
audioContext.resume()
|
||||||
}
|
}
|
||||||
|
resumeRecording()
|
||||||
}
|
}
|
||||||
|
|
||||||
function pause() {
|
function pause() {
|
||||||
|
@ -119,6 +120,7 @@ function pause() {
|
||||||
audioContext.suspend()
|
audioContext.suspend()
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
pauseRecording()
|
||||||
}
|
}
|
||||||
|
|
||||||
let offsetX, offsetXRoundedDown, gameZoneWidth, gameZoneWidthRoundedUp, gameZoneHeight, brickWidth, needsRender = true;
|
let offsetX, offsetXRoundedDown, gameZoneWidth, gameZoneWidthRoundedUp, gameZoneHeight, brickWidth, needsRender = true;
|
||||||
|
@ -353,7 +355,8 @@ async function openUpgradesPicker() {
|
||||||
while (repeats--) {
|
while (repeats--) {
|
||||||
const actions = pickRandomUpgrades(choices);
|
const actions = pickRandomUpgrades(choices);
|
||||||
if (!actions.length) break
|
if (!actions.length) break
|
||||||
let textAfterButtons = `<p>Upgrades picked so far : </p><p>${pickedUpgradesHTMl()}</p>`;
|
let textAfterButtons = `<p>Upgrades picked so far : </p><p>${pickedUpgradesHTMl()}</p>
|
||||||
|
<div id="level-recording-container"></div>`;
|
||||||
|
|
||||||
const cb = await asyncAlert({
|
const cb = await asyncAlert({
|
||||||
title: "Pick an upgrade " + (repeats ? "(" + (repeats + 1) + ")" : ""), actions, text, allowClose: false,
|
title: "Pick an upgrade " + (repeats ? "(" + (repeats + 1) + ")" : ""), actions, text, allowClose: false,
|
||||||
|
@ -394,7 +397,8 @@ function setLevel(l) {
|
||||||
flashes = [];
|
flashes = [];
|
||||||
|
|
||||||
background.src = 'data:image/svg+xml;base64,' + btoa(lvl.svg)
|
background.src = 'data:image/svg+xml;base64,' + btoa(lvl.svg)
|
||||||
|
stopRecording()
|
||||||
|
startRecordingGame()
|
||||||
}
|
}
|
||||||
|
|
||||||
function currentLevelInfo() {
|
function currentLevelInfo() {
|
||||||
|
@ -770,6 +774,8 @@ function restart() {
|
||||||
|
|
||||||
setLevel(0);
|
setLevel(0);
|
||||||
scoreStory.push(`You started playing with the upgrade "${upgrades.find(u => u.id === randomGift)?.name}" on the level "${runLevels[0].name}". `,);
|
scoreStory.push(`You started playing with the upgrade "${upgrades.find(u => u.id === randomGift)?.name}" on the level "${runLevels[0].name}". `,);
|
||||||
|
|
||||||
|
pauseRecording()
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMousePos(x) {
|
function setMousePos(x) {
|
||||||
|
@ -1041,7 +1047,7 @@ function tick() {
|
||||||
|
|
||||||
const windD = (puck - (offsetX + gameZoneWidth / 2)) / gameZoneWidth * 2 * perks.wind
|
const windD = (puck - (offsetX + gameZoneWidth / 2)) / gameZoneWidth * 2 * perks.wind
|
||||||
for (var i = 0; i < perks.wind; i++) {
|
for (var i = 0; i < perks.wind; i++) {
|
||||||
if(Math.random()*Math.abs(windD)>0.5) {
|
if (Math.random() * Math.abs(windD) > 0.5) {
|
||||||
flashes.push({
|
flashes.push({
|
||||||
type: "particle",
|
type: "particle",
|
||||||
duration: 150,
|
duration: 150,
|
||||||
|
@ -1049,9 +1055,9 @@ function tick() {
|
||||||
time: levelTime,
|
time: levelTime,
|
||||||
size: coinSize / 2,
|
size: coinSize / 2,
|
||||||
color: rainbowColor(),
|
color: rainbowColor(),
|
||||||
x: offsetXRoundedDown+ Math.random() * gameZoneWidthRoundedUp ,
|
x: offsetXRoundedDown + Math.random() * gameZoneWidthRoundedUp,
|
||||||
y: Math.random() * gameZoneHeight,
|
y: Math.random() * gameZoneHeight,
|
||||||
vx: windD*8,
|
vx: windD * 8,
|
||||||
vy: 0,
|
vy: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1298,6 +1304,7 @@ function addToTotalScore(points) {
|
||||||
function gameOver(title, intro) {
|
function gameOver(title, intro) {
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
pause()
|
pause()
|
||||||
|
stopRecording()
|
||||||
|
|
||||||
runStatistics.ended = Date.now()
|
runStatistics.ended = Date.now()
|
||||||
|
|
||||||
|
@ -1371,7 +1378,7 @@ function gameOver(title, intro) {
|
||||||
<p>${intro}</p>
|
<p>${intro}</p>
|
||||||
${unlocksInfo}
|
${unlocksInfo}
|
||||||
`, textAfterButtons: `
|
`, textAfterButtons: `
|
||||||
|
<div id="level-recording-container"></div>
|
||||||
${scoreStory.map((t) => "<p>" + t + "</p>").join("")}
|
${scoreStory.map((t) => "<p>" + t + "</p>").join("")}
|
||||||
`
|
`
|
||||||
}).then(() => restart());
|
}).then(() => restart());
|
||||||
|
@ -1737,8 +1744,9 @@ function render() {
|
||||||
ctx.fillStyle = puckColor;
|
ctx.fillStyle = puckColor;
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
if (offsetXRoundedDown) {
|
if (offsetXRoundedDown) {
|
||||||
ctx.fillRect(offsetX, 0, 1, height);
|
// draw outside of gaming area to avoid capturing borders in recordings
|
||||||
ctx.fillRect(width - offsetX - 1, 0, 1, height);
|
ctx.fillRect(offsetX - 1, 0, 1, height);
|
||||||
|
ctx.fillRect(width - offsetX + 1, 0, 1, height);
|
||||||
}
|
}
|
||||||
if (isSettingOn("mobile-mode")) {
|
if (isSettingOn("mobile-mode")) {
|
||||||
ctx.fillRect(offsetXRoundedDown, gameZoneHeight, gameZoneWidthRoundedUp, 1);
|
ctx.fillRect(offsetXRoundedDown, gameZoneHeight, gameZoneWidthRoundedUp, 1);
|
||||||
|
@ -1752,6 +1760,8 @@ function render() {
|
||||||
if (shaked) {
|
if (shaked) {
|
||||||
ctx.resetTransform();
|
ctx.resetTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recordOneFrame()
|
||||||
}
|
}
|
||||||
|
|
||||||
let cachedBricksRender = document.createElement("canvas");
|
let cachedBricksRender = document.createElement("canvas");
|
||||||
|
@ -2343,6 +2353,11 @@ const options = {
|
||||||
}, "color_blind": {
|
}, "color_blind": {
|
||||||
default: false, name: `Color blind mode`, help: `Removes mechanics about colors.`, restart: true,
|
default: false, name: `Color blind mode`, help: `Removes mechanics about colors.`, restart: true,
|
||||||
},
|
},
|
||||||
|
// Could not get the sharing to work without loading androidx and all the modern android things so for now i'll just disable sharing in the android app
|
||||||
|
"record": !window.location.search.includes('isInWebView=true') && {
|
||||||
|
default: false, name: `Record games`, help: `Get a video at the end of the run.`, restart: true,
|
||||||
|
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async function openSettingsPanel() {
|
async function openSettingsPanel() {
|
||||||
|
@ -2350,8 +2365,10 @@ async function openSettingsPanel() {
|
||||||
|
|
||||||
const optionsList = [];
|
const optionsList = [];
|
||||||
for (const key in options) {
|
for (const key in options) {
|
||||||
|
if (options[key])
|
||||||
optionsList.push({
|
optionsList.push({
|
||||||
checked: isSettingOn(key) ? 1 : 0, max: 1, text: options[key].name, help: options[key].help, value: () => {
|
checked: isSettingOn(key) ? 1 : 0,
|
||||||
|
max: 1, text: options[key].name, help: options[key].help, value: () => {
|
||||||
toggleSetting(key)
|
toggleSetting(key)
|
||||||
if (options[key].restart) {
|
if (options[key].restart) {
|
||||||
restart()
|
restart()
|
||||||
|
@ -2618,6 +2635,185 @@ function levelIconHTML(level, title) {
|
||||||
|
|
||||||
upgrades.forEach(u => u.icon = levelIconHTML(perkIconsLevels[u.id], u.name))
|
upgrades.forEach(u => u.icon = levelIconHTML(perkIconsLevels[u.id], u.name))
|
||||||
|
|
||||||
|
let mediaRecorder, captureStream, recordCanvas, recordCanvasCtx, levelGif, gifCanvas, gifCtx
|
||||||
|
|
||||||
|
|
||||||
|
function recordOneFrame() {
|
||||||
|
if (!isSettingOn('record')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!running) return;
|
||||||
|
drawMainCanvasOnSmallCanvas()
|
||||||
|
// Start recording after you hit something
|
||||||
|
if(levelSpawnedCoins && levelGif) {
|
||||||
|
recordGifFrame()
|
||||||
|
}
|
||||||
|
if (captureStream.requestFrame) {
|
||||||
|
captureStream.requestFrame()
|
||||||
|
} else {
|
||||||
|
captureStream.getVideoTracks()[0].requestFrame()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function drawMainCanvasOnSmallCanvas() {
|
||||||
|
recordCanvasCtx?.drawImage(canvas, offsetXRoundedDown, 0, gameZoneWidthRoundedUp, gameZoneHeight, 0, 0, recordCanvas.width, recordCanvas.height)
|
||||||
|
recordCanvasCtx.fillStyle = currentLevelInfo()?.black_puck ? '#000' : '#FFF'
|
||||||
|
recordCanvasCtx.textBaseline = "top";
|
||||||
|
recordCanvasCtx.font = "12px monospace";
|
||||||
|
recordCanvasCtx.textAlign = "right";
|
||||||
|
recordCanvasCtx.fillText(score.toString(), recordCanvas.width - 12, 12)
|
||||||
|
recordCanvasCtx.textAlign = "left";
|
||||||
|
recordCanvasCtx.fillText((currentLevel + 1) + '/' + max_levels(), 12, 12)
|
||||||
|
}
|
||||||
|
|
||||||
|
let nthFrame = 0, gifFrameReduction = 2
|
||||||
|
function recordGifFrame(){
|
||||||
|
gifCtx.globalCompositeOperation = 'screen'
|
||||||
|
gifCtx.globalAlpha = 1 / gifFrameReduction
|
||||||
|
gifCtx?.drawImage(canvas, offsetXRoundedDown, 0, gameZoneWidthRoundedUp, gameZoneHeight, 0, 0, gifCanvas.width, gifCanvas.height)
|
||||||
|
nthFrame++
|
||||||
|
if (nthFrame === gifFrameReduction) {
|
||||||
|
levelGif.addFrame(gifCtx, {delay: Math.round(gifFrameReduction * 1000 / 60), copy: true});
|
||||||
|
gifCtx.globalCompositeOperation = 'source-over'
|
||||||
|
gifCtx.fillStyle = 'black'
|
||||||
|
gifCtx.fillRect(0, 0, gifCanvas.width, gifCanvas.height)
|
||||||
|
nthFrame=0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startRecordingGame() {
|
||||||
|
if (!isSettingOn('record')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!recordCanvas) {
|
||||||
|
// Smaller canvas with less details
|
||||||
|
recordCanvas = document.createElement("canvas")
|
||||||
|
recordCanvasCtx = recordCanvas.getContext("2d", {antialias: false, alpha: false})
|
||||||
|
|
||||||
|
gifCanvas = document.createElement("canvas")
|
||||||
|
gifCtx = gifCanvas.getContext("2d", {antialias: false, alpha: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
let scale = 1
|
||||||
|
while (Math.max(gameZoneWidthRoundedUp, gameZoneHeight) * scale > 400 * 2) {
|
||||||
|
scale = scale / 2
|
||||||
|
}
|
||||||
|
console.log('Recording at scale ' + scale)
|
||||||
|
recordCanvas.width = gameZoneWidthRoundedUp * scale
|
||||||
|
recordCanvas.height = gameZoneHeight * scale
|
||||||
|
gifCanvas.width = Math.floor(gameZoneWidthRoundedUp * scale / 2)
|
||||||
|
gifCanvas.height = Math.floor(gameZoneHeight * scale / 2)
|
||||||
|
|
||||||
|
// if(isSettingOn('basic')){
|
||||||
|
levelGif = new GIF({
|
||||||
|
workers: 2,
|
||||||
|
quality: 10,
|
||||||
|
repeat: 0,
|
||||||
|
background: currentLevelInfo()?.color || '#000',
|
||||||
|
width: gifCanvas.width,
|
||||||
|
height: gifCanvas.height,
|
||||||
|
dither: false,
|
||||||
|
});
|
||||||
|
// }else{
|
||||||
|
// levelGif=null
|
||||||
|
// }
|
||||||
|
|
||||||
|
// drawMainCanvasOnSmallCanvas()
|
||||||
|
const recordedChunks = [];
|
||||||
|
captureStream = captureStream || recordCanvas.captureStream(0);
|
||||||
|
const instance = new MediaRecorder(captureStream);
|
||||||
|
mediaRecorder = instance
|
||||||
|
instance.start();
|
||||||
|
mediaRecorder.pause()
|
||||||
|
instance.ondataavailable = function (event) {
|
||||||
|
recordedChunks.push(event.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.onstop = async function () {
|
||||||
|
let targetDiv = document.getElementById("level-recording-container")
|
||||||
|
if (!targetDiv) return
|
||||||
|
const video = document.createElement("video")
|
||||||
|
video.autoplay = true
|
||||||
|
video.controls = false
|
||||||
|
video.disablepictureinpicture = true
|
||||||
|
video.disableremoteplayback = true
|
||||||
|
video.width = recordCanvas.width
|
||||||
|
video.height = recordCanvas.height
|
||||||
|
targetDiv.style.width = recordCanvas.width + 'px'
|
||||||
|
targetDiv.style.height = recordCanvas.height + 'px'
|
||||||
|
video.loop = true
|
||||||
|
video.muted = true
|
||||||
|
video.playsinline = true
|
||||||
|
let blob = new Blob(recordedChunks, {type: "video/webm"});
|
||||||
|
video.src = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
const a = document.createElement("a")
|
||||||
|
a.download = captureFileName('webm')
|
||||||
|
a.target = "_blank"
|
||||||
|
a.href = video.src
|
||||||
|
a.textContent = `Download video (${(blob.size / 1000000).toFixed(2)}MB)`
|
||||||
|
targetDiv.appendChild(video)
|
||||||
|
targetDiv.appendChild(a)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
levelGif?.on('finished', function (blob) {
|
||||||
|
let targetDiv = document.getElementById("level-recording-container")
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
const img = document.createElement("img")
|
||||||
|
img.src = url
|
||||||
|
targetDiv?.appendChild(img)
|
||||||
|
|
||||||
|
const giflink = document.createElement("a")
|
||||||
|
giflink.textContent = `Download GIF (${(blob.size / 1000000).toFixed(2)}MB)`
|
||||||
|
giflink.href = url
|
||||||
|
giflink.download = captureFileName('gif')
|
||||||
|
targetDiv?.appendChild(giflink)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function pauseRecording() {
|
||||||
|
if (!isSettingOn('record')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (mediaRecorder?.state === 'recording') {
|
||||||
|
mediaRecorder?.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resumeRecording() {
|
||||||
|
if (!isSettingOn('record')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (mediaRecorder?.state === 'paused') {
|
||||||
|
mediaRecorder.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopRecording() {
|
||||||
|
|
||||||
|
if (!isSettingOn('record')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!mediaRecorder) return;
|
||||||
|
|
||||||
|
mediaRecorder?.stop()
|
||||||
|
levelGif?.render()
|
||||||
|
mediaRecorder = null
|
||||||
|
levelGif = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function captureFileName(ext) {
|
||||||
|
return "breakout-71-capture-" + new Date().toISOString().replace(/[^0-9\-]+/gi, '-') + '.' + ext
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fitSize()
|
fitSize()
|
||||||
restart()
|
restart()
|
||||||
tick();
|
tick();
|
3
app/src/main/assets/gif.js
Normal file
3
app/src/main/assets/gif.js
Normal file
File diff suppressed because one or more lines are too long
3
app/src/main/assets/gif.worker.js
Normal file
3
app/src/main/assets/gif.worker.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -8,15 +8,16 @@
|
||||||
/>
|
/>
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||||
<title>Breakout 71</title>
|
<title>Breakout 71</title>
|
||||||
<link rel="stylesheet" href="style.css?v=28999986" />
|
<link rel="stylesheet" href="style.css?v=29000794" />
|
||||||
<link rel="icon" href="./icon.svg" />
|
<link rel="icon" href="./icon.svg" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<button id="menu">☰<span> menu</span></button>
|
<button id="menu">☰<span> menu</span></button>
|
||||||
<button id="score"></button>
|
<button id="score"></button>
|
||||||
<canvas id="game"></canvas>
|
<canvas id="game"></canvas>
|
||||||
<script>window.appVersion="?v=28999986".slice(3)</script>
|
<script>window.appVersion="?v=29000794".slice(3)</script>
|
||||||
<script src="levels.js?v=28999986"></script>
|
<script src="gif.js"></script>
|
||||||
<script src="game.js?v=28999986"></script>
|
<script src="levels.js?v=29000794"></script>
|
||||||
|
<script src="game.js?v=29000794"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
* {
|
* {
|
||||||
font-family:
|
font-family: Courier New,
|
||||||
Courier New,
|
|
||||||
Courier,
|
Courier,
|
||||||
Lucida Sans Typewriter,
|
Lucida Sans Typewriter,
|
||||||
Lucida Typewriter,
|
Lucida Typewriter,
|
||||||
|
@ -28,6 +27,7 @@ body {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
#score,
|
#score,
|
||||||
#menu {
|
#menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -43,24 +43,28 @@ body {
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.black_puck #score,
|
body.black_puck #score,
|
||||||
body.black_puck #menu {
|
body.black_puck #menu {
|
||||||
color:black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
#score:hover,
|
#score:hover,
|
||||||
#score:focus,
|
#score:focus,
|
||||||
#menu:hover,
|
#menu:hover,
|
||||||
#menu:focus {
|
#menu:focus {
|
||||||
background: rgba(0,0,0,0.3);
|
background: rgba(0, 0, 0, 0.3);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#score {
|
#score {
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu {
|
#menu {
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (orientation: portrait) {
|
@media screen and (orientation: portrait) {
|
||||||
#menu > span {
|
#menu > span {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -97,8 +101,9 @@ body.black_puck #menu {
|
||||||
.popup > div > p {
|
.popup > div > p {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup > div > button {
|
.popup > div > button {
|
||||||
font:inherit;
|
font: inherit;
|
||||||
background: rgba(0, 0, 0, 0.8);
|
background: rgba(0, 0, 0, 0.8);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
@ -119,25 +124,27 @@ body.black_puck #menu {
|
||||||
|
|
||||||
|
|
||||||
.popup button.close-modale {
|
.popup button.close-modale {
|
||||||
color:white;
|
color: white;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top:0;
|
top: 0;
|
||||||
right:0;
|
right: 0;
|
||||||
width: 60px;
|
width: 60px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
background:none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: rgba(0,0,0,0.2);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup button.close-modale:before {
|
.popup button.close-modale:before {
|
||||||
content: "+";
|
content: "+";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform: translate(-50%, -50%) rotate(45deg) ;
|
transform: translate(-50%, -50%) rotate(45deg);
|
||||||
font-size: 80px;
|
font-size: 80px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup button.close-modale:hover {
|
.popup button.close-modale:hover {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
@ -152,6 +159,7 @@ body.black_puck #menu {
|
||||||
.popup > div > button > div {
|
.popup > div > button > div {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup > div > button > div > em {
|
.popup > div > button > div > em {
|
||||||
display: block;
|
display: block;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
@ -161,11 +169,12 @@ body.black_puck #menu {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
gap:5px;
|
gap: 5px;
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.popup > div > button > span.checks>span {
|
|
||||||
|
.popup > div > button > span.checks > span {
|
||||||
flex-basis: 10px;
|
flex-basis: 10px;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
|
@ -175,21 +184,22 @@ body.black_puck #menu {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
}
|
}
|
||||||
.popup > div > button > span.checks>span.checked {
|
|
||||||
|
.popup > div > button > span.checks > span.checked {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup .textAfterButtons{
|
.popup .textAfterButtons {
|
||||||
color: rgba(255, 255, 255, 0.58);
|
color: rgba(255, 255, 255, 0.58);
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup a[href]{
|
.popup a[href] {
|
||||||
color:inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup a[href]:hover,
|
.popup a[href]:hover,
|
||||||
.popup a[href]:focus
|
.popup a[href]:focus {
|
||||||
{
|
color: white;
|
||||||
color:white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Unlocks progress bar*/
|
/*Unlocks progress bar*/
|
||||||
|
@ -197,17 +207,18 @@ body.black_puck #menu {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
background: #1c1c2f;
|
background: #1c1c2f;
|
||||||
color:#fff;
|
color: #fff;
|
||||||
box-shadow: inset 3px 3px 5px rgba(0,0,0,0.5);
|
box-shadow: inset 3px 3px 5px rgba(0, 0, 0, 0.5);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.progress >.progress_bar_part{
|
|
||||||
|
.progress > .progress_bar_part {
|
||||||
display: block;
|
display: block;
|
||||||
background: #4049ca;
|
background: #4049ca;
|
||||||
box-shadow: inset 3px 3px 5px rgba(0,0,0,0.5);
|
box-shadow: inset 3px 3px 5px rgba(0, 0, 0, 0.5);
|
||||||
left: 0;
|
left: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -217,13 +228,48 @@ body.black_puck #menu {
|
||||||
animation: grow 1s both ease-out;
|
animation: grow 1s both ease-out;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.progress> span {
|
|
||||||
|
.progress > span {
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes grow {
|
@keyframes grow {
|
||||||
0%{
|
0% {
|
||||||
transform: scale(0,1);
|
transform: scale(0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#level-recording-container {
|
||||||
|
max-width: 400px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#level-recording-container img,#level-recording-container video{
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
#level-recording-container a {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#level-recording-container a video {
|
||||||
|
border-radius: 10px;
|
||||||
|
display: block;
|
||||||
|
outline: 1px solid white;
|
||||||
|
box-shadow: 2px 2px 5px black;
|
||||||
|
margin: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
|
||||||
|
#level-recording-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 40px;
|
||||||
|
left: 40px;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,21 @@
|
||||||
package me.lecaro.breakout
|
package me.lecaro.breakout
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.webkit.ConsoleMessage
|
import android.webkit.ConsoleMessage
|
||||||
|
import android.webkit.DownloadListener
|
||||||
|
import android.webkit.JavascriptInterface
|
||||||
|
import android.webkit.MimeTypeMap
|
||||||
import android.webkit.WebChromeClient
|
import android.webkit.WebChromeClient
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
|
||||||
class MainActivity : android.app.Activity() {
|
class MainActivity : android.app.Activity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -18,7 +28,7 @@ class MainActivity : android.app.Activity() {
|
||||||
val webView = WebView(this)
|
val webView = WebView(this)
|
||||||
webView.settings.javaScriptEnabled = true
|
webView.settings.javaScriptEnabled = true
|
||||||
webView.settings.domStorageEnabled = true
|
webView.settings.domStorageEnabled = true
|
||||||
webView.loadUrl("file:///android_asset/index.html")
|
webView.loadUrl("file:///android_asset/index.html?isInWebView=true")
|
||||||
webView.webChromeClient = object : WebChromeClient() {
|
webView.webChromeClient = object : WebChromeClient() {
|
||||||
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
|
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
|
||||||
Log.d(
|
Log.d(
|
||||||
|
@ -28,6 +38,7 @@ class MainActivity : android.app.Activity() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setContentView(webView)
|
setContentView(webView)
|
||||||
}
|
}
|
||||||
}
|
}
|
966
package-lock.json
generated
966
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -4,13 +4,15 @@
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "nodemon editserver.js --watch editserver.js "
|
"start": "nodemon editserver.js --watch editserver.js ",
|
||||||
|
"serve": "http-server app/src/main/assets -o"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.20.3",
|
"body-parser": "^1.20.3",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
|
"http-server": "^14.1.1",
|
||||||
"nodemon": "^3.1.9"
|
"nodemon": "^3.1.9"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue