add 添加 4 种道具
This commit is contained in:
		
							
								
								
									
										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> | ||||
|   | ||||
							
								
								
									
										122
									
								
								src/core/game.ts
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								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) => { | ||||
|       return pre + curr; | ||||
|     }, 0); | ||||
|     const totalRandomBlockNum = gameConfig.randomBlocks.reduce( | ||||
|       (pre: number, curr: number) => { | ||||
|         return pre + curr; | ||||
|       }, | ||||
|       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,25 +5,25 @@ | ||||
|       <!-- 胜利 --> | ||||
|       <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-for="(block, idx) in levelBlocksVal" | ||||
|           v-show="gameStatus > 0" | ||||
|           :key="idx" | ||||
|           class="block level-block" | ||||
|           :class="{ disabled: block.lowerThanBlocks.length > 0 }" | ||||
|           :data-id="block.id" | ||||
|           :style="{ | ||||
|             zIndex: 100 + block.level, | ||||
|             left: block.x * widthUnit + 'px', | ||||
|             top: block.y * heightUnit + 'px', | ||||
|           }" | ||||
|           @click="(e) => doClickBlock(block, e)" | ||||
|         > | ||||
|           {{ block.type }} | ||||
|       <div v-show="gameStatus > 0" class="level-board"> | ||||
|         <div v-for="(block, idx) in levelBlocksVal" :key="idx"> | ||||
|           <div | ||||
|             v-if="block.status === 0" | ||||
|             class="block level-block" | ||||
|             :class="{ disabled: block.lowerThanBlocks.length > 0 }" | ||||
|             :data-id="block.id" | ||||
|             :style="{ | ||||
|               zIndex: 100 + block.level, | ||||
|               left: block.x * widthUnit + 'px', | ||||
|               top: block.y * heightUnit + 'px', | ||||
|             }" | ||||
|             @click="() => doClickBlock(block)" | ||||
|           > | ||||
|             {{ block.type }} | ||||
|           </div> | ||||
|         </div> | ||||
|       </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> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user