Compare commits

...
42 Commits
Author SHA1 Message Date
legonzaur 6c56c8682f Merge branch 'main' of git.legonzaur.fr:heronfief/front 2024-08-02 20:04:56 +02:00
legonzaur 92a9d52a8c Ci : skip ssh setup 2024-08-02 20:03:31 +02:00
legonzaur 80f6d02fc7 Update .gitea/workflows/build.yml 2024-08-01 10:44:09 +00:00
legonzaur da54495d1c Ci : add more tags 2024-08-01 08:12:50 +00:00
legonzaur f60ec850f0 Ci : fix repo name in tag 2024-08-01 08:12:33 +00:00
legonzaur bce8edc150 Ci : run on any tag creation
release-tag / release-image (push) Failing after 10s
2024-08-01 07:51:05 +00:00
legonzaur fadc8cc451 Ci : run on tag creation 2024-08-01 09:48:45 +02:00
legonzaur 8cb8bdfc5e Ci : add tags
release-tag / release-image (release) Failing after 10s
2024-07-31 22:42:28 +02:00
legonzaur 0f1ab017ed Update .gitea/workflows/build.yml
release-tag / release-image (push) Successful in 23s
2024-07-31 20:36:09 +00:00
legonzaur 9384f8f23e Ci : build on tag published
release-tag / release-image (push) Successful in 23s
2024-07-31 20:23:55 +00:00
legonzaur 443bfc128a Merge branch 'main' of git.legonzaur.fr:heronfief/front
release-tag / release-image (push) Successful in 24s
2024-05-24 20:56:38 +02:00
legonzaur a3630abf92 Fix : canvas pixelated not working 2024-05-24 20:56:36 +02:00
Erzangel 74d99aff6e feat(bourred%): card non broken link, hide tooltip (sorta????)
release-tag / release-image (push) Has been cancelled
2024-05-24 20:56:29 +02:00
legonzaur 76814efb18 fix : background of market and playlist
release-tag / release-image (push) Successful in 23s
2024-05-24 20:13:16 +02:00
legonzaur 571a35633b Fix shader 2024-05-24 20:13:05 +02:00
legonzaur 5fa34eda14 Merge pull request 'feat-shader-background' (#95) from feat-shader-background into main
release-tag / release-image (push) Successful in 33s
Reviewed-on: #95
2024-05-24 17:58:19 +00:00
legonzaur 37ad826a80 Merge pull request 'feat/panels-visuals' (#94) from feat/panels-visuals into main
release-tag / release-image (push) Has been cancelled
Reviewed-on: #94
2024-05-24 17:58:06 +00:00
legonzaur 3f8a580984 Better panel background 2024-05-24 19:57:44 +02:00
legonzaur b5f4d91ff8 Animate shader background 2024-05-24 19:15:47 +02:00
legonzaur 351cc06495 Allow using vsync 2024-05-24 17:49:08 +02:00
legonzaur 4ea5c25a47 Feat : handle settings gracefully 2024-05-24 17:19:31 +02:00
legonzaur 766dc92931 Merge branch 'feat/panels-visuals' into feat/shader-background 2024-05-24 14:19:13 +02:00
legonzaur f570b456ca Feat : center current player stats 2024-05-24 14:14:06 +02:00
legonzaur 8c7cc19991 Fix : remove rotation on locked cards too 2024-05-24 13:15:55 +02:00
legonzaur df6d77b870 Feat : more animations and transitions easing 2024-05-24 13:14:19 +02:00
legonzaur b2a3d4f701 Feat : add cards floating animation 2024-05-24 13:04:39 +02:00
legonzaur 6291989dd4 Feat : add background to player banner 2024-05-24 12:40:14 +02:00
legonzaur a6fcc28ced Feat : improve shadow effect 2024-05-24 12:28:36 +02:00
legonzaur 66b6cc61e9 wrap market and player list in containers 2024-05-24 12:14:37 +02:00
Erzangel 90ed8a6211 fix: forgot changes
release-tag / release-image (push) Successful in 23s
2024-05-22 20:41:00 +02:00
Erzangel ceab43e861 feat(rules): add mention of right click zoom 2024-05-22 20:40:41 +02:00
Erzangel ffd9f0187d feat: new favicon attempt
release-tag / release-image (push) Successful in 22s
2024-05-22 20:05:52 +02:00
Erzangel 0305dbe9b7 feat: we are so back, move settings img loc
release-tag / release-image (push) Successful in 25s
2024-05-22 19:47:44 +02:00
Erzangel db4aac845c feat(card): flushed on 002 2024-05-22 19:42:22 +02:00
legonzaur bd6978d392 Fix : prevent render retrigger when color changes 2024-05-22 12:45:34 +02:00
legonzaur 310b3e7b17 Merge remote-tracking branch 'origin/main' into feat-shader-background 2024-05-22 10:33:06 +02:00
legonzaur ca87817b33 WIP : shader Worker 2024-05-22 10:32:42 +02:00
legonzaur 6f7b1bdf03 Merge branch 'main' into feat-shader-background 2024-05-22 10:31:34 +02:00
legonzaur 17a1d23768 Update .gitea/workflows/build.yml
release-tag / release-image (push) Successful in 23s
2024-05-21 20:56:48 +00:00
Erzangel 0915c0a8d8 Merge pull request 'feat: new red cards non-bourred' (#88) from feat/new-red-cards into main
release-tag / release-image (push) Failing after 9s
Reviewed-on: #88
2024-05-21 20:36:21 +00:00
Erzangel 7ae052ee8b feat: new red cards non-bourred 2024-05-21 22:33:29 +02:00
legonzaur d9d8a7a8f0 Merge pull request 'feat/switch-assets' (#86) from feat/switch-assets into main
release-tag / release-image (push) Successful in 1m3s
Reviewed-on: legonzaur/heron-realms-front#86
2024-05-21 19:09:33 +00:00
38 changed files with 577 additions and 254 deletions
+24 -28
View File
@@ -1,19 +1,16 @@
name: release-tag
on:
release:
types: [released]
push:
branches:
- main
tags:
- "**"
jobs:
release-image:
runs-on: ubuntu-latest
env:
DOCKER_ORG: legonzaur
DOCKER_LATEST: nightly
REPO_NAME: ${{ gitea.repository }}
ENDPOINT: git.legonzaur.fr
steps:
@@ -23,24 +20,10 @@ jobs:
lfs: false
persist-credentials: false
- name: Get Meta
id: meta
run: |
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
- name: setup private key
- name: LFS Checkout
shell: bash
run: |
echo "${{ secrets.RUNNER_SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 400 ~/.ssh/id_ed25519
echo "{{ secrets.INSTANCE_SSH_PUBLIC_KEY }} > ~/.ssh/known_hosts"
- name: git lfs pull
shell: bash
run: |
ssh-keyscan ${{ env.ENDPOINT }} >> ~/.ssh/known_hosts
git remote set-url origin git@${{ env.ENDPOINT }}:${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}.git
git remote set-url origin https://${{ env.ENDPOINT }}/${{ env.REPO_NAME }}.git
git lfs pull
- name: Login to DockerHub
@@ -50,15 +33,28 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.ENDPOINT }}/${{ env.REPO_NAME }}
tags: |
type=schedule
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
secrets: |
"NUXT_PUBLIC_WEBSOCKET_ENDPOINT=${{ secrets.NUXT_PUBLIC_WEBSOCKET_ENDPOINT }}"
"NUXT_PUBLIC_DISCORD_LOGIN_ENDPOINT=${{ secrets.NUXT_PUBLIC_DISCORD_LOGIN_ENDPOINT }}"
build-args: |
NUXT_PUBLIC_ENDPOINT=${{ vars.NUXT_PUBLIC_ENDPOINT }}
NUXT_PUBLIC_DISCORD_LOGIN_ENDPOINT=${{ vars.NUXT_PUBLIC_DISCORD_LOGIN_ENDPOINT }}
NUXT_PUBLIC_WELCOME_MESSAGE=${{ vars.NUXT_PUBLIC_WELCOME_MESSAGE }}
push: true
tags: | # replace it with your local IP and tags
${{ env.ENDPOINT }}/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}
${{ env.ENDPOINT }}/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+3 -1
View File
@@ -15,7 +15,8 @@ uniform float u_time;
void main(){
vec2 st=(gl_FragCoord.xy+u_offset)/u_resolution;
vec2 q=vec2(0.);
q.x=fbm(st);
q.y=fbm(st+vec2(1.));
@@ -31,4 +32,5 @@ void main(){
vec3 color=mix(u_color_start,u_color_end,f*.3);
gl_FragColor=vec4(color,1.);
}
+214
View File
@@ -0,0 +1,214 @@
import noise from "~/assets/shaders/noise.frag?raw"
import resolveLygia from "~/assets/workers/resolve-lygia.esm"
let interval: number
let timeout: number
const shaderOptions = { quality: 1, framerate: 1000 / 60 }
const screen = { width: 0, height: 0, devicePixelRatio: 0 }
const _colorStart = [0, 0, 0]
const _colorEnd = [0, 0, 0]
let fragmentShader = resolveLygia(noise);
let canvas: OffscreenCanvas
let render: (time: number) => void
let must_resize = false
async function main(canvas: OffscreenCanvas) {
const gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// setup GLSL program
const program = gl.createProgram()
if (!program) { return }
const fragment = gl.createShader(gl.FRAGMENT_SHADER);
if (!fragment) { return }
gl.shaderSource(fragment, fragmentShader)
gl.compileShader(fragment);
if (!gl.getShaderParameter(fragment, gl.COMPILE_STATUS)) {
const lastError = gl.getShaderInfoLog(fragment);
console.error(lastError)
gl.deleteShader(fragment);
return
}
const vertex = gl.createShader(gl.VERTEX_SHADER);
if (!vertex) { return }
gl.shaderSource(vertex, `attribute vec4 a_position;void main() {gl_Position = a_position;}`)
gl.compileShader(vertex);
if (!gl.getShaderParameter(vertex, gl.COMPILE_STATUS)) {
const lastError = gl.getShaderInfoLog(vertex);
console.error(lastError)
gl.deleteShader(vertex);
return
}
gl.attachShader(program, fragment)
gl.attachShader(program, vertex)
gl.linkProgram(program)
const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) { return }
// const program = webglUtils.createProgramFromSources(gl, [vs, fragmentShader2]);
// look up where the vertex data needs to go.
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
// look up uniform locations
const offset = gl.getUniformLocation(program, "u_offset");
const resolutionLocation = gl.getUniformLocation(program, "u_resolution");
const colorStart = gl.getUniformLocation(program, "u_color_start");
const quality = gl.getUniformLocation(program, "u_quality");
const colorEnd = gl.getUniformLocation(program, "u_color_end");
const timeLocation = gl.getUniformLocation(program, "u_time");
// Create a buffer to put three 2d clip space points in
const positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// fill it with a 2 triangles that cover clipspace
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, // first triangle
1, -1,
-1, 1,
-1, 1, // second triangle
1, -1,
1, 1,
]), gl.STATIC_DRAW);
const gl2 = gl
function render(time: number) {
if (must_resize) {
canvas.width = screen.width * shaderOptions.quality / 10
canvas.height = screen.height * shaderOptions.quality / 10
must_resize = false;
}
time *= 0.001; // convert to seconds
// Tell WebGL how to convert from clip space to pixels
gl2.viewport(0, 0, gl2.canvas.width, gl2.canvas.height);
// Tell it to use our program (pair of shaders)
gl2.useProgram(program);
// Turn on the attribute
gl2.enableVertexAttribArray(positionAttributeLocation);
// Bind the position buffer.
gl2.bindBuffer(gl2.ARRAY_BUFFER, positionBuffer);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
gl2.vertexAttribPointer(
positionAttributeLocation,
2, // 2 components per iteration
gl2.FLOAT, // the data is 32bit floats
false, // don't normalize the data
0, // 0 = move forward size * sizeof(type) each iteration to get the next position
0, // start at the beginning of the buffer
);
const secondRatio = screen.width / screen.height
if (secondRatio < 1) {
const half = canvas.height / 2
gl2.uniform2f(resolutionLocation, canvas.width, canvas.height * secondRatio);
gl2.uniform2f(offset, 0, -half + ((half) / (1 / secondRatio)));
} else {
const half = canvas.width / 2
gl2.uniform2f(resolutionLocation, canvas.width / secondRatio, canvas.height);
gl2.uniform2f(offset, -half + ((half) / secondRatio), 0);
}
gl2.uniform1i(quality, shaderOptions.quality)
gl2.uniform3f(colorStart, _colorStart[0], _colorStart[1], _colorStart[2]);
gl2.uniform3f(colorEnd, _colorEnd[0], _colorEnd[1], _colorEnd[2]);
gl2.uniform1f(timeLocation, time);
gl2.drawArrays(
gl2.TRIANGLES,
0, // offset
6, // num vertices to process
);
if (timeout) {
self.clearTimeout(timeout)
timeout = self.setTimeout(() => {
requestAnimationFrame(render)
}, 1000 / shaderOptions.framerate)
}
}
requestAnimationFrame(render)
// self.clearInterval(interval)
// interval = self.setInterval(() => requestAnimationFrame(render), 1000 / shaderOptions.framerate)
self.clearTimeout(timeout)
if (shaderOptions.framerate == 0) {
return render
}
timeout = self.setTimeout(() => {
requestAnimationFrame(render)
}, 1000 / shaderOptions.framerate)
return render
}
self.addEventListener("message", (e) => {
const data = e.data
if (data.command == "initShader") {
canvas = data.canvas
shaderOptions.quality = data.shaderOptions.quality
shaderOptions.framerate = data.shaderOptions.framerate
if (shaderOptions.framerate == 65) {
shaderOptions.framerate == Infinity
}
screen.devicePixelRatio = data.screen.devicePixelRatio
screen.width = data.screen.width
screen.height = data.screen.height
loadShader()
} else if (data.command == "killShader") {
console.log("killShader")
self.clearInterval(interval)
self.clearTimeout(timeout)
}
else if (data.command == "resumeShader") {
self.clearInterval(interval)
self.clearTimeout(timeout)
shaderOptions.framerate = data.shaderOptions.framerate
if (shaderOptions.framerate == 65) {
shaderOptions.framerate == Infinity
}
timeout = self.setTimeout(() => {
requestAnimationFrame(render)
}, 1000 / shaderOptions.framerate)
// interval = self.setInterval(() => requestAnimationFrame(render), 1000 / shaderOptions.framerate)
} else if (data.command == "recolorShader") {
_colorStart[0] = data.colorStart[0]
_colorStart[1] = data.colorStart[1]
_colorStart[2] = data.colorStart[2]
_colorEnd[0] = data.colorEnd[0]
_colorEnd[1] = data.colorEnd[1]
_colorEnd[2] = data.colorEnd[2]
} else if (data.command == "resizeShader") {
shaderOptions.quality = data.shaderOptions.quality
screen.devicePixelRatio = data.screen.devicePixelRatio
screen.width = data.screen.width
screen.height = data.screen.height
must_resize = true
}
})
async function loadShader() {
if (!canvas) { return }
// let fragmentShader2 = fragmentShader.replace("#define FBM_OCTAVES 6", "#define FBM_OCTAVES " + shaderOptions.quality);
const temp = await main(canvas);
if (temp) {
render = temp
}
}
+52
View File
@@ -0,0 +1,52 @@
function getFile(url) {
let httpRequest = new XMLHttpRequest();
httpRequest.open("GET", url, false);
httpRequest.send();
if (httpRequest.status == 200)
return httpRequest.responseText;
else
return "";
}
function resolveLygia(lines) {
if ( !Array.isArray(lines) ) {
lines = lines.split(/\r?\n/);
}
let src = "";
lines.forEach( (line, i) => {
const line_trim = line.trim();
if (line_trim.startsWith('#include \"lygia') ) {
let include_url = line_trim.substring(15);
include_url = "https://lygia.xyz" + include_url.replace(/\"|\;|\s/g,'');
src += getFile(include_url) + '\n';
}
else {
src += line + '\n';
}
});
return src;
}
async function resolveLygiaAsync(lines) {
if (!Array.isArray(lines))
lines = lines.split(/\r?\n/);
let src = "";
const response = await Promise.all(
lines.map(async (line, i) => {
const line_trim = line.trim();
if (line_trim.startsWith('#include "lygia')) {
let include_url = line_trim.substring(15);
include_url = "https://lygia.xyz" + include_url.replace(/\"|\;|\s/g, "");
return fetch(include_url).then((res) => res.text());
}
else
return line;
})
);
return response.join("\n");
}
export { resolveLygia as default };
+88 -113
View File
@@ -1,151 +1,121 @@
<script setup lang="ts">
import noise from "~/assets/shaders/noise.frag?raw"
const canvas = ref<HTMLCanvasElement | null>(null)
const settingsStore = useSettingsStore()
declare const webglUtils: {
createProgramFromSources: (gl: WebGLRenderingContext, array: [string, string]) => WebGLProgram,
resizeCanvasToDisplaySize: (e: HTMLCanvasElement | OffscreenCanvas) => any
}
declare const resolveLygiaAsync: (source: string) => Promise<string>
import gsap from 'gsap';
import ShaderWorker from '~/assets/workers/ShaderWorker?worker'
import goStore from '~/netcode';
const worker = new ShaderWorker()
const canvas = ref<HTMLCanvasElement | null>(null)
const canvas_resize_temp = ref<HTMLCanvasElement | null>(null)
export interface Props {
colorStart?: [number, number, number]
colorEnd?: [number, number, number]
}
const props = withDefaults(defineProps<Props>(), {
colorStart: () => [0, .3, 0],
colorEnd: () => [0, 1, 0]
})
const targetFramerate = 1000 / settingsStore.shaderFramerate
const _colorStart = reactive(props.colorStart)
const _colorEnd = reactive(props.colorEnd)
const interval = ref<ReturnType<typeof setInterval>>()
let fragmentShader = await resolveLygiaAsync(noise);
async function main(canvas: HTMLCanvasElement, fragmentShader2: string) {
// Get A WebGL context
/** @type {HTMLCanvasElement} */
const gl = canvas.getContext("webgl");
if (!gl) {
return;
function initShader() {
if (!canvas.value) { return }
const offscreen = canvas.value?.transferControlToOffscreen()
worker.postMessage({
command: "initShader",
canvas: offscreen,
screen: { height: window.innerHeight * devicePixelRatio, width: window.innerWidth * devicePixelRatio, devicePixelRatio },
shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate },
}, [offscreen])
if (settingsStore.shaderFramerate == 0) {
killShader()
}
const vs = `attribute vec4 a_position;void main() {gl_Position = a_position;}`;
// setup GLSL program
const program = webglUtils.createProgramFromSources(gl, [vs, fragmentShader2]);
// look up where the vertex data needs to go.
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
// look up uniform locations
const offset = gl.getUniformLocation(program, "u_offset");
const resolutionLocation = gl.getUniformLocation(program, "u_resolution");
const colorStart = gl.getUniformLocation(program, "u_color_start");
const quality = gl.getUniformLocation(program, "u_quality");
const colorEnd = gl.getUniformLocation(program, "u_color_end");
const timeLocation = gl.getUniformLocation(program, "u_time");
// Create a buffer to put three 2d clip space points in
const positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// fill it with a 2 triangles that cover clipspace
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, // first triangle
1, -1,
-1, 1,
-1, 1, // second triangle
1, -1,
1, 1,
]), gl.STATIC_DRAW);
const gl2 = gl
function render(time: number) {
time *= 0.001; // convert to seconds
webglUtils.resizeCanvasToDisplaySize(gl2.canvas);
// Tell WebGL how to convert from clip space to pixels
gl2.viewport(0, 0, gl2.canvas.width, gl2.canvas.height);
// Tell it to use our program (pair of shaders)
gl2.useProgram(program);
// Turn on the attribute
gl2.enableVertexAttribArray(positionAttributeLocation);
// Bind the position buffer.
gl2.bindBuffer(gl2.ARRAY_BUFFER, positionBuffer);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
gl2.vertexAttribPointer(
positionAttributeLocation,
2, // 2 components per iteration
gl2.FLOAT, // the data is 32bit floats
false, // don't normalize the data
0, // 0 = move forward size * sizeof(type) each iteration to get the next position
0, // start at the beginning of the buffer
);
const bounds = canvas.getBoundingClientRect()
const aspectRatio = Math.max(window.screen.width, window.screen.height) * devicePixelRatio
gl2.uniform2f(offset, bounds.left, -bounds.bottom);
// console.log(canvas.getBoundingClientRect().left * window.devicePixelRatio)
gl2.uniform1i(quality, settingsStore.shaderQuality)
gl2.uniform2f(resolutionLocation, aspectRatio, aspectRatio);
gl2.uniform3f(colorStart, props.colorStart[0], props.colorStart[1], props.colorStart[2]);
gl2.uniform3f(colorEnd, props.colorEnd[0], props.colorEnd[1], props.colorEnd[2]);
gl2.uniform1f(timeLocation, time);
gl2.drawArrays(
gl2.TRIANGLES,
0, // offset
6, // num vertices to process
);
}
clearInterval(interval.value)
interval.value = setInterval(() => requestAnimationFrame(render), targetFramerate)
resizeShader()
}
async function reloadShader() {
nextTick(() => {
if (!canvas.value) { return }
let fragmentShader2 = fragmentShader.replace("#define FBM_OCTAVES 6", "#define FBM_OCTAVES " + settingsStore.shaderQuality);
clearInterval(interval.value)
main(canvas.value, fragmentShader2);
function recolorShader() {
worker.postMessage({
command: 'recolorShader',
colorStart: [..._colorStart],
colorEnd: [..._colorEnd]
})
}
watch(() => settingsStore.shaderQuality, (newValue, oldValue) => {
reloadShader()
function resizeShader() {
worker.postMessage({
command: 'resizeShader',
screen: { height: window.innerHeight * devicePixelRatio, width: window.innerWidth * devicePixelRatio, devicePixelRatio },
shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate },
})
}
function killShader() {
worker.postMessage({
command: 'killShader'
})
}
function resumeShader() {
worker.postMessage({
command: 'resumeShader',
shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate },
})
}
watch(() => [settingsStore.shaderFramerate], () => {
if (settingsStore.shaderFramerate == 0) {
killShader()
return
}
resumeShader()
})
watch(() => settingsStore.shaderFramerate, (newValue, oldValue) => {
reloadShader()
watch(() => [settingsStore.shaderQuality], () => {
resizeShader()
})
watch(() => [goStore.current_game?.current_turn], () => {
if (goStore.current_game?.current_turn != goStore.self?.team) {
gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: .2, 2: .3 })
gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 1 })
} else {
gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: .3, 2: 0 })
gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 0 })
}
}, { immediate: true })
watch([_colorEnd, _colorStart], () => {
recolorShader()
})
onMounted(async () => {
reloadShader()
nextTick(() => {
recolorShader()
initShader()
window.addEventListener("resize", resizeShader, true)
})
})
onUnmounted(() => {
clearInterval(interval.value)
window.removeEventListener("resize", resizeShader)
killShader()
})
</script>
<template>
<canvas v-if="settingsStore.shaderQuality != 0" ref="canvas" id="background">test</canvas>
<canvas ref="canvas" id="background" :class="{ pixelated: settingsStore.shaderPixelated }" width="480"
height="270">test</canvas>
</template>
<style scoped>
@@ -154,5 +124,10 @@ onUnmounted(() => {
position: absolute;
height: 100%;
width: 100%;
image-rendering: smooth;
}
#background.pixelated {
image-rendering: pixelated;
}
</style>
+50 -15
View File
@@ -11,7 +11,8 @@ export interface Props {
const props = withDefaults(defineProps<Props>(), {
wrapped: () => false,
tilted: () => true
tilted: () => true,
locked: () => true
})
@@ -21,6 +22,8 @@ const settingsStore = useSettingsStore()
const childCount = ref<number>(0)
const card = ref<HTMLElement>()
function cardClick() {
if (!props.card_id) return
emit("click")
@@ -41,10 +44,15 @@ watch(
props.wrapped ? rotation.value = (Math.random() * 4) - 2 : rotation.value = Math.random() > .5 ? 2 : -2
},
{ immediate: true })
onMounted(() => {
card.value?.style.setProperty("--random-seed", -Math.random() * 10 + 's')
})
</script>
<template>
<div :class="`card ${childCount > 0 ? 'active' : ''} ${wrapped ? 'wrapped' : ''} ${props.locked ? 'locked' : ''}`"
<div ref="card" class="card" :class="{ active: childCount > 0, wrapped, unlocked: !props.locked }"
@contextmenu.prevent="false" @click.left="cardClick" :style="tilted ? `rotate:${rotation}deg;` : ''"
@mouseenter="mouseenter" @mouseleave="mouseleave">
<div id="contextMenuButtons" :ref="(el) => { childCount = ((el as Element)?.childElementCount ?? 0) }">
@@ -66,15 +74,17 @@ watch(
<style scoped>
.card {
transition: 0.025s rotate linear;
--max-height: 250px;
--margin-bottom: calc(var(--max-height) * 0.05);
max-width: 256px;
position: relative;
display: flex;
margin-bottom: 0px;
margin-top: 15px;
transition: all .125s cubic-bezier(0.075, 0.82, 0.165, 1);
margin-top: var(--margin-bottom);
transition: filter .125s cubic-bezier(0.075, 0.82, 0.165, 1), rotate 0.075s cubic-bezier(1, 1.81, .59, 1.42);
isolation: isolate;
pointer-events: all;
max-height: var(--max-height);
}
.card.active {
@@ -82,8 +92,7 @@ watch(
}
.card_image {
max-height: 250px;
transition: all .125s cubic-bezier(0.075, 0.82, 0.165, 1);
max-height: var(--max-height);
z-index: 1;
}
@@ -92,20 +101,46 @@ watch(
outline-offset: -2px;
}
.card.locked {
@keyframes unlocked-float {
from {
transform: none;
filter: drop-shadow(calc(var(--margin-bottom)*.3) var(--margin-bottom) 1px rgba(0, 0, 0, .60));
}
to {
transform: translateY(calc(var(--margin-bottom)*-.25));
filter: drop-shadow(calc(var(--margin-bottom)*.3) calc(var(--margin-bottom)*1.5) 1px rgba(0, 0, 0, .60));
}
}
@keyframes unlocked-rotate {
from {
rotate: -2deg;
}
to {
rotate: 2deg;
}
}
.card.unlocked {
margin-top: 0px;
margin-bottom: 15px;
filter: drop-shadow(5px 15px 2px rgba(0, 0, 0, .60));
margin-bottom: var(--margin-bottom);
filter: drop-shadow(calc(var(--margin-bottom)*.3) var(--margin-bottom) 1px rgba(0, 0, 0, .60));
animation: 2s infinite alternate ease-in-out unlocked-float, 3.1s infinite alternate ease-in-out unlocked-rotate;
animation-delay: var(--random-seed);
animation-play-state: running;
}
.card:not(.unlocked):hover {
animation-play-state: paused;
rotate: none !important;
}
.card.wrapped {
max-width: 10px;
}
.card_image {
max-height: 250px;
}
#contextMenuButtons {
position: absolute;
display: flex;
@@ -115,7 +150,7 @@ watch(
bottom: 0;
width: 99%;
transform: translateY(-0px);
transition: 0.1s transform cubic-bezier(0.075, 0.82, 0.165, 1);
transition: 0.1s transform cubic-bezier(1, 1.81, .59, 1.42);
}
.card.active:hover #contextMenuButtons {
+36 -20
View File
@@ -59,18 +59,21 @@ function showWinnerScreen() {
<template>
<div @contextmenu.prevent="false" id="game_board">
<AnimatedBackground></AnimatedBackground>
<div id="market">
<MarketCards></MarketCards>
</div>
<div id="player_list">
<AnimatedBackground />
<PlayerListElement v-for="p in goStore.current_game?.players" :player="p">
</PlayerListElement>
<div id="player_list_container">
<div id="player_list">
<PlayerListElement v-for="p in goStore.current_game?.players" :player="p">
</PlayerListElement>
</div>
</div>
<div id="current_player">
<template v-for="p in current_players">
<AnimatedBackground :color-start="goStore.self == p ? [.0, .3, .3] : undefined"
:color-end="goStore.self == p ? [.0, 1, 1] : undefined" />
<!-- <AnimatedBackground :color-start="goStore.self == p ? [.0, .3, .3] : undefined"
:color-end="goStore.self == p ? [.0, 1, 1] : undefined" /> -->
<PlayerSlot :player="p"></PlayerSlot>
</template>
@@ -86,7 +89,7 @@ function showWinnerScreen() {
<style scoped>
#game_board {
display: grid;
grid-template-columns: 1fr 400px;
grid-template-columns: 1fr 450px;
grid-template-rows: min-content 1fr;
grid-template-areas:
"market player_list"
@@ -99,30 +102,43 @@ function showWinnerScreen() {
overflow: auto;
position: relative;
isolation: isolate;
background: url('/images/bg-darkgrass.png');
}
#market {
grid-area: market;
background-image: url("/images/bg-sand.png");
border-bottom: 3px solid black;
;
/* background: url('/images/bg-darkgrass.png'); */
}
#current_player {
grid-area: current_player;
}
#player_list {
#market {
display: flex;
flex-direction: row;
justify-content: center;
}
#player_list_container {
grid-area: player_list;
border-left: 5px solid black;
display: flex;
flex-direction: column;
height: 100%;
background-image: url("/images/bg-darkgrass.png");
justify-content: center;
/* background-image: url("/images/bg-darkgrass.png"); */
}
#player_list {
min-height: 90%;
background: rgba(0, 0, 0, 0.35);
border: 5px solid #000;
border-radius: 10px;
filter: drop-shadow(2.5px 7.5px 1px rgba(0, 0, 0, .60));
margin: 10px;
backdrop-filter: blur(10px) contrast(90%) brightness(165%);
background: rgba(26, 70, 91, 0.7);
}
</style>
<style>
#player_list .card_image {
max-width: 64px;
#player_list .card {
--max-height: 90px;
}
</style>
+1 -1
View File
@@ -7,7 +7,7 @@ const displayOptions = ref(false)
<template>
<div :class="`card_overlay`" ref="card">
<img class="settings_button" src="/settings.png" alt="settings" @click="displayOptions = true"/>
<img class="settings_button" src="/images/settings.png" alt="settings" @click="displayOptions = true"/>
</div>
<OptionsDialog :isVisible="displayOptions" @close="displayOptions = false" />
</template>
+10 -3
View File
@@ -12,7 +12,6 @@ const buyableFiregem = computed(() => goStore?.current_game?.gem_stack.at(-1))
<template>
<div id="market">
<AnimatedBackground :color-end="[1, 1, 0]" :color-start="[.3, .3, 0]"></AnimatedBackground>
<CardPreview :tilted="false" :card_id="undefined"></CardPreview>
<div class="separator"></div>
<CardPreview :tilted="false" :card_id="c?.card_id" v-for="c in goStore.current_game?.market"
@@ -55,6 +54,7 @@ const buyableFiregem = computed(() => goStore?.current_game?.gem_stack.at(-1))
<style scoped>
#market {
width: min-content;
display: flex;
flex-direction: row;
justify-content: center;
@@ -62,8 +62,15 @@ const buyableFiregem = computed(() => goStore?.current_game?.gem_stack.at(-1))
z-index: 2;
position: relative;
gap: 2px;
padding: 10px;
;
margin: 10px;
padding-left: 10px;
padding-right: 10px;
padding-bottom: 10px;
border-radius: 10px;
filter: drop-shadow(2.5px 7.5px 1px rgba(0, 0, 0, .60));
backdrop-filter: blur(5px) brightness(80%);
background: rgba(155, 62, 62, 0.46);
}
.separator {
+17 -16
View File
@@ -6,6 +6,9 @@ export interface Props {
isVisible: boolean;
}
const _devicePixelRatio = computed(() => devicePixelRatio)
const _window = computed(() => window)
const props = withDefaults(defineProps<Props>(), {
})
@@ -15,10 +18,6 @@ const close = () => {
emit('close')
}
watch(() => settingsStore.shaderQuality, async () => {
console.log(settingsStore.shaderQuality)
})
</script>
<template>
@@ -40,27 +39,29 @@ watch(() => settingsStore.shaderQuality, async () => {
<span>Shader Quality</span>
</div>
<div class="dialog-body">
<input type="range" min="0" max="10" v-model="settingsStore.shaderQuality">
<input type="range" min="1" max="10" v-model="settingsStore.shaderQuality"
:disabled="settingsStore.shaderFramerate == 0">
</div>
<div class="dialog-footer">
<span>
<template v-if="settingsStore.shaderQuality == 0">
Disabled
</template>
<template v-if="settingsStore.shaderQuality > 0">
{{ settingsStore.shaderQuality }}
</template>
</span>
<span>{{ Math.floor(_window.innerWidth * _devicePixelRatio * settingsStore.shaderQuality / 10) }}x{{
Math.floor(_window.innerHeight *
_devicePixelRatio * settingsStore.shaderQuality / 10) }}</span>
</div>
<div class="dialog-header">
<span>Shader Framerate</span>
</div>
<div class="dialog-body">
<input type="range" min="10" max="60" step="5" v-model="settingsStore.shaderFramerate"
:disabled="settingsStore.shaderQuality == 0">
<input type="range" min="0" max="65" step="5" v-model="settingsStore.shaderFramerate">
</div>
<div class="dialog-footer">
<span>{{ settingsStore.shaderFramerate }} fps</span>
<span v-if="settingsStore.shaderFramerate == 65">vsync</span>
<span v-else>{{ settingsStore.shaderFramerate }} fps</span>
</div>
<div class="dialog-header">
<span>Pixelated</span>
</div>
<div class="dialog-body">
<input type="checkbox" v-model="settingsStore.shaderPixelated">
</div>
</div>
</div>
View File
+14 -8
View File
@@ -2,6 +2,8 @@
import goStore from '@/netcode';
import type { Player } from "~/netcode/Player";
const settingsStore = useSettingsStore()
const view = ref<"main" | "discard">("main")
function show(data: "main" | "discard") {
@@ -18,11 +20,11 @@ const props = defineProps<Props>()
<template>
<div :class="`player_list_element ${goStore.self === props.player ? 'self' : ''}`">
<AnimatedBackground v-if="goStore.self === props.player" :color-start="[.0, .3, .3]" :color-end="[.0, 1, 1]" />
<!-- <AnimatedBackground v-if="goStore.self === props.player" :color-start="[.0, .3, .3]" :color-end="[.0, 1, 1]" /> -->
<div class="button" id="player_list_buttons">
<div class="main_button" @click="show('main')">
<div class="tooltip">Board</div>
<img :src="`/cards/${player.board.at(-1)?.card_id.toString().padStart(3, '0') ?? 'background'}.png`"
<img :src="`/cards/${settingsStore.cardStyle}/${player.board.at(-1)?.card_id.toString().padStart(3, '0') ?? 'background'}.png`"
draggable="false" :class="view === 'main' && 'selected'">
<span class="button_card_number">{{ player.board.length }}</span>
@@ -35,7 +37,7 @@ const props = defineProps<Props>()
</div>
<div class="discard_button" @click="show('discard')">
<div class="tooltip">Discard</div>
<img :src="`/cards/${player.discard_pile.at(-1)?.card_id.toString().padStart(3, '0') ?? 'background'}.png`"
<img :src="`/cards/${settingsStore.cardStyle}/${player.discard_pile.at(-1)?.card_id.toString().padStart(3, '0') ?? 'background'}.png`"
draggable="false" :class="view === 'discard' && 'selected'">
<span class="button_card_number">{{ player.discard_pile.length }}</span>
@@ -62,14 +64,17 @@ const props = defineProps<Props>()
.tooltip {
visibility: visible;
visibility: hidden;
position: absolute;
transition: .1s transform;
background: white;
right: 0;
right: -0px;
border-top-right-radius: 1em;
border-bottom-right-radius: 1em;
padding-right: 1em;
transition-property: visibility, transform;
transition-duration: 0.5s, 0.1s;
transition-timing-function: ease-in-out, ease-in-out;
}
.button {
@@ -96,8 +101,9 @@ const props = defineProps<Props>()
.button div:hover .tooltip {
visibility: visible;
display: block;
transform: translateX(100%);
transition-duration: 0, 0.1s;
}
.button_card_number {
@@ -118,13 +124,13 @@ const props = defineProps<Props>()
grid-template-columns: min-content 1fr;
grid-template-areas:
"button player";
border-bottom: 5px solid black;
border: 5px solid black;
min-height: 200px;
position: relative;
}
.player_list_element.self {
background-image: url('/images/bg-grass.png');
/* background-image: url('/images/bg-grass.png'); */
}
.view {
+18 -8
View File
@@ -60,7 +60,6 @@ function can_buy_fire_gem(): boolean {
function checkEndTurn() {
// TODO: Add a confirmation dialog if resources are left
console.log("Checking end turn")
if (
(!warning.value)
&& has_actions_remaining()) {
@@ -144,9 +143,8 @@ function check_for_guard(player: Player) {
<PlayerStats :player="props.player">
</PlayerStats>
<template v-if="!hide_stack_and_discard">
<button id="end_turn" v-if="isSelfTurn" class="end_turn_button turn_icon"
@click="checkEndTurn">END
TURN</button>
<button id="end_turn" class="end_turn_button turn_icon" :class="{ hidden: !isSelfTurn }"
@click="checkEndTurn">END TURN</button>
<div class="warning"
:class="{ 'warning-fire-gem': warningText == 'P\'tite Fire gem?', 'visible': warning }">
@@ -275,6 +273,10 @@ function check_for_guard(player: Player) {
height: 100%;
}
#end_turn.hidden {
visibility: hidden
}
.stack,
.discard,
.cards_container {
@@ -348,8 +350,8 @@ function check_for_guard(player: Player) {
"stack player"
"discard player";
transition: all 1s;
margin-bottom: -75px;
padding-bottom: 75px;
/* margin-bottom: -75px;
padding-bottom: 75px; */
}
.discard_overlay {
@@ -476,10 +478,18 @@ function check_for_guard(player: Player) {
#player_info {
display: flex;
align-items: center;
min-width: 200px;
}
#player_banner.big>#player_info {
margin-left: 500px;
#player_banner.big {
padding: 0;
left: 50%;
transform: translateX(-50%);
max-width: min-content;
border: 5px solid black;
border-radius: 10px;
filter: drop-shadow(2.5px 7.5px 1px rgba(0, 0, 0, .60));
backdrop-filter: blur(5px) brightness(80%) grayscale(80%);
}
#player_stats {
+2
View File
@@ -45,7 +45,9 @@ const close = () => {
Et n'oubliez pas, la prochaine fois, vous gagnerez! Vous verrez! Vous êtes un gagnant, vous êtes un champion!
Vous êtes le meilleur! Vous êtes
le roi du monde!
<img src="/images/so_back.png" alt="Une image décrivant le meme 'We are so back'" />
</p>
</div>
</div>
</div>
+2
View File
@@ -243,6 +243,8 @@ const router = useRouter();
</p>
<p>Quand vous avez un effet "Sacrifice", vous pouvez fouiller votre défausse pour choisir la carte à
sacrifier. Vous pouvez également sacrifier une carte de la main qui n'est pas encore locked!</p>
<h2>Zoomer sur les cartes</h2>
<p>Pour zoomer sur une carte, maintenez votre clic droit.</p>
</section>
</div>
</div>
-9
View File
@@ -12,13 +12,4 @@ export default defineNuxtConfig({
'@pinia/nuxt',
'@pinia-plugin-persistedstate/nuxt',
],
app: {
head: {
script: [{
src: "/webgl-utils.js"
}, {
src: "https://lygia.xyz/resolve.js"
}]
}
},
})
+7
View File
@@ -8,6 +8,7 @@
"hasInstallScript": true,
"dependencies": {
"@pinia/nuxt": "^0.5.1",
"gsap": "^3.12.5",
"nuxt": "^3.11.2",
"pinia": "^2.1.7",
"vue": "^3.4.21",
@@ -5670,6 +5671,12 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
"node_modules/gsap": {
"version": "3.12.5",
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.5.tgz",
"integrity": "sha512-srBfnk4n+Oe/ZnMIOXt3gT605BX9x5+rh/prT2F1SsNJsU1XuMiP0E2aptW481OnonOGACZWBqseH5Z7csHxhQ==",
"license": "Standard 'no charge' license: https://gsap.com/standard-license. Club GSAP members get more: https://gsap.com/licensing/. Why GreenSock doesn't employ an MIT license: https://gsap.com/why-license/"
},
"node_modules/gzip-size": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-7.0.0.tgz",
+1
View File
@@ -11,6 +11,7 @@
},
"dependencies": {
"@pinia/nuxt": "^0.5.1",
"gsap": "^3.12.5",
"nuxt": "^3.11.2",
"pinia": "^2.1.7",
"vue": "^3.4.21",
-1
View File
@@ -18,7 +18,6 @@ import goStore, { setReady } from "@/netcode";
<LoginView v-if="goStore.context === 'login'"></LoginView>
<GameBoard v-if="goStore.context != 'lobby' && goStore.context != 'login'"></GameBoard>
<LobbyView v-if="goStore.context == 'lobby'"></LobbyView>
<RulesDisplay v-if="goStore.context == 'rules'"></RulesDisplay>
</div>
</template>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+3 -2
View File
@@ -8,8 +8,9 @@ enum CardStyles {
export const useSettingsStore = defineStore("settingstore", {
state: () => ({
cardStyle: CardStyles.HERON,
shaderQuality: 0,
shaderFramerate: 10,
shaderQuality: 3,
shaderFramerate: 0,
shaderPixelated: true
}),
actions: {
switchCardStyles() {