add 添加 4 种道具
This commit is contained in:
parent
200b2a47d0
commit
9b8077c536
25
src/App.vue
25
src/App.vue
@ -3,20 +3,6 @@
|
||||
<div class="content">
|
||||
<router-view />
|
||||
</div>
|
||||
<div class="footer">
|
||||
鱼了个鱼 ©2022 by
|
||||
<a href="https://github.com/liyupi" target="_blank" style="color: #fff">
|
||||
程序员鱼皮
|
||||
</a>
|
||||
|
|
||||
<a
|
||||
href="https://github.com/liyupi/yulegeyu"
|
||||
target="_blank"
|
||||
style="color: #fff"
|
||||
>
|
||||
代码开源
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
@ -27,15 +13,4 @@
|
||||
min-height: 100vh;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: #fff;
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
|
120
src/core/game.ts
120
src/core/game.ts
@ -24,6 +24,7 @@ const useGame = () => {
|
||||
const currSlotNum = ref(0);
|
||||
|
||||
// 保存所有块(包括随机块)
|
||||
const allBlocks: BlockType[] = [];
|
||||
const blockData: Record<number, BlockType> = {};
|
||||
|
||||
// 总块数
|
||||
@ -43,6 +44,9 @@ const useGame = () => {
|
||||
// 保存整个 "棋盘" 的每个格子状态(下标为格子起始点横纵坐标)
|
||||
let chessBoard: ChessBoardUnitType[][] = [];
|
||||
|
||||
// 操作历史(存储点击的块)
|
||||
let opHistory: BlockType[] = [];
|
||||
|
||||
/**
|
||||
* 初始化指定大小的棋盘
|
||||
* @param width
|
||||
@ -80,9 +84,12 @@ const useGame = () => {
|
||||
console.log("块数单位", blockNumUnit);
|
||||
|
||||
// 随机生成的总块数
|
||||
const totalRandomBlockNum = gameConfig.randomBlocks.reduce((pre, curr) => {
|
||||
const totalRandomBlockNum = gameConfig.randomBlocks.reduce(
|
||||
(pre: number, curr: number) => {
|
||||
return pre + curr;
|
||||
}, 0);
|
||||
},
|
||||
0
|
||||
);
|
||||
console.log("随机生成的总块数", totalRandomBlockNum);
|
||||
|
||||
// 需要的最小块数
|
||||
@ -112,7 +119,6 @@ const useGame = () => {
|
||||
const randomAnimalBlocks = _.shuffle(animalBlocks);
|
||||
|
||||
// 初始化
|
||||
const allBlocks: BlockType[] = [];
|
||||
for (let i = 0; i < totalBlockNum; i++) {
|
||||
const newBlock = {
|
||||
id: i,
|
||||
@ -130,7 +136,7 @@ const useGame = () => {
|
||||
|
||||
// 3. 计算随机生成的块
|
||||
const randomBlocks: BlockType[][] = [];
|
||||
gameConfig.randomBlocks.forEach((randomBlock, idx) => {
|
||||
gameConfig.randomBlocks.forEach((randomBlock: number, idx: number) => {
|
||||
randomBlocks[idx] = [];
|
||||
for (let i = 0; i < randomBlock; i++) {
|
||||
randomBlocks[idx].push(allBlocks[pos]);
|
||||
@ -271,15 +277,15 @@ const useGame = () => {
|
||||
/**
|
||||
* 点击块事件
|
||||
* @param block
|
||||
* @param e
|
||||
* @param randomIdx 随机区域下标,>= 0 表示点击的是随机块
|
||||
* @param force 强制移除
|
||||
*/
|
||||
const doClickBlock = (block: BlockType, e: Event, randomIdx = -1) => {
|
||||
const doClickBlock = (block: BlockType, randomIdx = -1, force = false) => {
|
||||
// 已经输了 / 已经被点击 / 有上层块,不能再点击
|
||||
if (
|
||||
currSlotNum.value >= gameConfig.slotNum ||
|
||||
block.status !== 0 ||
|
||||
block.lowerThanBlocks.length > 0
|
||||
(block.lowerThanBlocks.length > 0 && !force)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@ -293,9 +299,8 @@ const useGame = () => {
|
||||
randomBlocksVal.value[randomIdx].length
|
||||
);
|
||||
} else {
|
||||
// 删除节点
|
||||
// @ts-ignore
|
||||
e.target.remove();
|
||||
// 非随机区才可撤回
|
||||
opHistory.push(block);
|
||||
// 移除覆盖关系
|
||||
block.higherThanBlocks.forEach((higherThanBlock) => {
|
||||
_.remove(higherThanBlock.lowerThanBlocks, (lowerThanBlock) => {
|
||||
@ -333,6 +338,8 @@ const useGame = () => {
|
||||
slotBlock.status = 2;
|
||||
// 已消除块数 +1
|
||||
clearBlockNum++;
|
||||
// 清除操作记录,防止撤回
|
||||
opHistory = [];
|
||||
return;
|
||||
}
|
||||
newSlotAreaVal[tempSlotNum++] = slotBlock;
|
||||
@ -364,6 +371,93 @@ const useGame = () => {
|
||||
gameStatus.value = 1;
|
||||
};
|
||||
|
||||
// region 技能
|
||||
|
||||
/**
|
||||
* 洗牌
|
||||
*
|
||||
* @desc 随机重洗所有未被点击的块
|
||||
*/
|
||||
const doShuffle = () => {
|
||||
// 遍历所有未消除的块
|
||||
const originBlocks = allBlocks.filter((block) => block.status === 0);
|
||||
const newBlockTypes = _.shuffle(originBlocks.map((block) => block.type));
|
||||
let pos = 0;
|
||||
originBlocks.forEach((block) => {
|
||||
block.type = newBlockTypes[pos++];
|
||||
});
|
||||
levelBlocksVal.value = [...levelBlocksVal.value];
|
||||
};
|
||||
|
||||
/**
|
||||
* 破坏
|
||||
*
|
||||
* @desc 消除一组层级块
|
||||
*/
|
||||
const doBroke = () => {
|
||||
// 类型,块列表映射
|
||||
const typeBlockMap: Record<string, BlockType[]> = {};
|
||||
const blocks = levelBlocksVal.value.filter((block) => block.status === 0);
|
||||
// 遍历所有未消除的层级块
|
||||
for (let i = 0; i < blocks.length; i++) {
|
||||
const block = blocks[i];
|
||||
if (!typeBlockMap[block.type]) {
|
||||
typeBlockMap[block.type] = [];
|
||||
}
|
||||
typeBlockMap[block.type].push(block);
|
||||
// 有能消除的一组块
|
||||
if (typeBlockMap[block.type].length >= gameConfig.composeNum) {
|
||||
typeBlockMap[block.type].forEach((clickBlock) => {
|
||||
doClickBlock(clickBlock, -1, true);
|
||||
});
|
||||
console.log("doBroke", typeBlockMap[block.type]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 撤回
|
||||
*
|
||||
* @desc 后退一步
|
||||
*/
|
||||
const doRevert = () => {
|
||||
if (opHistory.length < 1) {
|
||||
return;
|
||||
}
|
||||
opHistory[opHistory.length - 1].status = 0;
|
||||
// @ts-ignore
|
||||
slotAreaVal.value[currSlotNum.value - 1] = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 移出块
|
||||
*/
|
||||
const doRemove = () => {
|
||||
// 移除第一个块
|
||||
const block = slotAreaVal.value[0];
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
// 槽移除块
|
||||
for (let i = 0; i < slotAreaVal.value.length - 1; i++) {
|
||||
slotAreaVal.value[i] = slotAreaVal.value[i - 1];
|
||||
}
|
||||
// @ts-ignore
|
||||
slotAreaVal.value[slotAreaVal.value.length - 1] = null;
|
||||
// 改变新块的坐标
|
||||
block.x = Math.floor(Math.random() * (boxWidthNum - 2));
|
||||
block.y = boxHeightNum - 2;
|
||||
block.status = 0;
|
||||
// 移除的是随机块的元素,移到层级区域
|
||||
if (block.level < 1) {
|
||||
block.level = 10000;
|
||||
levelBlocksVal.value.push(block);
|
||||
}
|
||||
};
|
||||
|
||||
// endregion
|
||||
|
||||
return {
|
||||
gameStatus,
|
||||
levelBlocksVal,
|
||||
@ -371,8 +465,14 @@ const useGame = () => {
|
||||
slotAreaVal,
|
||||
widthUnit,
|
||||
heightUnit,
|
||||
currSlotNum,
|
||||
opHistory,
|
||||
doClickBlock,
|
||||
doStart,
|
||||
doShuffle,
|
||||
doBroke,
|
||||
doRemove,
|
||||
doRevert,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -23,7 +23,7 @@ const animals = [
|
||||
"🐂",
|
||||
];
|
||||
|
||||
export const defaultGameConfig: GameConfig = {
|
||||
export const defaultGameConfig: GameConfigType = {
|
||||
// 槽容量
|
||||
slotNum: 7,
|
||||
// 需要多少个一样块的才能合成
|
||||
@ -45,7 +45,7 @@ export const defaultGameConfig: GameConfig = {
|
||||
/**
|
||||
* 简单难度
|
||||
*/
|
||||
export const easyGameConfig: GameConfig = {
|
||||
export const easyGameConfig: GameConfigType = {
|
||||
// 槽容量
|
||||
slotNum: 7,
|
||||
// 需要多少个一样块的才能合成
|
||||
@ -67,7 +67,7 @@ export const easyGameConfig: GameConfig = {
|
||||
/**
|
||||
* 中等难度
|
||||
*/
|
||||
export const middleGameConfig: GameConfig = {
|
||||
export const middleGameConfig: GameConfigType = {
|
||||
// 槽容量
|
||||
slotNum: 7,
|
||||
// 需要多少个一样块的才能合成
|
||||
@ -89,7 +89,7 @@ export const middleGameConfig: GameConfig = {
|
||||
/**
|
||||
* 困难难度
|
||||
*/
|
||||
export const hardGameConfig: GameConfig = {
|
||||
export const hardGameConfig: GameConfigType = {
|
||||
// 槽容量
|
||||
slotNum: 7,
|
||||
// 需要多少个一样块的才能合成
|
||||
@ -111,7 +111,7 @@ export const hardGameConfig: GameConfig = {
|
||||
/**
|
||||
* 地狱难度
|
||||
*/
|
||||
export const lunaticGameConfig: GameConfig = {
|
||||
export const lunaticGameConfig: GameConfigType = {
|
||||
// 槽容量
|
||||
slotNum: 7,
|
||||
// 需要多少个一样块的才能合成
|
||||
|
@ -24,10 +24,10 @@ export const useGlobalStore = defineStore("global", {
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setGameConfig(gameConfig: GameConfig) {
|
||||
setGameConfig(gameConfig: GameConfigType) {
|
||||
this.gameConfig = gameConfig;
|
||||
},
|
||||
setCustomConfig(customConfig: GameConfig) {
|
||||
setCustomConfig(customConfig: GameConfigType) {
|
||||
this.customConfig = customConfig;
|
||||
},
|
||||
reset() {
|
||||
|
14
src/core/type.d.ts
vendored
14
src/core/type.d.ts
vendored
@ -24,9 +24,9 @@ interface ChessBoardUnitType {
|
||||
}
|
||||
|
||||
/**
|
||||
* 游戏配置
|
||||
* 游戏配置类型
|
||||
*/
|
||||
interface GameConfig {
|
||||
interface GameConfigType {
|
||||
// 槽容量
|
||||
slotNum: number;
|
||||
// 需要多少个一样块的才能合成
|
||||
@ -48,3 +48,13 @@ interface GameConfig {
|
||||
// 最下层块数最小值(已废弃)
|
||||
// minBottomBlockNum: 20,
|
||||
}
|
||||
|
||||
/**
|
||||
* 技能类型
|
||||
*/
|
||||
interface SkillType {
|
||||
name: string;
|
||||
desc: string;
|
||||
icon: string;
|
||||
action: function;
|
||||
}
|
||||
|
@ -53,9 +53,9 @@ import { defaultGameConfig } from "../core/gameConfig";
|
||||
const formRef = ref<FormInstance>();
|
||||
const router = useRouter();
|
||||
const { setGameConfig, setCustomConfig } = useGlobalStore();
|
||||
const config = reactive<GameConfig>({ ...defaultGameConfig });
|
||||
const config = reactive<GameConfigType>({ ...defaultGameConfig });
|
||||
|
||||
const handleFinish = (values: GameConfig) => {
|
||||
const handleFinish = (values: GameConfigType) => {
|
||||
setGameConfig(config);
|
||||
setCustomConfig(config);
|
||||
router.push("/game");
|
||||
|
@ -5,14 +5,13 @@
|
||||
<!-- 胜利 -->
|
||||
<div v-if="gameStatus === 3" style="text-align: center">
|
||||
<h2>恭喜,你赢啦!🎉</h2>
|
||||
<img src="../assets/kunkun.png" />
|
||||
<img alt="程序员鱼皮" src="../assets/kunkun.png" />
|
||||
</div>
|
||||
<!-- 分层选块 -->
|
||||
<div class="level-board">
|
||||
<div v-show="gameStatus > 0" class="level-board">
|
||||
<div v-for="(block, idx) in levelBlocksVal" :key="idx">
|
||||
<div
|
||||
v-for="(block, idx) in levelBlocksVal"
|
||||
v-show="gameStatus > 0"
|
||||
:key="idx"
|
||||
v-if="block.status === 0"
|
||||
class="block level-block"
|
||||
:class="{ disabled: block.lowerThanBlocks.length > 0 }"
|
||||
:data-id="block.id"
|
||||
@ -21,11 +20,12 @@
|
||||
left: block.x * widthUnit + 'px',
|
||||
top: block.y * heightUnit + 'px',
|
||||
}"
|
||||
@click="(e) => doClickBlock(block, e)"
|
||||
@click="() => doClickBlock(block)"
|
||||
>
|
||||
{{ block.type }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 随机选块 -->
|
||||
<div class="random-board">
|
||||
<div
|
||||
@ -35,11 +35,13 @@
|
||||
>
|
||||
<div
|
||||
v-if="randomBlock.length > 0"
|
||||
:data-id="randomBlock[0].id"
|
||||
class="block"
|
||||
@click="(e) => doClickBlock(randomBlock[0], e, index)"
|
||||
@click="() => doClickBlock(randomBlock[0], index)"
|
||||
>
|
||||
{{ randomBlock[0].type }}
|
||||
</div>
|
||||
<!-- 隐藏 -->
|
||||
<div
|
||||
v-for="num in Math.max(randomBlock.length - 1, 0)"
|
||||
:key="num"
|
||||
@ -57,6 +59,13 @@
|
||||
{{ slotBlock?.type }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 技能 -->
|
||||
<a-space style="margin-top: 16px">
|
||||
<a-button size="small" @click="doRevert">撤回</a-button>
|
||||
<a-button size="small" @click="doRemove">移出</a-button>
|
||||
<a-button size="small" @click="doShuffle">洗牌</a-button>
|
||||
<a-button size="small" @click="doBroke">破坏</a-button>
|
||||
</a-space>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
@ -77,6 +86,10 @@ const {
|
||||
heightUnit,
|
||||
doClickBlock,
|
||||
doStart,
|
||||
doShuffle,
|
||||
doBroke,
|
||||
doRemove,
|
||||
doRevert,
|
||||
} = useGame();
|
||||
|
||||
/**
|
||||
|
@ -39,6 +39,20 @@
|
||||
代码完全开源,欢迎 star
|
||||
</div>
|
||||
</a>
|
||||
<div class="footer">
|
||||
鱼了个鱼 ©2022 by
|
||||
<a href="https://github.com/liyupi" target="_blank" style="color: #fff">
|
||||
程序员鱼皮
|
||||
</a>
|
||||
|
|
||||
<a
|
||||
href="https://github.com/liyupi/yulegeyu"
|
||||
target="_blank"
|
||||
style="color: #fff"
|
||||
>
|
||||
代码开源
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -57,7 +71,7 @@ const router = useRouter();
|
||||
|
||||
const { setGameConfig } = useGlobalStore();
|
||||
|
||||
const toGamePage = (config?: GameConfig) => {
|
||||
const toGamePage = (config?: GameConfigType) => {
|
||||
if (config) {
|
||||
setGameConfig(config);
|
||||
router.push("/game");
|
||||
@ -71,4 +85,15 @@ const toGamePage = (config?: GameConfig) => {
|
||||
#indexPage {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: #fff;
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user