Commit 3cd073dd authored by fallenstardust's avatar fallenstardust

注释部分函数

parent 7c60c104
......@@ -1231,39 +1231,86 @@ void DuelClient::HandleSTOCPacketLan(unsigned char* data, int len) {
}
}
// Analyze STOC_GAME_MSG packet
/**
* @brief 分析并处理来自服务器的游戏消息包
*
* 该函数负责解析从服务器接收到的STOC_GAME_MSG类型数据包,根据消息类型执行相应的游戏逻辑处理。
* 主要功能包括:
* 1. 读取消息类型并保存到游戏信息中
* 2. 保存最后一次成功处理的消息用于错误恢复
* 3. 隐藏当前显示的菜单界面
* 4. 处理非重播模式下的界面清理工作
* 5. 处理观战者视角切换
* 6. 根据不同的消息类型分发到对应的处理逻辑
*
* @param msg 指向消息数据包的指针
* @param len 消息数据包的长度
* @return bool 处理成功返回true,失败返回false
*
* @note 该函数是游戏核心逻辑处理函数,负责处理所有游戏过程中服务器发送的消息
* @note 对于MSG_RETRY消息会进行特殊处理,尝试恢复上一次成功的操作
* @note 在处理过程中会根据需要锁定和解锁图形界面互斥锁以保证线程安全
*/
bool DuelClient::ClientAnalyze(unsigned char* msg, int len) {
// 定义一个指针,用于遍历消息数据
unsigned char* pbuf = msg;
// 定义一个宽字符缓冲区,用于存储文本信息
wchar_t textBuffer[256];
// 从消息数据中读取当前消息类型,并存储到游戏信息结构中
mainGame->dInfo.curMsg = BufferIO::Read<uint8_t>(pbuf);
// 如果当前消息不是重试消息,则将当前消息保存为最后成功处理的消息
if(mainGame->dInfo.curMsg != MSG_RETRY) {
// 复制消息数据到last_successful_msg缓冲区
std::memcpy(last_successful_msg, msg, len);
// 记录消息长度
last_successful_msg_length = len;
}
// 隐藏当前显示的游戏菜单
mainGame->dField.HideMenu();
// 如果不是回放模式,且当前消息不是等待或卡片选择消息
if(!mainGame->dInfo.isReplay && mainGame->dInfo.curMsg != MSG_WAITING && mainGame->dInfo.curMsg != MSG_CARD_SELECTED) {
// 重置等待帧计数器
mainGame->waitFrame = -1;
// 隐藏提示信息显示
mainGame->stHintMsg->setVisible(false);
// 如果卡片选择窗口当前可见
if(mainGame->wCardSelect->isVisible()) {
// 锁定图形界面互斥锁
mainGame->gMutex.lock();
// 隐藏卡片选择窗口
mainGame->HideElement(mainGame->wCardSelect);
// 解锁图形界面互斥锁
mainGame->gMutex.unlock();
// 等待11帧确保界面操作完成
mainGame->WaitFrameSignal(11);
}
// 如果选项窗口当前可见
if(mainGame->wOptions->isVisible()) {
// 锁定图形界面互斥锁
mainGame->gMutex.lock();
// 隐藏选项窗口
mainGame->HideElement(mainGame->wOptions);
// 解锁图形界面互斥锁
mainGame->gMutex.unlock();
// 等待11帧确保界面操作完成
mainGame->WaitFrameSignal(11);
}
}
// 如果当前计时玩家是玩家1,则将计时玩家设置为2(无)
if(mainGame->dInfo.time_player == 1)
mainGame->dInfo.time_player = 2;
// 如果正在进行视角交换
if(is_swapping) {
// 锁定图形界面互斥锁
mainGame->gMutex.lock();
// 执行回放视角交换操作
mainGame->dField.ReplaySwap();
// 解锁图形界面互斥锁
mainGame->gMutex.unlock();
// 重置视角交换标志
is_swapping = false;
}
// 根据消息类型分发到相应的处理函数
switch(mainGame->dInfo.curMsg) {
case MSG_RETRY: {
if(last_successful_msg_length) {
......@@ -3168,23 +3215,39 @@ bool DuelClient::ClientAnalyze(unsigned char* msg, int len) {
return true;
}
case MSG_SUMMONING: {
// 从消息缓冲区读取召唤的卡片密码
unsigned int code = BufferIO::Read<int32_t>(pbuf);
// 读取控制者位置(被注释掉,未使用)
/*int cc = */mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
// 读取召唤位置(被注释掉,未使用)
/*int cl = */BufferIO::Read<uint8_t>(pbuf);
// 读取召唤区域序列(被注释掉,未使用)
/*int cs = */BufferIO::Read<uint8_t>(pbuf);
// 读取召唤位置(被注释掉,未使用)
/*int cp = */BufferIO::Read<uint8_t>(pbuf);
// 如果不是回放模式或者不是跳过回放,则执行以下操作
if(!mainGame->dInfo.isReplay || !mainGame->dInfo.isReplaySkiping) {
// 尝试播放在sound/chants/{code}.mp3 该卡片id相同文件名所在的音效,如果失败则播放普通召唤音效
if(!mainGame->soundManager->PlayChant(code))
mainGame->soundManager->PlaySoundEffect(SoundManager::SFX::SUMMON);
// 构造召唤事件字符串,显示"召唤了xxx",会打印在消息记录窗口中
myswprintf(event_string, dataManager.GetSysString(1603), dataManager.GetName(code));
// 设置要显示的卡片密码
mainGame->showcardcode = code;
// 设置卡片显示差异参数为0
mainGame->showcarddif = 0;
// 设置卡片显示位置参数为0
mainGame->showcardp = 0;
// 设置显示卡片类型为7(召唤动画)
mainGame->showcard = 7;
// 等待30帧显示召唤动画
mainGame->WaitFrameSignal(30);
// 重置显示卡片类型
mainGame->showcard = 0;
// 再等待11帧
mainGame->WaitFrameSignal(11);
}
// 返回true表示处理成功
return true;
}
case MSG_SUMMONED: {
......
......@@ -16,12 +16,13 @@ namespace ygo {
bool ClientField::OnEvent(const irr::SEvent& event) {
if(OnCommonEvent(event))
return false;
#ifdef _IRR_ANDROID_PLATFORM_
// 定义一个事件变量用于处理Android平台的触摸事件转换
irr::SEvent transferEvent;
// 调用Android平台的触摸事件转换处理函数,如果事件已被处理则返回true
if (irr::android::TouchEventTransferAndroid::OnTransferCommon(event, false)) {
return true;
}
#endif
switch(event.EventType) {
case irr::EET_GUI_EVENT: {
if(mainGame->fadingList.size())
......@@ -30,28 +31,44 @@ bool ClientField::OnEvent(const irr::SEvent& event) {
switch(event.GUIEvent.EventType) {
case irr::gui::EGET_BUTTON_CLICKED: {
switch(id) {
// 处理猜拳选择的3个按钮的点击事件
case BUTTON_HAND1:
case BUTTON_HAND2:
case BUTTON_HAND3: {
// 隐藏手势选择窗口
mainGame->wHand->setVisible(false);
// 判断当前消息是否为猜拳(MSG_ROCK_PAPER_SCISSORS)
if(mainGame->dInfo.curMsg == MSG_ROCK_PAPER_SCISSORS) {
// 设置响应值为按钮索引+1(1=石头,2=布,3=剪刀)
DuelClient::SetResponseI(id - BUTTON_HAND1 + 1);
// 发送响应给服务器
DuelClient::SendResponse();
} else {
// 清空提示信息文本
mainGame->stHintMsg->setText(L"");
// 显示提示信息窗口
mainGame->stHintMsg->setVisible(true);
// 创建手势结果数据包
CTOS_HandResult cshr;
// 设置手势结果为按钮索引+1
cshr.res = id - BUTTON_HAND1 + 1;
// 发送手势结果数据包到服务器
DuelClient::SendPacketToServer(CTOS_HAND_RESULT, cshr);
}
break;
}
// 处理先后攻选择按钮的点击事件
case BUTTON_FIRST:
case BUTTON_SECOND: {
// 播放按钮点击音效
mainGame->soundManager->PlaySoundEffect(SoundManager::SFX::BUTTON);
// 隐藏先后手选择窗口
mainGame->HideElement(mainGame->wFTSelect);
// 创建先后手选择结果数据包
CTOS_TPResult cstr;
// 设置选择结果(BUTTON_SECOND-BUTTON_FIRST=1表示先手,BUTTON_SECOND-BUTTON_SECOND=0表示后手)
cstr.res = BUTTON_SECOND - id;
// 发送先后手选择结果数据包到服务器
DuelClient::SendPacketToServer(CTOS_TP_RESULT, cstr);
break;
}
......@@ -533,13 +550,19 @@ bool ClientField::OnEvent(const irr::SEvent& event) {
break;
}
case BUTTON_CMD_SUMMON: {
// 隐藏右键菜单
HideMenu();
// 如果没有选中卡片则退出
if(!menu_card)
break;
// 遍历可召唤卡片列表,找到与菜单选中卡片相同的卡片
for(size_t i = 0; i < summonable_cards.size(); ++i) {
if(summonable_cards[i] == menu_card) {
// 清除所有卡片的命令标记
ClearCommandFlag();
// 设置响应为召唤操作,i<<16表示召唤第i张可召唤的卡片
DuelClient::SetResponseI(i << 16);
// 发送响应到服务器
DuelClient::SendResponse();
break;
}
......@@ -547,55 +570,77 @@ bool ClientField::OnEvent(const irr::SEvent& event) {
break;
}
case BUTTON_CMD_SPSUMMON: {
// 隐藏右键菜单
HideMenu();
// 如果不是列表命令模式
if(!list_command) {
// 如果没有选中菜单卡片则退出
if(!menu_card)
break;
// 遍历可特殊召唤卡片列表,找到与菜单选中卡片相同的卡片
for(size_t i = 0; i < spsummonable_cards.size(); ++i) {
if(spsummonable_cards[i] == menu_card) {
// 清除所有卡片的命令标记
ClearCommandFlag();
// 设置响应为特殊召唤操作,(i<<16)+1表示特殊召唤第i张可特殊召唤的卡片
DuelClient::SetResponseI((i << 16) + 1);
// 发送响应到服务器
DuelClient::SendResponse();
break;
}
}
} else {
// 清空可选择卡片列表
selectable_cards.clear();
// 根据命令位置选择相应的卡片
switch(command_location) {
case LOCATION_DECK: {
// 从卡组中筛选出可特殊召唤的卡片
for(size_t i = 0; i < deck[command_controler].size(); ++i)
if(deck[command_controler][i]->cmdFlag & COMMAND_SPSUMMON)
selectable_cards.push_back(deck[command_controler][i]);
break;
}
case LOCATION_GRAVE: {
// 从墓地中筛选出可特殊召唤的卡片
for(size_t i = 0; i < grave[command_controler].size(); ++i)
if(grave[command_controler][i]->cmdFlag & COMMAND_SPSUMMON)
selectable_cards.push_back(grave[command_controler][i]);
break;
}
case LOCATION_EXTRA: {
// 从额外卡组中筛选出可特殊召唤的卡片
for(size_t i = 0; i < extra[command_controler].size(); ++i)
if(extra[command_controler][i]->cmdFlag & COMMAND_SPSUMMON)
selectable_cards.push_back(extra[command_controler][i]);
break;
}
}
// 设置列表命令为特殊召唤命令
list_command = COMMAND_SPSUMMON;
// 设置卡片选择窗口标题为"选择怪兽"
mainGame->stCardSelect->setText(dataManager.GetSysString(509));
// 显示卡片选择窗口
ShowSelectCard();
// 设置选择未就绪状态
select_ready = false;
// 显示取消/完成按钮
ShowCancelOrFinishButton(1);
}
break;
}
case BUTTON_CMD_MSET: {
// 隐藏右键菜单
HideMenu();
// 如果没有选中菜单卡片则退出
if(!menu_card)
break;
// 遍历可放置卡片列表,找到与菜单选中卡片相同的卡片
for(size_t i = 0; i < msetable_cards.size(); ++i) {
if(msetable_cards[i] == menu_card) {
// 设置响应为放置操作,(i<<16)+3表示放置第i张可放置的卡片
DuelClient::SetResponseI((i << 16) + 3);
// 发送响应到服务器
DuelClient::SendResponse();
break;
}
......@@ -2633,100 +2678,154 @@ void ClientField::GetHoverField(int x, int y) {
}
}
}
/**
* @brief 显示右键菜单界面,根据传入的标志位决定显示哪些操作按钮。
*
* 此函数用于在游戏客户端中响应用户点击卡牌时弹出的操作菜单。它会根据传入的 flag 参数,
* 判断需要显示哪些命令按钮(如召唤、特殊召唤、设置等),并动态调整这些按钮的位置。
* 同时禁用部分主界面按钮以防止误操作,并将菜单窗口定位到指定坐标位置。
*
* @param flag 操作命令标志位组合,每一位代表一个可执行的操作类型。
* @param x 菜单左上角横坐标(屏幕像素)。
* @param y 菜单左上角纵坐标(屏幕像素)。
*/
void ClientField::ShowMenu(int flag, int x, int y) {
// 如果没有可用操作,则隐藏当前菜单并直接返回
if(!flag) {
HideMenu();
return;
}
// 记录被点击的卡牌对象
menu_card = clicked_card;
// 初始化菜单高度计数器,从顶部开始排列按钮
int height = 1;
// 根据标志位依次判断是否启用各个功能按钮,并设置其可见性和相对位置
// 【发动】按钮处理逻辑
if(flag & COMMAND_ACTIVATE) {
mainGame->btnActivate->setVisible(true);
mainGame->btnActivate->setRelativePosition(irr::core::vector2di(1, height));
#ifdef _IRR_ANDROID_PLATFORM_
height += 60 * mainGame->yScale;
#endif
} else mainGame->btnActivate->setVisible(false);
} else {
mainGame->btnActivate->setVisible(false);
}
// 【召唤】按钮处理逻辑
if(flag & COMMAND_SUMMON) {
mainGame->btnSummon->setVisible(true);
mainGame->btnSummon->setRelativePosition(irr::core::vector2di(1, height));
#ifdef _IRR_ANDROID_PLATFORM_
height += 60 * mainGame->yScale;
#endif
} else mainGame->btnSummon->setVisible(false);
} else {
mainGame->btnSummon->setVisible(false);
}
// 【特殊召唤】按钮处理逻辑
if(flag & COMMAND_SPSUMMON) {
mainGame->btnSPSummon->setVisible(true);
mainGame->btnSPSummon->setRelativePosition(irr::core::vector2di(1, height));
#ifdef _IRR_ANDROID_PLATFORM_
height += 60 * mainGame->yScale;
#endif
} else mainGame->btnSPSummon->setVisible(false);
} else {
mainGame->btnSPSummon->setVisible(false);
}
// 【盖放(怪兽区)】按钮处理逻辑
if(flag & COMMAND_MSET) {
mainGame->btnMSet->setVisible(true);
mainGame->btnMSet->setRelativePosition(irr::core::vector2di(1, height));
#ifdef _IRR_ANDROID_PLATFORM_
height += 60 * mainGame->yScale;
#endif
} else mainGame->btnMSet->setVisible(false);
} else {
mainGame->btnMSet->setVisible(false);
}
// 【盖放(魔法·陷阱区)】按钮处理逻辑
if(flag & COMMAND_SSET) {
// 区分怪兽卡与其他类型卡片的文字提示
if(!(clicked_card->type & TYPE_MONSTER))
mainGame->btnSSet->setText(dataManager.GetSysString(1153));
mainGame->btnSSet->setText(dataManager.GetSysString(1153)); // 设置魔法陷阱卡
else
mainGame->btnSSet->setText(dataManager.GetSysString(1159));
mainGame->btnSSet->setText(dataManager.GetSysString(1159)); // 设置怪兽卡为里侧守备
mainGame->btnSSet->setVisible(true);
mainGame->btnSSet->setRelativePosition(irr::core::vector2di(1, height));
#ifdef _IRR_ANDROID_PLATFORM_
height += 60 * mainGame->yScale;
#endif
} else mainGame->btnSSet->setVisible(false);
} else {
mainGame->btnSSet->setVisible(false);
}
// 【改变表示形式】按钮处理逻辑
if(flag & COMMAND_REPOS) {
// 根据当前卡牌状态设置不同的按钮文字
if(clicked_card->position & POS_FACEDOWN)
mainGame->btnRepos->setText(dataManager.GetSysString(1154));
mainGame->btnRepos->setText(dataManager.GetSysString(1154)); // 反转
else if(clicked_card->position & POS_ATTACK)
mainGame->btnRepos->setText(dataManager.GetSysString(1155));
mainGame->btnRepos->setText(dataManager.GetSysString(1155)); // 改为守备
else
mainGame->btnRepos->setText(dataManager.GetSysString(1156));
mainGame->btnRepos->setText(dataManager.GetSysString(1156)); // 改为攻击
mainGame->btnRepos->setVisible(true);
mainGame->btnRepos->setRelativePosition(irr::core::vector2di(1, height));
#ifdef _IRR_ANDROID_PLATFORM_
height += 60 * mainGame->yScale;
#endif
} else mainGame->btnRepos->setVisible(false);
} else {
mainGame->btnRepos->setVisible(false);
}
// 【攻击宣言】按钮处理逻辑
if(flag & COMMAND_ATTACK) {
mainGame->btnAttack->setVisible(true);
mainGame->btnAttack->setRelativePosition(irr::core::vector2di(1, height));
#ifdef _IRR_ANDROID_PLATFORM_
height += 60 * mainGame->yScale;
#endif
} else mainGame->btnAttack->setVisible(false);
} else {
mainGame->btnAttack->setVisible(false);
}
// 【查看连锁列表】按钮处理逻辑
if(flag & COMMAND_LIST) {
mainGame->btnShowList->setVisible(true);
mainGame->btnShowList->setRelativePosition(irr::core::vector2di(1, height));
#ifdef _IRR_ANDROID_PLATFORM_
height += 60 * mainGame->yScale;
#endif
} else mainGame->btnShowList->setVisible(false);
} else {
mainGame->btnShowList->setVisible(false);
}
// 【操作】按钮处理逻辑
if(flag & COMMAND_OPERATION) {
mainGame->btnOperation->setVisible(true);
mainGame->btnOperation->setRelativePosition(irr::core::vector2di(1, height));
#ifdef _IRR_ANDROID_PLATFORM_
height += 60 * mainGame->yScale;
#endif
} else mainGame->btnOperation->setVisible(false);
} else {
mainGame->btnOperation->setVisible(false);
}
// 【重置】按钮处理逻辑
if(flag & COMMAND_RESET) {
mainGame->btnReset->setVisible(true);
mainGame->btnReset->setRelativePosition(irr::core::vector2di(1, height));
#ifdef _IRR_ANDROID_PLATFORM_
height += 60 * mainGame->yScale;
#endif
} else mainGame->btnReset->setVisible(false);
} else {
mainGame->btnReset->setVisible(false);
}
// 设置当前面板为命令菜单窗口,并使其可见
panel = mainGame->wCmdMenu;
mainGame->wCmdMenu->setVisible(true);
// 禁用战斗阶段相关按钮避免冲突
mainGame->btnBP->setEnabled(false);
mainGame->btnM2->setEnabled(false);
mainGame->btnEP->setEnabled(false);
mainGame->wCmdMenu->setRelativePosition(irr::core::recti(x - 20 * mainGame->xScale , y - 30 * mainGame->yScale - height, x + 130 * mainGame->xScale, y - 30 * mainGame->yScale));
// 设置悬浮命令菜单窗口的位置与尺寸
mainGame->wCmdMenu->setRelativePosition(
irr::core::recti(
x - 20 * mainGame->xScale,
y - 30 * mainGame->yScale - height,
x + 130 * mainGame->xScale,
y - 30 * mainGame->yScale
)
);
}
void ClientField::HideMenu() {
mainGame->wCmdMenu->setVisible(false);
mainGame->btnBP->setEnabled(true);
......@@ -2824,21 +2923,51 @@ void ClientField::ShowCardInfoInList(ClientCard* pcard, irr::gui::IGUIElement* e
mainGame->stCardListTip->setVisible(true);
}
}
/**
* @brief 设置客户端响应中选中的卡片信息
*
* 该函数将当前选中的卡片序列号打包成响应数据,并发送给决斗客户端。
* 响应数据格式为:第一个字节表示选中卡片数量,后续字节依次为各卡片的选择序号。
*
* @param 无参数
* @return 无返回值
*/
void ClientField::SetResponseSelectedCards() const {
// 准备响应数据缓冲区
unsigned char respbuf[SIZE_RETURN_VALUE]{};
// 获取选中卡片数量,最多不超过UINT8_MAX个
int len = (int)selected_cards.size();
if (len > UINT8_MAX)
len = UINT8_MAX;
// 设置响应数据:第一个字节为卡片数量
respbuf[0] = (unsigned char)len;
// 依次填充每张选中卡片的选择序号
for (int i = 0; i < len; ++i)
respbuf[i + 1] = selected_cards[i]->select_seq;
// 发送响应数据到决斗客户端
DuelClient::SetResponseB(respbuf, len + 1);
}
/**
* @brief 设置客户端选择的响应选项
*
* 根据当前游戏消息类型和选择的选项,设置相应的响应数据,
* 并隐藏选项窗口
*
* @param 无
* @return 无
*/
void ClientField::SetResponseSelectedOption() const {
// 处理选项选择消息
if(mainGame->dInfo.curMsg == MSG_SELECT_OPTION) {
DuelClient::SetResponseI(selected_option);
} else {
// 获取选择选项的索引
int index = select_options_index[selected_option];
// 根据不同的消息类型设置响应数据
if(mainGame->dInfo.curMsg == MSG_SELECT_IDLECMD) {
DuelClient::SetResponseI((index << 16) + 5);
} else if(mainGame->dInfo.curMsg == MSG_SELECT_BATTLECMD) {
......@@ -2847,11 +2976,19 @@ void ClientField::SetResponseSelectedOption() const {
DuelClient::SetResponseI(index);
}
}
// 隐藏选项窗口
mainGame->HideElement(mainGame->wOptions, true);
}
/**
* @brief 处理取消或完成操作的函数
*
* 根据当前游戏消息类型处理相应的取消或完成逻辑,包括隐藏界面元素、设置响应数据、发送响应等操作
*/
void ClientField::CancelOrFinish() {
// 根据当前游戏消息类型进行不同的处理
switch(mainGame->dInfo.curMsg) {
case MSG_WAITING: {
// 如果卡片选择窗口可见,则隐藏它并隐藏取消/完成按钮
if(mainGame->wCardSelect->isVisible()) {
mainGame->HideElement(mainGame->wCardSelect);
ShowCancelOrFinishButton(0);
......@@ -2859,10 +2996,12 @@ void ClientField::CancelOrFinish() {
break;
}
case MSG_SELECT_BATTLECMD: {
// 如果卡片选择窗口可见,则隐藏它并隐藏取消/完成按钮
if(mainGame->wCardSelect->isVisible()) {
mainGame->HideElement(mainGame->wCardSelect);
ShowCancelOrFinishButton(0);
}
// 如果选项窗口可见,则隐藏它并隐藏取消/完成按钮
if(mainGame->wOptions->isVisible()) {
mainGame->HideElement(mainGame->wOptions);
ShowCancelOrFinishButton(0);
......@@ -2870,10 +3009,12 @@ void ClientField::CancelOrFinish() {
break;
}
case MSG_SELECT_IDLECMD: {
// 如果卡片选择窗口可见,则隐藏它并隐藏取消/完成按钮
if(mainGame->wCardSelect->isVisible()) {
mainGame->HideElement(mainGame->wCardSelect);
ShowCancelOrFinishButton(0);
}
// 如果选项窗口可见,则隐藏它并隐藏取消/完成按钮
if(mainGame->wOptions->isVisible()) {
mainGame->HideElement(mainGame->wOptions);
ShowCancelOrFinishButton(0);
......@@ -2882,33 +3023,44 @@ void ClientField::CancelOrFinish() {
}
case MSG_SELECT_YESNO:
case MSG_SELECT_EFFECTYN: {
// 如果有高亮显示的卡片,则取消高亮
if(highlighting_card)
highlighting_card->is_highlighting = false;
highlighting_card = 0;
// 设置响应为否(0)
DuelClient::SetResponseI(0);
// 隐藏询问窗口并发送响应
mainGame->HideElement(mainGame->wQuery, true);
break;
}
case MSG_SELECT_CARD: {
// 如果没有选择任何卡片且可以选择取消
if(selected_cards.size() == 0) {
if(select_cancelable) {
// 设置响应为-1表示取消选择
DuelClient::SetResponseI(-1);
ShowCancelOrFinishButton(0);
// 如果卡片选择窗口可见则隐藏它,否则直接发送响应
if(mainGame->wCardSelect->isVisible())
mainGame->HideElement(mainGame->wCardSelect, true);
else
DuelClient::SendResponse();
}
}
// 如果询问窗口可见
if(mainGame->wQuery->isVisible()) {
// 设置选中卡片的响应并隐藏询问窗口
SetResponseSelectedCards();
ShowCancelOrFinishButton(0);
mainGame->HideElement(mainGame->wQuery, true);
break;
}
// 如果已准备好选择
if(select_ready) {
// 设置选中卡片的响应
SetResponseSelectedCards();
ShowCancelOrFinishButton(0);
// 如果卡片选择窗口可见则隐藏它,否则直接发送响应
if(mainGame->wCardSelect->isVisible())
mainGame->HideElement(mainGame->wCardSelect, true);
else
......@@ -2917,9 +3069,12 @@ void ClientField::CancelOrFinish() {
break;
}
case MSG_SELECT_UNSELECT_CARD: {
// 如果可以选择取消
if (select_cancelable) {
// 设置响应为-1表示取消选择
DuelClient::SetResponseI(-1);
ShowCancelOrFinishButton(0);
// 如果卡片选择窗口可见则隐藏它,否则直接发送响应
if (mainGame->wCardSelect->isVisible())
mainGame->HideElement(mainGame->wCardSelect, true);
else
......@@ -2928,10 +3083,13 @@ void ClientField::CancelOrFinish() {
break;
}
case MSG_SELECT_TRIBUTE: {
// 如果没有选择任何卡片且可以选择取消
if(selected_cards.size() == 0) {
if(select_cancelable) {
// 设置响应为-1表示取消选择
DuelClient::SetResponseI(-1);
ShowCancelOrFinishButton(0);
// 如果卡片选择窗口可见则隐藏它,否则直接发送响应
if(mainGame->wCardSelect->isVisible())
mainGame->HideElement(mainGame->wCardSelect, true);
else
......@@ -2939,13 +3097,17 @@ void ClientField::CancelOrFinish() {
}
break;
}
// 如果询问窗口可见
if(mainGame->wQuery->isVisible()) {
// 设置选中卡片的响应并隐藏询问窗口
SetResponseSelectedCards();
ShowCancelOrFinishButton(0);
mainGame->HideElement(mainGame->wQuery, true);
break;
}
// 如果已准备好选择
if(select_ready) {
// 设置选中卡片的响应并发送
SetResponseSelectedCards();
ShowCancelOrFinishButton(0);
DuelClient::SendResponse();
......@@ -2953,10 +3115,12 @@ void ClientField::CancelOrFinish() {
break;
}
case MSG_SELECT_SUM: {
// 如果已准备好选择
if (select_ready) {
// 设置选中卡片的响应
SetResponseSelectedCards();
ShowCancelOrFinishButton(0);
// 如果卡片选择窗口可见则隐藏它,否则直接发送响应
if(mainGame->wCardSelect->isVisible())
mainGame->HideElement(mainGame->wCardSelect, true);
else
......@@ -2965,21 +3129,28 @@ void ClientField::CancelOrFinish() {
break;
}
case MSG_SELECT_CHAIN: {
// 如果必须选择连锁则不能取消
if(chain_forced)
break;
// 如果卡片选择窗口可见则隐藏它
if(mainGame->wCardSelect->isVisible()) {
mainGame->HideElement(mainGame->wCardSelect);
break;
}
// 如果询问窗口可见
if(mainGame->wQuery->isVisible()) {
// 设置响应为-1表示不选择连锁并隐藏询问窗口
DuelClient::SetResponseI(-1);
ShowCancelOrFinishButton(0);
mainGame->HideElement(mainGame->wQuery, true);
} else {
// 弹出询问窗口
mainGame->PopupElement(mainGame->wQuery);
ShowCancelOrFinishButton(0);
}
// 如果选项窗口可见
if(mainGame->wOptions->isVisible()) {
// 设置响应为-1表示不选择选项并隐藏选项窗口
DuelClient::SetResponseI(-1);
ShowCancelOrFinishButton(0);
mainGame->HideElement(mainGame->wOptions);
......@@ -2987,26 +3158,35 @@ void ClientField::CancelOrFinish() {
break;
}
case MSG_SORT_CARD: {
// 如果卡片选择窗口可见
if(mainGame->wCardSelect->isVisible()) {
// 设置响应为-1表示取消排序并隐藏卡片选择窗口
DuelClient::SetResponseI(-1);
mainGame->HideElement(mainGame->wCardSelect, true);
// 清空排序列表
sort_list.clear();
}
break;
}
case MSG_SELECT_PLACE: {
// 如果可以选择取消
if(select_cancelable) {
// 准备响应数据,表示不选择任何位置
unsigned char respbuf[3];
respbuf[0] = mainGame->LocalPlayer(0);
respbuf[1] = 0;
respbuf[2] = 0;
// 清空可选择字段
mainGame->dField.selectable_field = 0;
// 设置并发送响应
DuelClient::SetResponseB(respbuf, 3);
DuelClient::SendResponse();
// 隐藏取消/完成按钮
ShowCancelOrFinishButton(0);
}
break;
}
}
}
}
......@@ -1903,16 +1903,30 @@ void Game::SaveConfig() {
// android::saveIntSetting(appMain, "control_mode", gameConf.control_mode);
}
/**
* @brief 显示指定卡牌的信息。
*
* 此函数用于在游戏界面中显示一张卡牌的详细信息,包括图像、名称、类型、种族、属性、攻击力/守备力等,
* 并根据卡牌是否有效(存在于数据表中)以及其具体类型(怪兽卡、魔法卡、陷阱卡等)进行不同的处理。
* 同时会设置文本区域以展示卡牌描述,并调整控件位置与缩放。
*
* @param code 卡牌编号,用作查找卡牌数据的关键字。
*/
void Game::ShowCardInfo(int code) {
// 定义格式化缓冲区和获取卡牌数据表引用
wchar_t formatBuffer[256];
auto& _datas = dataManager.GetDataTable();
auto cit = _datas.find(code);
bool is_valid = (cit != _datas.end());
// 设置卡片图片并启用自动缩放
imgCard->setImage(imageManager.GetTexture(code));
imgCard->setScaleImage(true);
// 根据卡牌是否存在决定如何显示名称:若存在且是替代卡则使用别名
if (is_valid) {
auto& cd = cit->second;
if (is_alternative(cd.code,cd.alias))
if (is_alternative(cd.code, cd.alias))
myswprintf(formatBuffer, L"%ls[%08d]", dataManager.GetName(cd.alias), cd.alias);
else
myswprintf(formatBuffer, L"%ls[%08d]", dataManager.GetName(code), code);
......@@ -1920,20 +1934,26 @@ void Game::ShowCardInfo(int code) {
else {
myswprintf(formatBuffer, L"%ls[%08d]", dataManager.GetName(code), code);
}
// 设置名称标签文本及提示工具文本(当文字超出宽度时)
stName->setText(formatBuffer);
if((int)guiFont->getDimension(formatBuffer).Width > stName->getRelativePosition().getWidth() - gameConf.textfontsize)
if ((int)guiFont->getDimension(formatBuffer).Width > stName->getRelativePosition().getWidth() - gameConf.textfontsize)
stName->setToolTipText(formatBuffer);
else
stName->setToolTipText(nullptr);
// 字段名显示逻辑
int offset = 0;
if (is_valid && !gameConf.hide_setname) {
auto& cd = cit->second;
auto target = cit;
// 若当前卡有同名卡并且该同名卡存在于数据库中,则使用该同名卡对应的数据
if (cd.alias && _datas.find(cd.alias) != _datas.end()) {
target = _datas.find(cd.alias);
}
// 如果目标卡具有字段码,则构造并显示字段名
if (target->second.setcode[0]) {
offset = 23;// *yScale;
offset = 23; // 固定偏移量用于布局调整
const auto& setname = dataManager.FormatSetName(target->second.setcode);
myswprintf(formatBuffer, L"%ls%ls", dataManager.GetSysString(1329), setname.c_str());
stSetName->setText(formatBuffer);
......@@ -1944,61 +1964,85 @@ void Game::ShowCardInfo(int code) {
else {
stSetName->setText(L"");
}
if(is_valid && cit->second.type & TYPE_MONSTER) {
// 怪物卡信息处理分支
if (is_valid && cit->second.type & TYPE_MONSTER) {
auto& cd = cit->second;
// 构造并显示怪物卡的基本信息(类型、种族、属性)
const auto& type = dataManager.FormatType(cd.type);
const auto& race = dataManager.FormatRace(cd.race);
const auto& attribute = dataManager.FormatAttribute(cd.attribute);
myswprintf(formatBuffer, L"[%ls] %ls/%ls", type.c_str(), race.c_str(), attribute.c_str());
stInfo->setText(formatBuffer);
// 判断文本长度是否需要换行偏移
int offset_info = 0;
irr::core::dimension2d<unsigned int> dtxt = guiFont->getDimension(formatBuffer);
if(dtxt.Width > (300 * xScale - 13) - 15)
if (dtxt.Width > (300 * xScale - 13) - 15)
offset_info = 15;
const wchar_t* form = L"\u2605";
// 准备等级符号和攻防数值字符串
const wchar_t* form = L"\u2605"; // 默认星数标记
wchar_t adBuffer[64]{};
wchar_t scaleBuffer[16]{};
if(!(cd.type & TYPE_LINK)) {
if(cd.type & TYPE_XYZ)
form = L"\u2606";
if(cd.attack < 0 && cd.defense < 0)
// 非连接卡处理
if (!(cd.type & TYPE_LINK)) {
if (cd.type & TYPE_XYZ)
form = L"\u2606"; // XYZ卡使用不同标记
// 攻击力/防御力未知情况下的特殊处理
if (cd.attack < 0 && cd.defense < 0)
myswprintf(adBuffer, L"?/?");
else if(cd.attack < 0)
else if (cd.attack < 0)
myswprintf(adBuffer, L"?/%d", cd.defense);
else if(cd.defense < 0)
else if (cd.defense < 0)
myswprintf(adBuffer, L"%d/?", cd.attack);
else
myswprintf(adBuffer, L"%d/%d", cd.attack, cd.defense);
} else {
}
// 连接卡处理
else {
form = L"LINK-";
const auto& link_marker = dataManager.FormatLinkMarker(cd.link_marker);
if(cd.attack < 0)
if (cd.attack < 0)
myswprintf(adBuffer, L"?/- %ls", link_marker.c_str());
else
myswprintf(adBuffer, L"%d/- %ls", cd.attack, link_marker.c_str());
}
if(cd.type & TYPE_PENDULUM) {
// 摆钟卡额外显示左右刻度
if (cd.type & TYPE_PENDULUM) {
myswprintf(scaleBuffer, L" %d/%d", cd.lscale, cd.rscale);
}
// 组合最终数据信息并设置到界面上
myswprintf(formatBuffer, L"[%ls%d] %ls%ls", form, cd.level, adBuffer, scaleBuffer);
stDataInfo->setText(formatBuffer);
// 调整控件的位置
stSetName->setRelativePosition(Resize(10, 83, 250, 106));
stText->setRelativePosition(Resize(10, 83 + offset, 251, 340));
scrCardText->setRelativePosition(Resize(255, 83 + offset, 258, 340));
}
else {
}else {// 非怪物卡或无效卡处理
if (is_valid) {
const auto& type = dataManager.FormatType(cit->second.type);
myswprintf(formatBuffer, L"[%ls]", type.c_str());
}
else
myswprintf(formatBuffer, L"[%ls]", dataManager.unknown_string);
stInfo->setText(formatBuffer);
stDataInfo->setText(L"");
// 调整控件位置适应非怪物卡布局
stSetName->setRelativePosition(Resize(10, 60, 250, 106));
stText->setRelativePosition(Resize(10, 60 + offset, 251, 340));
scrCardText->setRelativePosition(Resize(255, 60 + offset, 258, 340));
}
// 获取并初始化卡牌描述文本
showingtext = dataManager.GetText(code);
const auto& tsize = stText->getRelativePosition();
InitStaticText(stText, tsize.getWidth(), tsize.getHeight(), textFont, showingtext);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment