接下来告诉大家怎么实现一种卡密实现兑换游戏内多种项目的方法:

1.首先你服务器必须支持mysql模组extdb3,比如生活服,我们先在你的库下建立一个兑换码表,它储存着所有用来兑换各种游戏内项目的卡密。

CREATE TABLE `destiny_sell_key`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `card_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `card_type` int(0) NOT NULL DEFAULT 0 COMMENT '0=vip卡,1=钱',
  `card_active` int(0) NOT NULL DEFAULT 0,
  `vip_level` int(0) NULL DEFAULT 0,
  `vip_day_num` int(0) NULL DEFAULT 0,
  `game_money` int(0) NULL DEFAULT 0,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 49 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

有必要解释下这些字段是干嘛的:

card_key 是储存卡密 长度是255字符,这里可以根据你的卡密长度自己设置

card_type 是卡密类型

card_active 是是否已经使用

vip_level 是VIP卡密时,兑换的vip等级

vip_day_num 是VIP卡密时,兑换的vip的天数

game_money 是金币卡密时兑换的游戏币

2.然后在你的玩家表中添加一个字段:

一般带有数据库的服务器,玩家进来需要读取玩家的数据,在储存这个玩家数据的表上添加一个字段:vip_end_date 类型:int 长度:0

这个字段用来储存玩家VIP的到期时间,它是10位时间戳。

3.接下来需要写一个储存过程,它用来实现具体的兑换卡密功能:

CREATE DEFINER=`root`@`localhost` PROCEDURE `setVip`(IN `pid` varchar(255),IN `card` varchar(255))
BEGIN
	/*
	设置服务器VIP 
	描述: 添加VIP/变更vip到期时间/根据等级折算vip时间/根据金币卡密添加游戏币 by destiny studio
	-1  无效的卡密
	0 卡密已经被使用
	1 成功
	*/
	DECLARE CURRENT_VIP_TIME BIGINT DEFAULT 0;
	DECLARE VIP_END_TIME BIGINT DEFAULT 0;
	DECLARE donator_level BIGINT DEFAULT 0;
	DECLARE var_card_key varchar(255) DEFAULT "";
	DECLARE var_card_type BIGINT DEFAULT 0;
	DECLARE var_card_active BIGINT DEFAULT 0;
	DECLARE var_vip_level BIGINT DEFAULT 0;
	DECLARE var_vip_day_num BIGINT DEFAULT 0;
	DECLARE var_game_money BIGINT DEFAULT 0;
	
	SELECT card_key,card_type,card_active,vip_level,vip_day_num,game_money INTO var_card_key,var_card_type,var_card_active,var_vip_level,var_vip_day_num,var_game_money FROM destiny_sell_key WHERE card_key = card;
	IF var_card_key != ""
		THEN
			IF var_card_active = 0
				THEN
					IF var_card_type = 0
						THEN
							SELECT vip_end_date,donatorLevel INTO CURRENT_VIP_TIME,donator_level FROM phxclients where playerid = pid;
							IF unix_timestamp() > CURRENT_VIP_TIME
								THEN SET VIP_END_TIME = unix_timestamp(TIMESTAMPADD(day,var_vip_day_num,from_unixtime(unix_timestamp())));
							ELSE
								IF donator_level != var_vip_level
									THEN 
										SET VIP_END_TIME = unix_timestamp(TIMESTAMPADD(day,var_vip_day_num,from_unixtime(unix_timestamp(TIMESTAMPADD(day,-(ROUND(((CURRENT_VIP_TIME-unix_timestamp()) / (24*3600))/2)),from_unixtime(CURRENT_VIP_TIME))))));
								ELSE
									SET VIP_END_TIME = unix_timestamp(TIMESTAMPADD(day,var_vip_day_num,from_unixtime(CURRENT_VIP_TIME)));
								END IF;
							END IF;
							UPDATE phxclients SET vip_end_date = VIP_END_TIME,donatorLevel = var_vip_level WHERE playerid = pid;
					ELSE
						UPDATE phxclients SET bankacc = bankacc + var_game_money WHERE playerid = pid;
					END IF;
					UPDATE destiny_sell_key SET card_active = 1 WHERE card_key = card;
					SELECT 1;
			ELSE
				SELECT 0;
			END IF;
	ELSE
			SELECT -1;
	END IF;
END

相信你们都看明白了,这里有个折算操作,当你买了3个月VIP1,再次买1个月VIP2,也就是比VIP1更高的等级时,会出现把之前的VIP1时间折一半+当前卡密等级的时间。如果不需要可以自行修改,比如你买了60天VIP1,再买30天VIP2,那么就会 (60/2) + 30 = 最终的时间

再写一个获取VIP时间过程:

CREATE DEFINER=`root`@`localhost` PROCEDURE `getVipTime`(IN `pid` varchar(255))
BEGIN

	DECLARE CURRENT_VIP_TIME BIGINT DEFAULT 0;
	
	SELECT vip_end_date INTO CURRENT_VIP_TIME FROM phxclients where playerid = pid;
	
	IF unix_timestamp() > CURRENT_VIP_TIME
		THEN SELECT 0,FROM_UNIXTIME(CURRENT_VIP_TIME) AS vip_time;
	ELSE
		SELECT 1,FROM_UNIXTIME(CURRENT_VIP_TIME) AS vip_time,ROUND(((CURRENT_VIP_TIME-unix_timestamp()) / (24*3600))) AS vip_date;
	END IF;

END

注意里面的查询的表,我这里是phxclients 换成你的,其次查询的字段我这里是donatorLevel 是vip赞助等级,换成你的。还有playerid,换成你表中对应的玩家UID字段

4.SQF实现这些功能:

4.1:当玩家加入服务器读取玩家数据之前,我们要检查他的VIP是否过期:

[format ["UPDATE phxclients SET donatorLevel = (if ( unix_timestamp() > vip_end_date,0,donatorLevel)) WHERE playerid='%1'",_steamid], 1] call DB_fnc_asyncCall;

phxclients = 换成你的玩家表
donatorLevel = 换成你的VIP等级字段
_steamid = 换成初始化玩家的UID
DB_fnc_asyncCall = 换成你的调用函数

4.2:然后我们要实现真正的兑换代码:

destiny_fnc_setVip = {
	params ["_player", "_key"];
	_pid = getPlayerUID _player;
	if (_pid isEqualTo "") exitWith {};
	if (_key != "") then {
		_result = [format ["call setVip('%1','%2')",_pid,_key], 2,true] call DB_fnc_asyncCall;//DB_fnc_asyncCall执行你自己的数据库函数
		try{
			_result_type = _result # 0 # 0;
			if (_result_type != 1) exitWith {
				_msg = switch (_result_type) do {
					case -1: {"无效的卡密!"};
					case 0: {"卡密已经被使用!"};
					default {"未知的卡密状态!"};
				};
           //执行你自己的通知
			   //[_msg,"red"] remoteExec ["PHX_fnc_notify",_player];
			};
			_var_card_key = _result # 0 # 1;
			_var_card_type = _result # 0 # 2;
			_var_card_active = _result # 0 # 3;
			_var_vip_level = _result # 0 # 4;
			_var_vip_day_num = _result # 0 # 5;
			_var_game_money = _result # 0 # 6;
			_var_vip_end_time = _result # 0 # 7;
			if (_var_card_type == 0) then {
            //实现你自己的逻辑,兑换的项目
            //这里模拟兑换VIP
				[_var_vip_level] remoteExec ["destiny_fnc_vipLevel",_player];
				_time = format["%1-%2-%3 %4:%5:%6",_var_vip_end_time # 0,_var_vip_end_time # 1,_var_vip_end_time # 2,_var_vip_end_time # 3,_var_vip_end_time # 4,_var_vip_end_time # 5];
              
            //执行你自己的通知
				[format ["充值成功:现在您的VIP等级为 %1 到期日为:%2 ,尽情享受吧!",_var_vip_level,_time],"green"] remoteExec ["PHX_fnc_notify",_player];
			};
          
			if (_var_card_type == 1) then {
           //实现你自己的逻辑,兑换的项目
           //这里模拟兑换游戏币
				[_var_game_money,0,1] remoteExec ["destiny_fnc_handleMoney",_player];
              
            //执行你自己的通知
				[format ["充值成功:您的 %1 已汇入银行!",([_var_game_money] call BIS_fnc_numberText)],"green"] remoteExec ["PHX_fnc_notify",_player];
			};
		} catch {
			diag_log _exception;
         //执行你自己的通知
			["充值:执行存储过程等一系列操作时发生异常!请重试或联系管理员!","red"] remoteExec ["PHX_fnc_notify",_player];
		};
	};
};

4.3它是个服务器函数,所以我们玩家需要远程调用它给服务器执行,传入玩家对象和卡密就可以了!

然后再写一个获取VIP时间:

_pid = getPlayerUID _this;
_key = format ["destiny_var_vipQueryTime_%1",_pid];
_vtime = missionNamespace getVariable [_key,0];
if (serverTime > _vtime) then {
	missionNamespace setVariable [_key,(serverTime + 60)];
	_result = [format ["call getVipTime('%1')",_pid], 2,true] call DB_fnc_asyncCall;
	_newResult = _result # 0;
	_status = _newResult # 0;
	_vipDate = _newResult # 1;
	_data = [];
	if (_status == 1) then {
		_data = [format ["%1-%2-%3 %4:%5:%6",_vipDate#0,_vipDate#1,_vipDate#2,_vipDate#3,_vipDate#4,_vipDate#5],_newResult # 2]; 
	} else {
		_data = ["已到期",0]; 
	};
	_data remoteExecCall ["destiny_fnc_receiveVip",_this];
};

传入玩家对象就可以了,为了防止重复点击或其他因素,我们记录下时间+60秒,每60秒才可以同步时间,这也是服务器函数,需要玩家远程调用它给服务器执行, call DB_fnc_asyncCall 也要换成你的,其次 _data remoteExecCall [“destiny_fnc_receiveVip”,_this]; 中的destiny_fnc_receiveVip是个本地函数,它会接受到获取到的时间,自行处理。

5.实现游戏中的兑换卡密UI和功能:

我们需要来到你的任务文件夹,找到description.ext打开并写入一下代码:

class destiny_dialog_buy_vip
{
	idd = -1;
	movingEnable = true;
	class ControlsBackground
	{
		class Control_bg
		{
			type = 0;
			idc = 0;
			x = 0;
			y = 0;
			w = 1;
			h = 1;
			style = 0;
			text = "";
			colorBackground[] = {0,0,0,0.5};
			colorText[] = {0.851,0.9137,0.2353,1};
			font = "PuristaMedium";
			sizeEx = (((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 1);
			
		};
		class Control_title
		{
			type = 0;
			idc = 0;
			x = safeZoneX + safeZoneW * 0.29375;
			y = safeZoneY + safeZoneH * 0.225;
			w = safeZoneW * 0.4125;
			h = safeZoneH * 0.06851852;
			style = 2;
			text = "自由都市-在线赞助";
			colorBackground[] = {0.502,0.6,1,0.8};
			colorText[] = {1,1,1,1};
			font = "PuristaMedium";
			sizeEx = (((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 1.5);
			
		};
		class Control_tip
		{
			type = 0;
			idc = 0;
			x = safeZoneX + safeZoneW * 0.29375;
			y = safeZoneY + safeZoneH * 0.28981482;
			w = safeZoneW * 0.4125;
			h = safeZoneH * 0.03148149;
			style = 2;
			text = "注意:将你的卡密输入,然后点击确定,卡密有VIP卡,金币卡2种。";
			colorBackground[] = {0.502,0.6,1,0.8};
			colorText[] = {1,1,1,1};
			font = "PuristaMedium";
			sizeEx = (((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 1);
			
		};
		class Control_tip2
		{
			type = 0;
			idc = 0;
			x = safeZoneX + safeZoneW * 0.29375;
			y = safeZoneY + safeZoneH * 0.73425926;
			w = safeZoneW * 0.4125;
			h = safeZoneH * 0.04074075;
			style = 2;
			text = "注意:如果您是续费VIP,并且当前的VIP等级和卡密等级不匹配,到期日期将设置为:(当前VIP剩余时间 / 2) + 卡密VIP时间";
			colorBackground[] = {0.502,0.6,1,0.8};
			colorText[] = {0.902,0.302,0.302,1};
			font = "PuristaMedium";
			sizeEx = (((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 0.8);
			
		};
		class Control_text
		{
			type = 0;
			idc = 0;
			x = safeZoneX + safeZoneW * 0.37291667;
			y = safeZoneY + safeZoneH * 0.37314815;
			w = safeZoneW * 0.2640625;
			h = safeZoneH * 0.0962963;
			style = 2;
			text = "您的卡密:";
			colorBackground[] = {1,1,1,0};
			colorText[] = {1,1,1,1};
			font = "PuristaMedium";
			sizeEx = (((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 2);
			
		};
		
	};
	class Controls
	{
		class Control_input
		{
			type = 2;
			idc = 5670;
			x = safeZoneX + safeZoneW * 0.3234375;
			y = safeZoneY + safeZoneH * 0.475;
			w = safeZoneW * 0.353125;
			h = safeZoneH * 0.04074075;
			style = 0;
			text = "";
			autocomplete = "";
			colorBackground[] = {0.502,0.6,1,0.8};
			colorDisabled[] = {0.2,0.2,0.2,1};
			colorSelection[] = {1,0,0,1};
			colorText[] = {1,1,1,1};
			font = "PuristaMedium";
			sizeEx = (((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 1);
			onChar = "";
			
		};
		class Control1_sub
		{
			type = 1;
			idc = -1;
			x = safeZoneX + safeZoneW * 0.4125;
			y = safeZoneY + safeZoneH * 0.61388889;
			w = safeZoneW * 0.16510417;
			h = safeZoneH * 0.04074075;
			style = 0+2;
			text = "确定";
			borderSize = 0;
			colorBackground[] = {0,0,0,1};
			colorBackgroundActive[] = {0,0,0,1};
			colorBackgroundDisabled[] = {0,0,0,1};
			colorBorder[] = {0,0,0,0};
			colorDisabled[] = {0.2,0.2,0.2,1};
			colorFocused[] = {0.2,0.2,0.2,1};
			colorShadow[] = {0,0,0,1};
			colorText[] = {1,1,1,1};
			font = "PuristaMedium";
			offsetPressedX = 0.01;
			offsetPressedY = 0.01;
			offsetX = 0.01;
			offsetY = 0.01;
			sizeEx = (((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 1.5);
			soundClick[] = {"\A3\ui_f\data\sound\RscButton\soundClick",0.09,1.0};
			soundEnter[] = {"\A3\ui_f\data\sound\RscButton\soundEnter",0.09,1.0};
			soundEscape[] = {"\A3\ui_f\data\sound\RscButton\soundEscape",0.09,1.0};
			soundPush[] = {"\A3\ui_f\data\sound\RscButton\soundPush",0.09,1.0};
			onButtonClick = "_key = ctrlText 5670;[_key] call destiny_fnc_subBuy;";
		};
		
	};
	
};

我们需要来到你的任务文件夹,找到initPlayerLocal.sqf打开并写入一下代码:

destiny_var_subBuyTime = 0;
destiny_fnc_subBuy = {
	params ["_key"];
	closeDialog 0;
	if (time > destiny_var_subBuyTime) then {
		destiny_var_subBuyTime = time + 10;
		if (isNil "_key" || _key isEqualTo "") exitWith {
			["请输入卡密!","red"] spawn PHX_fnc_notify;
		};
		[player,_key] remoteExec ["destiny_fnc_setVip",2];
	} else {
		["服务器繁忙,请等待一会儿再试试...","red"] spawn PHX_fnc_notify;
	};
};
//这些代码可以储存在服务器然后通过publicVariable发送给所有客户端,达到不需要修改任务的目的

创建一个下面类型的对话框进行测试:

destiny_dialog_buy_vip
6.完成,去测试和实现你的功能吧~