feat(game): 添加连续奖励表格和银行选项图标
- 在游戏规则模态框中添加连续奖励表格组件 - 更新英文和印尼语的游戏规则文本内容 - 添加银行选项的图标显示功能,支持自定义logo或默认地标图标 - 实现连续奖励数据类型和接口定义 - 在游戏会话存储中添加连续奖励行数据管理 - 集成银行选择下拉菜单中的图标展示 - 更新WithdrawField组件以支持银行图标显示
This commit is contained in:
@@ -33,6 +33,7 @@ import type {
|
|||||||
GameLobbyInitDto,
|
GameLobbyInitDto,
|
||||||
GameLobbyInitResult,
|
GameLobbyInitResult,
|
||||||
GameLobbyPeriodDto,
|
GameLobbyPeriodDto,
|
||||||
|
GameLobbyStreakWinRewardRowDto,
|
||||||
GamePeriodTickDto,
|
GamePeriodTickDto,
|
||||||
GamePlaceBetDto,
|
GamePlaceBetDto,
|
||||||
GamePlaceBetRequestDto,
|
GamePlaceBetRequestDto,
|
||||||
@@ -45,6 +46,7 @@ import type {
|
|||||||
RoundPhase,
|
RoundPhase,
|
||||||
RoundSnapshot,
|
RoundSnapshot,
|
||||||
RoundSnapshotDto,
|
RoundSnapshotDto,
|
||||||
|
StreakWinRewardRow,
|
||||||
TrendEntry,
|
TrendEntry,
|
||||||
TrendEntryDto,
|
TrendEntryDto,
|
||||||
} from '@/type'
|
} from '@/type'
|
||||||
@@ -260,6 +262,26 @@ function normalizeLobbyCells(dictionary: GameLobbyInitDto['dictionary']) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeStreakWinRewardRows(rows?: GameLobbyStreakWinRewardRowDto[]) {
|
||||||
|
if (!Array.isArray(rows)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows
|
||||||
|
.filter((row) => Number.isFinite(row.streak))
|
||||||
|
.map(
|
||||||
|
(row) =>
|
||||||
|
({
|
||||||
|
fixedAmount: row.fixed_amount,
|
||||||
|
isJackpot: row.is_jackpot,
|
||||||
|
oddsFactor: Number(row.odds_factor) || 0,
|
||||||
|
rewardType: row.reward_type,
|
||||||
|
streak: Math.floor(row.streak),
|
||||||
|
}) satisfies StreakWinRewardRow,
|
||||||
|
)
|
||||||
|
.sort((left, right) => left.streak - right.streak)
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizePeriodTickRound(
|
export function normalizePeriodTickRound(
|
||||||
period: GamePeriodTickDto,
|
period: GamePeriodTickDto,
|
||||||
previousRound?: Pick<RoundSnapshot, 'id' | 'startedAt'> | null,
|
previousRound?: Pick<RoundSnapshot, 'id' | 'startedAt'> | null,
|
||||||
@@ -408,6 +430,9 @@ export async function getGameLobbyInit() {
|
|||||||
runtimeEnabled: dto.runtime_enabled,
|
runtimeEnabled: dto.runtime_enabled,
|
||||||
serverTime: dto.server_time,
|
serverTime: dto.server_time,
|
||||||
snapshot: normalizeGameLobbyInit(dto),
|
snapshot: normalizeGameLobbyInit(dto),
|
||||||
|
streakWinRewardRows: normalizeStreakWinRewardRows(
|
||||||
|
dto.streak_win_reward?.rows,
|
||||||
|
),
|
||||||
userSnapshot: dto.user_snapshot,
|
userSnapshot: dto.user_snapshot,
|
||||||
} satisfies GameLobbyInitResult
|
} satisfies GameLobbyInitResult
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,175 +1,191 @@
|
|||||||
|
import { Fragment, type ReactNode } from 'react'
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
import remarkGfm from 'remark-gfm'
|
import remarkGfm from 'remark-gfm'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const STREAK_REWARD_TABLE_SLOT = '{{STREAK_REWARD_TABLE}}'
|
||||||
|
|
||||||
interface RulesMarkdownContentProps {
|
interface RulesMarkdownContentProps {
|
||||||
|
children?: ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
content: string
|
content: string
|
||||||
compact?: boolean
|
compact?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function RulesMarkdownContent({
|
function RulesMarkdownContent({
|
||||||
|
children,
|
||||||
className,
|
className,
|
||||||
compact = false,
|
compact = false,
|
||||||
content,
|
content,
|
||||||
}: RulesMarkdownContentProps) {
|
}: RulesMarkdownContentProps) {
|
||||||
|
const contentParts = content.split(STREAK_REWARD_TABLE_SLOT)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<ReactMarkdown
|
{contentParts.map((contentPart, index) => (
|
||||||
remarkPlugins={[remarkGfm]}
|
<Fragment key={contentPart || STREAK_REWARD_TABLE_SLOT}>
|
||||||
components={{
|
{contentPart ? (
|
||||||
h1: ({ children }) => (
|
<ReactMarkdown
|
||||||
<h1
|
remarkPlugins={[remarkGfm]}
|
||||||
className={cn(
|
components={{
|
||||||
'font-bold leading-tight text-[#F4FFFF]',
|
h1: ({ children }) => (
|
||||||
compact
|
<h1
|
||||||
? 'mb-design-8 text-design-15'
|
className={cn(
|
||||||
: 'mb-design-14 text-design-24',
|
'font-bold leading-tight text-[#F4FFFF]',
|
||||||
)}
|
compact
|
||||||
|
? 'mb-design-8 text-design-15'
|
||||||
|
: 'mb-design-14 text-design-24',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</h1>
|
||||||
|
),
|
||||||
|
h2: ({ children }) => (
|
||||||
|
<h2
|
||||||
|
className={cn(
|
||||||
|
'font-bold leading-tight text-[#E7FFFF]',
|
||||||
|
compact
|
||||||
|
? 'mb-design-6 mt-design-12 text-design-14'
|
||||||
|
: 'mb-design-10 mt-design-18 text-design-22',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</h2>
|
||||||
|
),
|
||||||
|
h3: ({ children }) => (
|
||||||
|
<h3
|
||||||
|
className={cn(
|
||||||
|
'font-bold leading-tight text-[#8FE7EF]',
|
||||||
|
compact
|
||||||
|
? 'mb-design-5 mt-design-10 text-design-13'
|
||||||
|
: 'mb-design-8 mt-design-16 text-design-20',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</h3>
|
||||||
|
),
|
||||||
|
h4: ({ children }) => (
|
||||||
|
<h4
|
||||||
|
className={cn(
|
||||||
|
'font-semibold leading-tight text-[#FFF2B8]',
|
||||||
|
compact
|
||||||
|
? 'mb-design-4 mt-design-9 text-design-12'
|
||||||
|
: 'mb-design-7 mt-design-14 text-design-18',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</h4>
|
||||||
|
),
|
||||||
|
p: ({ children }) => (
|
||||||
|
<p
|
||||||
|
className={cn(
|
||||||
|
'text-[#B9E7EA]',
|
||||||
|
compact
|
||||||
|
? 'mb-design-7 leading-[1.66]'
|
||||||
|
: 'mb-design-11 leading-[1.76]',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</p>
|
||||||
|
),
|
||||||
|
strong: ({ children }) => (
|
||||||
|
<strong className="font-bold text-[#FFF2B8]">
|
||||||
|
{children}
|
||||||
|
</strong>
|
||||||
|
),
|
||||||
|
ul: ({ children }) => (
|
||||||
|
<ul
|
||||||
|
className={cn(
|
||||||
|
'list-disc text-[#B9E7EA]',
|
||||||
|
compact
|
||||||
|
? 'mb-design-8 space-y-design-4 pl-design-15'
|
||||||
|
: 'mb-design-12 space-y-design-6 pl-design-22',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ul>
|
||||||
|
),
|
||||||
|
ol: ({ children }) => (
|
||||||
|
<ol
|
||||||
|
className={cn(
|
||||||
|
'list-decimal text-[#B9E7EA]',
|
||||||
|
compact
|
||||||
|
? 'mb-design-8 space-y-design-4 pl-design-15'
|
||||||
|
: 'mb-design-12 space-y-design-6 pl-design-22',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ol>
|
||||||
|
),
|
||||||
|
li: ({ children }) => (
|
||||||
|
<li className={compact ? 'leading-[1.62]' : 'leading-[1.72]'}>
|
||||||
|
{children}
|
||||||
|
</li>
|
||||||
|
),
|
||||||
|
code: ({ children }) => (
|
||||||
|
<code className="rounded-[calc(var(--design-unit)*4)] border border-[rgba(255,226,41,0.18)] bg-[rgba(255,226,41,0.08)] px-design-5 py-[1px] font-mono text-[#FFF2B8]">
|
||||||
|
{children}
|
||||||
|
</code>
|
||||||
|
),
|
||||||
|
pre: ({ children }) => (
|
||||||
|
<pre
|
||||||
|
className={cn(
|
||||||
|
'overflow-x-auto rounded-[calc(var(--design-unit)*8)] border border-[rgba(126,234,244,0.16)] bg-[rgba(2,15,26,0.72)] font-mono text-[#D8FCFF]',
|
||||||
|
compact
|
||||||
|
? 'mb-design-9 px-design-9 py-design-7 text-design-10'
|
||||||
|
: 'mb-design-13 px-design-14 py-design-10 text-design-15',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</pre>
|
||||||
|
),
|
||||||
|
table: ({ children }) => (
|
||||||
|
<div className="mb-design-12 overflow-x-auto rounded-[calc(var(--design-unit)*8)] border border-[rgba(126,234,244,0.16)]">
|
||||||
|
<table className="w-full min-w-max border-collapse bg-[rgba(2,15,26,0.58)]">
|
||||||
|
{children}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
thead: ({ children }) => (
|
||||||
|
<thead className="bg-[rgba(126,234,244,0.12)] text-[#E9FFFF]">
|
||||||
|
{children}
|
||||||
|
</thead>
|
||||||
|
),
|
||||||
|
th: ({ children }) => (
|
||||||
|
<th
|
||||||
|
className={cn(
|
||||||
|
'border border-[rgba(126,234,244,0.13)] text-center font-semibold',
|
||||||
|
compact
|
||||||
|
? 'px-design-7 py-design-6'
|
||||||
|
: 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</th>
|
||||||
|
),
|
||||||
|
td: ({ children }) => (
|
||||||
|
<td
|
||||||
|
className={cn(
|
||||||
|
'border border-[rgba(126,234,244,0.1)] text-center text-[#C8F4F7]',
|
||||||
|
compact
|
||||||
|
? 'px-design-7 py-design-6'
|
||||||
|
: 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</td>
|
||||||
|
),
|
||||||
|
hr: () => (
|
||||||
|
<hr className="my-design-12 border-0 border-t border-[rgba(126,234,244,0.16)]" />
|
||||||
|
),
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{contentPart}
|
||||||
</h1>
|
</ReactMarkdown>
|
||||||
),
|
) : null}
|
||||||
h2: ({ children }) => (
|
{index < contentParts.length - 1 ? children : null}
|
||||||
<h2
|
</Fragment>
|
||||||
className={cn(
|
))}
|
||||||
'font-bold leading-tight text-[#E7FFFF]',
|
|
||||||
compact
|
|
||||||
? 'mb-design-6 mt-design-12 text-design-14'
|
|
||||||
: 'mb-design-10 mt-design-18 text-design-22',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</h2>
|
|
||||||
),
|
|
||||||
h3: ({ children }) => (
|
|
||||||
<h3
|
|
||||||
className={cn(
|
|
||||||
'font-bold leading-tight text-[#8FE7EF]',
|
|
||||||
compact
|
|
||||||
? 'mb-design-5 mt-design-10 text-design-13'
|
|
||||||
: 'mb-design-8 mt-design-16 text-design-20',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</h3>
|
|
||||||
),
|
|
||||||
h4: ({ children }) => (
|
|
||||||
<h4
|
|
||||||
className={cn(
|
|
||||||
'font-semibold leading-tight text-[#FFF2B8]',
|
|
||||||
compact
|
|
||||||
? 'mb-design-4 mt-design-9 text-design-12'
|
|
||||||
: 'mb-design-7 mt-design-14 text-design-18',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</h4>
|
|
||||||
),
|
|
||||||
p: ({ children }) => (
|
|
||||||
<p
|
|
||||||
className={cn(
|
|
||||||
'text-[#B9E7EA]',
|
|
||||||
compact
|
|
||||||
? 'mb-design-7 leading-[1.66]'
|
|
||||||
: 'mb-design-11 leading-[1.76]',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</p>
|
|
||||||
),
|
|
||||||
strong: ({ children }) => (
|
|
||||||
<strong className="font-bold text-[#FFF2B8]">{children}</strong>
|
|
||||||
),
|
|
||||||
ul: ({ children }) => (
|
|
||||||
<ul
|
|
||||||
className={cn(
|
|
||||||
'list-disc text-[#B9E7EA]',
|
|
||||||
compact
|
|
||||||
? 'mb-design-8 space-y-design-4 pl-design-15'
|
|
||||||
: 'mb-design-12 space-y-design-6 pl-design-22',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</ul>
|
|
||||||
),
|
|
||||||
ol: ({ children }) => (
|
|
||||||
<ol
|
|
||||||
className={cn(
|
|
||||||
'list-decimal text-[#B9E7EA]',
|
|
||||||
compact
|
|
||||||
? 'mb-design-8 space-y-design-4 pl-design-15'
|
|
||||||
: 'mb-design-12 space-y-design-6 pl-design-22',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</ol>
|
|
||||||
),
|
|
||||||
li: ({ children }) => (
|
|
||||||
<li className={compact ? 'leading-[1.62]' : 'leading-[1.72]'}>
|
|
||||||
{children}
|
|
||||||
</li>
|
|
||||||
),
|
|
||||||
code: ({ children }) => (
|
|
||||||
<code className="rounded-[calc(var(--design-unit)*4)] border border-[rgba(255,226,41,0.18)] bg-[rgba(255,226,41,0.08)] px-design-5 py-[1px] font-mono text-[#FFF2B8]">
|
|
||||||
{children}
|
|
||||||
</code>
|
|
||||||
),
|
|
||||||
pre: ({ children }) => (
|
|
||||||
<pre
|
|
||||||
className={cn(
|
|
||||||
'overflow-x-auto rounded-[calc(var(--design-unit)*8)] border border-[rgba(126,234,244,0.16)] bg-[rgba(2,15,26,0.72)] font-mono text-[#D8FCFF]',
|
|
||||||
compact
|
|
||||||
? 'mb-design-9 px-design-9 py-design-7 text-design-10'
|
|
||||||
: 'mb-design-13 px-design-14 py-design-10 text-design-15',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</pre>
|
|
||||||
),
|
|
||||||
table: ({ children }) => (
|
|
||||||
<div className="mb-design-12 overflow-x-auto rounded-[calc(var(--design-unit)*8)] border border-[rgba(126,234,244,0.16)]">
|
|
||||||
<table className="w-full min-w-max border-collapse bg-[rgba(2,15,26,0.58)]">
|
|
||||||
{children}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
thead: ({ children }) => (
|
|
||||||
<thead className="bg-[rgba(126,234,244,0.12)] text-[#E9FFFF]">
|
|
||||||
{children}
|
|
||||||
</thead>
|
|
||||||
),
|
|
||||||
th: ({ children }) => (
|
|
||||||
<th
|
|
||||||
className={cn(
|
|
||||||
'border border-[rgba(126,234,244,0.13)] text-center font-semibold',
|
|
||||||
compact
|
|
||||||
? 'px-design-7 py-design-6'
|
|
||||||
: 'px-design-12 py-design-9',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</th>
|
|
||||||
),
|
|
||||||
td: ({ children }) => (
|
|
||||||
<td
|
|
||||||
className={cn(
|
|
||||||
'border border-[rgba(126,234,244,0.1)] text-center text-[#C8F4F7]',
|
|
||||||
compact
|
|
||||||
? 'px-design-7 py-design-6'
|
|
||||||
: 'px-design-12 py-design-9',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</td>
|
|
||||||
),
|
|
||||||
hr: () => (
|
|
||||||
<hr className="my-design-12 border-0 border-t border-[rgba(126,234,244,0.16)]" />
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{content}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
197
src/components/streak-reward-table.tsx
Normal file
197
src/components/streak-reward-table.tsx
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { useGameSessionStore } from '@/store'
|
||||||
|
import type { StreakRewardType } from '@/type'
|
||||||
|
|
||||||
|
interface StreakRewardTableProps {
|
||||||
|
compact?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatFixedAmount(value: string, locale: string) {
|
||||||
|
const amount = Number(value)
|
||||||
|
|
||||||
|
if (!Number.isFinite(amount)) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
return amount.toLocaleString(locale, {
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
minimumFractionDigits: amount % 1 === 0 ? 0 : 2,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function StreakRewardTable({ compact = false }: StreakRewardTableProps) {
|
||||||
|
const { i18n, t } = useTranslation()
|
||||||
|
const rows = useGameSessionStore((state) => state.streakWinRewardRows)
|
||||||
|
const sortedRows = useMemo(
|
||||||
|
() => [...rows].sort((left, right) => left.streak - right.streak),
|
||||||
|
[rows],
|
||||||
|
)
|
||||||
|
|
||||||
|
const getRewardTypeLabel = (rewardType: StreakRewardType) => {
|
||||||
|
if (rewardType === 'coefficient') {
|
||||||
|
return t('game.modals.rules.streakRewardTable.rewardTypes.coefficient')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rewardType === 'fixed') {
|
||||||
|
return t('game.modals.rules.streakRewardTable.rewardTypes.fixed')
|
||||||
|
}
|
||||||
|
|
||||||
|
return rewardType
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'overflow-hidden rounded-[calc(var(--design-unit)*8)] border border-[rgba(126,234,244,0.16)] bg-[rgba(2,15,26,0.58)]',
|
||||||
|
compact ? 'mb-design-9' : 'mb-design-13',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'border-b border-[rgba(126,234,244,0.16)] bg-[rgba(126,234,244,0.08)] font-semibold text-[#FFF2B8]',
|
||||||
|
compact ? 'px-design-8 py-design-6' : 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t('game.modals.rules.streakRewardTable.title')}
|
||||||
|
</div>
|
||||||
|
{sortedRows.length > 0 ? (
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full min-w-max border-collapse">
|
||||||
|
<thead className="bg-[rgba(126,234,244,0.12)] text-[#E9FFFF]">
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
className={cn(
|
||||||
|
'border border-[rgba(126,234,244,0.13)] text-center font-semibold',
|
||||||
|
compact
|
||||||
|
? 'px-design-7 py-design-6'
|
||||||
|
: 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t('game.modals.rules.streakRewardTable.columns.streak')}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className={cn(
|
||||||
|
'border border-[rgba(126,234,244,0.13)] text-center font-semibold',
|
||||||
|
compact
|
||||||
|
? 'px-design-7 py-design-6'
|
||||||
|
: 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t('game.modals.rules.streakRewardTable.columns.rewardType')}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className={cn(
|
||||||
|
'border border-[rgba(126,234,244,0.13)] text-center font-semibold',
|
||||||
|
compact
|
||||||
|
? 'px-design-7 py-design-6'
|
||||||
|
: 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t('game.modals.rules.streakRewardTable.columns.oddsFactor')}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className={cn(
|
||||||
|
'border border-[rgba(126,234,244,0.13)] text-center font-semibold',
|
||||||
|
compact
|
||||||
|
? 'px-design-7 py-design-6'
|
||||||
|
: 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t('game.modals.rules.streakRewardTable.columns.fixedAmount')}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className={cn(
|
||||||
|
'border border-[rgba(126,234,244,0.13)] text-center font-semibold',
|
||||||
|
compact
|
||||||
|
? 'px-design-7 py-design-6'
|
||||||
|
: 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t('game.modals.rules.streakRewardTable.columns.isJackpot')}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{sortedRows.map((row) => (
|
||||||
|
<tr key={`${row.streak}-${row.rewardType}`}>
|
||||||
|
<td
|
||||||
|
className={cn(
|
||||||
|
'border border-[rgba(126,234,244,0.1)] text-center text-[#C8F4F7]',
|
||||||
|
compact
|
||||||
|
? 'px-design-7 py-design-6'
|
||||||
|
: 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t('game.modals.rules.streakRewardTable.streakValue', {
|
||||||
|
streak: row.streak,
|
||||||
|
})}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className={cn(
|
||||||
|
'border border-[rgba(126,234,244,0.1)] text-center text-[#C8F4F7]',
|
||||||
|
compact
|
||||||
|
? 'px-design-7 py-design-6'
|
||||||
|
: 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{getRewardTypeLabel(row.rewardType)}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className={cn(
|
||||||
|
'border border-[rgba(126,234,244,0.1)] text-center text-[#C8F4F7]',
|
||||||
|
compact
|
||||||
|
? 'px-design-7 py-design-6'
|
||||||
|
: 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{row.rewardType === 'coefficient' && row.oddsFactor > 0
|
||||||
|
? `1:${row.oddsFactor}`
|
||||||
|
: '-'}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className={cn(
|
||||||
|
'border border-[rgba(126,234,244,0.1)] text-center text-[#C8F4F7]',
|
||||||
|
compact
|
||||||
|
? 'px-design-7 py-design-6'
|
||||||
|
: 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{row.rewardType === 'fixed'
|
||||||
|
? `💎${formatFixedAmount(row.fixedAmount, i18n.language)}`
|
||||||
|
: '-'}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className={cn(
|
||||||
|
'border border-[rgba(126,234,244,0.1)] text-center font-semibold',
|
||||||
|
row.isJackpot ? 'text-[#FFF2B8]' : 'text-[#C8F4F7]',
|
||||||
|
compact
|
||||||
|
? 'px-design-7 py-design-6'
|
||||||
|
: 'px-design-12 py-design-9',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{row.isJackpot
|
||||||
|
? t('game.modals.rules.streakRewardTable.yes')
|
||||||
|
: t('game.modals.rules.streakRewardTable.no')}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'text-center text-[#B9E7EA]',
|
||||||
|
compact ? 'px-design-8 py-design-10' : 'px-design-12 py-design-14',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t('game.modals.rules.streakRewardTable.empty')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StreakRewardTable
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Minus, Plus } from 'lucide-react'
|
import { Landmark, Minus, Plus } from 'lucide-react'
|
||||||
import { type ReactNode, useState } from 'react'
|
import { type ReactNode, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import lengthBlueBtn from '@/assets/system/length-blue-btn.webp'
|
import lengthBlueBtn from '@/assets/system/length-blue-btn.webp'
|
||||||
@@ -16,7 +16,7 @@ import { useWithdrawSubmit } from '@/hooks/use-withdraw-submit'
|
|||||||
import { useWithdrawVm } from '@/hooks/use-withdraw-vm'
|
import { useWithdrawVm } from '@/hooks/use-withdraw-vm'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { useModalStore } from '@/store'
|
import { useModalStore } from '@/store'
|
||||||
import type { FinanceCurrencyConfig } from '@/type'
|
import type { FinanceCurrencyConfig, FinanceWithdrawBank } from '@/type'
|
||||||
|
|
||||||
const PANEL_CLASS =
|
const PANEL_CLASS =
|
||||||
'rounded-md border border-[rgba(110,229,243,0.24)] bg-[linear-gradient(180deg,rgba(7,30,43,0.9),rgba(3,15,26,0.94))] shadow-[inset_0_0_calc(var(--design-unit)*14)_rgba(88,225,238,0.08),0_0_calc(var(--design-unit)*10)_rgba(32,163,186,0.12)]'
|
'rounded-md border border-[rgba(110,229,243,0.24)] bg-[linear-gradient(180deg,rgba(7,30,43,0.9),rgba(3,15,26,0.94))] shadow-[inset_0_0_calc(var(--design-unit)*14)_rgba(88,225,238,0.08),0_0_calc(var(--design-unit)*10)_rgba(32,163,186,0.12)]'
|
||||||
@@ -33,6 +33,22 @@ function getPaymentGlyph(code: string, name: string) {
|
|||||||
return name.trim().slice(0, 1).toUpperCase() || code.slice(0, 1).toUpperCase()
|
return name.trim().slice(0, 1).toUpperCase() || code.slice(0, 1).toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function BankOptionLogo({ bank }: { bank: FinanceWithdrawBank }) {
|
||||||
|
if (bank.logo.trim()) {
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
src={bank.logo}
|
||||||
|
alt={bank.label}
|
||||||
|
className="h-design-22 w-design-22 shrink-0 rounded-[calc(var(--design-unit)*4)] object-contain"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Landmark className="h-design-18 w-design-18 shrink-0 text-[#8DE4EA]" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function WithdrawField({
|
function WithdrawField({
|
||||||
label,
|
label,
|
||||||
children,
|
children,
|
||||||
@@ -557,7 +573,10 @@ function DesktopWithdraw() {
|
|||||||
<SelectContent>
|
<SelectContent>
|
||||||
{vm.sortedBanks.map((bank) => (
|
{vm.sortedBanks.map((bank) => (
|
||||||
<SelectItem key={bank.code} value={bank.code}>
|
<SelectItem key={bank.code} value={bank.code}>
|
||||||
{bank.label}
|
<span className="flex min-w-0 items-center gap-design-6">
|
||||||
|
<BankOptionLogo bank={bank} />
|
||||||
|
<span className="min-w-0 truncate">{bank.label}</span>
|
||||||
|
</span>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Minus, Plus } from 'lucide-react'
|
import { Landmark, Minus, Plus } from 'lucide-react'
|
||||||
import { type ReactNode, useState } from 'react'
|
import { type ReactNode, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import lengthBlueBtn from '@/assets/system/length-blue-btn.webp'
|
import lengthBlueBtn from '@/assets/system/length-blue-btn.webp'
|
||||||
@@ -16,7 +16,7 @@ import { useWithdrawSubmit } from '@/hooks/use-withdraw-submit'
|
|||||||
import { useWithdrawVm } from '@/hooks/use-withdraw-vm'
|
import { useWithdrawVm } from '@/hooks/use-withdraw-vm'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { useModalStore } from '@/store'
|
import { useModalStore } from '@/store'
|
||||||
import type { FinanceCurrencyConfig } from '@/type'
|
import type { FinanceCurrencyConfig, FinanceWithdrawBank } from '@/type'
|
||||||
|
|
||||||
const PANEL_CLASS =
|
const PANEL_CLASS =
|
||||||
'rounded-md border border-[rgba(110,229,243,0.24)] bg-[linear-gradient(180deg,rgba(7,30,43,0.9),rgba(3,15,26,0.94))] shadow-[inset_0_0_calc(var(--design-unit)*10)_rgba(88,225,238,0.08)]'
|
'rounded-md border border-[rgba(110,229,243,0.24)] bg-[linear-gradient(180deg,rgba(7,30,43,0.9),rgba(3,15,26,0.94))] shadow-[inset_0_0_calc(var(--design-unit)*10)_rgba(88,225,238,0.08)]'
|
||||||
@@ -33,6 +33,22 @@ function getPaymentGlyph(code: string, name: string) {
|
|||||||
return name.trim().slice(0, 1).toUpperCase() || code.slice(0, 1).toUpperCase()
|
return name.trim().slice(0, 1).toUpperCase() || code.slice(0, 1).toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function BankOptionLogo({ bank }: { bank: FinanceWithdrawBank }) {
|
||||||
|
if (bank.logo.trim()) {
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
src={bank.logo}
|
||||||
|
alt={bank.label}
|
||||||
|
className="h-design-18 w-design-18 shrink-0 rounded-[calc(var(--design-unit)*3)] object-contain"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Landmark className="h-design-15 w-design-15 shrink-0 text-[#8DE4EA]" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function WithdrawField({
|
function WithdrawField({
|
||||||
label,
|
label,
|
||||||
children,
|
children,
|
||||||
@@ -537,7 +553,10 @@ function MobileWithdraw() {
|
|||||||
<SelectContent>
|
<SelectContent>
|
||||||
{vm.sortedBanks.map((bank) => (
|
{vm.sortedBanks.map((bank) => (
|
||||||
<SelectItem key={bank.code} value={bank.code}>
|
<SelectItem key={bank.code} value={bank.code}>
|
||||||
{bank.label}
|
<span className="flex min-w-0 items-center gap-design-4">
|
||||||
|
<BankOptionLogo bank={bank} />
|
||||||
|
<span className="min-w-0 truncate">{bank.label}</span>
|
||||||
|
</span>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|||||||
@@ -70,10 +70,10 @@ export function useTopupVm() {
|
|||||||
)
|
)
|
||||||
const methods = useMemo(
|
const methods = useMemo(
|
||||||
() =>
|
() =>
|
||||||
config.deposit.methods.filter(
|
(selectedPayChannel?.methods ?? []).filter(
|
||||||
(method) => method.currencyCode === selectedCurrencyCode,
|
(method) => method.currencyCode === selectedCurrencyCode,
|
||||||
),
|
),
|
||||||
[config.deposit.methods, selectedCurrencyCode],
|
[selectedCurrencyCode, selectedPayChannel?.methods],
|
||||||
)
|
)
|
||||||
const [selectedPaymentMethodCode, setSelectedPaymentMethodCode] = useState(
|
const [selectedPaymentMethodCode, setSelectedPaymentMethodCode] = useState(
|
||||||
() => methods[0]?.code ?? '',
|
() => methods[0]?.code ?? '',
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ export function useWithdrawVm() {
|
|||||||
convertedFiatAmount > selectedPaymentMaximumAmount
|
convertedFiatAmount > selectedPaymentMaximumAmount
|
||||||
const sortedBanks = useMemo(
|
const sortedBanks = useMemo(
|
||||||
() =>
|
() =>
|
||||||
[...config.withdraw.banks]
|
[...(selectedPaymentChannel?.banks ?? [])]
|
||||||
.filter(
|
.filter(
|
||||||
(bank) =>
|
(bank) =>
|
||||||
bank.status === 1 &&
|
bank.status === 1 &&
|
||||||
@@ -188,7 +188,7 @@ export function useWithdrawVm() {
|
|||||||
(!bank.currencyCode || bank.currencyCode === selectedCurrencyCode),
|
(!bank.currencyCode || bank.currencyCode === selectedCurrencyCode),
|
||||||
)
|
)
|
||||||
.sort((left, right) => left.sort - right.sort),
|
.sort((left, right) => left.sort - right.sort),
|
||||||
[config.withdraw.banks, selectedCurrencyCode],
|
[selectedPaymentChannel, selectedCurrencyCode],
|
||||||
)
|
)
|
||||||
const setAmount = useCallback(
|
const setAmount = useCallback(
|
||||||
(nextAmount: number) => {
|
(nextAmount: number) => {
|
||||||
|
|||||||
@@ -215,134 +215,153 @@ export default {
|
|||||||
rules: {
|
rules: {
|
||||||
title: 'Game Rules',
|
title: 'Game Rules',
|
||||||
content: [
|
content: [
|
||||||
'## 36 Zihua · Gameplay Guide',
|
'## 36 Zihua Rules Guide',
|
||||||
'',
|
'',
|
||||||
'### 1. Game Introduction',
|
'### 1. Game Introduction',
|
||||||
'',
|
'',
|
||||||
'**36 Zihua** is a fast-paced number guessing game with **one round every 30 seconds**. Each round, the system draws **one winning number** from **36 symbols** (Rat, Ox, Tiger… Azure Dragon, White Tiger, Vermilion Bird, Black Tortoise, and more). During the betting window, players pick numbers and place bets. Guess correctly to win prizes. Win consecutive rounds to increase your rewards — **10 wins in a row** can earn you the **Million Jackpot**.',
|
'**36 Zihua** is a number prediction game. In each round, **1 winning number** is drawn from **36 symbols**. Players select numbers and place bets during the betting stage. If they guess correctly, they win a reward. Consecutive wins can increase rewards, and after reaching the jackpot tier, players also have a chance to win the **Million Jackpot**.',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 2. How to Play',
|
'### 2. Gameplay',
|
||||||
'',
|
'',
|
||||||
'Each round lasts about **30 seconds**:',
|
'Each round is divided into the following stages:',
|
||||||
'',
|
'',
|
||||||
'**Step 1 — Betting window (0–20 seconds)**',
|
'1. **Betting Stage**',
|
||||||
'Pick numbers on the 36-grid (up to **5 numbers** by default), choose a chip amount (e.g. 💎1, 5, 10, 25, 50, 100), then confirm your bet. Your balance is deducted and the bet is placed.',
|
' Players choose the numbers they want to bet on from 36 numbers, select chips, confirm the bet, and the system deducts the balance and creates the bet order.',
|
||||||
|
' The maximum number of selections per round depends on the current system configuration.',
|
||||||
'',
|
'',
|
||||||
'**Step 2 — Lock (at 20 seconds)**',
|
'2. **Bet Closing Stage**',
|
||||||
'No more bets are accepted. Any bet placed after lock is invalid.',
|
' Once the round enters the closing stage, the system stops accepting new bets.',
|
||||||
'',
|
'',
|
||||||
'**Step 3 — Draw & settlement (20–30 seconds)**',
|
'3. **Draw and Settlement Stage**',
|
||||||
'The winning number is announced and prizes are paid according to the reward rules.',
|
' The system announces the winning number for the round and automatically settles rewards according to the current rules.',
|
||||||
'',
|
'',
|
||||||
'**Step 4 — Next round**',
|
'4. **Next Round Begins**',
|
||||||
'After settlement, the next round begins automatically.',
|
' After the current round is settled, the system automatically starts the next round.',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 3. Winning Rules',
|
'### 3. Winning Rules',
|
||||||
'',
|
'',
|
||||||
'- Only **one** winning number is drawn each round (from 1 to 36).',
|
'1. Only **1 winning number** is drawn in each round.',
|
||||||
'- You win if **your selected numbers include the winning number**.',
|
'2. If the player’s selected numbers **include the winning number**, the bet is considered a win.',
|
||||||
'- If you don’t match, you receive no prize for that round.',
|
'3. If a player selects multiple numbers and the bet amount is the same for each number, then:',
|
||||||
'',
|
'',
|
||||||
'**Hit stake (amount on the winning number):**',
|
'```text',
|
||||||
'- When every selected number has the same stake:',
|
'Winning Number Stake = Total Bet Amount ÷ Number of Selected Numbers',
|
||||||
' **Hit stake = total bet amount ÷ number of selected numbers**',
|
'```',
|
||||||
'- Example: 5 numbers, total bet 💎50 → 💎10 per number. If the winning number is among your picks, 💎10 is used for prize calculation.',
|
'',
|
||||||
|
'**Example:**',
|
||||||
|
'If a player selects **5 numbers** with a total bet of **💎50**, then each number carries a stake of **💎10**.',
|
||||||
|
'If the winning number is among those 5 numbers, the reward is calculated based on **💎10**.',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 4. Reward Types',
|
'### 4. Reward Rules',
|
||||||
'',
|
'',
|
||||||
'Streak rewards come in **two types**. Each tier uses **one type only** — they are **not combined**:',
|
'Streak rewards are divided into **two types**, and only one type applies to each streak tier:',
|
||||||
'',
|
'',
|
||||||
'#### Type A — Coefficient Reward',
|
'#### 1. Coefficient Reward',
|
||||||
'',
|
'',
|
||||||
'Prize is calculated using an odds multiplier:',
|
'The reward is calculated by multiplying the winning number stake by the odds coefficient of the current tier:',
|
||||||
'',
|
'',
|
||||||
'```',
|
'```text',
|
||||||
'Prize = hit stake × odds factor',
|
'Winning Amount = Winning Number Stake × Odds Coefficient',
|
||||||
'```',
|
'```',
|
||||||
'',
|
'',
|
||||||
'#### Type B — Fixed Reward',
|
'#### 2. Fixed Reward',
|
||||||
'',
|
'',
|
||||||
'You receive a **fixed amount** set in the configuration.',
|
'The system directly pays the configured fixed amount, regardless of the bet amount of the current round:',
|
||||||
'**It does not depend on your bet size and does not use an odds multiplier.**',
|
|
||||||
'',
|
'',
|
||||||
'```',
|
'```text',
|
||||||
'Prize = fixed amount',
|
'Winning Amount = Fixed Reward Amount',
|
||||||
'```',
|
'```',
|
||||||
'',
|
'',
|
||||||
'**Comparison (Tier 10 example):**',
|
'#### 3. Reward Table Notes',
|
||||||
'',
|
'',
|
||||||
'| Reward Type | Hit Stake | Prize |',
|
'The current streak reward table is based on the system’s backend configuration, and the frontend should display it using the live configuration data.',
|
||||||
'|:---:|:---:|:---:|',
|
'Each tier contains the following information:',
|
||||||
'| Coefficient (1:330) | 💎50 | 💎50 × 330 = **16,500** |',
|
'',
|
||||||
'| Fixed reward | Any | **1,000,000** (configured value) |',
|
'- Streak tier',
|
||||||
|
'- Reward type',
|
||||||
|
'- Odds coefficient',
|
||||||
|
'- Fixed reward amount',
|
||||||
|
'- Whether it is a jackpot tier',
|
||||||
|
'',
|
||||||
|
'In other words, the reward table is not hardcoded. The actual content always follows the current system configuration.',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 5. Win Streak Rules',
|
'### 5. Streak Rules',
|
||||||
'',
|
'',
|
||||||
'When you **win consecutive rounds**, rewards increase by streak tier. By default, **coefficient rewards** apply with escalating odds:',
|
'When a player wins consecutive rounds, they enter a streak state. The higher the streak, the higher the reward tier.',
|
||||||
'',
|
'',
|
||||||
'| Streak | Odds | Factor |',
|
'#### 1. Streak Calculation Rules',
|
||||||
'|:---:|:---:|:---:|',
|
|
||||||
'| 1st win | 1:33 | 33 |',
|
|
||||||
'| 2 in a row | 1:66 | 66 |',
|
|
||||||
'| 3 in a row | 1:99 | 99 |',
|
|
||||||
'| 4 in a row | 1:132 | 132 |',
|
|
||||||
'| 5 in a row | 1:165 | 165 |',
|
|
||||||
'| 6 in a row | 1:198 | 198 |',
|
|
||||||
'| 7 in a row | 1:231 | 231 |',
|
|
||||||
'| 8 in a row | 1:264 | 264 |',
|
|
||||||
'| 9 in a row | 1:297 | 297 |',
|
|
||||||
'| 10 in a row | 1:330 | 330 |',
|
|
||||||
'',
|
'',
|
||||||
'**Streak rules:**',
|
'1. After a player wins, the streak count increases.',
|
||||||
|
'2. Rewards are calculated based on the **streak tier at the time the bet is placed**.',
|
||||||
|
'3. If the player loses any round, the streak is immediately reset to **0**.',
|
||||||
|
'4. Under the current implementation, a normal win continues the streak, but if the player hits a **jackpot tier**, the streak is reset to **0** after that round is settled.',
|
||||||
'',
|
'',
|
||||||
'- Rewards are based on your **streak at the time you place the bet**: if you already have N consecutive wins, a win this round pays at tier **(N+1)**.',
|
'#### 2. Jackpot Streak Bet Limit',
|
||||||
'- **Any losing round** resets your streak to zero — you start again from tier 1.',
|
|
||||||
'- **Skipping a round** (no bet placed) during an active streak also **resets your streak to zero**.',
|
|
||||||
'',
|
'',
|
||||||
'**Examples (coefficient reward):**',
|
'When a player’s streak reaches the first **jackpot tier** configured by the system, the player enters the jackpot streak bet restriction stage.',
|
||||||
'',
|
'',
|
||||||
'| Scenario | Hit Stake | Streak Tier | Odds | Prize |',
|
'The rules are as follows:',
|
||||||
'|:---:|:---:|:---:|:---:|:---:|',
|
'',
|
||||||
'| First win | 💎10 | 1st | 1:33 | **330** |',
|
'1. In every following round, the **total bet amount** must **not exceed** the total bet amount of the **first jackpot-winning round** in the current streak.',
|
||||||
'| 3-win streak | 💎50 | 3rd | 1:99 | **4,950** |',
|
'2. This cap remains locked throughout the current streak and does not change even if the player wins jackpot again later.',
|
||||||
'| 10-win streak | 💎50 | 10th | 1:330 | **16,500** |',
|
'3. Once the streak is broken, the restriction is removed immediately.',
|
||||||
|
'4. Normal streaks before reaching the jackpot tier are not affected by this rule.',
|
||||||
|
'',
|
||||||
|
'{{STREAK_REWARD_TABLE}}',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 6. Million Jackpot',
|
'### 6. Million Jackpot',
|
||||||
'',
|
'',
|
||||||
'When a player **bets and wins 10 consecutive rounds**, and each bet during the streak is **at least 💎50**, the Million Jackpot is triggered.',
|
'When a player reaches the system-configured jackpot tier through consecutive wins, and that jackpot tier is configured as a fixed jackpot reward, the player can receive the Million Jackpot.',
|
||||||
'',
|
'',
|
||||||
'**Requirements:**',
|
'#### 1. Trigger Conditions',
|
||||||
'',
|
'',
|
||||||
'1. **10 consecutive rounds** with a bet placed and won in each;',
|
'1. The player must keep winning until reaching the current system-configured jackpot tier.',
|
||||||
'2. **Every round** in the streak must have a stake of **at least 💎50**;',
|
'2. If the jackpot tier is configured as a **fixed reward**, for example **💎1,000,000**, the player will directly receive the Million Jackpot when hitting that tier.',
|
||||||
'3. If any round in the streak is below 💎50, you only receive the regular streak reward — **the Million Jackpot does not apply**.',
|
'3. Under the current implementation, there is **no extra minimum bet requirement**. As long as the player meets the platform’s minimum allowed bet amount, they can participate in the jackpot streak.',
|
||||||
|
'4. After hitting the jackpot tier, once the settlement is completed, the streak will be reset to **0**.',
|
||||||
'',
|
'',
|
||||||
'**How it is paid (depends on backend configuration):**',
|
'#### 2. Payout Rules',
|
||||||
'',
|
'',
|
||||||
'- **Fixed reward**: If tier 10 is set as a fixed amount, 10 consecutive wins pay **💎1,000,000** directly.',
|
'- If the jackpot tier is configured as a **fixed reward**: the configured fixed jackpot amount is paid directly.',
|
||||||
'- **Coefficient reward**: If tier 10 uses coefficient odds, you first receive the regular prize at **1:330**, plus any additional jackpot per campaign rules.',
|
'- If the jackpot tier is configured as a **coefficient reward**: settlement still follows “Winning Number Stake × Odds Coefficient”, and a fixed million reward is not automatically paid.',
|
||||||
'',
|
'',
|
||||||
'**Example — fixed reward at tier 10 (💎1,000,000), hit stake 💎50 each round:**',
|
'**Example:**',
|
||||||
'',
|
'',
|
||||||
'- 10 wins in a row → **💎1,000,000**',
|
'- If the jackpot tier is configured as a fixed reward of **💎1,000,000**',
|
||||||
'- Streak resets to **zero** after the jackpot is awarded',
|
'- The player reaches that jackpot tier and wins',
|
||||||
|
'- The player directly receives **💎1,000,000**',
|
||||||
'',
|
'',
|
||||||
'**Example — coefficient reward at tier 10 (1:330), hit stake 💎50:**',
|
'---',
|
||||||
'',
|
|
||||||
'- Tier 10 regular prize: 💎50 × 330 = **💎16,500**',
|
|
||||||
'- If a separate Jackpot campaign applies, an additional million-prize may be added on top',
|
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
confirm: 'Understood',
|
confirm: 'Understood',
|
||||||
|
streakRewardTable: {
|
||||||
|
title: 'Streak Reward Table',
|
||||||
|
columns: {
|
||||||
|
streak: 'Streak',
|
||||||
|
rewardType: 'Reward Type',
|
||||||
|
oddsFactor: 'Odds',
|
||||||
|
fixedAmount: 'Fixed Reward',
|
||||||
|
isJackpot: 'Jackpot',
|
||||||
|
},
|
||||||
|
rewardTypes: {
|
||||||
|
coefficient: 'Coefficient Reward',
|
||||||
|
fixed: 'Fixed Reward',
|
||||||
|
},
|
||||||
|
streakValue: 'Round {{streak}}',
|
||||||
|
yes: TEXT_YES,
|
||||||
|
no: TEXT_NO,
|
||||||
|
empty: 'No reward configuration available',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
procedures: {
|
procedures: {
|
||||||
title: 'Top Up / Withdraw',
|
title: 'Top Up / Withdraw',
|
||||||
|
|||||||
@@ -214,134 +214,153 @@ export default {
|
|||||||
rules: {
|
rules: {
|
||||||
title: 'Aturan Permainan',
|
title: 'Aturan Permainan',
|
||||||
content: [
|
content: [
|
||||||
'## 36 Zihua · Panduan Permainan',
|
'## Panduan Peraturan 36 Zihua',
|
||||||
'',
|
'',
|
||||||
'### 1. Pengenalan Permainan',
|
'### 1. Pengenalan Game',
|
||||||
'',
|
'',
|
||||||
'**36 Zihua** adalah permainan tebak angka berkecepatan tinggi dengan **satu putaran setiap 30 detik**. Setiap putaran, sistem mengeluarkan **satu nomor pemenang** dari **36 simbol** (Tikus, Kerbau, Harimau… Naga Biru, Harimau Putih, Burung Merah, Kura-kura Hitam, dan lainnya). Selama periode taruhan, pemain memilih nomor dan memasang taruhan. Tebak dengan benar untuk memenangkan hadiah. Menang berturut-turut dapat meningkatkan hadiah Anda — **10 kemenangan berturut-turut** dapat memberikan **Jackpot Satu Juta**.',
|
'**36 Zihua** adalah permainan prediksi angka. Dalam setiap putaran, **1 nomor pemenang** akan dipilih dari **36 simbol**. Pemain memilih nomor dan memasang taruhan pada tahap taruhan. Jika tebakan benar, pemain akan mendapatkan hadiah. Kemenangan beruntun dapat meningkatkan hadiah, dan setelah mencapai tier jackpot, pemain juga berkesempatan memenangkan **Jackpot Satu Juta**.',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 2. Cara Bermain',
|
'### 2. Cara Bermain',
|
||||||
'',
|
'',
|
||||||
'Setiap putaran berlangsung sekitar **30 detik**:',
|
'Setiap putaran dibagi menjadi beberapa tahap berikut:',
|
||||||
'',
|
'',
|
||||||
'**Langkah 1 — Periode taruhan (0–20 detik)**',
|
'1. **Tahap Taruhan**',
|
||||||
'Pilih nomor pada grid 36 kotak (default maksimum **5 nomor**), pilih nilai chip (mis. 💎1, 5, 10, 25, 50, 100), lalu konfirmasi taruhan. Saldo Anda akan dipotong dan taruhan dikonfirmasi.',
|
' Pemain memilih nomor yang ingin dipertaruhkan dari 36 nomor, memilih chip, mengonfirmasi taruhan, dan sistem akan memotong saldo serta membuat catatan taruhan.',
|
||||||
|
' Jumlah maksimum nomor yang dapat dipilih per putaran bergantung pada konfigurasi sistem saat ini.',
|
||||||
'',
|
'',
|
||||||
'**Langkah 2 — Penutupan (pada detik ke-20)**',
|
'2. **Tahap Penutupan Taruhan**',
|
||||||
'Tidak ada taruhan baru yang diterima. Taruhan yang dipasang setelah penutupan tidak berlaku.',
|
' Setelah putaran memasuki tahap penutupan, sistem berhenti menerima taruhan baru.',
|
||||||
'',
|
'',
|
||||||
'**Langkah 3 — Pengundian & penyelesaian (20–30 detik)**',
|
'3. **Tahap Pengundian dan Penyelesaian**',
|
||||||
'Nomor pemenang diumumkan dan hadiah dibayarkan sesuai aturan hadiah.',
|
' Sistem akan mengumumkan nomor pemenang untuk putaran tersebut dan secara otomatis menyelesaikan hadiah sesuai aturan yang berlaku.',
|
||||||
'',
|
'',
|
||||||
'**Langkah 4 — Putaran berikutnya**',
|
'4. **Putaran Berikutnya Dimulai**',
|
||||||
'Setelah penyelesaian, putaran berikutnya dimulai secara otomatis.',
|
' Setelah putaran saat ini selesai diselesaikan, sistem akan otomatis memulai putaran berikutnya.',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 3. Aturan Kemenangan',
|
'### 3. Aturan Kemenangan',
|
||||||
'',
|
'',
|
||||||
'- Hanya **satu** nomor pemenang yang dikeluarkan setiap putaran (dari 1 hingga 36).',
|
'1. Hanya **1 nomor pemenang** yang akan dikeluarkan di setiap putaran.',
|
||||||
'- Anda menang jika **nomor yang dipilih mencakup nomor pemenang**.',
|
'2. Jika nomor yang dipilih pemain **mencakup nomor pemenang**, maka taruhan tersebut dianggap menang.',
|
||||||
'- Jika tidak kena, tidak ada hadiah untuk putaran tersebut.',
|
'3. Jika pemain memilih beberapa nomor dan jumlah taruhan untuk setiap nomor sama, maka:',
|
||||||
'',
|
'',
|
||||||
'**Jumlah taruhan pada nomor kena:**',
|
'```text',
|
||||||
'- Jika setiap nomor dipilih dengan jumlah taruhan yang sama:',
|
'Nilai Taruhan Nomor Menang = Total Taruhan ÷ Jumlah Nomor yang Dipilih',
|
||||||
' **Jumlah taruhan pada nomor kena = total taruhan ÷ jumlah nomor yang dipilih**',
|
'```',
|
||||||
'- Contoh: 5 nomor, total taruhan 💎50 → 💎10 per nomor. Jika nomor pemenang termasuk dalam pilihan Anda, 💎10 digunakan untuk perhitungan hadiah.',
|
'',
|
||||||
|
'**Contoh:**',
|
||||||
|
'Jika pemain memilih **5 nomor** dengan total taruhan **💎50**, maka setiap nomor memiliki nilai taruhan **💎10**.',
|
||||||
|
'Jika nomor pemenang berada di antara 5 nomor tersebut, hadiah akan dihitung berdasarkan **💎10**.',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 4. Jenis Hadiah',
|
'### 4. Aturan Hadiah',
|
||||||
'',
|
'',
|
||||||
'Hadiah kemenangan berturut-turut terdiri dari **dua jenis**. Setiap tingkat hanya menggunakan **satu jenis** — **tidak digabungkan**:',
|
'Hadiah kemenangan beruntun dibagi menjadi **dua jenis**, dan hanya satu jenis yang berlaku pada setiap tier kemenangan beruntun:',
|
||||||
'',
|
'',
|
||||||
'#### Jenis A — Hadiah Koefisien',
|
'#### 1. Hadiah Koefisien',
|
||||||
'',
|
'',
|
||||||
'Hadiah dihitung menggunakan pengali odds:',
|
'Hadiah dihitung dengan mengalikan nilai taruhan nomor menang dengan koefisien odds pada tier saat ini:',
|
||||||
'',
|
'',
|
||||||
'```',
|
'```text',
|
||||||
'Hadiah = jumlah taruhan pada nomor kena × koefisien odds',
|
'Jumlah Kemenangan = Nilai Taruhan Nomor Menang × Koefisien Odds',
|
||||||
'```',
|
'```',
|
||||||
'',
|
'',
|
||||||
'#### Jenis B — Hadiah Tetap',
|
'#### 2. Hadiah Tetap',
|
||||||
'',
|
'',
|
||||||
'Anda menerima **jumlah tetap** yang ditetapkan dalam konfigurasi.',
|
'Sistem akan langsung membayar jumlah tetap yang telah dikonfigurasi, tanpa bergantung pada jumlah taruhan pada putaran tersebut:',
|
||||||
'**Tidak bergantung pada jumlah taruhan dan tidak menggunakan koefisien odds.**',
|
|
||||||
'',
|
'',
|
||||||
'```',
|
'```text',
|
||||||
'Hadiah = jumlah tetap',
|
'Jumlah Kemenangan = Jumlah Hadiah Tetap',
|
||||||
'```',
|
'```',
|
||||||
'',
|
'',
|
||||||
'**Perbandingan (contoh Tingkat 10):**',
|
'#### 3. Catatan Tabel Hadiah',
|
||||||
'',
|
'',
|
||||||
'| Jenis Hadiah | Jumlah Taruhan pada Nomor Kena | Hadiah |',
|
'Tabel hadiah kemenangan beruntun saat ini mengikuti konfigurasi backend sistem, dan frontend harus menampilkannya berdasarkan data konfigurasi terbaru.',
|
||||||
'|:---:|:---:|:---:|',
|
'Setiap tier berisi informasi berikut:',
|
||||||
'| Koefisien (1:330) | 💎50 | 💎50 × 330 = **16.500** |',
|
'',
|
||||||
'| Hadiah tetap | Berapa pun | **1.000.000** (nilai konfigurasi) |',
|
'- Tier kemenangan beruntun',
|
||||||
|
'- Jenis hadiah',
|
||||||
|
'- Koefisien odds',
|
||||||
|
'- Jumlah hadiah tetap',
|
||||||
|
'- Apakah termasuk tier jackpot',
|
||||||
|
'',
|
||||||
|
'Dengan kata lain, tabel hadiah tidak ditulis tetap. Isi sebenarnya selalu mengikuti konfigurasi sistem saat ini.',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 5. Aturan Kemenangan Berturut-turut',
|
'### 5. Aturan Kemenangan Berturut-turut',
|
||||||
'',
|
'',
|
||||||
'Saat Anda **menang berturut-turut**, hadiah meningkat sesuai tingkat kemenangan. Secara default, **hadiah koefisien** digunakan dengan odds yang meningkat:',
|
'Saat pemain menang dalam beberapa putaran berturut-turut, pemain akan masuk ke status kemenangan beruntun. Semakin tinggi kemenangan beruntun, semakin tinggi tier hadiah.',
|
||||||
'',
|
'',
|
||||||
'| Kemenangan Berturut-turut | Odds | Koefisien |',
|
'#### 1. Aturan Perhitungan Kemenangan Beruntun',
|
||||||
'|:---:|:---:|:---:|',
|
|
||||||
'| Kemenangan ke-1 | 1:33 | 33 |',
|
|
||||||
'| 2 berturut-turut | 1:66 | 66 |',
|
|
||||||
'| 3 berturut-turut | 1:99 | 99 |',
|
|
||||||
'| 4 berturut-turut | 1:132 | 132 |',
|
|
||||||
'| 5 berturut-turut | 1:165 | 165 |',
|
|
||||||
'| 6 berturut-turut | 1:198 | 198 |',
|
|
||||||
'| 7 berturut-turut | 1:231 | 231 |',
|
|
||||||
'| 8 berturut-turut | 1:264 | 264 |',
|
|
||||||
'| 9 berturut-turut | 1:297 | 297 |',
|
|
||||||
'| 10 berturut-turut | 1:330 | 330 |',
|
|
||||||
'',
|
'',
|
||||||
'**Aturan kemenangan berturut-turut:**',
|
'1. Setelah pemain menang, jumlah kemenangan beruntun akan bertambah.',
|
||||||
|
'2. Hadiah dihitung berdasarkan **tier kemenangan beruntun saat taruhan dipasang**.',
|
||||||
|
'3. Jika pemain kalah di putaran mana pun, kemenangan beruntun akan langsung direset ke **0**.',
|
||||||
|
'4. Dalam implementasi saat ini, kemenangan biasa akan melanjutkan kemenangan beruntun, tetapi jika pemain mengenai **tier jackpot**, maka kemenangan beruntun akan langsung direset ke **0** setelah putaran tersebut selesai diselesaikan.',
|
||||||
'',
|
'',
|
||||||
'- Hadiah dihitung berdasarkan **status kemenangan berturut-turut saat Anda memasang taruhan**: jika Anda sudah memiliki N kemenangan berturut-turut, kemenangan putaran ini dibayar pada tingkat **(N+1)**.',
|
'#### 2. Batas Taruhan Saat Kemenangan Beruntun Jackpot',
|
||||||
'- **Putaran tanpa kemenangan** akan mengatur ulang kemenangan berturut-turut ke nol — Anda mulai lagi dari tingkat 1.',
|
|
||||||
'- **Melewatkan putaran** (tidak memasang taruhan) selama kemenangan berturut-turut aktif juga akan **mengatur ulang kemenangan berturut-turut ke nol**.',
|
|
||||||
'',
|
'',
|
||||||
'**Contoh (hadiah koefisien):**',
|
'Ketika kemenangan beruntun pemain mencapai tier **jackpot pertama** yang dikonfigurasi oleh sistem, pemain akan masuk ke tahap pembatasan taruhan jackpot.',
|
||||||
'',
|
'',
|
||||||
'| Skenario | Jumlah Taruhan pada Nomor Kena | Tingkat | Odds | Hadiah |',
|
'Aturannya sebagai berikut:',
|
||||||
'|:---:|:---:|:---:|:---:|:---:|',
|
'',
|
||||||
'| Kemenangan pertama | 💎10 | Ke-1 | 1:33 | **330** |',
|
'1. Dalam setiap putaran berikutnya, **total jumlah taruhan** **tidak boleh melebihi** jumlah taruhan dari **putaran pertama yang memenangkan jackpot** dalam kemenangan beruntun saat ini.',
|
||||||
'| 3 kemenangan berturut-turut | 💎50 | Ke-3 | 1:99 | **4.950** |',
|
'2. Batas ini akan tetap terkunci selama kemenangan beruntun saat ini berlangsung dan tidak akan berubah meskipun pemain kembali memenangkan jackpot setelah itu.',
|
||||||
'| 10 kemenangan berturut-turut | 💎50 | Ke-10 | 1:330 | **16.500** |',
|
'3. Setelah kemenangan beruntun terputus, pembatasan ini akan langsung dihapus.',
|
||||||
|
'4. Kemenangan beruntun biasa sebelum mencapai tier jackpot tidak terpengaruh oleh aturan ini.',
|
||||||
|
'',
|
||||||
|
'{{STREAK_REWARD_TABLE}}',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 6. Jackpot Satu Juta',
|
'### 6. Jackpot Satu Juta',
|
||||||
'',
|
'',
|
||||||
'Saat pemain **bertaruh dan menang 10 putaran berturut-turut**, dan setiap taruhan selama kemenangan berturut-turut **minimal 💎50**, Jackpot Satu Juta akan dipicu.',
|
'Ketika pemain mencapai tier jackpot yang dikonfigurasi sistem melalui kemenangan beruntun, dan tier jackpot tersebut dikonfigurasi sebagai hadiah tetap, pemain bisa menerima Jackpot Satu Juta.',
|
||||||
'',
|
'',
|
||||||
'**Syarat:**',
|
'#### 1. Syarat Pemicu',
|
||||||
'',
|
'',
|
||||||
'1. **10 putaran berturut-turut** dengan taruhan dipasang dan dimenangkan di setiap putaran;',
|
'1. Pemain harus terus menang sampai mencapai tier jackpot yang saat ini dikonfigurasi oleh sistem.',
|
||||||
'2. **Setiap putaran** dalam kemenangan berturut-turut harus memiliki taruhan **minimal 💎50**;',
|
'2. Jika tier jackpot dikonfigurasi sebagai **hadiah tetap**, misalnya **💎1,000,000**, pemain akan langsung menerima Jackpot Satu Juta saat berhasil mengenai tier tersebut.',
|
||||||
'3. Jika ada putaran dalam kemenangan berturut-turut di bawah 💎50, Anda hanya menerima hadiah kemenangan berturut-turut biasa — **Jackpot Satu Juta tidak berlaku**.',
|
'3. Dalam implementasi saat ini, **tidak ada syarat taruhan minimum tambahan**. Selama pemain memenuhi jumlah taruhan minimum yang diizinkan oleh platform, pemain dapat mengikuti kemenangan beruntun jackpot.',
|
||||||
|
'4. Setelah mengenai tier jackpot, begitu penyelesaian selesai, kemenangan beruntun akan direset ke **0**.',
|
||||||
'',
|
'',
|
||||||
'**Cara pembayaran (tergantung konfigurasi backend):**',
|
'#### 2. Aturan Pembayaran',
|
||||||
'',
|
'',
|
||||||
'- **Hadiah tetap**: Jika tingkat 10 ditetapkan sebagai jumlah tetap, 10 kemenangan berturut-turut membayar **💎1.000.000** secara langsung.',
|
'- Jika tier jackpot dikonfigurasi sebagai **hadiah tetap**, jumlah jackpot tetap yang telah dikonfigurasi akan dibayarkan langsung.',
|
||||||
'- **Hadiah koefisien**: Jika tingkat 10 menggunakan odds koefisien, Anda menerima hadiah reguler pada **1:330** terlebih dahulu, ditambah jackpot tambahan sesuai aturan kampanye.',
|
'- Jika tier jackpot dikonfigurasi sebagai **hadiah koefisien**, hadiah akan dihitung sebagai **nilai taruhan nomor menang × koefisien odds**, dan satu juta tetap tidak akan dibayarkan secara otomatis.',
|
||||||
'',
|
'',
|
||||||
'**Contoh — hadiah tetap pada tingkat 10 (💎1.000.000), jumlah taruhan pada nomor kena 💎50 setiap putaran:**',
|
'**Contoh:**',
|
||||||
'',
|
'',
|
||||||
'- 10 kemenangan berturut-turut → **💎1.000.000**',
|
'- Jika tier jackpot dikonfigurasi sebagai hadiah tetap **💎1,000,000**',
|
||||||
'- Kemenangan berturut-turut diatur ulang ke **nol** setelah jackpot diberikan',
|
'- Pemain mencapai tier jackpot tersebut dan menang',
|
||||||
|
'- Maka pemain akan langsung menerima **💎1,000,000**',
|
||||||
'',
|
'',
|
||||||
'**Contoh — hadiah koefisien pada tingkat 10 (1:330), jumlah taruhan pada nomor kena 💎50:**',
|
'---',
|
||||||
'',
|
|
||||||
'- Hadiah reguler tingkat 10: 💎50 × 330 = **💎16.500**',
|
|
||||||
'- Jika kampanye Jackpot terpisah berlaku, hadiah satu juta tambahan dapat ditambahkan di atasnya',
|
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
confirm: 'Saya Mengerti',
|
confirm: 'Saya Mengerti',
|
||||||
|
streakRewardTable: {
|
||||||
|
title: 'Tabel Hadiah Kemenangan Beruntun',
|
||||||
|
columns: {
|
||||||
|
streak: 'Tier',
|
||||||
|
rewardType: 'Jenis Hadiah',
|
||||||
|
oddsFactor: 'Odds',
|
||||||
|
fixedAmount: 'Hadiah Tetap',
|
||||||
|
isJackpot: 'Jackpot',
|
||||||
|
},
|
||||||
|
rewardTypes: {
|
||||||
|
coefficient: 'Hadiah Koefisien',
|
||||||
|
fixed: 'Hadiah Tetap',
|
||||||
|
},
|
||||||
|
streakValue: 'Putaran {{streak}}',
|
||||||
|
yes: TEXT_YES,
|
||||||
|
no: TEXT_NO,
|
||||||
|
empty: 'Belum ada konfigurasi hadiah',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
procedures: {
|
procedures: {
|
||||||
title: 'Isi Ulang / Tarik Dana',
|
title: 'Isi Ulang / Tarik Dana',
|
||||||
|
|||||||
@@ -217,134 +217,153 @@ export default {
|
|||||||
rules: {
|
rules: {
|
||||||
title: 'Peraturan Permainan',
|
title: 'Peraturan Permainan',
|
||||||
content: [
|
content: [
|
||||||
'## 36 Zihua · Panduan Permainan',
|
'## Panduan Peraturan 36 Zihua',
|
||||||
'',
|
'',
|
||||||
'### 1. Pengenalan Permainan',
|
'### 1. Pengenalan Permainan',
|
||||||
'',
|
'',
|
||||||
'**36 Zihua** ialah permainan teka nombor berkelajuan tinggi dengan **satu pusingan setiap 30 saat**. Setiap pusingan, sistem akan mengeluarkan **satu nombor kemenangan** daripada **36 simbol** (Tikus, Lembu, Harimau… Naga Biru, Harimau Putih, Burung Merah, Kura-kura Hitam, dan lain-lain). Dalam tempoh pertaruhan, pemain memilih nombor dan membuat pertaruhan. Teka dengan betul untuk memenangi hadiah. Menang berturut-turut boleh meningkatkan ganjaran anda — **10 kemenangan berturut-turut** boleh membawa anda **Jackpot Sejuta**.',
|
'**36 Zihua** ialah permainan ramalan nombor. Dalam setiap pusingan, **1 nombor kemenangan** akan dipilih daripada **36 simbol**. Pemain memilih nombor dan membuat pertaruhan semasa peringkat pertaruhan. Jika tekaan tepat, pemain akan menerima ganjaran. Kemenangan berturut-turut boleh meningkatkan ganjaran, dan selepas mencapai peringkat jackpot, pemain juga berpeluang memenangi **Jackpot Sejuta**.',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 2. Cara Bermain',
|
'### 2. Cara Bermain',
|
||||||
'',
|
'',
|
||||||
'Setiap pusingan mengambil masa kira-kira **30 saat**:',
|
'Setiap pusingan dibahagikan kepada beberapa peringkat berikut:',
|
||||||
'',
|
'',
|
||||||
'**Langkah 1 — Tempoh pertaruhan (0–20 saat)**',
|
'1. **Peringkat Pertaruhan**',
|
||||||
'Pilih nombor pada grid 36 petak (lalai maksimum **5 nombor**), pilih nilai cip (cth. 💎1, 5, 10, 25, 50, 100), kemudian sahkan pertaruhan. Baki anda akan ditolak dan pertaruhan disahkan.',
|
' Pemain memilih nombor yang ingin dipertaruhkan daripada 36 nombor, memilih cip, mengesahkan pertaruhan, dan sistem akan menolak baki serta menjana rekod pertaruhan.',
|
||||||
|
' Bilangan maksimum nombor yang boleh dipilih bagi setiap pusingan bergantung pada konfigurasi semasa sistem.',
|
||||||
'',
|
'',
|
||||||
'**Langkah 2 — Penutupan (pada saat ke-20)**',
|
'2. **Peringkat Penutupan Pertaruhan**',
|
||||||
'Tiada pertaruhan baharu diterima. Sebarang pertaruhan selepas penutupan adalah tidak sah.',
|
' Apabila pusingan memasuki peringkat penutupan, sistem akan berhenti menerima pertaruhan baharu.',
|
||||||
'',
|
'',
|
||||||
'**Langkah 3 — Cabutan & penyelesaian (20–30 saat)**',
|
'3. **Peringkat Cabutan dan Penyelesaian**',
|
||||||
'Nombor kemenangan diumumkan dan hadiah dibayar mengikut peraturan ganjaran.',
|
' Sistem akan mengumumkan nombor kemenangan bagi pusingan tersebut dan secara automatik menyelesaikan ganjaran mengikut peraturan semasa.',
|
||||||
'',
|
'',
|
||||||
'**Langkah 4 — Pusingan seterusnya**',
|
'4. **Pusingan Seterusnya Bermula**',
|
||||||
'Selepas penyelesaian, pusingan seterusnya bermula secara automatik.',
|
' Selepas pusingan semasa selesai diselesaikan, sistem akan memulakan pusingan seterusnya secara automatik.',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 3. Peraturan Kemenangan',
|
'### 3. Peraturan Kemenangan',
|
||||||
'',
|
'',
|
||||||
'- Hanya **satu** nombor kemenangan dikeluarkan setiap pusingan (daripada 1 hingga 36).',
|
'1. Hanya **1 nombor kemenangan** akan dikeluarkan dalam setiap pusingan.',
|
||||||
'- Anda menang jika **nombor yang dipilih termasuk nombor kemenangan**.',
|
'2. Jika nombor yang dipilih oleh pemain **mengandungi nombor kemenangan**, pertaruhan tersebut dianggap menang.',
|
||||||
'- Jika tidak kena, tiada hadiah untuk pusingan tersebut.',
|
'3. Jika pemain memilih beberapa nombor dan jumlah pertaruhan bagi setiap nombor adalah sama, maka:',
|
||||||
'',
|
'',
|
||||||
'**Jumlah pertaruhan pada nombor kena:**',
|
'```text',
|
||||||
'- Apabila setiap nombor dipilih dengan jumlah pertaruhan yang sama:',
|
'Nilai Pertaruhan Nombor Menang = Jumlah Pertaruhan ÷ Bilangan Nombor yang Dipilih',
|
||||||
' **Jumlah pertaruhan pada nombor kena = jumlah pertaruhan ÷ bilangan nombor dipilih**',
|
'```',
|
||||||
'- Contoh: 5 nombor, jumlah pertaruhan 💎50 → 💎10 setiap nombor. Jika nombor kemenangan termasuk dalam pilihan anda, 💎10 digunakan untuk pengiraan hadiah.',
|
'',
|
||||||
|
'**Contoh:**',
|
||||||
|
'Jika pemain memilih **5 nombor** dengan jumlah pertaruhan **💎50**, maka setiap nombor membawa nilai pertaruhan **💎10**.',
|
||||||
|
'Jika nombor kemenangan berada dalam 5 nombor tersebut, ganjaran akan dikira berdasarkan **💎10**.',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 4. Jenis Ganjaran',
|
'### 4. Peraturan Ganjaran',
|
||||||
'',
|
'',
|
||||||
'Ganjaran kemenangan berturut-turut terdiri daripada **dua jenis**. Setiap peringkat hanya menggunakan **satu jenis** — **tidak digabungkan**:',
|
'Ganjaran kemenangan berturut-turut dibahagikan kepada **dua jenis**, dan hanya satu jenis akan digunakan bagi setiap peringkat kemenangan berturut-turut:',
|
||||||
'',
|
'',
|
||||||
'#### Jenis A — Ganjaran Pekali',
|
'#### 1. Ganjaran Pekali',
|
||||||
'',
|
'',
|
||||||
'Hadiah dikira menggunakan pekali odds:',
|
'Ganjaran dikira dengan mendarab nilai pertaruhan nombor menang dengan pekali odds bagi peringkat semasa:',
|
||||||
'',
|
'',
|
||||||
'```',
|
'```text',
|
||||||
'Hadiah = jumlah pertaruhan pada nombor kena × pekali odds',
|
'Jumlah Kemenangan = Nilai Pertaruhan Nombor Menang × Pekali Odds',
|
||||||
'```',
|
'```',
|
||||||
'',
|
'',
|
||||||
'#### Jenis B — Ganjaran Tetap',
|
'#### 2. Ganjaran Tetap',
|
||||||
'',
|
'',
|
||||||
'Anda menerima **jumlah tetap** yang ditetapkan dalam konfigurasi.',
|
'Sistem akan terus membayar jumlah tetap yang telah dikonfigurasikan, tanpa mengambil kira jumlah pertaruhan dalam pusingan semasa:',
|
||||||
'**Tidak bergantung pada jumlah pertaruhan dan tidak menggunakan pekali odds.**',
|
|
||||||
'',
|
'',
|
||||||
'```',
|
'```text',
|
||||||
'Hadiah = jumlah tetap',
|
'Jumlah Kemenangan = Jumlah Ganjaran Tetap',
|
||||||
'```',
|
'```',
|
||||||
'',
|
'',
|
||||||
'**Perbandingan (contoh Peringkat 10):**',
|
'#### 3. Nota Jadual Ganjaran',
|
||||||
'',
|
'',
|
||||||
'| Jenis Ganjaran | Jumlah Pertaruhan pada Nombor Kena | Hadiah |',
|
'Jadual ganjaran kemenangan berturut-turut semasa adalah berdasarkan konfigurasi backend sistem, dan frontend perlu memaparkannya menggunakan data konfigurasi semasa.',
|
||||||
'|:---:|:---:|:---:|',
|
'Setiap peringkat mengandungi maklumat berikut:',
|
||||||
'| Pekali (1:330) | 💎50 | 💎50 × 330 = **16,500** |',
|
'',
|
||||||
'| Ganjaran tetap | Sebarang | **1,000,000** (nilai konfigurasi) |',
|
'- Peringkat kemenangan berturut-turut',
|
||||||
|
'- Jenis ganjaran',
|
||||||
|
'- Pekali odds',
|
||||||
|
'- Jumlah ganjaran tetap',
|
||||||
|
'- Sama ada ia peringkat jackpot',
|
||||||
|
'',
|
||||||
|
'Dengan kata lain, jadual ganjaran tidak ditulis tetap. Kandungan sebenar sentiasa mengikut konfigurasi semasa sistem.',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 5. Peraturan Kemenangan Berturut-turut',
|
'### 5. Peraturan Kemenangan Berturut-turut',
|
||||||
'',
|
'',
|
||||||
'Apabila anda **menang berturut-turut**, ganjaran meningkat mengikut peringkat kemenangan. Secara lalai, **ganjaran pekali** digunakan dengan odds yang meningkat:',
|
'Apabila pemain menang dalam beberapa pusingan berturut-turut, pemain akan memasuki status kemenangan berturut-turut. Semakin tinggi kemenangan berturut-turut, semakin tinggi peringkat ganjaran.',
|
||||||
'',
|
'',
|
||||||
'| Kemenangan Berturut-turut | Odds | Pekali |',
|
'#### 1. Peraturan Pengiraan Kemenangan Berturut-turut',
|
||||||
'|:---:|:---:|:---:|',
|
|
||||||
'| Kemenangan ke-1 | 1:33 | 33 |',
|
|
||||||
'| 2 berturut-turut | 1:66 | 66 |',
|
|
||||||
'| 3 berturut-turut | 1:99 | 99 |',
|
|
||||||
'| 4 berturut-turut | 1:132 | 132 |',
|
|
||||||
'| 5 berturut-turut | 1:165 | 165 |',
|
|
||||||
'| 6 berturut-turut | 1:198 | 198 |',
|
|
||||||
'| 7 berturut-turut | 1:231 | 231 |',
|
|
||||||
'| 8 berturut-turut | 1:264 | 264 |',
|
|
||||||
'| 9 berturut-turut | 1:297 | 297 |',
|
|
||||||
'| 10 berturut-turut | 1:330 | 330 |',
|
|
||||||
'',
|
'',
|
||||||
'**Peraturan kemenangan berturut-turut:**',
|
'1. Selepas pemain menang, bilangan kemenangan berturut-turut akan bertambah.',
|
||||||
|
'2. Ganjaran dikira berdasarkan **peringkat kemenangan berturut-turut semasa pertaruhan dibuat**.',
|
||||||
|
'3. Jika pemain kalah dalam mana-mana pusingan, kemenangan berturut-turut akan terus direset kepada **0**.',
|
||||||
|
'4. Dalam pelaksanaan semasa, kemenangan biasa akan meneruskan kemenangan berturut-turut, tetapi jika pemain mengenai **peringkat jackpot**, kemenangan berturut-turut akan direset kepada **0** selepas penyelesaian pusingan tersebut.',
|
||||||
'',
|
'',
|
||||||
'- Ganjaran dikira berdasarkan **status kemenangan berturut-turut semasa anda membuat pertaruhan**: jika anda sudah mempunyai N kemenangan berturut-turut, kemenangan pusingan ini dibayar pada peringkat **(N+1)**.',
|
'#### 2. Had Pertaruhan Semasa Kemenangan Berturut-turut Jackpot',
|
||||||
'- **Sebarang pusingan tanpa kemenangan** akan menetapkan semula kemenangan berturut-turut kepada sifar — anda bermula semula dari peringkat 1.',
|
|
||||||
'- **Melangkau pusingan** (tiada pertaruhan dibuat) semasa kemenangan berturut-turut aktif juga akan **menetapkan semula kemenangan berturut-turut kepada sifar**.',
|
|
||||||
'',
|
'',
|
||||||
'**Contoh (ganjaran pekali):**',
|
'Apabila kemenangan berturut-turut pemain mencapai peringkat **jackpot pertama** yang ditetapkan oleh sistem, pemain akan memasuki peringkat sekatan pertaruhan jackpot.',
|
||||||
'',
|
'',
|
||||||
'| Senario | Jumlah Pertaruhan pada Nombor Kena | Peringkat | Odds | Hadiah |',
|
'Peraturannya adalah seperti berikut:',
|
||||||
'|:---:|:---:|:---:|:---:|:---:|',
|
'',
|
||||||
'| Kemenangan pertama | 💎10 | Ke-1 | 1:33 | **330** |',
|
'1. Dalam setiap pusingan berikutnya, **jumlah keseluruhan pertaruhan** **tidak boleh melebihi** jumlah pertaruhan bagi **pusingan pertama yang memenangi jackpot** dalam kemenangan berturut-turut semasa.',
|
||||||
'| 3 kemenangan berturut-turut | 💎50 | Ke-3 | 1:99 | **4,950** |',
|
'2. Had ini akan kekal dikunci sepanjang kemenangan berturut-turut semasa dan tidak akan berubah walaupun pemain menang jackpot lagi selepas itu.',
|
||||||
'| 10 kemenangan berturut-turut | 💎50 | Ke-10 | 1:330 | **16,500** |',
|
'3. Sebaik sahaja kemenangan berturut-turut terputus, sekatan ini akan dibuang serta-merta.',
|
||||||
|
'4. Kemenangan berturut-turut biasa sebelum mencapai peringkat jackpot tidak tertakluk kepada peraturan ini.',
|
||||||
|
'',
|
||||||
|
'{{STREAK_REWARD_TABLE}}',
|
||||||
'',
|
'',
|
||||||
'---',
|
'---',
|
||||||
'',
|
'',
|
||||||
'### 6. Jackpot Sejuta',
|
'### 6. Jackpot Sejuta',
|
||||||
'',
|
'',
|
||||||
'Apabila pemain **bertaruh dan menang 10 pusingan berturut-turut**, dan setiap pertaruhan semasa kemenangan berturut-turut adalah **sekurang-kurangnya 💎50**, Jackpot Sejuta akan dicetuskan.',
|
'Apabila pemain mencapai peringkat jackpot yang dikonfigurasikan sistem melalui kemenangan berturut-turut, dan peringkat jackpot tersebut dikonfigurasikan sebagai ganjaran tetap, pemain boleh menerima Jackpot Sejuta.',
|
||||||
'',
|
'',
|
||||||
'**Syarat:**',
|
'#### 1. Syarat Pencetus',
|
||||||
'',
|
'',
|
||||||
'1. **10 pusingan berturut-turut** dengan pertaruhan dibuat dan dimenangi dalam setiap pusingan;',
|
'1. Pemain mesti terus menang sehingga mencapai peringkat jackpot semasa yang dikonfigurasikan oleh sistem.',
|
||||||
'2. **Setiap pusingan** dalam kemenangan berturut-turut mesti mempunyai pertaruhan **sekurang-kurangnya 💎50**;',
|
'2. Jika peringkat jackpot dikonfigurasikan sebagai **ganjaran tetap**, contohnya **💎1,000,000**, pemain akan terus menerima Jackpot Sejuta apabila mengenai peringkat tersebut.',
|
||||||
'3. Jika mana-mana pusingan dalam kemenangan berturut-turut kurang daripada 💎50, anda hanya menerima ganjaran kemenangan berturut-turut biasa — **Jackpot Sejuta tidak terpakai**.',
|
'3. Dalam pelaksanaan semasa, **tiada syarat pertaruhan minimum tambahan**. Selagi pemain memenuhi jumlah pertaruhan minimum yang dibenarkan oleh platform, pemain boleh menyertai kemenangan berturut-turut jackpot.',
|
||||||
|
'4. Selepas mengenai peringkat jackpot, sebaik sahaja penyelesaian selesai, kemenangan berturut-turut akan direset kepada **0**.',
|
||||||
'',
|
'',
|
||||||
'**Cara pembayaran (bergantung pada konfigurasi backend):**',
|
'#### 2. Peraturan Bayaran',
|
||||||
'',
|
'',
|
||||||
'- **Ganjaran tetap**: Jika peringkat 10 ditetapkan sebagai jumlah tetap, 10 kemenangan berturut-turut membayar **💎1,000,000** secara langsung.',
|
'- Jika peringkat jackpot dikonfigurasikan sebagai **ganjaran tetap**, jumlah jackpot tetap yang telah dikonfigurasikan akan dibayar terus.',
|
||||||
'- **Ganjaran pekali**: Jika peringkat 10 menggunakan odds pekali, anda menerima hadiah biasa pada **1:330** terlebih dahulu, ditambah jackpot tambahan mengikut peraturan kempen.',
|
'- Jika peringkat jackpot dikonfigurasikan sebagai **ganjaran pekali**, ganjaran tetap akan dikira sebagai **nilai pertaruhan nombor menang × pekali odds**, dan satu juta tetap tidak akan dibayar secara automatik.',
|
||||||
'',
|
'',
|
||||||
'**Contoh — ganjaran tetap pada peringkat 10 (💎1,000,000), jumlah pertaruhan pada nombor kena 💎50 setiap pusingan:**',
|
'**Contoh:**',
|
||||||
'',
|
'',
|
||||||
'- 10 kemenangan berturut-turut → **💎1,000,000**',
|
'- Jika peringkat jackpot dikonfigurasikan sebagai ganjaran tetap **💎1,000,000**',
|
||||||
'- Kemenangan berturut-turut ditetapkan semula kepada **sifar** selepas jackpot diberikan',
|
'- Pemain mencapai peringkat jackpot tersebut dan menang',
|
||||||
|
'- Maka pemain akan terus menerima **💎1,000,000**',
|
||||||
'',
|
'',
|
||||||
'**Contoh — ganjaran pekali pada peringkat 10 (1:330), jumlah pertaruhan pada nombor kena 💎50:**',
|
'---',
|
||||||
'',
|
|
||||||
'- Hadiah biasa peringkat 10: 💎50 × 330 = **💎16,500**',
|
|
||||||
'- Jika kempen Jackpot berasingan terpakai, hadiah sejuta tambahan mungkin ditambah di atasnya',
|
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
confirm: 'Saya Faham',
|
confirm: 'Saya Faham',
|
||||||
|
streakRewardTable: {
|
||||||
|
title: 'Jadual Ganjaran Kemenangan Berturut-turut',
|
||||||
|
columns: {
|
||||||
|
streak: 'Peringkat',
|
||||||
|
rewardType: 'Jenis Ganjaran',
|
||||||
|
oddsFactor: 'Odds',
|
||||||
|
fixedAmount: 'Ganjaran Tetap',
|
||||||
|
isJackpot: 'Jackpot',
|
||||||
|
},
|
||||||
|
rewardTypes: {
|
||||||
|
coefficient: 'Ganjaran Pekali',
|
||||||
|
fixed: 'Ganjaran Tetap',
|
||||||
|
},
|
||||||
|
streakValue: 'Pusingan {{streak}}',
|
||||||
|
yes: TEXT_YES,
|
||||||
|
no: TEXT_NO,
|
||||||
|
empty: 'Tiada konfigurasi ganjaran',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
procedures: {
|
procedures: {
|
||||||
title: 'Tambah Nilai / Pengeluaran',
|
title: 'Tambah Nilai / Pengeluaran',
|
||||||
|
|||||||
@@ -211,119 +211,143 @@ export default {
|
|||||||
rules: {
|
rules: {
|
||||||
title: '玩法规则',
|
title: '玩法规则',
|
||||||
content: [
|
content: [
|
||||||
'## 36 字花 · 玩法指南',
|
'## 36字花规则说明',
|
||||||
'',
|
'',
|
||||||
'### 一、游戏介绍',
|
'### 一、游戏介绍',
|
||||||
'',
|
'',
|
||||||
'「36字花」是一款 **30 秒一期** 的高频数字竞猜游戏。系统每期从 **36 个字号**(鼠、牛、虎……青龙、白虎、朱雀、玄武等)中开出 **1 个中奖号码**。玩家在下注期内选号押注,猜中即可获得奖金;连续猜中可提升奖励,**连中 10 场** 还可获得 **百万大奖**。',
|
'「36字花」是一款数字竞猜游戏。每期从 **36 个字号** 中开出 **1 个中奖号码**。玩家在下注阶段选号押注,猜中即获奖;连续猜中可提升奖励,达到大奖档后还有机会获得 **百万大奖**。',
|
||||||
'',
|
'',
|
||||||
'### 二、玩法说明',
|
'### 二、玩法说明',
|
||||||
'',
|
'',
|
||||||
'每期约 **30 秒**,流程如下:',
|
'每期游戏分为以下几个阶段:',
|
||||||
'',
|
'',
|
||||||
'**1. 下注期(0~20 秒)**',
|
'1. **下注阶段**',
|
||||||
'在 36 宫格中选择号码(默认最多 **5 个**),选择筹码面额(如 💎1、5、10、25、50、100),点击确认下注,系统扣款后注单生效。',
|
' 玩家在 36 个号码中选择想要押注的号码,选择筹码后确认下注,系统扣款并生成注单。',
|
||||||
|
' 单期最多可选号码数,以系统当前配置为准。',
|
||||||
'',
|
'',
|
||||||
'**2. 封盘(20 秒)**',
|
'2. **封盘阶段**',
|
||||||
'停止接受新注单,之后下注无效。',
|
' 进入封盘后,系统停止接受新注单。',
|
||||||
'',
|
'',
|
||||||
'**3. 开奖结算(20~30 秒)**',
|
'3. **开奖结算阶段**',
|
||||||
'公布本期中奖号码,系统按规则结算派彩。',
|
' 系统公布本期中奖号码,并按当前规则自动派彩。',
|
||||||
'',
|
'',
|
||||||
'**4. 下一期**',
|
'4. **下一期开始**',
|
||||||
'结算完成后自动进入下一期,循环进行。',
|
' 当前期结算完成后,系统自动进入下一期。',
|
||||||
'',
|
'',
|
||||||
'### 三、中奖说明',
|
'### 三、中奖说明',
|
||||||
'',
|
'',
|
||||||
'- 每期只开出 **1 个** 中奖号码(1~36 中之一)。',
|
'1. 每期只开出 **1 个中奖号码**。',
|
||||||
'- 玩家押注的号码中,**包含开奖号码即算中奖**。',
|
'2. 如果玩家押注的号码中 **包含开奖号码**,则视为中奖。',
|
||||||
'- 未押中则本期无奖金。',
|
'3. 如果玩家选择了多个号码,且每个号码押注金额相同,则:',
|
||||||
'',
|
'',
|
||||||
'**压中号码面额** 计算方式:',
|
'```text',
|
||||||
'- 同一期内各号押注相同时:',
|
'压中号码面额 = 本笔压注总额 ÷ 所选号码个数',
|
||||||
' **压中号码面额 = 本笔压注总额 ÷ 所选号码个数**',
|
'```',
|
||||||
'- 示例:选 5 个号、总押 💎50 → 每个号面额 💎10;若开奖号在其中,按 💎10 参与奖金计算。',
|
'',
|
||||||
|
'**示例:**',
|
||||||
|
'若玩家选择 **5 个号码**,总押注 **💎50**,则每个号码面额为 **💎10**。',
|
||||||
|
'若开奖号码在这 5 个号码中,则按 **💎10** 作为中奖计算基础。',
|
||||||
'',
|
'',
|
||||||
'### 四、奖励说明',
|
'### 四、奖励说明',
|
||||||
'',
|
'',
|
||||||
'连胜奖励分为 **两种类型**,每档 **二选一**,不会同时享受:',
|
'连胜奖励分为 **两种类型**,每个连胜档位只会生效其中一种:',
|
||||||
'',
|
'',
|
||||||
'#### 1. 系数奖励',
|
'#### 1. 系数奖励',
|
||||||
'',
|
'',
|
||||||
'按赔率系数计算:',
|
'按压中号码面额乘以当前档位的赔率系数结算:',
|
||||||
'',
|
'',
|
||||||
'`中奖金额 = 压中号码面额 × 赔率系数`',
|
'```text',
|
||||||
|
'中奖金额 = 压中号码面额 × 赔率系数',
|
||||||
|
'```',
|
||||||
'',
|
'',
|
||||||
'#### 2. 固定奖励',
|
'#### 2. 固定奖励',
|
||||||
'',
|
'',
|
||||||
'直接发放配置的 **固定金额**,**与押注面额无关,也不使用赔率系数**。',
|
'直接发放系统配置的固定金额,与本期压注面额无关:',
|
||||||
'',
|
'',
|
||||||
'`中奖金额 = 固定金额`',
|
'```text',
|
||||||
|
'中奖金额 = 固定奖励金额',
|
||||||
|
'```',
|
||||||
'',
|
'',
|
||||||
'**示例对比**(第 10 档):',
|
'#### 3. 奖励表说明',
|
||||||
'',
|
'',
|
||||||
'| 奖励类型 | 押中面额 | 中奖金额 |',
|
'当前游戏的连胜奖励表以系统后台配置为准,前端展示应读取当前配置数据。',
|
||||||
'|:---:|:---:|:---:|',
|
'每个档位包含以下信息:',
|
||||||
'| 系数奖励(1:330) | 💎50 | 💎50 × 330 = **16,500** |',
|
'',
|
||||||
'| 固定奖励 | 任意 | **1,000,000**(配置值) |',
|
'- 连胜档位',
|
||||||
|
'- 奖励类型',
|
||||||
|
'- 赔率系数',
|
||||||
|
'- 固定奖励金额',
|
||||||
|
'- 是否大奖档',
|
||||||
|
'',
|
||||||
|
'也就是说,奖励表不是固定写死的,实际以系统当前配置内容为准。',
|
||||||
'',
|
'',
|
||||||
'### 五、连胜说明',
|
'### 五、连胜说明',
|
||||||
'',
|
'',
|
||||||
'玩家 **连续中奖** 时,奖励按连胜场次递增。默认采用 **系数奖励**,赔率累加如下:',
|
'玩家连续中奖后,会进入连胜状态;连胜越高,对应奖励档位越高。',
|
||||||
'',
|
'',
|
||||||
'| 连胜场次 | 赔率 | 系数 |',
|
'#### 1. 连胜计算规则',
|
||||||
'|:---:|:---:|:---:|',
|
|
||||||
'| 第 1 场中奖 | 1:33 | 33 |',
|
|
||||||
'| 连中 2 场 | 1:66 | 66 |',
|
|
||||||
'| 连中 3 场 | 1:99 | 99 |',
|
|
||||||
'| 连中 4 场 | 1:132 | 132 |',
|
|
||||||
'| 连中 5 场 | 1:165 | 165 |',
|
|
||||||
'| 连中 6 场 | 1:198 | 198 |',
|
|
||||||
'| 连中 7 场 | 1:231 | 231 |',
|
|
||||||
'| 连中 8 场 | 1:264 | 264 |',
|
|
||||||
'| 连中 9 场 | 1:297 | 297 |',
|
|
||||||
'| 连中 10 场 | 1:330 | 330 |',
|
|
||||||
'',
|
'',
|
||||||
'**连胜规则:**',
|
'1. 玩家中奖后,连胜场次增加。',
|
||||||
|
'2. 奖励按 **下注时所在的连胜档位** 计算。',
|
||||||
|
'3. 任意一期未中奖,连胜立即清零。',
|
||||||
|
'4. 当前实现中,普通中奖会继续累加连胜;如果本次命中的是 **大奖档**,则本次结算完成后,连胜会直接重置为 **0**。',
|
||||||
'',
|
'',
|
||||||
'- 赔率按 **下注时的连胜状态** 计算:下注时已是 N 连胜,本期中奖则按第 **(N+1)** 场奖励结算。',
|
'#### 2. 大奖连胜压注限制',
|
||||||
'- **任意一期未中奖** → 连胜清零,从第 1 场重新计算。',
|
|
||||||
'- **连胜期间跳局未下注** → 连胜也会清零。',
|
|
||||||
'',
|
'',
|
||||||
'**计算示例**(系数奖励):',
|
'当玩家连胜达到系统配置的首个 **大奖档** 后,会进入大奖连胜压注限制阶段。',
|
||||||
'',
|
'',
|
||||||
'| 场景 | 压中面额 | 连胜场次 | 赔率 | 中奖金额 |',
|
'规则如下:',
|
||||||
'|:---:|:---:|:---:|:---:|:---:|',
|
'',
|
||||||
'| 首次中奖 | 💎10 | 第 1 场 | 1:33 | **330** |',
|
'1. 后续每一期的**压注总额**,**不得超过** 本次连胜中“首次中大奖”那一笔的压注总额。',
|
||||||
'| 三连胜 | 💎50 | 第 3 场 | 1:99 | **4,950** |',
|
'2. 该上限在本轮连胜期间保持锁定,之后即使再次中大奖,也不会改变。',
|
||||||
'| 十连胜 | 💎50 | 第 10 场 | 1:330 | **16,500** |',
|
'3. 一旦连胜中断,限制立即解除。',
|
||||||
|
'4. 未达到大奖档之前的普通连胜,不受该限制。',
|
||||||
|
'',
|
||||||
|
'{{STREAK_REWARD_TABLE}}',
|
||||||
'',
|
'',
|
||||||
'### 六、百万大奖',
|
'### 六、百万大奖',
|
||||||
'',
|
'',
|
||||||
'当玩家 **连续 10 期均有下注且全部中奖**,且连胜期间 **每期押注不低于 💎50**,即可触发百万大奖。',
|
'当玩家连续中奖达到系统配置的大奖档,并且该大奖档配置为固定大奖时,即可获得百万奖励。',
|
||||||
'',
|
'',
|
||||||
'**触发条件:**',
|
'#### 1. 触发条件',
|
||||||
'',
|
'',
|
||||||
'1. 连续 **10 期** 均有下注且全部中奖;',
|
'1. 玩家需连续中奖,直到达到系统当前配置的大奖档。',
|
||||||
'2. 连胜期间 **每一期押注面额均不低于 💎50**;',
|
'2. 若大奖档配置为 **固定奖励**,例如 **💎1,000,000**,则命中该档时可直接获得百万大奖。',
|
||||||
'3. 若连胜中有任意一期低于 💎50,仅享受常规连胜奖励,**不触发百万大奖**。',
|
'3. 当前实现下,**无额外最低压注要求**。只要满足平台本身允许的最小下注金额,即可参与大奖连胜。',
|
||||||
|
'4. 命中大奖档后,本次结算完成,连胜将重置为 **0**。',
|
||||||
'',
|
'',
|
||||||
'**派发方式(视后台配置):**',
|
'#### 2. 派发规则',
|
||||||
'',
|
'',
|
||||||
'- **固定奖励**:第 10 档设为固定金额时,连中 10 场直接获得 **💎1,000,000**。',
|
'- 若大奖档配置为 **固定奖励**:直接发放配置的固定大奖金额。',
|
||||||
'- **系数奖励**:第 10 档为系数奖励时,先按 **1:330** 发放常规奖金,再按活动规则叠加额外大奖。',
|
'- 若大奖档配置为 **系数奖励**:则仍按“压中号码面额 × 赔率系数”结算,不会自动发放固定一百万。',
|
||||||
'',
|
'',
|
||||||
'**示例**(第 10 档为固定奖励 💎1,000,000,每局压中面额 💎50):',
|
'**示例:**',
|
||||||
'',
|
'',
|
||||||
'- 连中 10 场 → 获得 **💎1,000,000**',
|
'- 若大奖档配置为固定奖励 **💎1,000,000**',
|
||||||
'- 触发后连胜 **归零**,下一期从第 1 场重新计算',
|
'- 玩家达到该大奖档并成功中奖',
|
||||||
|
'- 则直接获得 **💎1,000,000**',
|
||||||
'',
|
'',
|
||||||
'**示例**(第 10 档为系数奖励 1:330,每局压中面额 💎50):',
|
'---',
|
||||||
'',
|
|
||||||
'- 第 10 场常规奖金:💎50 × 330 = **💎16,500**',
|
|
||||||
'- 若另有 Jackpot 活动叠加,可额外获得百万奖金',
|
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
confirm: '我知道了',
|
confirm: '我知道了',
|
||||||
|
streakRewardTable: {
|
||||||
|
title: '连胜奖励表',
|
||||||
|
columns: {
|
||||||
|
streak: '连胜场次',
|
||||||
|
rewardType: '奖励类型',
|
||||||
|
oddsFactor: '奖励倍率',
|
||||||
|
fixedAmount: '固定奖励',
|
||||||
|
isJackpot: '是否大奖',
|
||||||
|
},
|
||||||
|
rewardTypes: {
|
||||||
|
coefficient: '倍率奖励',
|
||||||
|
fixed: '固定奖励',
|
||||||
|
},
|
||||||
|
streakValue: '第 {{streak}} 场',
|
||||||
|
yes: TEXT_YES,
|
||||||
|
no: TEXT_NO,
|
||||||
|
empty: '暂无奖励配置',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
procedures: {
|
procedures: {
|
||||||
title: '充值 / 提现',
|
title: '充值 / 提现',
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ export function MainEntryPage() {
|
|||||||
const selectChip = useGameRoundStore((state) => state.selectChip)
|
const selectChip = useGameRoundStore((state) => state.selectChip)
|
||||||
const hydrateSession = useGameSessionStore((state) => state.hydrateSession)
|
const hydrateSession = useGameSessionStore((state) => state.hydrateSession)
|
||||||
const syncConnection = useGameSessionStore((state) => state.syncConnection)
|
const syncConnection = useGameSessionStore((state) => state.syncConnection)
|
||||||
|
const setStreakWinRewardRows = useGameSessionStore(
|
||||||
|
(state) => state.setStreakWinRewardRows,
|
||||||
|
)
|
||||||
const setCurrentUser = useAuthStore((state) => state.setCurrentUser)
|
const setCurrentUser = useAuthStore((state) => state.setCurrentUser)
|
||||||
const authStatus = useAuthStore((state) => state.status)
|
const authStatus = useAuthStore((state) => state.status)
|
||||||
const authIsHydrated = useAuthStore((state) => state.isHydrated)
|
const authIsHydrated = useAuthStore((state) => state.isHydrated)
|
||||||
@@ -97,6 +100,7 @@ export function MainEntryPage() {
|
|||||||
connection: snapshot.connection,
|
connection: snapshot.connection,
|
||||||
dashboard: snapshot.dashboard,
|
dashboard: snapshot.dashboard,
|
||||||
})
|
})
|
||||||
|
setStreakWinRewardRows(result.streakWinRewardRows)
|
||||||
|
|
||||||
const currentUser = useAuthStore.getState().currentUser
|
const currentUser = useAuthStore.getState().currentUser
|
||||||
|
|
||||||
@@ -151,6 +155,7 @@ export function MainEntryPage() {
|
|||||||
isReloginRequired,
|
isReloginRequired,
|
||||||
selectChip,
|
selectChip,
|
||||||
setCurrentUser,
|
setCurrentUser,
|
||||||
|
setStreakWinRewardRows,
|
||||||
syncConnection,
|
syncConnection,
|
||||||
t,
|
t,
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import lengthBlueBtn from '@/assets/system/length-blue-btn.webp'
|
|||||||
import { CenterModal } from '@/components/center-modal.tsx'
|
import { CenterModal } from '@/components/center-modal.tsx'
|
||||||
import RulesMarkdownContent from '@/components/rules-markdown-content.tsx'
|
import RulesMarkdownContent from '@/components/rules-markdown-content.tsx'
|
||||||
import { SmartBackground } from '@/components/smart-background.tsx'
|
import { SmartBackground } from '@/components/smart-background.tsx'
|
||||||
|
import StreakRewardTable from '@/components/streak-reward-table.tsx'
|
||||||
import { useModalStore } from '@/store'
|
import { useModalStore } from '@/store'
|
||||||
|
|
||||||
function DesktopRulesModal() {
|
function DesktopRulesModal() {
|
||||||
@@ -29,7 +30,9 @@ function DesktopRulesModal() {
|
|||||||
>
|
>
|
||||||
<div className="flex h-full flex-col gap-design-24 px-design-28 pb-design-30 pt-design-10">
|
<div className="flex h-full flex-col gap-design-24 px-design-28 pb-design-30 pt-design-10">
|
||||||
<div className="flex-1 overflow-y-auto rounded-[12px] bg-black/35 p-design-20 text-design-18 text-[#B9E7EA]">
|
<div className="flex-1 overflow-y-auto rounded-[12px] bg-black/35 p-design-20 text-design-18 text-[#B9E7EA]">
|
||||||
<RulesMarkdownContent content={t('game.modals.rules.content')} />
|
<RulesMarkdownContent content={t('game.modals.rules.content')}>
|
||||||
|
<StreakRewardTable />
|
||||||
|
</RulesMarkdownContent>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import lengthBlueBtn from '@/assets/system/length-blue-btn.webp'
|
|||||||
import { MobileCenterModal } from '@/components/mobile-center-modal.tsx'
|
import { MobileCenterModal } from '@/components/mobile-center-modal.tsx'
|
||||||
import RulesMarkdownContent from '@/components/rules-markdown-content.tsx'
|
import RulesMarkdownContent from '@/components/rules-markdown-content.tsx'
|
||||||
import { SmartBackground } from '@/components/smart-background.tsx'
|
import { SmartBackground } from '@/components/smart-background.tsx'
|
||||||
|
import StreakRewardTable from '@/components/streak-reward-table.tsx'
|
||||||
import { useModalStore } from '@/store'
|
import { useModalStore } from '@/store'
|
||||||
|
|
||||||
function MobileRulesModal() {
|
function MobileRulesModal() {
|
||||||
@@ -32,7 +33,9 @@ function MobileRulesModal() {
|
|||||||
<RulesMarkdownContent
|
<RulesMarkdownContent
|
||||||
compact
|
compact
|
||||||
content={t('game.modals.rules.content')}
|
content={t('game.modals.rules.content')}
|
||||||
/>
|
>
|
||||||
|
<StreakRewardTable compact />
|
||||||
|
</RulesMarkdownContent>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex shrink-0 justify-center">
|
<div className="flex shrink-0 justify-center">
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import type {
|
|||||||
DashboardState,
|
DashboardState,
|
||||||
GameBootstrapSnapshot,
|
GameBootstrapSnapshot,
|
||||||
JackpotBroadcastItem,
|
JackpotBroadcastItem,
|
||||||
|
StreakWinRewardRow,
|
||||||
} from '@/type'
|
} from '@/type'
|
||||||
|
|
||||||
type GameSessionSlice = Pick<
|
type GameSessionSlice = Pick<
|
||||||
@@ -34,6 +35,8 @@ export interface GameSessionStoreState extends GameSessionSlice {
|
|||||||
requestRealtimeConnection: () => void
|
requestRealtimeConnection: () => void
|
||||||
resetRealtimeConnectionRequest: () => void
|
resetRealtimeConnectionRequest: () => void
|
||||||
shouldConnectRealtime: boolean
|
shouldConnectRealtime: boolean
|
||||||
|
setStreakWinRewardRows: (rows: StreakWinRewardRow[]) => void
|
||||||
|
streakWinRewardRows: StreakWinRewardRow[]
|
||||||
setConnectionLatency: (latencyMs: number | null) => void
|
setConnectionLatency: (latencyMs: number | null) => void
|
||||||
setConnectionStatus: (status: ConnectionStatus) => void
|
setConnectionStatus: (status: ConnectionStatus) => void
|
||||||
syncConnection: (patch: Partial<ConnectionState>) => void
|
syncConnection: (patch: Partial<ConnectionState>) => void
|
||||||
@@ -54,6 +57,7 @@ export const useGameSessionStore = create<GameSessionStoreState>()((set) => ({
|
|||||||
...createInitialSessionState(),
|
...createInitialSessionState(),
|
||||||
jackpotBroadcasts: [],
|
jackpotBroadcasts: [],
|
||||||
shouldConnectRealtime: false,
|
shouldConnectRealtime: false,
|
||||||
|
streakWinRewardRows: [],
|
||||||
dismissAnnouncement: (announcementId) => {
|
dismissAnnouncement: (announcementId) => {
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
announcements: {
|
announcements: {
|
||||||
@@ -122,6 +126,9 @@ export const useGameSessionStore = create<GameSessionStoreState>()((set) => ({
|
|||||||
resetRealtimeConnectionRequest: () => {
|
resetRealtimeConnectionRequest: () => {
|
||||||
set({ shouldConnectRealtime: false })
|
set({ shouldConnectRealtime: false })
|
||||||
},
|
},
|
||||||
|
setStreakWinRewardRows: (rows) => {
|
||||||
|
set({ streakWinRewardRows: rows })
|
||||||
|
},
|
||||||
setConnectionLatency: (latencyMs) => {
|
setConnectionLatency: (latencyMs) => {
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
connection: {
|
connection: {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export type GamePeriodStatus =
|
|||||||
| (string & {})
|
| (string & {})
|
||||||
export type RevealAnimationPhase = 'idle' | 'spinning' | 'stopping' | 'result'
|
export type RevealAnimationPhase = 'idle' | 'spinning' | 'stopping' | 'result'
|
||||||
export type RewardAnimationType = 'none' | 'small' | 'big'
|
export type RewardAnimationType = 'none' | 'small' | 'big'
|
||||||
|
export type StreakRewardType = 'coefficient' | 'fixed' | (string & {})
|
||||||
export type FinanceRecordType = 'deposit' | 'withdraw'
|
export type FinanceRecordType = 'deposit' | 'withdraw'
|
||||||
export type ConfirmState =
|
export type ConfirmState =
|
||||||
| 'idle'
|
| 'idle'
|
||||||
@@ -199,6 +200,14 @@ export interface JackpotBroadcastItem {
|
|||||||
totalWin: string
|
totalWin: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface StreakWinRewardRow {
|
||||||
|
fixedAmount: string
|
||||||
|
isJackpot: boolean
|
||||||
|
oddsFactor: number
|
||||||
|
rewardType: StreakRewardType
|
||||||
|
streak: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface UserStreakMessageData {
|
export interface UserStreakMessageData {
|
||||||
currentStreak: number
|
currentStreak: number
|
||||||
maxBetPerNumber?: number
|
maxBetPerNumber?: number
|
||||||
@@ -418,18 +427,34 @@ export interface GameLobbyDictionaryItemDto {
|
|||||||
|
|
||||||
export interface GameLobbyUserSnapshotDto {
|
export interface GameLobbyUserSnapshotDto {
|
||||||
coin: string
|
coin: string
|
||||||
|
fixed_amount?: string
|
||||||
current_streak: number
|
current_streak: number
|
||||||
is_jackpot?: boolean
|
is_jackpot?: boolean
|
||||||
odds_factor?: number
|
odds_factor?: number
|
||||||
|
reward_type?: StreakRewardType
|
||||||
streak_level?: number
|
streak_level?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GameLobbyStreakWinRewardRowDto {
|
||||||
|
fixed_amount: string
|
||||||
|
is_jackpot: boolean
|
||||||
|
odds_factor: number
|
||||||
|
reward_type: StreakRewardType
|
||||||
|
streak: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GameLobbyStreakWinRewardDto {
|
||||||
|
first_jackpot_streak_level?: number
|
||||||
|
rows: GameLobbyStreakWinRewardRowDto[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface GameLobbyInitDto {
|
export interface GameLobbyInitDto {
|
||||||
bet_config: GameLobbyBetConfigDto
|
bet_config: GameLobbyBetConfigDto
|
||||||
dictionary: GameLobbyDictionaryItemDto[]
|
dictionary: GameLobbyDictionaryItemDto[]
|
||||||
period?: GameLobbyPeriodDto | null
|
period?: GameLobbyPeriodDto | null
|
||||||
runtime_enabled: boolean
|
runtime_enabled: boolean
|
||||||
server_time: number
|
server_time: number
|
||||||
|
streak_win_reward?: GameLobbyStreakWinRewardDto | null
|
||||||
user_snapshot: GameLobbyUserSnapshotDto
|
user_snapshot: GameLobbyUserSnapshotDto
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,6 +462,7 @@ export interface GameLobbyInitResult {
|
|||||||
runtimeEnabled: boolean
|
runtimeEnabled: boolean
|
||||||
serverTime: number
|
serverTime: number
|
||||||
snapshot: GameBootstrapSnapshot
|
snapshot: GameBootstrapSnapshot
|
||||||
|
streakWinRewardRows: StreakWinRewardRow[]
|
||||||
userSnapshot: GameLobbyInitDto['user_snapshot']
|
userSnapshot: GameLobbyInitDto['user_snapshot']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user