-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
414 additions
and
174 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# 大帝的挑战 | ||
|
||
游玩一个根据线索猜测这是哪位干员的游戏。 | ||
|
||
## 如何游玩 | ||
|
||
整局游戏由10道题目组成,玩家们将会依次猜测这十位干员是谁。 | ||
|
||
游戏主面板中,由两部分组成,线索总览和表格面板。 | ||
|
||
移动版页面如下: | ||
![alt text](/rules/CypherChallenge-image.png) | ||
桌面版页面如下: | ||
![alt text](/rules/CypherChallenge-image-1.png) | ||
|
||
所有玩家可以随意打字说出某个方舟中干员的代号。 | ||
|
||
一开始没有任何线索,玩家可以任意发言,比如某位玩家此时发送**阿米娅** | ||
|
||
![alt text](/rules/CypherChallenge-image-2.png) | ||
|
||
游戏面板中会立刻将列出阿米娅的名字,并且在后面跟随一系列大拇指图标。这些图标可能是红色或者绿色。 | ||
|
||
绿色表示**该干员**,本例中是**阿米娅**和**题目的谜底**在这个词条上相同。 | ||
比如图中**性别**和**职业**两个词条下是绿色大拇指,就表示这一题的谜底干员,他的性别和阿米娅一样,都是女性干员。而他的职业和阿米娅一样,都是术师。 | ||
|
||
红色表示该干员在这个词条和谜底不一致。 | ||
|
||
游戏中的每一题,都会在**势力**, **职业**, **子职业**, **稀有度**, **性别**, **队伍**, **阵营**, **画师**中抽取6个词条(取决于游戏房间设置)。所有词条初始均显示为???。直到某位玩家发送的干员和谜底在该词条上一致,此时会解锁该词条并标记一个绿色大拇指。 | ||
|
||
每一题有10次回答干员名称的机会,若全部耗尽还未猜出谜底,会公布答案并进入下一题。 | ||
|
||
上一题的答案,会作为下一题的第一条线索。因此从第二题开始,玩家们只有9次机会猜测。 | ||
|
||
## 计分规则 | ||
|
||
每答出一个干员 +200分 | ||
|
||
每首次解锁一个线索 +50分 | ||
|
||
不设扣分项 | ||
|
||
## 生涯统计 | ||
|
||
一局游戏只有至少两人参与游戏并都有得分,才会计入玩家生涯统计数据。 | ||
|
||
只有当你发送的内容确实是一个干员代号时,才会参与计算正确率,你回答的干员是正确谜底,或者首次解锁了一个线索时,记为正确答案,否则记为错误。 |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# 技能方格 | ||
|
||
游玩一个在方格里找干员技能名字的游戏。 | ||
|
||
## 如何游玩 | ||
|
||
游戏主面板中,你将看到一个有100个字组成的10x10的方格。 | ||
方格中其实包含了多位干员的多个技能,如下图所示: | ||
|
||
![技能方格总图](/rules/SchulteGrid-image-1.png) | ||
|
||
你的任务,就是抢在其他玩家之前,找到某个技能,并回答**拥有该技能的干员的干员代号。** | ||
|
||
比如你发现了**假日风暴**,并记起来他是**假日威龙陈**的技能。 | ||
|
||
![alt text](/rules/SchulteGrid-image-2.png) | ||
|
||
你就可以在聊天框中打出**假日威龙陈**并发送,这样你就得到了200分。 | ||
|
||
一个技能的每个字都是按顺序连在一起的。 | ||
|
||
游戏将会一直进行到房主关闭游戏房间,或所有干员全部回答出来。 | ||
游戏房间关闭时,如果还有未答出的干员,兔兔会公布答案。 | ||
|
||
## 备注 | ||
|
||
1. 谜题的生成经过算法的控制,答案是确保唯一的。 | ||
2. 一个干员可能有不止一个技能在方格中,回答该干员可以多倍得分。 | ||
|
||
## 计分规则 | ||
|
||
每答出一个干员 +200分 | ||
|
||
不设扣分项 | ||
|
||
## 生涯统计 | ||
|
||
一局游戏只有至少两人参与游戏并都有得分,才会计入玩家生涯统计数据。 | ||
|
||
只有当你发送的内容确实是一个干员代号时,才会参与计算正确率,该干员在本题则记为正确答案,否则记为错误答案。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,196 +1,65 @@ | ||
<template> | ||
<div class="waiting-room" v-if="gameRoomData"> | ||
<n-space vertical> | ||
<game-info-card :room-data="gameRoomData"> | ||
<template #tags> | ||
<n-tag type="info">等待中</n-tag> | ||
<waiting-room-base :on-on-settings-loaded="onSettingsChange" :settings="roomSettings"> | ||
<div class="waiting-room"> | ||
<n-popover trigger="click" placement="bottom"> | ||
<template #trigger> | ||
<n-button text> | ||
<icon :icon="Help" /> | ||
</n-button> | ||
</template> | ||
<template #buttons> | ||
<template v-if="isHost"> | ||
<icon-button :icon="Play" type="success" @click="startGame">开始游戏</icon-button> | ||
<icon-button :icon="Close" type="error" @click="closeRoom">关闭房间</icon-button> | ||
</template> | ||
<template v-else> | ||
<icon-button :icon="Logout" type="warning" @click="leaveRoom">退出房间</icon-button> | ||
</template> | ||
</template> | ||
</game-info-card> | ||
<n-card> | ||
<template #header> | ||
玩家列表 | ||
<n-tag type="default"> | ||
<n-space :size="0"> | ||
<icon :icon="Peoples" /> | ||
{{ Object.keys(players).length }} | ||
</n-space> | ||
</n-tag> | ||
</template> | ||
<template #header-extra> | ||
<icon-button :icon="ShareOne" type="primary" @click="shareRoom">邀请玩家</icon-button> | ||
</template> | ||
<n-space :size="20" style="padding-top: 10px"> | ||
<n-space vertical align="center" v-for="(item, index) in players" :key="index" :size="0"> | ||
<n-popover :trigger="isHost ? 'hover' : 'manual'"> | ||
<template #trigger> | ||
<n-badge | ||
:type="item.id === hostId ? 'error' : 'info'" | ||
:value="item.id === hostId ? '房主' : '玩家'" | ||
> | ||
<n-avatar size="large" :src="item.avatar" :img-props="{ referrerpolicy: 'no-referrer' }"/> | ||
</n-badge> | ||
</template> | ||
<span>I wish they all could be California girls</span> | ||
</n-popover> | ||
<span>{{ item.name }}</span> | ||
</n-space> | ||
</n-space> | ||
</n-card> | ||
</n-space> | ||
<div style="height: 100%"> | ||
<chat-board :players="players" :room-id="roomId" /> | ||
<span>私人房间只能通过房间号或邀请链接加入,<br/>不能从游戏大厅搜到。</span> | ||
</n-popover> | ||
<n-switch :rail-style="railStyle" v-model:value="isPrivateRoom"> | ||
<template #checked>私人房间</template> | ||
<template #unchecked>公开房间</template> | ||
</n-switch> | ||
</div> | ||
</div> | ||
</waiting-room-base> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
import { onMounted, onUnmounted, ref } from 'vue' | ||
import { useRoute, useRouter } from 'vue-router' | ||
import { Close, Logout, Peoples, Play, ShareOne } from '@icon-park/vue-next' | ||
import { useGameHubStore } from '@/stores/gamehub' | ||
import { getData, removeData, toast } from '@/utils' | ||
import type { GameTypes } from '@/def/games' | ||
import { getGameTypeMap } from '@/def/games' | ||
import type { GameRoom } from '@/api/game' | ||
import { getGame, getShortenUrl } from '@/api/game' | ||
import type { Player } from '@/def/players' | ||
import type { SignalrResponse } from '@/api/signalr' | ||
import IconButton from '@/universal/components/IconButton.vue' | ||
import type { CSSProperties } from 'vue' | ||
import { ref,watch } from 'vue' | ||
import { Help } from '@icon-park/vue-next' | ||
import WaitingRoomBase from '@/mobile/views/room/WaitingRoomBase.vue' | ||
import Icon from '@/universal/components/Icon.vue' | ||
import ChatBoard from '@/desktop/components/ChatBoard.vue' //注意这里是有意使用桌面端ChatBoard的 | ||
import GameInfoCard from '@/universal/components/GameInfoCard.vue' | ||
const route = useRoute() | ||
const router = useRouter() | ||
const gameHub = useGameHubStore() | ||
const userId = getData<string>('user-id') | ||
const roomId = Array.isArray(route.params.roomId) ? route.params.roomId.join(',') : route.params.roomId | ||
const isHost = ref(false) | ||
const hostId = ref('') | ||
const gameRoomData = ref<GameRoom>() | ||
const gameTypeMap = ref<GameTypes>(getGameTypeMap()) | ||
const players = ref<Player[]>([]) | ||
let getGameInterval: any = null | ||
async function gameInfoListener(response: SignalrResponse) { | ||
const playerList = response.PlayerList | ||
isHost.value = response.CreatorId == userId | ||
hostId.value = response.CreatorId | ||
players.value = playerList.map((p: SignalrResponse) => { | ||
const avatar = p.UserAvatar ? p.UserAvatar : '/avatar.webp' | ||
return { | ||
id: p.UserId, | ||
name: p.UserName, | ||
avatar: avatar | ||
} | ||
}) | ||
const roomSettings = ref<any>() | ||
const isPrivateRoom = ref(false) | ||
if (response.GameStarted) { | ||
await startGame() | ||
watch(isPrivateRoom, (value) => { | ||
roomSettings.value = { | ||
IsPrivate: value | ||
} | ||
} | ||
async function playerJoinedListener() { | ||
gameHub.invokeGameHub('GetGame', roomId) | ||
} | ||
async function playerLeftListener(response: SignalrResponse) { | ||
const playerId = response.LeavingPlayerId | ||
const method = response.LeavingMethod | ||
players.value = players.value.filter((p) => p.id !== playerId) | ||
}) | ||
// 如果是自己被踢,弹出提示并返回首页 | ||
if (playerId == userId && method == 'Kicked') { | ||
await toast('您已被房主踢出房间', 'warning') | ||
function railStyle({ focused, checked }: { focused: boolean; checked: boolean }) { | ||
const style: CSSProperties = { | ||
margin: '0 10px' | ||
} | ||
} | ||
async function gameStartedListener() { | ||
if (gameRoomData.value?.gameType) { | ||
const gameData = gameTypeMap.value[gameRoomData.value?.gameType] | ||
await router.push(gameData.route + "game/" + roomId) | ||
if (checked) { | ||
style.background = '#7b40f2' | ||
if (focused) { | ||
style.boxShadow = '0 0 0 2px #d0305040' | ||
} | ||
} else { | ||
style.background = '#18a058' | ||
if (focused) { | ||
style.boxShadow = '0 0 0 2px #2080f040' | ||
} | ||
} | ||
return style | ||
} | ||
async function gameClosedListener() { | ||
await toast('房间已关闭', 'warning') | ||
await router.push('/regular-home') | ||
} | ||
async function startGame() { | ||
gameHub.invokeGameHub('StartGame', roomId) | ||
} | ||
async function closeRoom() { | ||
gameHub.invokeGameHub('CloseGame', roomId) | ||
} | ||
async function leaveRoom() { | ||
removeData('current-game-id') | ||
gameHub.invokeGameHub('LeaveGame', roomId) | ||
await router.push('/regular-home') | ||
} | ||
async function shareRoom() { | ||
const sUrl = await getShortenUrl(roomId) | ||
await navigator.clipboard.writeText(`快来和大家一起玩游戏吧,点击链接: ${sUrl} 立刻加入房间。房间号[${gameRoomData.value?.joinCode}]。`) | ||
await toast('已复制加入链接到剪贴板', 'success') | ||
function onSettingsChange(Settings:any){ | ||
isPrivateRoom.value = Settings.isPrivateRoom | ||
} | ||
onMounted(async () => { | ||
gameRoomData.value = await getGame(roomId) | ||
gameHub.addGameHubListener('GameInfo', gameInfoListener) | ||
gameHub.addGameHubListener('PlayerJoined', playerJoinedListener) | ||
gameHub.addGameHubListener('PlayerLeft', playerLeftListener) | ||
gameHub.addGameHubListener('PlayerKicked', playerLeftListener) | ||
gameHub.addGameHubListener('GameClosed', gameClosedListener) | ||
gameHub.addGameHubListener('GameStarted', gameStartedListener) | ||
gameHub.invokeGameHub('GetGame', roomId) | ||
// 间隔一段时间获取一次房间信息 | ||
getGameInterval = setInterval(() => { | ||
gameHub.invokeGameHub('GetGame', roomId) | ||
}, 4000) | ||
}) | ||
onUnmounted(async () => { | ||
gameHub.removeGameHubListener('GameInfo', gameInfoListener) | ||
gameHub.removeGameHubListener('PlayerJoined', playerJoinedListener) | ||
gameHub.removeGameHubListener('PlayerLeft', playerLeftListener) | ||
gameHub.removeGameHubListener('PlayerKicked', playerLeftListener) | ||
gameHub.removeGameHubListener('GameClosed', gameClosedListener) | ||
gameHub.removeGameHubListener('GameStarted', gameStartedListener) | ||
clearInterval(getGameInterval) | ||
}) | ||
</script> | ||
|
||
<style lang="scss" scoped> | ||
.waiting-room { | ||
height: 100%; | ||
display: flex; | ||
flex-direction: column; | ||
overflow: auto; | ||
& > div { | ||
margin-bottom: 10px; | ||
} | ||
} | ||
</style> |
Oops, something went wrong.