- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
104 lines
5.1 KiB
C++
104 lines
5.1 KiB
C++
// Public M2Client argument bounds shared by the Classic and m2dev backends.
|
|
#include "../src/net/net_bounds.h"
|
|
#include "../src/net/item_action_rules.h"
|
|
|
|
#include <cstdio>
|
|
|
|
static int g_fail = 0;
|
|
#define CHECK(c, msg) \
|
|
do { \
|
|
if (!(c)) { \
|
|
std::fprintf(stderr, "FAIL: %s\n", msg); \
|
|
++g_fail; \
|
|
} \
|
|
} while (0)
|
|
|
|
int main() {
|
|
using namespace mtnet::bounds;
|
|
|
|
CHECK(u8(0) && u8(255), "u8 accepts wire range");
|
|
CHECK(!u8(-1) && !u8(256), "u8 rejects outside range");
|
|
CHECK(u16(65535) && !u16(-1) && !u16(65536), "u16 range");
|
|
CHECK(u32(0) && u32(0xffffffffLL) && !u32(-1), "u32 range");
|
|
CHECK(i32(-2147483648LL) && i32(2147483647LL), "i32 accepts signed range");
|
|
CHECK(!i32(-2147483649LL) && !i32(2147483648LL), "i32 rejects outside range");
|
|
// ClientVS22 uicharacter.py sends quest-log buttons as
|
|
// (INT32_MIN + questIndex), then the packet stores the bit pattern in a
|
|
// uint32. The public GDScript API must allow that sentinel range while
|
|
// rejecting arbitrary negative values.
|
|
CHECK(script_button_index(0) && script_button_index(65535),
|
|
"script button accepts ordinary and quest-index endpoints");
|
|
CHECK(script_button_index(-2147483648LL) && script_button_index(-2147418113LL),
|
|
"script button accepts INT32_MIN quest sentinel range");
|
|
CHECK(!script_button_index(-2147483649LL) && !script_button_index(-2147418112LL),
|
|
"script button rejects values outside quest sentinel range");
|
|
|
|
CHECK(quick_slot(0) && quick_slot(35), "quick slot endpoints");
|
|
CHECK(!quick_slot(-1) && !quick_slot(36), "quick slot outside range");
|
|
CHECK(private_shop_slot(0) && private_shop_slot(39), "private shop endpoints");
|
|
CHECK(!private_shop_slot(40), "private shop outside range");
|
|
CHECK(exchange_slot(0) && exchange_slot(11) && !exchange_slot(12), "exchange range");
|
|
CHECK(cube_slot(0) && cube_slot(23) && !cube_slot(24), "cube range");
|
|
|
|
// §3.8 mod 3: use_skill skill index. 1..254 accepted, 0 / >=255 / negative rejected.
|
|
CHECK(SKILL_MAX_NUM == 255, "SKILL_MAX_NUM matches Packet.h");
|
|
CHECK(skill_index(1) && skill_index(254), "skill index endpoints");
|
|
CHECK(!skill_index(0) && !skill_index(255) && !skill_index(-1), "skill index outside range");
|
|
CHECK(!skill_index(0x7fffffffLL + 1), "skill index rejects large value");
|
|
CHECK(refine_request(0, 0, 90) && refine_request(89, 3, 90),
|
|
"refine accepts inventory positions");
|
|
CHECK(refine_request(255, 255, 90), "refine accepts the 40250 cancel sentinel");
|
|
CHECK(!refine_request(90, 0, 90) && !refine_request(255, 0, 90)
|
|
&& !refine_request(0, 255, 90), "refine rejects invalid non-sentinel requests");
|
|
|
|
// ClientVS22 TItemPos::IsValidCell() parity: a wire-sized integer is not
|
|
// automatically a valid inventory position.
|
|
CHECK(item_cell(1, 0) && item_cell(1, 167), "inventory cell endpoints");
|
|
CHECK(!item_cell(1, 168) && !item_cell(1, 65535), "inventory cell outside range");
|
|
CHECK(item_cell(2, 0) && item_cell(2, 133), "equipment window endpoints");
|
|
CHECK(!item_cell(2, 134), "equipment window outside range");
|
|
CHECK(item_cell(5, 179) && !item_cell(5, 180), "dragon soul cell range");
|
|
CHECK(item_cell(7, 15) && !item_cell(7, 16), "belt cell range");
|
|
CHECK(!item_cell(3, 0) && !item_cell(0, 0), "unsupported item windows rejected");
|
|
|
|
using namespace mtnet::item_action;
|
|
CHECK(is_equipment_type(1) && is_equipment_type(2),
|
|
"CItemData::IsEquipment accepts weapon and armor");
|
|
CHECK(!is_equipment_type(3) && !is_equipment_type(28) && !is_equipment_type(33),
|
|
"CItemData::IsEquipment rejects use, costume and ring types");
|
|
CHECK(use_sound_type(1, 2) == ItemSoundType::Bow,
|
|
"weapon bow uses the bow sound");
|
|
CHECK(use_sound_type(1, 6) == ItemSoundType::Default,
|
|
"weapon arrow uses the default sound");
|
|
CHECK(use_sound_type(2, 0) == ItemSoundType::Armor &&
|
|
use_sound_type(2, 5) == ItemSoundType::Accessory,
|
|
"body and accessory armor use their reference sounds");
|
|
CHECK(use_sound_type(3, 7) == ItemSoundType::Potion &&
|
|
use_sound_type(3, 0) == ItemSoundType::None &&
|
|
use_sound_type(3, 1) == ItemSoundType::Portal,
|
|
"use-item sound branches match ability-up, potion, talisman");
|
|
CHECK(drop_sound_type(1, 2) == ItemSoundType::Bow &&
|
|
drop_sound_type(2, 0) == ItemSoundType::Armor &&
|
|
drop_sound_type(3, 7) == ItemSoundType::Default,
|
|
"drop sound branches match weapon, armor and default items");
|
|
Context action{};
|
|
action.has_main = true;
|
|
CHECK(can_act_main(action), "item action allowed for a live main actor");
|
|
action.dead = true;
|
|
CHECK(!can_act_main(action), "item action blocked for a dead main actor");
|
|
action.dead = false;
|
|
action.exchange_active = true;
|
|
CHECK(!can_touch_equipment(action, true), "equipment action blocked during exchange");
|
|
CHECK(can_touch_equipment(action, false), "non-equipment action remains allowed during exchange");
|
|
action.exchange_active = false;
|
|
action.attacking = true;
|
|
CHECK(!can_touch_equipment(action, true), "equipment action blocked while attacking");
|
|
|
|
if (g_fail) {
|
|
std::fprintf(stderr, "%d checks FAILED\n", g_fail);
|
|
return 1;
|
|
}
|
|
std::puts("net_bounds_test: OK");
|
|
return 0;
|
|
}
|