...
This commit is contained in:
@@ -168,25 +168,7 @@
|
||||
}
|
||||
|
||||
|
||||
#result-overlay {
|
||||
position: fixed; /* 화면 전체에 고정 */
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw; /* 뷰포트 너비 100% */
|
||||
height: 100vh; /* 뷰포트 높이 100% */
|
||||
background-color: rgba(0, 0, 0, 0.75); /* 반투명 검은 배경 */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 100;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
#result-overlay.visible {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#result-modal {
|
||||
background-color: white;
|
||||
padding: 20px 40px;
|
||||
@@ -350,6 +332,14 @@
|
||||
/*<![CDATA[*/
|
||||
const puzzleData = /*[[${puzzle}]]*/ null;
|
||||
/*]]>*/
|
||||
|
||||
if (puzzleData) {
|
||||
window.pageContext = {
|
||||
pageType: 'game',
|
||||
gameType: 'NONOGRAM',
|
||||
contextId: puzzleData.id
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -700,55 +690,9 @@
|
||||
hintBtn.disabled = (points <= 0 || isGameFinished);
|
||||
}
|
||||
|
||||
/**
|
||||
* (★ 신규) 노노그램 랭킹 등록을 처리하는 함수
|
||||
* 이 함수는 user.js에 정의된 공통 submitRank 함수를 호출합니다.
|
||||
*/
|
||||
async function submitNonogramRank(completionTime, hintsUsed) {
|
||||
const playerName = prompt("랭킹에 등록할 이름을 입력하세요:", "Player");
|
||||
if (!playerName || playerName.trim() === "") return;
|
||||
|
||||
try {
|
||||
// (★ 신규) user.js의 공통 submitRank 함수 호출
|
||||
// 주 점수(primaryScore) = 완료 시간(초) (낮을수록 좋음)
|
||||
// 보조 점수(secondaryScore) = 사용한 힌트 수(5-남은포인트) (낮을수록 좋음)
|
||||
await submitRank(
|
||||
'NONOGRAM', // GameType
|
||||
puzzleData.id, // ContextId (퍼즐 고유 ID)
|
||||
playerName.trim(), // playerName
|
||||
completionTime, // primaryScore (시간)
|
||||
hintsUsed // secondaryScore (힌트 사용 횟수)
|
||||
);
|
||||
|
||||
alert("랭킹이 등록되었습니다!");
|
||||
// 랭킹 등록 버튼 비활성화 (중복 제출 방지)
|
||||
const submitBtn = document.getElementById('modal-submit-rank-btn');
|
||||
if (submitBtn) submitBtn.disabled = true;
|
||||
|
||||
} catch (error) {
|
||||
console.error("Rank submission failed:", error);
|
||||
alert("랭킹 등록에 실패했습니다: " + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (★ 수정) 성공/실패 모달 (랭킹 등록 버튼 추가를 위해 ID 할당 기능 추가)
|
||||
*/
|
||||
function showResultModal(config) {
|
||||
modalTitle.textContent = config.title;
|
||||
modalMessage.textContent = config.message;
|
||||
modalButtons.innerHTML = '';
|
||||
config.buttons.forEach(btnInfo => {
|
||||
const button = document.createElement('button');
|
||||
button.textContent = btnInfo.text;
|
||||
button.className = btnInfo.class || '';
|
||||
button.onclick = btnInfo.action;
|
||||
if (btnInfo.id) button.id = btnInfo.id; // (★ 신규) 버튼 ID 할당 기능
|
||||
modalButtons.appendChild(button);
|
||||
});
|
||||
resultOverlay.classList.remove('hidden');
|
||||
setTimeout(() => resultOverlay.classList.add('visible'), 10);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -759,11 +703,12 @@
|
||||
isGameFinished = true;
|
||||
document.querySelector('.puzzle-grid-container').style.pointerEvents = 'none';
|
||||
hintBtn.disabled = true;
|
||||
showResultModal({
|
||||
title: 'Failure', message: '포인트를 모두 사용했습니다.', buttons: [
|
||||
{ text: '재시도 (Retry)', class: 'primary', action: () => window.location.reload() },
|
||||
{ text: '홈으로 (Home)', action: () => window.location.href = '/' }
|
||||
]
|
||||
showGameSuccessModal({
|
||||
gameType: 'NONOGRAM',
|
||||
contextId: puzzleData.id,
|
||||
successMessage: `퍼즐 완성! (시간: ${completionTimeSeconds}초, 힌트 사용: ${hintsUsed}개)`,
|
||||
primaryScore: completionTimeSeconds,
|
||||
secondaryScore: hintsUsed
|
||||
});
|
||||
}
|
||||
|
||||
@@ -797,7 +742,10 @@
|
||||
img.style.left = `${left}px`;
|
||||
img.style.width = `${gridRect.width}px`;
|
||||
img.style.height = `${gridRect.height}px`;
|
||||
img.src = (img.id === 'grayscale-reveal') ? puzzleData.grayscaleImage : puzzleData.originalImage;
|
||||
// [수정] Base64 대신 URL 경로를 사용하도록 변경
|
||||
img.src = (img.id === 'grayscale-reveal')
|
||||
? `/puzzle/images/${puzzleData.grayscaleImageFile}`
|
||||
: `/puzzle/images/${puzzleData.originalImageFile}`;
|
||||
});
|
||||
|
||||
// --- 애니메이션 순차 실행 ---
|
||||
@@ -807,19 +755,12 @@
|
||||
originalImg.style.opacity = '1';
|
||||
setTimeout(() => {
|
||||
// (★ 수정) 모달 버튼 설정에 "랭킹 등록" 버튼 추가
|
||||
showResultModal({
|
||||
title: 'Success! 🎉',
|
||||
message: `퍼즐을 완성했습니다! (시간: ${completionTimeSeconds}초, 힌트 사용: ${hintsUsed}개)`,
|
||||
buttons: [
|
||||
{
|
||||
text: '랭킹 등록',
|
||||
class: 'primary',
|
||||
id: 'modal-submit-rank-btn', // (★ 신규) 랭킹 제출 버튼
|
||||
action: () => submitNonogramRank(completionTimeSeconds, hintsUsed)
|
||||
},
|
||||
{ text: '다른 문제 풀기', action: () => window.location.href = '/puzzle/play' },
|
||||
{ text: '홈으로', action: () => window.location.href = '/' }
|
||||
]
|
||||
showGameSuccessModal({
|
||||
gameType: 'NONOGRAM',
|
||||
contextId: puzzleData.id,
|
||||
successMessage: `퍼즐 완성! (시간: ${completionTimeSeconds}초, 힌트 사용: ${hintsUsed}개)`,
|
||||
primaryScore: completionTimeSeconds,
|
||||
secondaryScore: hintsUsed
|
||||
});
|
||||
}, 2000);
|
||||
}, 2000);
|
||||
@@ -850,7 +791,7 @@
|
||||
checkAndLockCompletedLines(hintAffectedRows, hintAffectedCols);
|
||||
checkWinCondition();
|
||||
} else {
|
||||
alert("더 이상 사용할 힌트가 없습니다!");
|
||||
showAlert("알림","더 이상 사용할 힌트가 없습니다!");
|
||||
points++;
|
||||
updatePointsDisplay();
|
||||
}
|
||||
@@ -880,26 +821,7 @@
|
||||
*/
|
||||
let currentPuzzleData = null; // 업로드 성공 시 퍼즐 데이터 저장
|
||||
|
||||
// (★ 수정 없음) 업로드 페이지용 성공 애니메이션 함수
|
||||
function showSuccessAnimation() {
|
||||
if (!currentPuzzleData) return;
|
||||
|
||||
const puzzleContainer = document.getElementById('puzzle-container');
|
||||
const grayscaleImg = document.getElementById('grayscale-reveal');
|
||||
const originalImg = document.getElementById('original-reveal');
|
||||
|
||||
grayscaleImg.src = currentPuzzleData.grayscaleImage;
|
||||
originalImg.src = currentPuzzleData.originalImage;
|
||||
|
||||
puzzleContainer.style.transition = 'opacity 0.5s';
|
||||
puzzleContainer.style.opacity = '0';
|
||||
grayscaleImg.style.opacity = '1';
|
||||
|
||||
setTimeout(() => {
|
||||
grayscaleImg.style.opacity = '0';
|
||||
originalImg.style.opacity = '1';
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// (★ 수정 없음) 업로드 페이지용 퍼즐 미리보기 그리기 함수
|
||||
function drawPuzzle(puzzleData) {
|
||||
@@ -947,106 +869,6 @@
|
||||
}
|
||||
|
||||
|
||||
// upload.js의 DOMContentLoaded 리스너
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
const createBtn = document.getElementById('createBtn');
|
||||
|
||||
// createBtn이 없는 nonogram.html(게임 페이지)에서는 이 리스너가 아무것도 실행하지 않음.
|
||||
if (!createBtn) {
|
||||
return;
|
||||
}
|
||||
|
||||
// (업로드 페이지 전용 로직)
|
||||
createBtn.addEventListener('click', async () => {
|
||||
const uploader = document.getElementById('imageUploader');
|
||||
const statusDiv = document.getElementById('status');
|
||||
const puzzleContainer = document.getElementById('puzzle-container');
|
||||
const testSuccessBtn = document.getElementById('test-success-btn');
|
||||
const deleteBtn = document.getElementById('delete-btn');
|
||||
const playBtn = document.getElementById('play-btn');
|
||||
|
||||
if (uploader.files.length === 0) {
|
||||
statusDiv.textContent = '이미지 파일을 선택해주세요.';
|
||||
return;
|
||||
}
|
||||
|
||||
const imageFile = uploader.files[0];
|
||||
const formData = new FormData();
|
||||
formData.append('imageFile', imageFile);
|
||||
|
||||
statusDiv.textContent = '문제를 생성하는 중...';
|
||||
puzzleContainer.innerHTML = '';
|
||||
|
||||
try {
|
||||
// (★ 수정) API 경로 변경 -> 통합 컨트롤러의 /puzzle/upload.bjx 호출
|
||||
const response = await fetch('/puzzle/upload.bjx', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const puzzleData = await response.json();
|
||||
statusDiv.textContent = '문제 생성 성공!';
|
||||
drawPuzzle(puzzleData); // 미리보기 그리기
|
||||
|
||||
currentPuzzleData = puzzleData;
|
||||
testSuccessBtn.addEventListener('click', showSuccessAnimation);
|
||||
|
||||
testSuccessBtn.style.display = 'inline-block';
|
||||
deleteBtn.style.display = 'inline-block';
|
||||
playBtn.style.display = 'inline-block';
|
||||
} else {
|
||||
const errorMessage = await response.text();
|
||||
statusDiv.textContent = `생성 실패: ${errorMessage}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('네트워크 오류:', error);
|
||||
statusDiv.textContent = '서버와 통신 중 오류가 발생했습니다.';
|
||||
}
|
||||
|
||||
deleteBtn.addEventListener('click', async () => {
|
||||
if (!currentPuzzleData || !currentPuzzleData.id) {
|
||||
alert('삭제할 퍼즐이 선택되지 않았습니다.');
|
||||
return;
|
||||
}
|
||||
if (!confirm('정말로 이 퍼즐을 삭제하시겠습니까?')) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// (★ 수정) API 경로 변경 -> 통합 컨트롤러의 /puzzle/{id}.bjx 호출
|
||||
const response = await fetch(`/puzzle/${currentPuzzleData.id}.bjx`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
statusDiv.textContent = '퍼즐이 성공적으로 삭제되었습니다.';
|
||||
puzzleContainer.innerHTML = '';
|
||||
// (버그 수정) success-animation-container 내부의 img src를 초기화해야 함
|
||||
document.getElementById('grayscale-reveal').src = "";
|
||||
document.getElementById('original-reveal').src = "";
|
||||
|
||||
testSuccessBtn.style.display = 'none';
|
||||
deleteBtn.style.display = 'none';
|
||||
playBtn.style.display = 'none';
|
||||
currentPuzzleData = null;
|
||||
} else {
|
||||
statusDiv.textContent = `삭제 실패: 서버 오류 (${response.status})`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('삭제 중 네트워크 오류:', error);
|
||||
statusDiv.textContent = '삭제 중 오류가 발생했습니다.';
|
||||
}
|
||||
});
|
||||
|
||||
playBtn.addEventListener('click', () => {
|
||||
if (currentPuzzleData && currentPuzzleData.id) {
|
||||
// (★ 수정 없음) 이 경로는 PuzzleController의 페이지 서빙 경로와 일치하므로 올바름.
|
||||
window.location.href = `/puzzle/play/${currentPuzzleData.id}`;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</th:block>
|
||||
</html>
|
||||
Reference in New Issue
Block a user