更新ab 复活金币

This commit is contained in:
COMPUTER\EDY 2026-08-21 18:51:43 +08:00
parent 6351520e35
commit 05f5f3f6ea
8 changed files with 735 additions and 36 deletions

View File

@ -13,6 +13,7 @@ import { MiniGameSdk } from "./Sdk/MiniGameSdk";
import Animation = cc.Animation;
// import Revive from "./Revive";
import Utils from "./module/Pay/Utils";
import { getABReviveCoinCost, MAX_REVIVE_COST_STEP } from "./module/Config/ReviveCostConfig";
import Vine from "./prop/vine";
const { ccclass, property } = cc._decorator;
@ -21,7 +22,6 @@ const { ccclass, property } = cc._decorator;
@ccclass
export default class MapConroler extends cc.Component {
private MAX_RETRY_COUNT = 6; // 最大重试次数
private readonly reviveCoinCosts: number[] = [500, 500, 500, 500, 500, 500];
private _blocksEmptyTime: number | null = null; // 保底机制计时
static _instance: any;
@property(cc.SpriteAtlas)
@ -4242,8 +4242,9 @@ export default class MapConroler extends cc.Component {
}
private getReviveCoinCost(): number {
const reviveCount = Math.max(0, cc.fx.GameConfig.GM_INFO.review || 0);
return this.reviveCoinCosts[Math.min(reviveCount, this.reviveCoinCosts.length - 1)];
const assignment = cc.fx.GameConfig.GM_INFO.abTestAssignments
&& cc.fx.GameConfig.GM_INFO.abTestAssignments.layer_3;
return getABReviveCoinCost(assignment, cc.fx.GameConfig.GM_INFO.review);
}
private updateReviveCoinDisplay(buyBtn: cc.Node) {
@ -4355,7 +4356,7 @@ export default class MapConroler extends cc.Component {
this.node.parent.getChildByName("Ice").active = false;
}
if (cc.fx.GameConfig.GM_INFO.review < this.reviveCoinCosts.length - 1)
if (cc.fx.GameConfig.GM_INFO.review < MAX_REVIVE_COST_STEP)
cc.fx.GameConfig.GM_INFO.review += 1;
this.icetimeNode[0].active = true;

View File

@ -0,0 +1,25 @@
export const REVIVE_COST_AB_NAME = "revive_cost_ascend";
export const MAX_REVIVE_COST_STEP = 5;
const DEFAULT_REVIVE_COSTS = [500];
const REVIVE_COSTS_BY_GROUP = {
B: [500, 700, 900],
C: [500, 700, 900, 1100, 1300, 1500]
};
export function getABReviveCoinCost(assignment: any, reviveCount: any): number {
const isTargetExperiment = assignment
&& String(assignment.layer) === "3"
&& assignment.name === REVIVE_COST_AB_NAME
&& assignment.enabled !== false;
const group = isTargetExperiment
? String(assignment.group || "Default").toUpperCase()
: "DEFAULT";
const costs = group === "B"
? REVIVE_COSTS_BY_GROUP.B
: group === "C"
? REVIVE_COSTS_BY_GROUP.C
: DEFAULT_REVIVE_COSTS;
const step = Math.max(0, Math.floor(Number(reviveCount) || 0));
return costs[Math.min(step, costs.length - 1)];
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "763b8065-12e6-4c06-9796-051f0a1d89d8",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -1,12 +1,12 @@
import cloud from '@lafjs/cloud'
const db = cloud.database();
import Utils from "@/Utils";
const CDKreward={
wucaixiaoxiaole666:{
1001:1000,
2001:1,
2002:1,
2003:1,
const CDKreward = {
wucaixiaoxiaole666: {
1001: 1000,
2001: 1,
2002: 1,
2003: 1,
}
}
export default async function (ctx: FunctionContext) {
@ -14,9 +14,9 @@ export default async function (ctx: FunctionContext) {
if (!uid) {
return { code: 0, data: null, msg: "参数错误" };
}
let cdk= ctx.body.cdk;
if(!cdk){
return {code:0, data:null, msg:"未获取到cdk"}
let cdk = ctx.body.cdk;
if (!cdk) {
return { code: 0, data: null, msg: "未获取到cdk" }
}
cdk = cdk.toLowerCase();
let dbname = "users";
@ -64,30 +64,34 @@ export default async function (ctx: FunctionContext) {
}, msg: "可以购买"
};
}
let info = await db.collection("idcount").where({ _id:"694e064b35004ffb5aba0dc6"}).getOne();
let count= info.data.count;
if(count.includes(cdk)){
let index=count.indexOf(cdk);
count.splice(index,1);
let info = await db.collection("idcount").where({ _id: "69fac04fb4d89b170da4bf64" }).getOne();
let count = info.data.count;
if (count.includes(cdk)) {
let index = count.indexOf(cdk);
count.splice(index, 1);
redeem.push(cdk);
db.collection(dbname).where({ _id: uid }).update({ redeem: redeem });
await db.collection("idcount").where({ _id: "694e064b35004ffb5aba0dc6" }).update({count:count});
let money=600;
let num=1;
if(cdk.length==6){
money=1500;
num=3;
}else if(cdk.length==8){
money=600;
num=1;
await db.collection("idcount").where({ _id: "69fac04fb4d89b170da4bf64" }).update({ count: count });
let money = 600;
let num = 1;
if (cdk.length == 6) {
money = 1500;
num = 3;
} else if (cdk.length == 8) {
money = 600;
num = 1;
}
return { code: 1, data: { reward: {
1001:money,
2001: num,
2002: num,
2003: num,
} }, msg: "可以购买" };
}else{
return {
code: 1, data: {
reward: {
1001: money,
2001: num,
2002: num,
2003: num,
}
}, msg: "可以购买"
};
} else {
let reward = CDKreward[cdk];
if (!reward) {
return { code: 0, data: null, msg: "未获取到奖励信息" };
@ -96,5 +100,5 @@ export default async function (ctx: FunctionContext) {
db.collection(dbname).where({ _id: uid }).update({ redeem: redeem });
return { code: 1, data: { reward: reward }, msg: "可以购买" };
}
}

View File

@ -165,7 +165,7 @@ test("Default is a case-insensitive reserved group name", async () => {
});
test("the admin page explains edge-triggered reset behavior", async () => {
const ui = await readFile(new URL("../../xxwzUI.html", import.meta.url), "utf8");
const ui = await readFile(new URL("../../xxwz.html", import.meta.url), "utf8");
assert.match(ui, /关闭→开启并发布/);
assert.match(ui, /关闭后未登录用户不补做/);

View File

@ -0,0 +1,48 @@
import test from "node:test";
import assert from "node:assert/strict";
const configUrl = new URL("../../../assets/Script/module/Config/ReviveCostConfig.ts", import.meta.url);
const { getABReviveCoinCost, MAX_REVIVE_COST_STEP, REVIVE_COST_AB_NAME } = await import(configUrl);
function assignment(group, overrides = {}) {
return {
experimentId: "exp_layer_3_003",
layer: "3",
name: REVIVE_COST_AB_NAME,
enabled: true,
group,
...overrides,
};
}
test("Default and group A always cost 500", () => {
for (const group of ["Default", "A"]) {
assert.deepEqual(
Array.from({ length: 9 }, (_, count) => getABReviveCoinCost(assignment(group), count)),
Array(9).fill(500),
);
}
});
test("group B ascends 500, 700, 900 and stays at 900", () => {
assert.deepEqual(
Array.from({ length: 7 }, (_, count) => getABReviveCoinCost(assignment("B"), count)),
[500, 700, 900, 900, 900, 900, 900],
);
});
test("group C ascends through 1500 and stays at 1500", () => {
assert.deepEqual(
Array.from({ length: 8 }, (_, count) => getABReviveCoinCost(assignment("C"), count)),
[500, 700, 900, 1100, 1300, 1500, 1500, 1500],
);
assert.equal(MAX_REVIVE_COST_STEP, 5);
});
test("disabled, missing, or unrelated assignments use the 500 fallback", () => {
assert.equal(getABReviveCoinCost(null, 5), 500);
assert.equal(getABReviveCoinCost(assignment("C", { enabled: false }), 5), 500);
assert.equal(getABReviveCoinCost(assignment("C", { layer: "2" }), 5), 500);
assert.equal(getABReviveCoinCost(assignment("C", { name: "another_experiment" }), 5), 500);
assert.equal(getABReviveCoinCost(assignment("D"), 5), 500);
});

348
server/消消王者cdk.html Normal file
View File

@ -0,0 +1,348 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>兑换码生成器</title>
<style>
body {
font-family: 'Arial', sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
h1 {
color: #333;
text-align: center;
}
.container {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.generator-section {
margin-bottom: 30px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
}
.generator-section h2 {
margin-top: 0;
color: #444;
}
.input-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input[type="text"] {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
button {
background-color: #4CAF50;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
margin-right: 10px;
margin-bottom: 10px;
}
button:hover {
background-color: #45a049;
}
button.secondary {
background-color: #2196F3;
}
button.secondary:hover {
background-color: #0b7dda;
}
.code-display {
margin: 20px 0;
padding: 15px;
background-color: #f9f9f9;
border: 1px dashed #ccc;
border-radius: 4px;
font-family: 'Courier New', monospace;
word-break: break-all;
min-height: 20px;
}
.note {
font-size: 14px;
color: #666;
margin-top: 5px;
}
.success-message {
color: #4CAF50;
margin-top: 10px;
font-weight: bold;
}
.error-message {
color: #f44336;
margin-top: 10px;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<h1>兑换码生成器</h1>
<!-- 6位兑换码生成器 -->
<div class="generator-section">
<h2>6位兑换码</h2>
<p class="note">1500金币 + 锤子,时间停止,魔法棒各3个道具</p>
<div class="input-group">
<label for="code6">兑换码(留空则随机生成):</label>
<input type="text" id="code6" placeholder="输入6位字母数字组合或留空">
</div>
<button onclick="generateCode(6)">随机生成</button>
<button onclick="copyCode('code6')">复制兑换码</button>
<button class="secondary" onclick="sendToServer('code6')">保存到服务器</button>
<div class="code-display" id="display6"></div>
<div id="message6"></div>
</div>
<!-- 7位兑换码生成器 -->
<div class="generator-section">
<h2>7位兑换码</h2>
<p class="note">600金币 + 锤子,时间停止,魔法棒各1个道具</p>
<div class="input-group">
<label for="code7">兑换码(留空则随机生成):</label>
<input type="text" id="code7" placeholder="输入7位字母数字组合或留空">
</div>
<button onclick="generateCode(7)">随机生成</button>
<button onclick="copyCode('code7')">复制兑换码</button>
<button class="secondary" onclick="sendToServer('code7')">保存到服务器</button>
<div class="code-display" id="display7"></div>
<div id="message7"></div>
</div>
<!-- 8位兑换码生成器 -->
<!-- <div class="generator-section">
<h2>8位兑换码</h2>
<p class="note">600金币 + 1个道具</p>
<div class="input-group">
<label for="code8">兑换码(留空则随机生成):</label>
<input type="text" id="code8" placeholder="输入8位字母数字组合或留空">
</div>
<button onclick="generateCode(8)">随机生成</button>
<button onclick="copyCode('code8')">复制兑换码</button>
<button class="secondary" onclick="sendToServer('code8')">保存到服务器</button>
<div class="code-display" id="display8"></div>
<div id="message8"></div>
</div> -->
</div>
<script>
// 生成随机兑换码
function generateRandomCode(length) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
// 生成兑换码
function generateCode(length) {
let inputId;
let displayId;
let messageId;
switch(length){
case 6:
inputId="code6";
displayId="display6";
messageId="message6";
break;
case 7:
inputId="code7";
displayId="display7";
messageId="message7";
break;
case 8:
inputId="code8";
displayId="display8";
messageId="message8";
break;
}
const input = document.getElementById(inputId);
const display = document.getElementById(displayId);
const message = document.getElementById(messageId);
// 清空消息
message.innerHTML = '';
// 如果输入为空,则生成随机码
if (!input.value.trim()) {
input.value = generateRandomCode(length);
}
// 验证长度
if (input.value.length !== length) {
message.innerHTML = `<div class="error-message">错误:兑换码必须是${length}位</div>`;
return;
}
// 验证字符(只允许字母和数字)
if (!/^[A-Za-z0-9]+$/.test(input.value)) {
message.innerHTML = '<div class="error-message">错误:兑换码只能包含字母和数字</div>';
return;
}
// 显示兑换码
display.textContent = input.value;
}
// 复制兑换码
function copyCode(inputId) {
const input = document.getElementById(inputId);
let displayId;
let messageId;
switch(inputId){
case "code6":
displayId="display6";
messageId="message6";
break;
case "code7":
displayId="display7";
messageId="message7";
break;
case "code8":
displayId="display8";
messageId="message8";
break;
}
const display = document.getElementById(displayId);
const message = document.getElementById(messageId);
if (!display.textContent) {
message.innerHTML = '<div class="error-message">错误:没有可复制的兑换码</div>';
return;
}
// 创建一个临时的textarea元素来复制文本
const textarea = document.createElement('textarea');
textarea.value = display.textContent;
document.body.appendChild(textarea);
textarea.select();
try {
const successful = document.execCommand('copy');
if (successful) {
message.innerHTML = '<div class="success-message">兑换码已复制到剪贴板</div>';
} else {
message.innerHTML = '<div class="error-message">复制失败,请手动选择并复制</div>';
}
} catch (err) {
message.innerHTML = '<div class="error-message">复制失败,请手动选择并复制</div>';
}
document.body.removeChild(textarea);
}
// 发送到服务器
function sendToServer(inputId) {
let displayId;
let messageId;
switch(inputId){
case "code6":
displayId="display6";
messageId="message6";
break;
case "code7":
displayId="display7";
messageId="message7";
break;
case "code8":
displayId="display8";
messageId="message8";
break;
}
const input = document.getElementById(inputId);
const display = document.getElementById(displayId);
const message = document.getElementById(messageId);
if (!display.textContent) {
message.innerHTML = '<div class="error-message">错误:没有可保存的兑换码</div>';
return;
}
// 准备发送的数据
const codeType = inputId === 'code6' ? 'type6' : 'type7';
const data = {
cdk: display.textContent,
type: codeType,
timestamp: new Date().toISOString()
};
console.log(data);
// 发送POST请求
fetch('https://q6rvwvtnga.sealoshzh.site/creatCdk', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
throw new Error('网络响应不正常');
}
return response.json();
})
.then(data => {
message.innerHTML = '<div class="success-message">兑换码已成功保存到服务器</div>';
console.log('成功:', data);
})
.catch(error => {
message.innerHTML = `<div class="error-message">保存失败: ${error.message}</div>`;
console.error('错误:', error);
});
fetch('https://cnsl1li6q4.sealoshzh.site/creatCdk', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
throw new Error('网络响应不正常');
}
return response.json();
})
.then(data => {
message.innerHTML = '<div class="success-message">兑换码已成功保存到服务器</div>';
console.log('成功:', data);
})
.catch(error => {
message.innerHTML = `<div class="error-message">保存失败: ${error.message}</div>`;
console.error('错误:', error);
});
}
</script>
</body>
</html>

View File

@ -0,0 +1,263 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>兑换码生成器</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
text-align: center;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input[type="text"], input[type="datetime-local"] {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.code-display {
display: flex;
margin-bottom: 15px;
}
#codeInput {
flex-grow: 1;
font-size: 18px;
padding: 10px;
}
.button-group {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
button {
padding: 10px 15px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
flex-grow: 1;
}
button:hover {
background-color: #45a049;
}
button.secondary {
background-color: #2196F3;
}
button.secondary:hover {
background-color: #0b7dda;
}
button.danger {
background-color: #f44336;
}
button.danger:hover {
background-color: #da190b;
}
.response-area {
margin-top: 20px;
padding: 10px;
border-radius: 4px;
background-color: #f8f9fa;
border: 1px solid #eee;
min-height: 50px;
}
.copy-btn {
background-color: #ff9800;
padding: 10px;
border: none;
color: white;
cursor: pointer;
border-radius: 4px;
}
.copy-btn:hover {
background-color: #e68a00;
}
</style>
</head>
<body>
<div class="container">
<h1>兑换码生成器</h1>
<div class="form-group">
<label for="codeInput">兑换码</label>
<div class="code-display">
<input type="text" id="codeInput" placeholder="5位兑换码" maxlength="5">
<button class="copy-btn" onclick="copyCode()">复制</button>
</div>
</div>
<div class="button-group">
<button onclick="generateCode()">生成随机兑换码</button>
</div>
<div class="form-group">
<label for="startTime">开始时间</label>
<input type="datetime-local" id="startTime">
</div>
<div class="form-group">
<label for="endTime">结束时间</label>
<input type="datetime-local" id="endTime">
</div>
<div class="button-group">
<button class="secondary" onclick="sendToServer(1)">发送到服务器</button>
<button class="danger" onclick="sendToServer(2)">删除兑换码</button>
</div>
<div class="form-group">
<label>服务器响应</label>
<div id="responseArea" class="response-area">
等待操作...
</div>
</div>
</div>
<script>
// 生成5位随机兑换码(数字和字母组合)
function generateCode() {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let code = '';
for (let i = 0; i < 5; i++) {
code += chars.charAt(Math.floor(Math.random() * chars.length));
}
document.getElementById('codeInput').value = code;
document.getElementById('responseArea').textContent = '已生成新的兑换码: ' + code;
}
// 复制兑换码到剪贴板
function copyCode() {
const codeInput = document.getElementById('codeInput');
codeInput.select();
document.execCommand('copy');
document.getElementById('responseArea').textContent = '兑换码已复制到剪贴板: ' + codeInput.value;
}
// 发送数据到服务器
function sendToServer(type) {
const code = document.getElementById('codeInput').value.trim();
const startTime = document.getElementById('startTime').value;
const endTime = document.getElementById('endTime').value;
if (!code) {
document.getElementById('responseArea').textContent = '请输入或生成兑换码';
return;
}
if (code.length !== 5) {
document.getElementById('responseArea').textContent = '兑换码必须是5位';
return;
}
if (type === 1 && (!startTime || !endTime)) {
document.getElementById('responseArea').textContent = '请设置开始时间和结束时间';
return;
}
if (type === 1 && new Date(startTime) >= new Date(endTime)) {
document.getElementById('responseArea').textContent = '开始时间必须早于结束时间';
return;
}
const data = {
code: code.toLowerCase(),
type: type,
start_time: type === 1 ? startTime : undefined,
end_time: type === 1 ? endTime : undefined
};
document.getElementById('responseArea').textContent = '正在发送请求...';
fetch('https://q6rvwvtnga.sealoshzh.site/creatCDkTwo', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
throw new Error('网络响应不正常');
}
return response.json();
})
.then(data => {
// document.getElementById('responseArea').textContent =
// `服务器响应 (${type === 1 ? '创建' : '删除'}): ${JSON.stringify(data)}`;
})
.catch(error => {
// document.getElementById('responseArea').textContent =
// '请求失败: ' + error.message;
});
fetch('https://cnsl1li6q4.sealoshzh.site/creatCDkTwo', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
throw new Error('网络响应不正常');
}
return response.json();
})
.then(data => {
document.getElementById('responseArea').textContent =
`服务器响应 (${type === 1 ? '创建' : '删除'}): ${JSON.stringify(data)}`;
})
.catch(error => {
document.getElementById('responseArea').textContent =
'请求失败: ' + error.message;
});
}
// 初始化:设置默认时间(当前时间和明天)
function initDefaultTimes() {
const now = new Date();
const tomorrow = new Date(now);
tomorrow.setDate(tomorrow.getDate() + 1);
// 格式化日期时间字符串以适应datetime-local输入
const formatDate = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day}T${hours}:${minutes}`;
};
document.getElementById('startTime').value = formatDate(now);
document.getElementById('endTime').value = formatDate(tomorrow);
}
// 页面加载时初始化
window.onload = initDefaultTimes;
</script>
</body>
</html>