first commit
This commit is contained in:
commit
d059bab473
|
@ -0,0 +1,21 @@
|
||||||
|
# Minesweeper made by SPYN
|
||||||
|
|
||||||
|
An easy game which is similar to the Minesweeper made by Microsoft.
|
||||||
|
|
||||||
|
The game is made for SPYN's practical engineering assignments.
|
||||||
|
|
||||||
|
No commercial use, only for learning reference.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This Game is built in JavaScript with HTML & CSS.
|
||||||
|
|
||||||
|
To pack the code properly, you might need use NW.js(https://nwjs.io).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Additional Information
|
||||||
|
|
||||||
|
The game supports 3 languages (ZH_CN\EN-WW\JA-JP), but the code comments are written in Chinese.
|
|
@ -0,0 +1,39 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh_cn">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>game</title>
|
||||||
|
<link rel="stylesheet" href="./style/game.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<div class="grid"></div>
|
||||||
|
</main>
|
||||||
|
<div class="right">
|
||||||
|
<div class="time">
|
||||||
|
<p class="timeTitle">Time</p>
|
||||||
|
<p class="timeCon">0h0min0s</p>
|
||||||
|
</div>
|
||||||
|
<button class="new">New Game</button>
|
||||||
|
<button class="back">Back</button>
|
||||||
|
</div>
|
||||||
|
<div class="mask">
|
||||||
|
<p class="content">You win!</p>
|
||||||
|
<div class="enterName">
|
||||||
|
<input type="text" placeholder="Please enter your name" />
|
||||||
|
<button class="confirm">Confirm</button>
|
||||||
|
</div>
|
||||||
|
<div class="maskBtn">
|
||||||
|
<button class="new">New Game</button>
|
||||||
|
<button class="back">Back</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="./scripts/game/leaderBoard.js"></script>
|
||||||
|
<script src="./scripts/game/time.js"></script>
|
||||||
|
<script src="./scripts/game/tile.js"></script>
|
||||||
|
<script src="./scripts/game/grid.js"></script>
|
||||||
|
<script src="./scripts/game/listener.js"></script>
|
||||||
|
<script src="./scripts/game/language.js"></script>
|
||||||
|
<script src="./scripts/game/manager.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh_cn">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>hello</title>
|
||||||
|
<link rel="stylesheet" href="./style/index.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="hello">石皮幼鸟制作</div>
|
||||||
|
<script src="./scripts/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,54 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh_cn">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>menu</title>
|
||||||
|
<link rel="stylesheet" href="./style/menu.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p class="menu">Minesweeper</p>
|
||||||
|
<div class="selection menuList">
|
||||||
|
<button class="start">Start Game</button>
|
||||||
|
<button class="tutorial">Tutorial</button>
|
||||||
|
<button class="leaderBoard">LeaderBoard</button>
|
||||||
|
<button class="exit">Exit</button>
|
||||||
|
</div>
|
||||||
|
<div class="difficulty">
|
||||||
|
<p class="diffCon">Difficulty</p>
|
||||||
|
<div class="selection">
|
||||||
|
<button class="easy">Easy</button>
|
||||||
|
<button class="hard">Hard</button>
|
||||||
|
</div>
|
||||||
|
<button class="extra">Extra</button>
|
||||||
|
<button class="back">Back</button>
|
||||||
|
</div>
|
||||||
|
<div class="tutorialBlk">
|
||||||
|
<p class="tutoCon">Tutorial</p>
|
||||||
|
<div class="tutor">
|
||||||
|
<P>Click the left mouse button to crack open the square</P>
|
||||||
|
<p>If a mine is triggered, the game is over</p>
|
||||||
|
<p>If a number appears, it means that the number of mines with the corresponding number is around</p>
|
||||||
|
<p>If blank, there are no mines around</p>
|
||||||
|
<p>Right mouse click on the square that marks what you suspect is a mine</p>
|
||||||
|
<p>Right click again to unmark</p>
|
||||||
|
<p>Knock out all the non-mine squares to win the final victory</p>
|
||||||
|
</div>
|
||||||
|
<button class="back">Back</button>
|
||||||
|
</div>
|
||||||
|
<div class="leaderBlk">
|
||||||
|
<p class="leaderCon">LeaderBoard</p>
|
||||||
|
<p class="leaderDif">Easy</p>
|
||||||
|
<ul class="leadList"></ul>
|
||||||
|
<button class="next">Hard</button>
|
||||||
|
<button class="back">Back</button>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<ul class="language"></ul>
|
||||||
|
<div class="langSltBtn">Lang</div>
|
||||||
|
</footer>
|
||||||
|
<script src="./scripts/menu/language.js"></script>
|
||||||
|
<script src="./scripts/menu/listener.js"></script>
|
||||||
|
<script src="./scripts/menu/leaderBoard.js"></script>
|
||||||
|
<script src="./scripts/menu/menu.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"name": "minesweeper",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "minesweeper",
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"main": "index.html",
|
||||||
|
"name": "minesweeper",
|
||||||
|
"description": "A web game which is similar to the Minesweeper made by Microsoft.",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"keywords": ["admin"],
|
||||||
|
"window": {
|
||||||
|
"title": "Minesweeper",
|
||||||
|
"icon": "icon.ico",
|
||||||
|
"toolbar": false,
|
||||||
|
"frame": true,
|
||||||
|
"width": 1280,
|
||||||
|
"height": 760,
|
||||||
|
"position": "center",
|
||||||
|
"min_width": 900,
|
||||||
|
"min_height": 600,
|
||||||
|
"resizable":true
|
||||||
|
},
|
||||||
|
"webkit": {
|
||||||
|
"plugin": true,
|
||||||
|
"java":false,
|
||||||
|
"page-cache":true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,538 @@
|
||||||
|
/**************************************
|
||||||
|
文件名:grid.js
|
||||||
|
功能:该模块用于处理游戏网格对象相关内容
|
||||||
|
版本:2.0(23.01.08)
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
对象名:Grid
|
||||||
|
参数:lang: 语言, mode: 游戏难度
|
||||||
|
**************************************/
|
||||||
|
function Grid(lang, mode){
|
||||||
|
//缓存标签
|
||||||
|
this.gameKey = `${mode}State`;
|
||||||
|
//存放tile对象数组
|
||||||
|
this.grid = null;
|
||||||
|
//方格大小
|
||||||
|
this.size = "54px";
|
||||||
|
//字体大小
|
||||||
|
this.fontSize = "25px";
|
||||||
|
//横向方格数
|
||||||
|
this.wSize = 9;
|
||||||
|
//纵向方格数
|
||||||
|
this.hSize = 9;
|
||||||
|
//游戏难度
|
||||||
|
this.mode = mode;
|
||||||
|
//地雷数量
|
||||||
|
this.mine = 10;
|
||||||
|
//当前状态
|
||||||
|
this.now = "Waiting";
|
||||||
|
//判断是否是第一次点击
|
||||||
|
this.first = true;
|
||||||
|
//实例化时间对象
|
||||||
|
this.time = new Time(0, 0, 0);
|
||||||
|
//时间计时器
|
||||||
|
this.t = null;
|
||||||
|
//非地雷方块数量
|
||||||
|
this.emptyNum = null;
|
||||||
|
//获取语言
|
||||||
|
this.lang = lang;
|
||||||
|
//获取HTML中grid元素
|
||||||
|
this.gridBlk = document.querySelector(".grid");
|
||||||
|
//排行榜对象实例化
|
||||||
|
this.leaderBoard = new LeaderBoard(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:setMode()
|
||||||
|
功能:按照难度初始化grid
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.setMode = function(){
|
||||||
|
switch(this.mode){
|
||||||
|
case "easy":
|
||||||
|
this.mine = 10;
|
||||||
|
this.wSize = 9;
|
||||||
|
this.hSize = 9;
|
||||||
|
break;
|
||||||
|
case "hard":
|
||||||
|
this.mine = 40;
|
||||||
|
this.wSize = 16;
|
||||||
|
this.hSize = 16;
|
||||||
|
break;
|
||||||
|
case "extra":
|
||||||
|
this.mine = 99;
|
||||||
|
this.wSize = 30;
|
||||||
|
this.hSize = 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.createGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:createGrid()
|
||||||
|
功能:创建HTML中grid网格,初步渲染游戏界面
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.createGrid = function(){
|
||||||
|
this.gridBlk.innerHTML = '';
|
||||||
|
if(this.mode == "extra") {
|
||||||
|
this.gridBlk.style.width = "1050px";
|
||||||
|
this.gridBlk.style.marginLeft = "calc((100vw - 1350px)/2)";
|
||||||
|
}else {
|
||||||
|
this.gridBlk.style.width = "630px";
|
||||||
|
this.gridBlk.style.marginLeft = "calc((100vw - 930px)/2)";
|
||||||
|
}
|
||||||
|
for (let i = 0; i < this.hSize; i++){
|
||||||
|
let newLine = document.createElement("div");
|
||||||
|
newLine.setAttribute("class", `line-${i} tileLine`);
|
||||||
|
if(this.mode == "extra") {
|
||||||
|
newLine.style.width = "1050px";
|
||||||
|
}else {
|
||||||
|
newLine.style.width = "630px";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0; j < this.wSize; j++){
|
||||||
|
let newTile = document.createElement("div");
|
||||||
|
newTile.setAttribute("class", `nonTriggered tile tile-${i}-${j}`);
|
||||||
|
switch(this.mode){
|
||||||
|
case "easy":
|
||||||
|
this.size = "54px";
|
||||||
|
this.fontSize = "25px";
|
||||||
|
break;
|
||||||
|
case "hard":
|
||||||
|
this.size = "30px";
|
||||||
|
this.fontSize = "14px";
|
||||||
|
break;
|
||||||
|
case "extra":
|
||||||
|
this.size = "30px";
|
||||||
|
this.fontSize = "14px";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
newLine.appendChild(newTile);
|
||||||
|
newTile.style.width = this.size;
|
||||||
|
newTile.style.height = this.size;
|
||||||
|
}
|
||||||
|
this.gridBlk.appendChild(newLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
//读取缓存,如果缓存里有未完成的游戏,则恢复游戏
|
||||||
|
let recentGame = this.getGame();
|
||||||
|
if(recentGame){
|
||||||
|
this.recover(recentGame.grid, recentGame.time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:createGridObj()
|
||||||
|
功能:初始化Grid数组
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.createGridObj = function(){
|
||||||
|
for (let i = 0; i < this.hSize; i++){
|
||||||
|
this.grid.push([]);
|
||||||
|
for (let j = 0; j < this.wSize; j++){
|
||||||
|
this.grid[i].push(new Tile(i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:setValue()
|
||||||
|
功能:生成地雷,设置tile的value数值
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.setValue = function(){
|
||||||
|
//生成地雷算法
|
||||||
|
mineNum = this.mine;
|
||||||
|
for (let i = 0; i < this.hSize; i++){
|
||||||
|
for (let j = 0; j < this.wSize; j++){
|
||||||
|
//随机0~9的整数
|
||||||
|
let num = Math.floor(Math.random() * 10);
|
||||||
|
//数字为0,4时该方块为雷
|
||||||
|
if ((num == 0 || num == 4) && mineNum != 0){
|
||||||
|
this.grid[i][j].isMine = true;
|
||||||
|
let theTile = document.querySelector(`.tile-${i}-${j}`);
|
||||||
|
theTile.setAttribute("class", `nonTriggered tile tile-${i}-${j} mine`);
|
||||||
|
mineNum--;
|
||||||
|
}else if(mineNum == 0){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(mineNum != 0) this.setValue();
|
||||||
|
|
||||||
|
//计算周围雷数算法
|
||||||
|
for (let i = 0; i < this.hSize; i++){
|
||||||
|
for (let j = 0; j < this.wSize; j++){
|
||||||
|
if(this.grid[i][j].isMine){
|
||||||
|
if (i != 0){
|
||||||
|
if (j != 0) this.grid[i-1][j-1].value++;
|
||||||
|
this.grid[i-1][j].value++;
|
||||||
|
if (j != this.wSize-1) this.grid[i-1][j+1].value++;
|
||||||
|
}
|
||||||
|
if (i != this.hSize-1){
|
||||||
|
if (j != 0) this.grid[i+1][j-1].value++;
|
||||||
|
this.grid[i+1][j].value++;
|
||||||
|
if (j != this.wSize-1) this.grid[i+1][j+1].value++;
|
||||||
|
}
|
||||||
|
if (j != 0) this.grid[i][j-1].value++;
|
||||||
|
if (j != this.wSize-1) this.grid[i][j+1].value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//设置每一个tile对象内和class中value值算法
|
||||||
|
for (let i = 0; i < this.hSize; i++){
|
||||||
|
for (let j = 0; j < this.wSize; j++){
|
||||||
|
let tileObj = this.grid[i][j];
|
||||||
|
let theTile = document.querySelector(`.tile-${i}-${j}`);
|
||||||
|
theTile.innerHTML = '';
|
||||||
|
|
||||||
|
//添加内置图像
|
||||||
|
let img = document.createElement("img");
|
||||||
|
img.setAttribute("src","./image/flag.png");
|
||||||
|
img.style.width = this.size;
|
||||||
|
img.style.height = this.size;
|
||||||
|
theTile.appendChild(img);
|
||||||
|
|
||||||
|
//添加value数字
|
||||||
|
if (tileObj.value != 0){
|
||||||
|
let valueCon = document.createElement("p");
|
||||||
|
valueCon.innerHTML = tileObj.value;
|
||||||
|
valueCon.style.width = this.size;
|
||||||
|
valueCon.style.height = this.size;
|
||||||
|
valueCon.style.fontSize = this.fontSize;
|
||||||
|
valueCon.style.lineHeight = this.size;
|
||||||
|
valueCon.style.bottom = `calc(${this.size}/2)`;
|
||||||
|
theTile.appendChild(valueCon);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.grid[i][j].isMine) theTile.setAttribute("class", `nonTriggered tile tile-${i}-${j} value-${tileObj.value}`);
|
||||||
|
else theTile.setAttribute("class", `nonTriggered tile tile-${i}-${j} mine value-${tileObj.value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:start()
|
||||||
|
功能:开始一局新游戏
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.start = function(){
|
||||||
|
//停止计时器
|
||||||
|
if (this.t){
|
||||||
|
clearInterval(this.t);
|
||||||
|
}
|
||||||
|
this.now = "Doing";
|
||||||
|
this.grid = [];
|
||||||
|
this.time = new Time(0, 0, 0);
|
||||||
|
this.emptyNum = this.hSize * this.wSize - this.mine;
|
||||||
|
this.createGridObj();
|
||||||
|
this.setValue();
|
||||||
|
this.timeStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:click()
|
||||||
|
参数:i: 横坐标, j: 纵坐标
|
||||||
|
功能:左键单击tile判断
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.click = function(i, j){
|
||||||
|
let theTile = document.querySelector(`.tile-${i}-${j}`);
|
||||||
|
|
||||||
|
//若第一次触发的不是空白,游戏重置
|
||||||
|
if(this.first){
|
||||||
|
while(this.grid[i][j].value != 0 || this.grid[i][j].isMine){
|
||||||
|
this.start();
|
||||||
|
}
|
||||||
|
this.first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.grid[i][j].recent == "nonTriggered"){
|
||||||
|
this.grid[i][j].recent = "triggered";
|
||||||
|
let tileCon = theTile.getAttribute("class").split(" ");
|
||||||
|
tileCon.splice(tileCon.indexOf("nonTriggered"), 1);
|
||||||
|
tileCon.push("triggered");
|
||||||
|
tileCon = tileCon.join(" ");
|
||||||
|
theTile.setAttribute("class", tileCon);
|
||||||
|
|
||||||
|
if(this.grid[i][j].isMine){
|
||||||
|
let img = document.querySelector(`.tile-${i}-${j} img`);
|
||||||
|
img.setAttribute("src", "./image/bomb.png");
|
||||||
|
//停止计时器
|
||||||
|
clearInterval(this.t);
|
||||||
|
this.now = "Failed";
|
||||||
|
}else if(!this.grid[i][j].isMine){
|
||||||
|
this.emptyNum--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.grid[i][j].value == 0 && !this.grid[i][j].isMine) this.checkEmpty(i, j);
|
||||||
|
|
||||||
|
if(this.emptyNum == 0){
|
||||||
|
//停止计时器
|
||||||
|
clearInterval(this.t);
|
||||||
|
this.now = "Win";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.maskBlk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:checkEmpty()
|
||||||
|
参数:i: 横坐标, j: 纵坐标
|
||||||
|
功能:检测单击方块周围是否为非雷方块
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.checkEmpty = function(i, j){
|
||||||
|
if (i != 0){
|
||||||
|
if (j != 0 && !this.grid[i-1][j-1].isMine && this.grid[i-1][j-1].recent == "nonTriggered") this.click(i-1, j-1);
|
||||||
|
if (!this.grid[i-1][j].isMine && this.grid[i-1][j].recent == "nonTriggered") this.click(i-1, j);
|
||||||
|
if (j != this.wSize-1 && !this.grid[i-1][j+1].isMine && this.grid[i-1][j+1].recent == "nonTriggered") this.click(i-1, j+1);
|
||||||
|
}
|
||||||
|
if (i != this.hSize-1){
|
||||||
|
if (j != 0 && !this.grid[i+1][j-1].isMine && this.grid[i+1][j-1].recent == "nonTriggered") this.click(i+1, j-1);
|
||||||
|
if (!this.grid[i+1][j].isMine && this.grid[i+1][j].recent == "nonTriggered") this.click(i+1, j);
|
||||||
|
if (j != this.wSize-1 && !this.grid[i+1][j+1].isMine && this.grid[i+1][j+1].recent == "nonTriggered") this.click(i+1, j+1);
|
||||||
|
}
|
||||||
|
if (j != 0 && !this.grid[i][j-1].isMine && this.grid[i][j-1].recent == "nonTriggered") this.click(i, j-1);
|
||||||
|
if (j != this.wSize-1 && !this.grid[i][j+1].isMine && this.grid[i][j+1].recent == "nonTriggered") this.click(i, j+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:rightClick()
|
||||||
|
参数:i: 横坐标, j: 纵坐标
|
||||||
|
功能:右键单击tile判断
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.rightClick = function(i, j){
|
||||||
|
let theTile = document.querySelector(`.tile-${i}-${j}`);
|
||||||
|
|
||||||
|
if(this.grid[i][j].recent == "nonTriggered"){
|
||||||
|
this.grid[i][j].recent = "marked";
|
||||||
|
|
||||||
|
let tileCon = theTile.getAttribute("class").split(" ");
|
||||||
|
tileCon.splice(tileCon.indexOf("nonTriggered"), 1);
|
||||||
|
tileCon.push("marked");
|
||||||
|
tileCon = tileCon.join(" ");
|
||||||
|
theTile.setAttribute("class", tileCon);
|
||||||
|
}else if(this.grid[i][j].recent == "marked"){
|
||||||
|
this.grid[i][j].recent = "nonTriggered";
|
||||||
|
|
||||||
|
let tileCon = theTile.getAttribute("class").split(" ");
|
||||||
|
tileCon.splice(tileCon.indexOf("marked"), 1);
|
||||||
|
tileCon.push("nonTriggered");
|
||||||
|
tileCon = tileCon.join(" ");
|
||||||
|
theTile.setAttribute("class", tileCon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:timeStart()
|
||||||
|
功能:开始计时
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.timeStart = function(){
|
||||||
|
let timeCon = document.querySelector(".timeCon");
|
||||||
|
let self = this;
|
||||||
|
if (this.now = "Doing"){
|
||||||
|
this.t = setInterval(function(){
|
||||||
|
self.time.time.s++;
|
||||||
|
if (self.time.time.s == 60){
|
||||||
|
self.time.time.min++;
|
||||||
|
self.time.time.s = 0;
|
||||||
|
}
|
||||||
|
if (self.time.time.min == 60){
|
||||||
|
self.time.time.h++;
|
||||||
|
self.time.time.min = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeCon.innerHTML = self.time.getTime();
|
||||||
|
|
||||||
|
//每一秒保存一次游戏
|
||||||
|
self.setGame();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:restart()
|
||||||
|
功能:恢复初始状态
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.restart = function(){
|
||||||
|
//隐藏结束界面蒙版
|
||||||
|
document.querySelector(".mask").style.display = "none";
|
||||||
|
//恢复第一次点击
|
||||||
|
this.first = true;
|
||||||
|
//停止计时器
|
||||||
|
clearInterval(this.t);
|
||||||
|
//恢复所有tile的class
|
||||||
|
for (let i = 0; i < this.hSize; i++){
|
||||||
|
for (let j = 0; j < this.wSize; j++){
|
||||||
|
let theTile = document.querySelector(`.tile-${i}-${j}`);
|
||||||
|
theTile.setAttribute("class", `nonTriggered tile tile-${i}-${j}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//初始化时间
|
||||||
|
let timeCon = document.querySelector(".timeCon");
|
||||||
|
timeCon.innerHTML = "0h0min0s";
|
||||||
|
//初始化当前状态
|
||||||
|
this.now = "Waiting";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:maskBlk()
|
||||||
|
功能:判断游戏是否结束,渲染结束界面蒙版
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.maskBlk = function(){
|
||||||
|
if(this.now == "Win"){
|
||||||
|
window.localStorage.removeItem(this.gameKey);
|
||||||
|
document.querySelector(".mask").style.display = "block";
|
||||||
|
switch (this.lang){
|
||||||
|
case "zh_cn":
|
||||||
|
document.querySelector(".content").innerHTML = "你赢了!";
|
||||||
|
document.querySelector(".enterName input").setAttribute("placeholder", "请输入您的昵称");
|
||||||
|
break;
|
||||||
|
case "en":
|
||||||
|
document.querySelector(".content").innerHTML = "You win!";
|
||||||
|
document.querySelector(".enterName input").setAttribute("placeholder", "Please enter your nickname");
|
||||||
|
break;
|
||||||
|
case "jp":
|
||||||
|
document.querySelector(".content").innerHTML = "ユーウィン!";
|
||||||
|
document.querySelector(".enterName input").setAttribute("placeholder", "ニックネームを入力してください");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
document.querySelector(".enterName").style.display = "flex";
|
||||||
|
}else if(this.now == "Failed"){
|
||||||
|
window.localStorage.removeItem(this.gameKey);
|
||||||
|
document.querySelector(".mask").style.display = "block";
|
||||||
|
switch (this.lang){
|
||||||
|
case "zh_cn":
|
||||||
|
document.querySelector(".content").innerHTML = "你输了!";
|
||||||
|
break;
|
||||||
|
case "en":
|
||||||
|
document.querySelector(".content").innerHTML = "You failed!";
|
||||||
|
break;
|
||||||
|
case "jp":
|
||||||
|
document.querySelector(".content").innerHTML = "残念!";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
document.querySelector(".enterName").style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:rankConfirm()
|
||||||
|
功能:处理胜利界面确认按钮动作
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.rankConfirm = function(){
|
||||||
|
const ID = document.querySelector("input").value;
|
||||||
|
const time = this.time.getTime();
|
||||||
|
this.leaderBoard.setHigh(ID, time);
|
||||||
|
document.querySelector(".enterName").style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:serialize()
|
||||||
|
功能:grid序列化
|
||||||
|
返回值:序列化后的grid以及游戏时间的对象
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.serialize = function(){
|
||||||
|
const grid = [];
|
||||||
|
for (let i = 0; i < this.hSize; i++){
|
||||||
|
grid.push([]);
|
||||||
|
for (let j = 0; j < this.wSize; j++){
|
||||||
|
grid[i].push(this.grid[i][j].serialize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
grid: grid,
|
||||||
|
time: {
|
||||||
|
h: this.time.time.h,
|
||||||
|
min: this.time.time.min,
|
||||||
|
s: this.time.time.s
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:serialize()
|
||||||
|
参数:grid: 序列化的grid, time: 游戏时间
|
||||||
|
功能:游戏内容反序列化
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.recover = function(grid, time){
|
||||||
|
this.first = false;
|
||||||
|
this.now = "Doing";
|
||||||
|
this.time.time.h = time.h;
|
||||||
|
this.time.time.min = time.min;
|
||||||
|
this.time.time.s = time.s;
|
||||||
|
this.timeStart();
|
||||||
|
this.grid = [];
|
||||||
|
for (let i = 0; i < this.hSize; i++){
|
||||||
|
this.grid.push([]);
|
||||||
|
for (let j = 0; j < this.wSize; j++){
|
||||||
|
let sTile = grid[i][j];
|
||||||
|
let newTile = new Tile(i, j);
|
||||||
|
newTile.value = sTile.value;
|
||||||
|
newTile.isMine = sTile.isMine;
|
||||||
|
newTile.recent = sTile.recent;
|
||||||
|
this.grid[i].push(newTile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.recoverValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:recoverValue()
|
||||||
|
功能:反序列化后重新渲染界面
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.recoverValue = function(){
|
||||||
|
this.emptyNum = this.hSize * this.wSize - this.mine;
|
||||||
|
for (let i = 0; i < this.hSize; i++){
|
||||||
|
for (let j = 0; j < this.wSize; j++){
|
||||||
|
let tileObj = this.grid[i][j];
|
||||||
|
let theTile = document.querySelector(`.tile-${i}-${j}`);
|
||||||
|
let tileCon = ["tile", `tile-${i}-${j}`];
|
||||||
|
if(tileObj.isMine) tileCon.push("mine");
|
||||||
|
tileCon.push(tileObj.recent);
|
||||||
|
if(tileObj.recent == "triggered") this.emptyNum--;
|
||||||
|
tileCon.push(`value-${tileObj.value}`);
|
||||||
|
tileCon = tileCon.join(" ");
|
||||||
|
theTile.setAttribute("class", tileCon);
|
||||||
|
|
||||||
|
//添加内置图像
|
||||||
|
let img = document.createElement("img");
|
||||||
|
img.setAttribute("src","./image/flag.png");
|
||||||
|
img.style.width = this.size;
|
||||||
|
img.style.height = this.size;
|
||||||
|
theTile.appendChild(img);
|
||||||
|
|
||||||
|
//添加value数字
|
||||||
|
if (tileObj.value != 0){
|
||||||
|
let valueCon = document.createElement("p");
|
||||||
|
valueCon.innerHTML = tileObj.value;
|
||||||
|
valueCon.style.width = this.size;
|
||||||
|
valueCon.style.height = this.size;
|
||||||
|
valueCon.style.fontSize = this.fontSize;
|
||||||
|
valueCon.style.lineHeight = this.size;
|
||||||
|
valueCon.style.bottom = `calc(${this.size}/2)`;
|
||||||
|
theTile.appendChild(valueCon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:setGame()
|
||||||
|
功能:游戏记录写入缓存
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.setGame = function(){
|
||||||
|
window.localStorage.setItem(
|
||||||
|
this.gameKey,
|
||||||
|
JSON.stringify(this.serialize())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:getGame()
|
||||||
|
功能:从缓存中读取游戏记录
|
||||||
|
返回值:若缓存中存在游戏记录,返回游戏记录,否则返回null
|
||||||
|
**************************************/
|
||||||
|
Grid.prototype.getGame = function(){
|
||||||
|
const state = window.localStorage.getItem(this.gameKey);
|
||||||
|
return state ? JSON.parse(state) : null;
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
/**************************************
|
||||||
|
文件名:language.js
|
||||||
|
功能:该模块用于处理主菜单语言相关内容
|
||||||
|
版本:2.0(23.01.08)
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
对象名:Language
|
||||||
|
参数:lang: 传入语言(zh_cn为中文,en为英文,jp为日文)
|
||||||
|
**************************************/
|
||||||
|
function Language(lang){
|
||||||
|
this.lang = lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:langRender()
|
||||||
|
功能:根据语种渲染对应的语言
|
||||||
|
**************************************/
|
||||||
|
Language.prototype.langRender = function(){
|
||||||
|
switch (this.lang){
|
||||||
|
case "zh_cn":
|
||||||
|
this.chineseRender();
|
||||||
|
break;
|
||||||
|
case "en":
|
||||||
|
this.englishRender();
|
||||||
|
break;
|
||||||
|
case "jp":
|
||||||
|
this.japaneseRender();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:chineseRender()
|
||||||
|
功能:渲染中文
|
||||||
|
**************************************/
|
||||||
|
Language.prototype.chineseRender = function(){
|
||||||
|
document.querySelector(".timeTitle").innerHTML = "游戏时长";
|
||||||
|
document.querySelector(".confirm").innerHTML = "确认";
|
||||||
|
let newBtn = document.querySelectorAll(".new");
|
||||||
|
for (let i = 0; i < newBtn.length; i++){
|
||||||
|
newBtn[i].innerHTML = "重开一局";
|
||||||
|
}
|
||||||
|
let back = document.querySelectorAll(".back");
|
||||||
|
for (let i = 0; i < back.length; i++){
|
||||||
|
back[i].innerHTML = "返回";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:englishRender()
|
||||||
|
功能:渲染英文
|
||||||
|
**************************************/
|
||||||
|
Language.prototype.englishRender = function(){
|
||||||
|
document.querySelector(".timeTitle").innerHTML = "Time";
|
||||||
|
document.querySelector(".confirm").innerHTML = "Confirm";
|
||||||
|
let newBtn = document.querySelectorAll(".new");
|
||||||
|
for (let i = 0; i < newBtn.length; i++){
|
||||||
|
newBtn[i].innerHTML = "New Game";
|
||||||
|
}
|
||||||
|
let back = document.querySelectorAll(".back");
|
||||||
|
for (let i = 0; i < back.length; i++){
|
||||||
|
back[i].innerHTML = "Back";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:japaneseRender()
|
||||||
|
功能:渲染日语
|
||||||
|
**************************************/
|
||||||
|
Language.prototype.japaneseRender = function(){
|
||||||
|
document.querySelector(".timeTitle").innerHTML = "プレイ時間";
|
||||||
|
document.querySelector(".confirm").innerHTML = "確認する";
|
||||||
|
let newBtn = document.querySelectorAll(".new");
|
||||||
|
for (let i = 0; i < newBtn.length; i++){
|
||||||
|
newBtn[i].innerHTML = "ニューゲーム";
|
||||||
|
}
|
||||||
|
let back = document.querySelectorAll(".back");
|
||||||
|
for (let i = 0; i < back.length; i++){
|
||||||
|
back[i].innerHTML = "戻る";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
/**************************************
|
||||||
|
文件名:leaderBoard.js
|
||||||
|
功能:该模块用于处理排行榜相关内容
|
||||||
|
版本:2.0(23.01.08)
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
对象名:LeaderBoard
|
||||||
|
参数:mode: 游戏难度
|
||||||
|
**************************************/
|
||||||
|
function LeaderBoard(mode){
|
||||||
|
//获取难度
|
||||||
|
this.mode = mode
|
||||||
|
//设置排行榜缓存标签
|
||||||
|
this.HighRankKey = 'MinesweeperRank';
|
||||||
|
//从缓存中获取排行榜
|
||||||
|
this.highRank = this.getRank();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:setHigh()
|
||||||
|
参数:ID: 玩家昵称, time: 游戏时长字符串
|
||||||
|
功能:设置最高分
|
||||||
|
**************************************/
|
||||||
|
LeaderBoard.prototype.setHigh = function(ID, time){
|
||||||
|
let newHigh = {
|
||||||
|
ID: ID,
|
||||||
|
time: time
|
||||||
|
}
|
||||||
|
switch(this.mode){
|
||||||
|
case "easy":
|
||||||
|
this.rankCompare(this.highRank.easyRank, newHigh);
|
||||||
|
break;
|
||||||
|
case "hard":
|
||||||
|
this.rankCompare(this.highRank.hardRank, newHigh);
|
||||||
|
break;
|
||||||
|
case "extra":
|
||||||
|
this.rankCompare(this.highRank.extraRank, newHigh);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:rankCompare()
|
||||||
|
参数:rank: 对应难度排行榜数组, newHigh: 通关成绩对象
|
||||||
|
功能:对比现有排行榜内容和传入成绩,如果破纪录则加入排行榜
|
||||||
|
**************************************/
|
||||||
|
LeaderBoard.prototype.rankCompare = function(rank, newHigh){
|
||||||
|
let nH = newHigh.time.substring(0, newHigh.time.indexOf("h"));
|
||||||
|
let nMin = newHigh.time.substring(newHigh.time.indexOf("h")+1, newHigh.time.indexOf("min"));
|
||||||
|
let nS = newHigh.time.substring(newHigh.time.indexOf("min")+3, newHigh.time.indexOf("s"));
|
||||||
|
let flag = false;
|
||||||
|
if(rank.length == 0){
|
||||||
|
rank.splice(0, 0, newHigh);
|
||||||
|
flag = true;
|
||||||
|
}else {
|
||||||
|
for (let i = 0; i < rank.length; i++){
|
||||||
|
let obj = rank[i];
|
||||||
|
let h = parseInt(obj.time.substring(0, obj.time.indexOf("h")));
|
||||||
|
let min = parseInt(obj.time.substring(obj.time.indexOf("h")+1, obj.time.indexOf("min")));
|
||||||
|
let s = parseInt(obj.time.substring(obj.time.indexOf("min")+3, obj.time.indexOf("s")));
|
||||||
|
if(nH < h){
|
||||||
|
flag = true;
|
||||||
|
}else if(nH == h && nMin < min){
|
||||||
|
flag = true;
|
||||||
|
}else if(nH == h && nMin == min && nS < s){
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
if(flag){
|
||||||
|
rank.splice(i, 0, newHigh);
|
||||||
|
if(rank.length == 11){
|
||||||
|
rank.pop();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rank.length < 10 && !flag){
|
||||||
|
rank.splice(rank.length, 0, newHigh);
|
||||||
|
flag = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(flag){
|
||||||
|
this.setRank();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:setRank()
|
||||||
|
功能:将排行榜写入缓存
|
||||||
|
**************************************/
|
||||||
|
LeaderBoard.prototype.setRank = function(){
|
||||||
|
window.localStorage.setItem(
|
||||||
|
this.HighRankKey,
|
||||||
|
JSON.stringify(this.highRank)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:getRank()
|
||||||
|
功能:从缓存中获取排行榜
|
||||||
|
返回值:返回排行榜对象
|
||||||
|
**************************************/
|
||||||
|
LeaderBoard.prototype.getRank = function(){
|
||||||
|
let newRank = {
|
||||||
|
easyRank: [],
|
||||||
|
hardRank: [],
|
||||||
|
extraRank: []
|
||||||
|
}
|
||||||
|
const rank = window.localStorage.getItem(this.HighRankKey);
|
||||||
|
//如果缓存中没有排行榜,返回空的排行榜对象
|
||||||
|
return rank ? JSON.parse(rank) : newRank;
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/**************************************
|
||||||
|
文件名:listener.js
|
||||||
|
功能:该模块用于处理事件监听相关内容
|
||||||
|
版本:2.0(23.01.08)
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
对象名:Listener
|
||||||
|
参数:lang: 传入语言, grid: 传入grid对象
|
||||||
|
**************************************/
|
||||||
|
function Listener(lang, grid){
|
||||||
|
//在设置监听的匿名函数中,“this”不是指代的Listener对象,必须单独获取
|
||||||
|
let self = this;
|
||||||
|
//获取语言
|
||||||
|
this.lang = lang;
|
||||||
|
//获取grid对象
|
||||||
|
this.grid = grid;
|
||||||
|
//获取所有返回按钮
|
||||||
|
this.back = document.querySelectorAll(".back");
|
||||||
|
//获取所有新游戏按钮
|
||||||
|
this.newBtn = document.querySelectorAll(".new");
|
||||||
|
//监听输入框旁的确认按钮
|
||||||
|
this.confirm = document.querySelector(".confirm");
|
||||||
|
|
||||||
|
//监听返回按钮点击事件
|
||||||
|
for (let i = 0; i < this.back.length; i++){
|
||||||
|
this.back[i].addEventListener("click", function(){
|
||||||
|
window.open(`./menu.html?lang=${self.lang}`, "_self");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//监听新游戏按钮点击事件
|
||||||
|
for (let i = 0; i < this.newBtn.length; i++){
|
||||||
|
this.newBtn[i].addEventListener("click", function(){
|
||||||
|
self.grid.restart();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//监听确认按钮点击事件
|
||||||
|
this.confirm.addEventListener("click", function(){
|
||||||
|
self.grid.rankConfirm();
|
||||||
|
})
|
||||||
|
|
||||||
|
//监听tile点击事件
|
||||||
|
for (let i = 0; i < this.grid.hSize; i++){
|
||||||
|
for (let j = 0; j < this.grid.wSize; j++){
|
||||||
|
let theTile = document.querySelector(`.tile-${i}-${j}`);
|
||||||
|
|
||||||
|
theTile.addEventListener("click", function (){
|
||||||
|
if(self.grid.now == "Waiting"){
|
||||||
|
self.grid.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.grid.click(i, j);
|
||||||
|
});
|
||||||
|
|
||||||
|
theTile.addEventListener("contextmenu", function (){
|
||||||
|
self.grid.rightClick(i, j);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//屏蔽右键菜单
|
||||||
|
document.oncontextmenu = function (event){
|
||||||
|
if(window.event){
|
||||||
|
event = window.event;
|
||||||
|
}try{
|
||||||
|
var the = event.srcElement;
|
||||||
|
if (!((the.tagName == "INPUT" && the.type.toLowerCase() == "text") || the.tagName == "TEXTAREA")){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}catch (e){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**************************************
|
||||||
|
文件名:manager.js
|
||||||
|
功能:该模块用于处理游戏界面相关内容
|
||||||
|
版本:2.0(23.01.08)
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
//新建Manager对象
|
||||||
|
new Manager();
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
对象名:Manager
|
||||||
|
**************************************/
|
||||||
|
function Manager(){
|
||||||
|
//获取URL中“?”及后续部分
|
||||||
|
let url = location.search;
|
||||||
|
if (url.indexOf("?") != -1){
|
||||||
|
//截取1到url.length的部分
|
||||||
|
url = url.substring(1);
|
||||||
|
//若传入了多个参数,以&分隔,此处将参数分解为数组内多个元素
|
||||||
|
url = url.split('&');
|
||||||
|
}
|
||||||
|
//参数第0项是语言参数lang,第一项是难度参数mode,以等号分开,取等号后内容
|
||||||
|
this.lang = url[0].split('=')[1];
|
||||||
|
this.mode = url[1].split('=')[1];
|
||||||
|
|
||||||
|
//Language对象实例化
|
||||||
|
this.language = new Language(this.lang);
|
||||||
|
this.language.langRender();
|
||||||
|
|
||||||
|
//Grid对象实例化
|
||||||
|
this.grid = new Grid(this.lang, this.mode);
|
||||||
|
this.grid.setMode();
|
||||||
|
|
||||||
|
//Listener对象实例化
|
||||||
|
this.listener = new Listener(this.lang, this.grid);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**************************************
|
||||||
|
文件名:tile.js
|
||||||
|
功能:该模块用于处理单个小方格相关内容
|
||||||
|
版本:2.0(23.01.08)
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
对象名:Tile
|
||||||
|
参数:x: 横坐标, y: 纵坐标
|
||||||
|
**************************************/
|
||||||
|
function Tile(x, y){
|
||||||
|
//方块的位置
|
||||||
|
this.position = {
|
||||||
|
positionX: x,
|
||||||
|
positionY: y
|
||||||
|
}
|
||||||
|
//value为数字0~8时,指代周围有对应数字的雷数
|
||||||
|
this.value = 0;
|
||||||
|
//为true时是雷
|
||||||
|
this.isMine = false;
|
||||||
|
//方块目前状态,“nonTriggered”是未触发,“triggered”是已触发,“marked”是被标记
|
||||||
|
this.recent = "nonTriggered";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:serialize()
|
||||||
|
功能:序列化当前Tile对象
|
||||||
|
返回值:包含value、isMine、recent的对象
|
||||||
|
**************************************/
|
||||||
|
Tile.prototype.serialize = function(){
|
||||||
|
return {
|
||||||
|
value: this.value,
|
||||||
|
isMine: this.isMine,
|
||||||
|
recent: this.recent
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/**************************************
|
||||||
|
文件名:time.js
|
||||||
|
功能:该模块用于处理游戏时间相关内容
|
||||||
|
版本:2.0(23.01.08)
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
对象名:Time
|
||||||
|
参数:h: 小时, min: 分钟, s: 秒
|
||||||
|
**************************************/
|
||||||
|
function Time(h, min, s){
|
||||||
|
this.time = {
|
||||||
|
h: h,
|
||||||
|
min: min,
|
||||||
|
s: s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:getTime()
|
||||||
|
功能:获得当前游戏时间
|
||||||
|
返回值:当前游戏时间字符串
|
||||||
|
**************************************/
|
||||||
|
Time.prototype.getTime = function(){
|
||||||
|
return `${this.time.h}h${this.time.min}min${this.time.s}s`;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
/**************************************
|
||||||
|
文件名:index.js
|
||||||
|
功能:该模块用于处理开始界面相关内容
|
||||||
|
版本:2.0(23.01.08)
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
//3秒后进入主菜单
|
||||||
|
setTimeout(function (){
|
||||||
|
window.open('./menu.html?lang=en', "_self");
|
||||||
|
}, 3000);
|
|
@ -0,0 +1,118 @@
|
||||||
|
/**************************************
|
||||||
|
文件名:language.js
|
||||||
|
功能:该模块用于处理主菜单语言相关内容
|
||||||
|
版本:2.0(23.01.08)
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
对象名:Language
|
||||||
|
参数:lang: 传入语言(zh_cn为中文,en为英文,jp为日文)
|
||||||
|
**************************************/
|
||||||
|
function Language(lang){
|
||||||
|
this.lang = lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:langRender()
|
||||||
|
功能:根据语种渲染对应的语言
|
||||||
|
**************************************/
|
||||||
|
Language.prototype.langRender = function(){
|
||||||
|
switch (this.lang){
|
||||||
|
case "zh_cn":
|
||||||
|
this.chineseRender();
|
||||||
|
break;
|
||||||
|
case "en":
|
||||||
|
this.englishRender();
|
||||||
|
break;
|
||||||
|
case "jp":
|
||||||
|
this.japaneseRender();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:chineseRender()
|
||||||
|
功能:渲染中文
|
||||||
|
**************************************/
|
||||||
|
Language.prototype.chineseRender = function(){
|
||||||
|
document.querySelector(".menu").innerHTML = "扫雷";
|
||||||
|
document.querySelector(".start").innerHTML = "开始游戏";
|
||||||
|
document.querySelector(".tutorial").innerHTML = "新手教程";
|
||||||
|
document.querySelector(".leaderBoard").innerHTML = "排行榜";
|
||||||
|
document.querySelector(".exit").innerHTML = "退出游戏";
|
||||||
|
document.querySelector(".diffCon").innerHTML = "难度选择";
|
||||||
|
document.querySelector(".easy").innerHTML = "简单";
|
||||||
|
document.querySelector(".hard").innerHTML = "困难";
|
||||||
|
let back = document.querySelectorAll(".back");
|
||||||
|
for (let i = 0; i < back.length; i++){
|
||||||
|
back[i].innerHTML = "返回";
|
||||||
|
}
|
||||||
|
document.querySelector(".tutoCon").innerHTML = "新手教程";
|
||||||
|
document.querySelector(".tutor").innerHTML =
|
||||||
|
`<P>点击鼠标左键敲开方块</P>
|
||||||
|
<p>如果触发了地雷,游戏结束</p>
|
||||||
|
<p>如果出现数字,则意味着周围有对应数字的地雷数</p>
|
||||||
|
<p>如果是空白,则周围无地雷</p>
|
||||||
|
<p>鼠标右击标记你怀疑是地雷的方块</p>
|
||||||
|
<p>再右击一次可以取消标记</p>
|
||||||
|
<p>敲开所有非地雷方块,赢得最终胜利</p>`;
|
||||||
|
document.querySelector(".leaderCon").innerHTML = "排行榜";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:englishRender()
|
||||||
|
功能:渲染英文
|
||||||
|
**************************************/
|
||||||
|
Language.prototype.englishRender = function(){
|
||||||
|
document.querySelector(".menu").innerHTML = "Minesweeper";
|
||||||
|
document.querySelector(".start").innerHTML = "Start Game";
|
||||||
|
document.querySelector(".tutorial").innerHTML = "Tutorial";
|
||||||
|
document.querySelector(".leaderBoard").innerHTML = "Leaderboard";
|
||||||
|
document.querySelector(".exit").innerHTML = "Exit";
|
||||||
|
document.querySelector(".diffCon").innerHTML = "Difficulty";
|
||||||
|
document.querySelector(".easy").innerHTML = "Easy";
|
||||||
|
document.querySelector(".hard").innerHTML = "Hard";
|
||||||
|
let back = document.querySelectorAll(".back");
|
||||||
|
for (let i = 0; i < back.length; i++){
|
||||||
|
back[i].innerHTML = "Back";
|
||||||
|
}
|
||||||
|
document.querySelector(".tutoCon").innerHTML = "Tutorial";
|
||||||
|
document.querySelector(".tutor").innerHTML =
|
||||||
|
`<P>Click the left mouse button to crack open the square</P>
|
||||||
|
<p>If a mine is triggered, the game is over</p>
|
||||||
|
<p>If a number appears, it means that the number of mines with the corresponding number is around</p>
|
||||||
|
<p>If blank, there are no mines around</p>
|
||||||
|
<p>Right mouse click on the square that marks what you suspect is a mine</p>
|
||||||
|
<p>Right click again to unmark</p>
|
||||||
|
<p>Knock out all the non-mine squares to win the final victory</p>`;
|
||||||
|
document.querySelector(".leaderCon").innerHTML = "Leaderboard";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:japaneseRender()
|
||||||
|
功能:渲染日语
|
||||||
|
**************************************/
|
||||||
|
Language.prototype.japaneseRender = function(){
|
||||||
|
document.querySelector(".menu").innerHTML = "マインスイーパー";
|
||||||
|
document.querySelector(".start").innerHTML = "スタート";
|
||||||
|
document.querySelector(".tutorial").innerHTML = "私は初心者";
|
||||||
|
document.querySelector(".leaderBoard").innerHTML = "リーダーボード";
|
||||||
|
document.querySelector(".exit").innerHTML = "ゲーム終了";
|
||||||
|
document.querySelector(".diffCon").innerHTML = "難易度";
|
||||||
|
document.querySelector(".easy").innerHTML = "簡単";
|
||||||
|
document.querySelector(".hard").innerHTML = "難しい";
|
||||||
|
let back = document.querySelectorAll(".back");
|
||||||
|
for (let i = 0; i < back.length; i++){
|
||||||
|
back[i].innerHTML = "戻る";
|
||||||
|
}
|
||||||
|
document.querySelector(".tutoCon").innerHTML = "チュートリアル";
|
||||||
|
document.querySelector(".tutor").innerHTML =
|
||||||
|
`<P>マウスの左ボタンで四角を発動します</P>
|
||||||
|
<p>地雷が発生したらゲームオーバー</p>
|
||||||
|
<p>数字が表示された場合、その数字に対応する地雷の数が周辺にあることを意味します</p>
|
||||||
|
<p>空白の場合、地雷はありません</p>
|
||||||
|
<p>地雷と思われるマークをマウスの右ボタンでクリックする</p>
|
||||||
|
<p>もう一度右クリックすると、マークが消えます</p>
|
||||||
|
<p>地雷のないマスをすべてトリガーにして最終的に勝利する</p>`;
|
||||||
|
document.querySelector(".leaderCon").innerHTML = "リーダーボード";
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/**************************************
|
||||||
|
文件名:leaderBoard.js
|
||||||
|
功能:该模块用于处理主菜单排行榜相关内容
|
||||||
|
版本:2.0(23.01.08)
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
对象名:LeaderBoard
|
||||||
|
参数:lang: 传入语言
|
||||||
|
**************************************/
|
||||||
|
function LeaderBoard(lang){
|
||||||
|
//设置排行榜缓存标签
|
||||||
|
this.HighRankKey = 'MinesweeperRank';
|
||||||
|
//从缓存中获取排行榜
|
||||||
|
this.highRank = this.getRank();
|
||||||
|
//获取HTML中排行榜
|
||||||
|
this.leadList = document.querySelector(".leadList");
|
||||||
|
//获取排行榜下一页按钮
|
||||||
|
this.next = document.querySelector(".next");
|
||||||
|
//获取语言
|
||||||
|
this.lang = lang;
|
||||||
|
|
||||||
|
//在设置监听的匿名函数中,“this”不是指代的leaderBoard对象,必须单独获取
|
||||||
|
let self = this;
|
||||||
|
//下一页按钮事件监听
|
||||||
|
this.next.addEventListener("click", function(){
|
||||||
|
let nextDiff = self.next.innerHTML;
|
||||||
|
if(nextDiff == "Easy" || nextDiff == "简单" || nextDiff == "簡単"){
|
||||||
|
self.rankRender("easy");
|
||||||
|
}else if(nextDiff == "Hard" || nextDiff == "困难" || nextDiff == "難しい"){
|
||||||
|
self.rankRender("hard");
|
||||||
|
}else if(nextDiff == "Extra"){
|
||||||
|
self.rankRender("extra");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:getRank()
|
||||||
|
功能:从缓存中获取排行榜
|
||||||
|
返回值:返回排行榜对象
|
||||||
|
**************************************/
|
||||||
|
LeaderBoard.prototype.getRank = function(){
|
||||||
|
let newRank = {
|
||||||
|
easyRank: [],
|
||||||
|
hardRank: [],
|
||||||
|
extraRank: []
|
||||||
|
}
|
||||||
|
const rank = window.localStorage.getItem(this.HighRankKey);
|
||||||
|
//如果缓存中没有排行榜,返回空的排行榜对象
|
||||||
|
return rank ? JSON.parse(rank) : newRank;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:rankRender(difficulty)
|
||||||
|
参数:difficulty: 游戏难度(easy为简单,hard为困难,extra为ex难度)
|
||||||
|
功能:渲染排行榜
|
||||||
|
**************************************/
|
||||||
|
LeaderBoard.prototype.rankRender = function(difficulty){
|
||||||
|
let rank = null;
|
||||||
|
let diffCon = '';
|
||||||
|
let nextCon = '';
|
||||||
|
switch(difficulty){
|
||||||
|
case "easy":
|
||||||
|
rank = this.highRank.easyRank;
|
||||||
|
switch(this.lang){
|
||||||
|
case "zh_cn":
|
||||||
|
diffCon = "简单";
|
||||||
|
nextCon = "困难";
|
||||||
|
break;
|
||||||
|
case "en":
|
||||||
|
diffCon = "Easy";
|
||||||
|
nextCon = "Hard";
|
||||||
|
break;
|
||||||
|
case "jp":
|
||||||
|
diffCon = "簡単";
|
||||||
|
nextCon = "難しい";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "hard":
|
||||||
|
rank = this.highRank.hardRank;
|
||||||
|
switch(this.lang){
|
||||||
|
case "zh_cn":
|
||||||
|
diffCon = "困难";
|
||||||
|
break;
|
||||||
|
case "en":
|
||||||
|
diffCon = "Hard";
|
||||||
|
break;
|
||||||
|
case "jp":
|
||||||
|
diffCon = "難しい";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nextCon = "Extra";
|
||||||
|
break;
|
||||||
|
case "extra":
|
||||||
|
rank = this.highRank.extraRank;
|
||||||
|
switch(this.lang){
|
||||||
|
case "zh_cn":
|
||||||
|
nextCon = "简单";
|
||||||
|
break;
|
||||||
|
case "en":
|
||||||
|
nextCon = "Easy";
|
||||||
|
break;
|
||||||
|
case "jp":
|
||||||
|
nextCon = "簡単";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
diffCon = "Extra";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.leadList.innerHTML = '';
|
||||||
|
for (let i = 0; i < rank.length; i++){
|
||||||
|
let li = document.createElement("li");
|
||||||
|
li.innerHTML =
|
||||||
|
`<div>${i+1}</div><div>${rank[i].ID}</div><div>${rank[i].time}</div>`;
|
||||||
|
this.leadList.appendChild(li);
|
||||||
|
}
|
||||||
|
document.querySelector(".leaderDif").innerHTML = diffCon;
|
||||||
|
this.next.innerHTML = nextCon;
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
/**************************************
|
||||||
|
文件名:listener.js
|
||||||
|
功能:该模块用于处理事件监听相关内容
|
||||||
|
版本:2.0(23.01.08)
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
对象名:Listener
|
||||||
|
参数:lang: 传入语言
|
||||||
|
**************************************/
|
||||||
|
function Listener(lang) {
|
||||||
|
//获取HTML中的Language按钮
|
||||||
|
this.langSltBtn = document.querySelector(".langSltBtn");
|
||||||
|
//获取语言按钮列表
|
||||||
|
this.langSlt = document.querySelector("footer .language");
|
||||||
|
//获取所有的语言按钮
|
||||||
|
this.langs = document.querySelectorAll(".lang");
|
||||||
|
//获取难度菜单
|
||||||
|
this.difficulty = document.querySelector(".difficulty");
|
||||||
|
//获取主菜单列表按钮
|
||||||
|
this.selection = document.querySelectorAll(".menuList button");
|
||||||
|
//获取难度列表按钮
|
||||||
|
this.diffBtn = document.querySelectorAll(".difficulty button");
|
||||||
|
//获取新手教程界面
|
||||||
|
this.tutorial = document.querySelector(".tutorialBlk");
|
||||||
|
//获取新手教程界面返回按钮
|
||||||
|
this.tutoBack = document.querySelector(".tutorialBlk .back");
|
||||||
|
//获取排行榜界面
|
||||||
|
this.leaderBlk = document.querySelector(".leaderBlk");
|
||||||
|
//获取排行榜界面返回按钮
|
||||||
|
this.leadBack = document.querySelector(".leaderBlk .back");
|
||||||
|
//获取语言
|
||||||
|
this.lang = lang;
|
||||||
|
|
||||||
|
//在设置监听的匿名函数中,“this”不是指代的Listener对象,必须单独获取
|
||||||
|
let self = this;
|
||||||
|
//语言按钮点击事件
|
||||||
|
let flag = false;
|
||||||
|
this.langSltBtn.addEventListener("click", function (){
|
||||||
|
flag = !flag;
|
||||||
|
self.langSlt.style.display = (flag) ? "flex" : "none";
|
||||||
|
});
|
||||||
|
|
||||||
|
//主菜单列表按钮点击事件
|
||||||
|
for (let i = 0; i < this.selection.length; i++){
|
||||||
|
this.selection[i].addEventListener("click", function (){
|
||||||
|
self.btnClick(i);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//难度列表按钮点击事件
|
||||||
|
for (let i = 0; i < this.diffBtn.length; i++){
|
||||||
|
this.diffBtn[i].addEventListener("click", function (){
|
||||||
|
self.diffBtnClick(i);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//新手教程界面返回按钮点击事件
|
||||||
|
this.tutoBack.addEventListener("click", function (){
|
||||||
|
self.tutorial.style.display = "none";
|
||||||
|
});
|
||||||
|
|
||||||
|
//排行榜界面返回按钮点击事件
|
||||||
|
this.leadBack.addEventListener("click", function (){
|
||||||
|
self.leaderBlk.style.display = "none";
|
||||||
|
});
|
||||||
|
|
||||||
|
//切换语言
|
||||||
|
for (let i = 0; i < this.langs.length; i++) {
|
||||||
|
this.langs[i].addEventListener("click", function (){
|
||||||
|
let lang = self.langs[i].innerHTML;
|
||||||
|
let langCode = '';
|
||||||
|
switch(lang){
|
||||||
|
case "中文":
|
||||||
|
langCode = "zh_cn";
|
||||||
|
break;
|
||||||
|
case "English":
|
||||||
|
langCode = "en";
|
||||||
|
break;
|
||||||
|
case "日本語":
|
||||||
|
langCode = "jp";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
window.open(`./menu.html?lang=${langCode}`,"_self");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:btnClick()
|
||||||
|
参数:i: 第i个按钮
|
||||||
|
功能:处理主菜单按钮事件
|
||||||
|
**************************************/
|
||||||
|
Listener.prototype.btnClick = function(i){
|
||||||
|
switch (i){
|
||||||
|
case 0:
|
||||||
|
this.difficulty.style.display = "block";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
this.tutorial.style.display = "block";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
this.leaderBlk.style.display = "block";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
window.location.href = "about:blank";
|
||||||
|
window.close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
方式名:diffBtnClick()
|
||||||
|
参数:i: 第i个按钮
|
||||||
|
功能:处理难度选单按钮事件
|
||||||
|
**************************************/
|
||||||
|
Listener.prototype.diffBtnClick = function(i){
|
||||||
|
switch (i){
|
||||||
|
case 0:
|
||||||
|
window.open(`./game.html?lang=${this.lang}&mode=easy`, "_self");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
window.open(`./game.html?lang=${this.lang}&mode=hard`, "_self");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
window.open(`./game.html?lang=${this.lang}&mode=extra`, "_self");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
this.difficulty.style.display = "none";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//屏蔽右键菜单
|
||||||
|
document.oncontextmenu = function(event){
|
||||||
|
if(window.event){
|
||||||
|
event = window.event;
|
||||||
|
}try{
|
||||||
|
var the = event.srcElement;
|
||||||
|
if (!((the.tagName == "INPUT" && the.type.toLowerCase() == "text") || the.tagName == "TEXTAREA")){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}catch (e){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**************************************
|
||||||
|
文件名:menu.js
|
||||||
|
功能:该模块用于主菜单相关内容
|
||||||
|
版本:2.0(23.01.08)
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
new Menu();
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
对象名:Menu
|
||||||
|
**************************************/
|
||||||
|
function Menu(){
|
||||||
|
//获取URL中“?”及后续部分
|
||||||
|
let url = location.search;
|
||||||
|
if (url.indexOf("?") != -1){
|
||||||
|
//截取1到url.length的部分
|
||||||
|
url = url.substring(1);
|
||||||
|
//若传入了多个参数,以&分隔,此处将参数分解为数组内多个元素
|
||||||
|
url = url.split('&');
|
||||||
|
}
|
||||||
|
|
||||||
|
//url第0项是语言
|
||||||
|
this.lang = url[0].split('=')[1];
|
||||||
|
|
||||||
|
//获取HTML语言列表
|
||||||
|
this.langSlt = document.querySelector("footer .language");
|
||||||
|
this.languages = ["中文", "English", "日本語"];
|
||||||
|
//插入语言功能
|
||||||
|
for (let lang of this.languages){
|
||||||
|
const langLi = document.createElement("li");
|
||||||
|
langLi.setAttribute("class", `lang ${lang}`);
|
||||||
|
langLi.innerHTML = lang;
|
||||||
|
this.langSlt.appendChild(langLi);
|
||||||
|
}
|
||||||
|
this.listener = new Listener(this.lang);
|
||||||
|
this.language = new Language(this.lang);
|
||||||
|
this.language.langRender();
|
||||||
|
this.leaderBoard = new LeaderBoard(this.lang);
|
||||||
|
this.leaderBoard.rankRender("easy");
|
||||||
|
}
|
|
@ -0,0 +1,266 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
ul, li{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: "雅黑", "Clear Sans", "Helvetica Neue", Arial, sans-serif;
|
||||||
|
background-color: #eeca98;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
position: fixed;
|
||||||
|
width: 300px;
|
||||||
|
height: 100vh;
|
||||||
|
right: 0;
|
||||||
|
border-left: 2px solid black;
|
||||||
|
background-color: rgb(135, 133, 132, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
height: 100vh;
|
||||||
|
width: calc(100vw - 300px);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
/* 简单9*9模式下方块宽度54px*9+间隔18px*8 = 630px */
|
||||||
|
/* 困难16*16模式下方块宽度30px*16+间隔10px*15 = 630px */
|
||||||
|
/* Extra30*16模式下高度同困难,宽度由js改为单个方块宽度30px*30+间隔10px*15 = 1050px */
|
||||||
|
width: 630px;
|
||||||
|
height: 630px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: calc((100vh - 630px)/2);
|
||||||
|
margin-left: calc((100vw - 930px)/2);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 630px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .tile {
|
||||||
|
display: flex;
|
||||||
|
width: 54px;
|
||||||
|
height: 54px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #8f7a67;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .nonTriggered:active {
|
||||||
|
background: #6f5c4b;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .marked:active {
|
||||||
|
background: #6f5c4b;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .triggered {
|
||||||
|
background-color: #bca794;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .tile p{
|
||||||
|
display: none;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
font-family: Arial-Black;
|
||||||
|
font-weight: 700;
|
||||||
|
width: 54px;
|
||||||
|
height: 54px;
|
||||||
|
line-height: 54px;
|
||||||
|
font-size: 25px;
|
||||||
|
bottom: 27px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .triggered.value-1 p{
|
||||||
|
display: block;
|
||||||
|
color: aqua;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .triggered.value-2 p{
|
||||||
|
display: block;
|
||||||
|
color: #bee952;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .triggered.value-3 p{
|
||||||
|
display: block;
|
||||||
|
color: chartreuse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .triggered.value-4 p{
|
||||||
|
display: block;
|
||||||
|
color: rgb(233, 24, 233);
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .triggered.value-5 p{
|
||||||
|
display: block;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .triggered.value-6 p{
|
||||||
|
display: block;
|
||||||
|
color: purple;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .triggered.value-7 p{
|
||||||
|
display: block;
|
||||||
|
color: purple;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .triggered.value-8 p{
|
||||||
|
display: block;
|
||||||
|
color: purple;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .tile img{
|
||||||
|
display: none;
|
||||||
|
width: 54px;
|
||||||
|
height: 54px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .triggered.mine p{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .triggered.mine img{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid .tileLine .marked img{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 120px;
|
||||||
|
height: 50px;
|
||||||
|
background: #8f7a67;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #ffffff;
|
||||||
|
text-align: center;
|
||||||
|
font-family: "雅黑", Arial-Black;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 44px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #6f5c4b;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
background: #6f5c4b;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right .back {
|
||||||
|
margin-left: 90px;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right .new {
|
||||||
|
margin-left: 90px;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
margin-top: 60px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
align-items: center;
|
||||||
|
height: 200px;
|
||||||
|
font-family: "雅黑", Arial-Black;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time .timeTitle {
|
||||||
|
font-size: 40px;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time .timeCon {
|
||||||
|
display: block;
|
||||||
|
font-size: 25px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 50px;
|
||||||
|
height: 50px;
|
||||||
|
width: 160px;
|
||||||
|
border: 1px solid black;
|
||||||
|
background-color: #ebe84d;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mask {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
background-color: rgb(118, 115, 115, 0.6);
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mask .content {
|
||||||
|
text-align: center;
|
||||||
|
display: block;
|
||||||
|
font-size: 98px;
|
||||||
|
line-height: 98px;
|
||||||
|
height: 98px;
|
||||||
|
top: 300px;
|
||||||
|
font-family: Arial-Black;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mask .enterName {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 600px;
|
||||||
|
margin-top: 100px;
|
||||||
|
margin-left: calc((100vw - 600px)/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mask .enterName input {
|
||||||
|
width: 400px;
|
||||||
|
height: 50px;
|
||||||
|
padding-left: 20px;
|
||||||
|
font-size: 18px;
|
||||||
|
border: 2px solid black;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #bca794;
|
||||||
|
color: white;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mask .enterName input:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mask .maskBtn {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 260px;
|
||||||
|
bottom: 200px;
|
||||||
|
margin-left: calc((100vw - 260px)/2);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
.hello {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 98px;
|
||||||
|
display: block;
|
||||||
|
margin: -59px -300px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 98px;
|
||||||
|
height: 98px;
|
||||||
|
width: 600px;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: "雅黑", "Clear Sans", "Helvetica Neue", Arial, sans-serif;
|
||||||
|
background-color: #eeca98;
|
||||||
|
}
|
|
@ -0,0 +1,273 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
ul, li{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
position: relative;
|
||||||
|
font-size: 98px;
|
||||||
|
display: block;
|
||||||
|
margin-top: 5%;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 98px;
|
||||||
|
height: 98px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: "雅黑", "Clear Sans", "Helvetica Neue", Arial, sans-serif;
|
||||||
|
background-color: #eeca98;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selection {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 120px;
|
||||||
|
height: 50px;
|
||||||
|
background: #8f7a67;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #ffffff;
|
||||||
|
text-align: center;
|
||||||
|
font-family: "雅黑", Arial-Black;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 44px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #6f5c4b;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
background: #6f5c4b;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
left: 60px;
|
||||||
|
bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .langSltBtn {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 2px solid black;
|
||||||
|
font-size: 14px;
|
||||||
|
background: #8f7a67;
|
||||||
|
color: #ffffff;
|
||||||
|
text-align: center;
|
||||||
|
font-family: "雅黑", Arial-Black;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .langSltBtn:hover {
|
||||||
|
background: #6f5c4b;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .langSltBtn:active {
|
||||||
|
background: #6f5c4b;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .language {
|
||||||
|
width: 100px;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 2px solid black;
|
||||||
|
font-size: 14px;
|
||||||
|
background: #8f7a67;
|
||||||
|
color: #ffffff;
|
||||||
|
text-align: center;
|
||||||
|
font-family: "雅黑", Arial-Black;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 60px;
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .language .lang {
|
||||||
|
width: 100px;
|
||||||
|
height: 40px;
|
||||||
|
border: 2px solid white;
|
||||||
|
border-top: none;
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
display: block;
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .language .lang:hover {
|
||||||
|
background: #6f5c4b;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .language .lang:active {
|
||||||
|
background: #6f5c4b;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .language .lang:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.difficulty {
|
||||||
|
display: none;
|
||||||
|
z-index: 20;
|
||||||
|
position: fixed;
|
||||||
|
width: 320px;
|
||||||
|
height: 380px;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin: -190px -160px;
|
||||||
|
border: 4px solid black;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #ed9e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.difficulty .diffCon {
|
||||||
|
font-size: 60px;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
margin-top: 10%;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 60px;
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.difficulty .selection {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
line-height: 60px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tutorialBlk {
|
||||||
|
display: none;
|
||||||
|
z-index: 20;
|
||||||
|
position: fixed;
|
||||||
|
width: 600px;
|
||||||
|
height: 500px;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin: -250px -300px;
|
||||||
|
border: 4px solid black;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #ed9e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tutorialBlk .tutoCon {
|
||||||
|
font-size: 60px;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 60px;
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tutorialBlk .tutor {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-right: 20px;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
align-items: flex-start;
|
||||||
|
height: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tutorialBlk .tutor p {
|
||||||
|
font-size: 20px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
line-height: 60px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderBlk {
|
||||||
|
display: none;
|
||||||
|
z-index: 20;
|
||||||
|
position: fixed;
|
||||||
|
width: 600px;
|
||||||
|
height: 500px;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin: -250px -300px;
|
||||||
|
border: 4px solid black;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #ed9e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderBlk .leaderCon {
|
||||||
|
font-size: 60px;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 60px;
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderBlk .leaderDif {
|
||||||
|
font-size: 40px;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
margin-top: 10px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 60px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderBlk .leadList {
|
||||||
|
display: block;
|
||||||
|
margin-top: 40px;
|
||||||
|
margin-left: 30px;
|
||||||
|
margin-right: 30px;
|
||||||
|
height: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderBlk .leadList li {
|
||||||
|
display: flex;
|
||||||
|
font-size: 20px;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin: 20 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderBlk .next {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
line-height: 60px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
}
|
Loading…
Reference in New Issue