Commit e223f4d5 authored by 董政锦's avatar 董政锦

fix: 修复 game5 跑道和倒计时蒙层在375px宽度问题;

parent 43ac0066
......@@ -232,11 +232,12 @@ onBeforeUnmount(() => {
<div class="rule-title">游戏规则</div>
<div class="rule-txt">
<!-- 微信用户通过扫描大屏幕二维码参与,可容纳多人同时进行,摇晃手机控制赛马奔跑,摇晃越快赛马跑得越快,游戏结束按分值高低先后排名,有机会获得新疆福彩定制周边礼品一份。 -->
1. 主持人开赛即可摇手机,单次摇晃得 5 分;
2. 得分越高大屏幕上面的马跑得越快;
3. 限时比拼摇速,结束后按积分排名;
4. 高分用户有机会领取新疆福彩定制周边;
5. 禁止外挂作弊,违规账号取消成绩与领奖资格;
1. 主持人开赛即可摇手机,单次摇晃得 5 分;<br />
2. 得分越高大屏幕上面的马跑得越快;<br />
3. 限时比拼摇速,结束后按积分排名;<br />
4. 高分用户有机会领取新疆福彩定制周边;<br />
5. 禁止外挂作弊,违规账号取消成绩与领奖资格;<br /><br />
祝您游戏愉快!
</div>
</div>
<div class="rule-close">
......
......@@ -191,34 +191,10 @@ function handleRoomBack() {
}, 200)
}
const touchHandler = () => {
if (isGuPlaying.value) {
return
}
tick.value += 5
const hitHandler = (score: number) => {
tick.value += score
submitScore()
playGame1Music();
isGuPlaying.value = true
let frameIndex = 0
const playNextFrame = () => {
currentGu.value = guFrames[frameIndex] ?? imageUrls.gu01
frameIndex += 1
if (frameIndex >= guFrames.length) {
guFrameTimer = window.setTimeout(() => {
currentGu.value = guFrames[0]
isGuPlaying.value = false
guFrameTimer = undefined
}, 200)
return
}
guFrameTimer = window.setTimeout(playNextFrame, 200)
}
playNextFrame()
playGame1Music()
}
if (wechat) {
......@@ -350,10 +326,10 @@ const rankCloseHandler = () => {
</template>
<LoadingView v-if="currentView === 'loading'" :image-urls="imageUrls" :user-join-status="userJoinStatus"
@touchGameRule="showGameRuleHandler" />
<PlayingView v-else-if="currentView === 'playing'" :image-urls="imageUrls" :tick="tick" :rank="rank"
<!-- <PlayingView v-else :image-urls="imageUrls" :tick="tick" :rank="rank" -->
<PlayingView v-else-if="currentView === 'playing'" :image-urls="imageUrls" :tick="tick" :rank="rank"
:current-gu="currentGu" :countdown-interval="countdownInterval" :is-div-desc-visible="isDivDescVisible"
@touch="touchHandler" />
<!-- <PlayingView v-else :image-urls="imageUrls" :tick="tick" :rank="rank" -->
@hit="hitHandler" />
<!-- <ScoreView v-else :image-urls="imageUrls" :rank="rank" :tick="tick" /> -->
</MobileStage>
<div v-else style="text-align: center; width: 100vw; height: 100vh; line-height: 30; font-size: 20px;">
......
<script setup lang="ts">
defineProps<{
import { assetUrl } from '@/commons/assets.ts'
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
const props = defineProps<{
imageUrls: Record<string, string>
countdownInterval: number
isDivDescVisible: boolean
......@@ -8,13 +11,409 @@ defineProps<{
currentGu: string
}>()
defineEmits<{
touch: []
const emit = defineEmits<{
hit: [score: number]
}>()
// ── 圈圈丢出动画帧 (31帧, 原图325×733 → 渲染宽165) ──
const circleAnimFrames = Array.from({ length: 31 }, (_, i) =>
assetUrl(`game5/circle-animation/qcnf-quan_${String(i).padStart(2, '0')}.png`)
)
// ── 四种马的帧 (各21帧, 原图196×204 → 渲染180×160) ──
const horseFrames: Record<number, string[]> = {}
for (let h = 1; h <= 4; h++) {
horseFrames[h] = Array.from({ length: 21 }, (_, i) =>
assetUrl(`game5/horse${h}/no-circle/qcnf-ma${h}_${String(i).padStart(2, '0')}.png`)
)
}
// ── 布局常量 ──
const STAGE_WIDTH = 750
const STAGE_HEIGHT = 1624
const RUNWAY_TOP = 696
const RUNWAY_HEIGHT = 306
const RUNWAY_HALF_Y = RUNWAY_TOP + RUNWAY_HEIGHT / 2 // 849
const HORSE_WIDTH = 180
const HORSE_HEIGHT = 200 // 原图196×204→渲染180×200,留足跳跃帧头部空间
const HORSE_Y = RUNWAY_TOP + (RUNWAY_HEIGHT - HORSE_HEIGHT) / 2 // 749
const CIRCLE_THROW_MS = 1200 // 圈圈飞行时长
const HIT_FADE_MS = 400 // 被套中后马消失延迟
// ── 难度递增参数:随游戏时间从 0→60s 指数曲线变化 ──
const HORSE_SPEED_MIN = 40 // px/s 初始速度
const HORSE_SPEED_MAX = 280 // px/s 最大速度(60s 时,7倍提升)
const SPAWN_MIN_START = 3000 // ms 初始最短生成间隔
const SPAWN_MIN_END = 800 // ms 最终最短生成间隔
const SPAWN_MAX_START = 6000 // ms 初始最长生成间隔
const SPAWN_MAX_END = 1800 // ms 最终最长生成间隔
const MAX_HORSES_ON_SCREEN = 8 // 场上最多马匹数
// 马动画帧率控制(参考 Rank1View:每 N 个 raf tick 切一帧 → ~20fps)
const HORSE_FRAME_TICK = 3
// circle-outer: bottom:72, height:345 → outerTop = 1624 - 72 - 345 = 1207
// circle-inner 圆心 Y = outerTop + 345/2 = 1379.5
const CIRCLE_CENTER_X = 375
const CIRCLE_CENTER_Y = STAGE_HEIGHT - 72 - 345 + 345 / 2 // 1379.5
const CIRCLE_ANIM_WIDTH = 165
const CIRCLE_ANIM_HEIGHT = Math.round(733 * (CIRCLE_ANIM_WIDTH / 325)) // ≈ 372
// ── 类型 ──
interface Horse {
id: number
type: number
x: number
frameIdx: number
frameTimer: number // 帧切换滴答计数器(每帧+1,满 HORSE_FRAME_TICK 切帧)
alive: boolean
dying: boolean
dieAt: number
}
interface CircleThrow {
y: number
frameIdx: number
startY: number
startTime: number
}
interface HitEffect {
id: number
x: number
y: number
score: number
}
// ── 响应式状态 ──
const horses = ref<Horse[]>([])
const circleThrow = ref<CircleThrow | null>(null)
const isCircleFlying = ref(false)
const hitEffects = ref<HitEffect[]>([])
// ── 内部变量 ──
let horseIdSeq = 0
let hitEffectIdSeq = 0
let raf = 0
let prevTs = 0
let spawnAccum = 0
let nextSpawn = 2000
let gameElapsed = 0 // 游戏已进行时间 ms(用于难度递增)
const HIT_EFFECT_MS = 800 // "+10" 动画持续时长
// ── 游戏状态 ──
const gameStarted = ref(false) // 3-2-1 倒计时完成标志,控制动画启停
const gameActive = ref(true) // 游戏进行中(倒计时到 0 后变 false)
const preCountdown = ref(3) // 开场倒计时 3/2/1
const preCountdownKey = ref(0) // 强制动画重触发
const preCountdownVisible = ref(false) // 开场倒计时可见
let preCountdownTimer: ReturnType<typeof setTimeout> | undefined
// ── Web Audio API 音效 ──
let audioContext: AudioContext | null = null
function getAudioContext(): AudioContext {
if (!audioContext) {
audioContext = new AudioContext()
}
if (audioContext.state === 'suspended') {
audioContext.resume()
}
return audioContext
}
/** 丢圈"咻"声 — 短促上升频率扫频 */
function playThrowSound() {
try {
const ctx = getAudioContext()
const now = ctx.currentTime
const osc = ctx.createOscillator()
const gain = ctx.createGain()
osc.connect(gain)
gain.connect(ctx.destination)
osc.type = 'sine'
osc.frequency.setValueAtTime(400, now)
osc.frequency.exponentialRampToValueAtTime(800, now + 0.08)
osc.frequency.exponentialRampToValueAtTime(200, now + 0.2)
gain.gain.setValueAtTime(0.25, now)
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.2)
osc.start(now)
osc.stop(now + 0.2)
} catch { /* ignore audio errors */ }
}
/** 套中"叮"声 — 两个简短的上升音 */
function playHitSound() {
try {
const ctx = getAudioContext()
const now = ctx.currentTime
// 第一音 880 Hz
const o1 = ctx.createOscillator()
const g1 = ctx.createGain()
o1.connect(g1)
g1.connect(ctx.destination)
o1.type = 'sine'
o1.frequency.setValueAtTime(880, now)
g1.gain.setValueAtTime(0.3, now)
g1.gain.exponentialRampToValueAtTime(0.01, now + 0.25)
o1.start(now)
o1.stop(now + 0.25)
// 第二音 1320 Hz 延时 0.08s
const o2 = ctx.createOscillator()
const g2 = ctx.createGain()
o2.connect(g2)
g2.connect(ctx.destination)
o2.type = 'sine'
o2.frequency.setValueAtTime(1320, now + 0.08)
g2.gain.setValueAtTime(0.22, now + 0.08)
g2.gain.exponentialRampToValueAtTime(0.01, now + 0.35)
o2.start(now + 0.08)
o2.stop(now + 0.35)
} catch { /* ignore audio errors */ }
}
// ── 清理全部音效(组件卸载时) ──
function disposeAudioContext() {
if (audioContext) {
audioContext.close().catch(() => {})
audioContext = null
}
}
// ── 难度系数(0~1,游戏 60s,指数曲线让后期明显加速) ──
function difficultyFactor(): number {
return Math.min(gameElapsed / 60000, 1)
}
// ── 当前马速度(二次曲线:前期缓、后期猛) ──
function currentSpeed(): number {
const t = difficultyFactor()
// 二次曲线:t² 让前期平缓、30s后加速明显
return HORSE_SPEED_MIN + (HORSE_SPEED_MAX - HORSE_SPEED_MIN) * t * t
}
// ── 生成一匹马 ──
function spawnHorse() {
const type = Math.floor(Math.random() * 4) + 1
horses.value.push({
id: ++horseIdSeq,
type,
x: -HORSE_WIDTH - Math.random() * 200,
frameIdx: Math.floor(Math.random() * 21), // 随机起始帧,避免多马同帧
frameTimer: 0,
alive: true,
dying: false,
dieAt: 0,
})
}
// ── 点击丢出圈圈 ──
function throwCircle() {
if (!gameActive.value || !gameStarted.value || isCircleFlying.value) return
isCircleFlying.value = true
playThrowSound()
circleThrow.value = {
y: CIRCLE_CENTER_Y,
frameIdx: 0,
startY: CIRCLE_CENTER_Y,
startTime: performance.now(),
}
}
// ── 碰撞检测(收紧:圈心必须落在马身体中间区域) ──
function checkHit(ts: number): boolean {
if (!circleThrow.value) return false
const ct = circleThrow.value
const cx = CIRCLE_CENTER_X
const cy = ct.y
// 命中区域:马身体宽度 20%~80%、高度 20%~80%(中间 60% 区域)
const hitMarginX = HORSE_WIDTH * 0.2
const hitMarginY = HORSE_HEIGHT * 0.2
const halfHitW = HORSE_WIDTH / 2 - hitMarginX
const halfHitH = HORSE_HEIGHT / 2 - hitMarginY
for (const h of horses.value) {
if (!h.alive || h.dying) continue
const horseCenterX = h.x + HORSE_WIDTH / 2
const horseCenterY = HORSE_Y + HORSE_HEIGHT / 2
if (
Math.abs(cx - horseCenterX) <= halfHitW &&
Math.abs(cy - horseCenterY) <= halfHitH
) {
h.dying = true
h.dieAt = ts
circleThrow.value = null
isCircleFlying.value = false
// 在马头顶生成 "+10" 飘字效果
const effectId = ++hitEffectIdSeq
hitEffects.value.push({
id: effectId,
x: horseCenterX,
y: HORSE_Y,
score: 10,
})
setTimeout(() => {
const idx = hitEffects.value.findIndex(e => e.id === effectId)
if (idx >= 0) hitEffects.value.splice(idx, 1)
}, HIT_EFFECT_MS)
playHitSound()
emit('hit', 10)
return true
}
}
return false
}
// ── 主循环 ──
function loop(ts: number) {
if (!gameActive.value) {
// 游戏结束:保持最后一帧,不再更新动画
raf = requestAnimationFrame(loop)
return
}
if (!prevTs) prevTs = ts
const dt = Math.min(ts - prevTs, 50)
prevTs = ts
gameElapsed += dt
// 1. 更新飞行中的圈圈
if (circleThrow.value) {
const ct = circleThrow.value
const elapsed = ts - ct.startTime
const progress = Math.min(elapsed / CIRCLE_THROW_MS, 1)
ct.y = ct.startY - (ct.startY - RUNWAY_HALF_Y) * progress
ct.frameIdx = Math.min(Math.floor(progress * 31), 30)
if (checkHit(ts)) {
// 命中:已处理
} else if (progress >= 1) {
// 落空
circleThrow.value = null
isCircleFlying.value = false
}
}
// 2. 更新马匹位置(速度随难度递增,帧率控制防闪烁)
const speed = currentSpeed()
for (let i = horses.value.length - 1; i >= 0; i--) {
const h = horses.value[i]!
if (h.dying) {
if (ts - h.dieAt >= HIT_FADE_MS) {
horses.value.splice(i, 1)
}
continue
}
h.x += speed * (dt / 1000)
// 帧率控制:每 HORSE_FRAME_TICK 次循环切换一帧(约 20fps)
h.frameTimer++
if (h.frameTimer >= HORSE_FRAME_TICK) {
h.frameTimer = 0
h.frameIdx = (h.frameIdx + 1) % 21
}
if (h.x > STAGE_WIDTH + 120) {
horses.value.splice(i, 1)
}
}
// 3. 生成新马(难度递增:间隔越来越短、数量受上限约束)
spawnAccum += dt
if (spawnAccum >= nextSpawn) {
spawnAccum = 0
const t = difficultyFactor()
const t2 = t * t // 二次曲线与速度同步
const minSpawn = SPAWN_MIN_START + (SPAWN_MIN_END - SPAWN_MIN_START) * t2
const maxSpawn = SPAWN_MAX_START + (SPAWN_MAX_END - SPAWN_MAX_START) * t2
nextSpawn = minSpawn + Math.random() * (maxSpawn - minSpawn)
if (horses.value.length < MAX_HORSES_ON_SCREEN) {
spawnHorse()
}
}
raf = requestAnimationFrame(loop)
}
// ── 预加载所有帧图片,避免运行时切换帧的闪烁 ──
function preloadFrames() {
// 圈圈动画 31 帧
for (const url of circleAnimFrames) {
const img = new Image()
img.src = url
}
// 四种马各 21 帧
for (const frames of Object.values(horseFrames)) {
for (const url of frames) {
const img = new Image()
img.src = url
}
}
}
// ── 开场 3-2-1 倒计时 ──
function startPreGameCountdown() {
preCountdown.value = 3
preCountdownVisible.value = true
const tick = () => {
preCountdownKey.value++
if (preCountdown.value <= 1) {
preCountdownVisible.value = false
preCountdownTimer = undefined
// 倒计时结束 → 启动游戏
gameStarted.value = true
gameActive.value = true
prevTs = performance.now()
spawnHorse()
setTimeout(spawnHorse, 800)
setTimeout(spawnHorse, 1600)
raf = requestAnimationFrame(loop)
return
}
preCountdown.value--
preCountdownTimer = setTimeout(tick, 1000)
}
preCountdownTimer = setTimeout(tick, 1000)
}
function stopPreCountdown() {
if (preCountdownTimer) {
clearTimeout(preCountdownTimer)
preCountdownTimer = undefined
}
}
// ── 监听父组件倒计时,到 0 时暂停游戏 ──
watch(() => props.countdownInterval, (val) => {
if (val <= 0 && gameActive.value) {
gameActive.value = false
isCircleFlying.value = false
circleThrow.value = null
}
})
onMounted(() => {
preloadFrames()
// 先启动音频上下文(需用户首次交互后 resume,此处尝试提前激活)
try { getAudioContext() } catch { /* ignore */ }
// 开场 3-2-1 倒计时
startPreGameCountdown()
})
onBeforeUnmount(() => {
cancelAnimationFrame(raf)
stopPreCountdown()
disposeAudioContext()
})
</script>
<template>
<div class="h5-page game-stage">
<div class="game-stage">
<!-- 开场 3-2-1 倒计时遮罩 -->
<div v-if="preCountdownVisible" class="pre-countdown-overlay">
<div :key="preCountdownKey" class="pre-countdown-number">{{ preCountdown }}</div>
</div>
<Transition name="div-desc-slide" appear>
<div v-if="isDivDescVisible" class="div-desc">
<span class="desc-clock" aria-hidden="true"></span>
......@@ -33,23 +432,98 @@ defineEmits<{
<div class="txt-bold">{{ tick }}</div>
</div>
</div>
<!-- <div class="bg-bottom"></div> -->
<div class="paodao-runway"></div>
<div class="circle-outer" @click="$emit('touch')">
<div class="paodao-runway-container">
<div class="paodao-runway" :class="{ 'paodao-paused': !gameActive }"></div>
</div>
<!-- 奔跑的马匹(改用 <img> 防闪烁,参考 Rank1View) -->
<template v-if="gameStarted">
<div
v-for="horse in horses"
:key="horse.id"
class="horse"
:class="{ 'horse-dying': horse.dying }"
:style="{ transform: `translateX(${horse.x}px)` }"
>
<img
class="horse-img"
:src="horseFrames[horse.type]?.[horse.frameIdx] ?? ''"
alt="horse"
/>
<div class="horse-shadow" :class="{ 'shadow-dying': horse.dying }"></div>
</div>
</template>
<!-- 套中加分飘字效果 -->
<div
v-for="effect in hitEffects"
:key="effect.id"
class="hit-effect"
:style="{ left: effect.x + 'px', top: effect.y + 'px' }"
>+{{ effect.score }}</div>
<!-- 飞行中的圈圈动画 -->
<div
v-if="gameStarted && circleThrow"
class="circle-throw"
:style="{
transform: `translate(${CIRCLE_CENTER_X - CIRCLE_ANIM_WIDTH / 2}px, ${circleThrow.y - CIRCLE_ANIM_HEIGHT / 2}px)`,
width: CIRCLE_ANIM_WIDTH + 'px',
height: CIRCLE_ANIM_HEIGHT + 'px',
backgroundImage: `url(${circleAnimFrames[circleThrow.frameIdx]})`,
}"
></div>
<div class="circle-outer" :class="{ 'circle-disabled': !gameActive || !gameStarted, 'circle-paused': !gameActive }" @click="throwCircle">
<div class="circle-inner"></div>
</div>
<div class="play-click">点击按钮,开始套圈</div>
<div class="play-click">{{ !gameStarted ? '准备开始…' : !gameActive ? '游戏结束' : '点击按钮,开始套圈' }}</div>
</div>
</template>
<style scoped>
.game-stage {
position: absolute;
left: 0;
top: 0;
width: 750px;
height: 1624px;
margin: 0;
padding: 0;
}
/* ── 开场 3-2-1 倒计时遮罩 ── */
/* 必须使用 MobileStage 的 viewport CSS 变量,才能覆盖缩放产生的侧边空白 */
.pre-countdown-overlay {
position: absolute;
left: var(--stage-viewport-left);
top: var(--stage-viewport-top);
width: var(--stage-viewport-width);
height: var(--stage-viewport-height);
z-index: 50;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.45);
pointer-events: none;
}
.pre-countdown-number {
color: #FFD700;
font-size: 200px;
font-weight: 900;
line-height: 1;
text-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
animation: countdown-pop 0.9s ease both;
}
@keyframes countdown-pop {
0% { opacity: 0; transform: scale(0.3); }
25% { opacity: 1; transform: scale(1.15); }
55% { opacity: 1; transform: scale(1); }
100% { opacity: 0; transform: scale(0.8); }
}
.div-desc {
position: absolute;
top: 150px;
......@@ -108,16 +582,6 @@ defineEmits<{
transform: translateX(0);
}
.bg-bottom {
position: absolute;
bottom: 0;
left: var(--stage-viewport-left, 0);
width: var(--stage-viewport-width, 750px);
height: 479px;
background: v-bind('imageUrls.bg2') center/cover;
opacity: 0.6;
}
.img-logo {
position: absolute;
top: 66px;
......@@ -150,6 +614,7 @@ defineEmits<{
transform: translate(-50%, -50%);
font-size: 24px;
color: #A10A06;
z-index: 2;
.txt-bold {
font-size: 40px;
......@@ -166,27 +631,119 @@ defineEmits<{
}
}
.paodao-runway {
/* ── 跑道容器:撑满 viewport 宽度并裁剪溢出 ── */
.paodao-runway-container {
position: absolute;
left: 0;
left: var(--stage-viewport-left, 0);
top: 696px;
width: 750px;
width: var(--stage-viewport-width, 750px);
height: 306px;
overflow: hidden;
z-index: 2;
}
/* ── 跑道内层:2x 宽度通过 transform 滚动,适配任意 viewport 宽 ── */
.paodao-runway {
width: 200%;
height: 100%;
background: v-bind('imageUrls.paodao') repeat-x;
background-size: auto 100%;
animation: runway-scroll 8s linear infinite;
}
@keyframes runway-scroll {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
/* ── 跑道暂停 ── */
.paodao-paused {
animation-play-state: paused;
}
/* ── 马匹 ── */
.horse {
position: absolute;
left: 0;
top: v-bind('HORSE_Y + "px"');
width: 180px;
height: 200px;
z-index: 3;
will-change: transform;
}
.horse-img {
width: 100%;
height: 100%;
object-fit: cover;
}
.horse-dying {
opacity: 0;
transition: opacity 0.3s ease, transform 0.3s ease;
}
/* ── 马匹脚底阴影 ── */
.horse-shadow {
position: absolute;
bottom: 12px;
left: 50%;
width: 110px;
height: 18px;
transform: translateX(-50%);
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.38) 0%, rgba(0, 0, 0, 0.12) 40%, rgba(0, 0, 0, 0) 72%);
border-radius: 50%;
pointer-events: none;
}
.shadow-dying {
opacity: 0;
transition: opacity 0.3s ease;
}
/* ── 套中加分飘字 ── */
.hit-effect {
position: absolute;
z-index: 20;
color: #FFD700;
font-size: 40px;
font-weight: 900;
pointer-events: none;
text-shadow: 0 3px 6px rgba(0, 0, 0, 0.6);
animation: score-float 0.8s ease-out forwards;
white-space: nowrap;
transform: translate(-50%, -100%);
}
@keyframes score-float {
0% {
background-position-x: 0;
opacity: 1;
transform: translate(-50%, -100%) scale(0.5);
}
30% {
opacity: 1;
transform: translate(-50%, -130%) scale(1.1);
}
100% {
background-position-x: -750px;
opacity: 0;
transform: translate(-50%, -220%) scale(0.8);
}
}
/* ── 飞行中的圈圈 ── */
.circle-throw {
position: absolute;
left: 0;
top: 0;
background-position: center;
background-size: contain;
background-repeat: no-repeat;
z-index: 10;
pointer-events: none;
will-change: transform, background-image;
image-rendering: auto;
}
.circle-outer {
position: absolute;
left: 50%;
......@@ -196,6 +753,16 @@ defineEmits<{
background: v-bind('imageUrls.circleBg') center/cover;
transform: translateX(-50%);
animation: circle-spin 3s linear infinite;
z-index: 4;
}
.circle-disabled {
pointer-events: none;
opacity: 0.5;
}
.circle-paused {
animation-play-state: paused;
}
@keyframes circle-spin {
......@@ -227,5 +794,6 @@ defineEmits<{
font-weight: 400;
font-size: 26px;
color: #FFFFFF;
z-index: 5;
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment