port: PythonItem + item module (batch 2 P3-b)

Verbatim 40250 CPythonItem and the item Python module replace the
pending stand-in and the module stub; CPythonApplication::Create calls
m_pyItem.Create() in 40250 order. itemGetIconImage returns its handle
through Py_BuildPointer (the ILP32 int truncated it and wndMgr.SetSlot
crashed on the live inventory). CMapManager::GetNormal evaluates
CTerrain::CalculateNormal on the native height tile; the item text-tail
members stay stubs, Pick answering -1 like an empty tail map.

The fake server drops vnum 19 next to the actor at entergame and answers
CG_ITEM_PICKUP with GC_ITEM_GROUND_DEL; login_flow picks it up through
player.PickCloseItem and checks item.SelectItem(19).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-24 00:50:16 +09:00
co-authored by Claude Opus 5.5
parent f0564a7f12
commit 9298500df9
19 changed files with 2178 additions and 454 deletions
+30 -2
View File
@@ -48,6 +48,7 @@
#include "GameLib/FlyingData.h"
#include "GameLib/FlyingObjectManager.h"
#include "GameLib/ItemManager.h"
#include "UserInterface/PythonItem.h"
#include "UserInterface/StdAfx.h"
#include "UserInterface/PythonCharacterManager.h"
#include "UserInterface/InstanceBase.h"
@@ -491,7 +492,33 @@ int main(int argc, char** argv)
CHECK(!CFlyingManager::Instance().RegisterFlyingData(msf));
}
// 8. (批次 2V3) Click-to-move: a left click on the ground below the actor reaches game.GameWindow's
// item module (PythonItemModule.cpp) over the loaded item_proto: SelectItem + the selected item's fields.
CHECK(py("import item\n"
"item.SelectItem(19)\n"
"assert item.GetItemName() == 'Sword+9', item.GetItemName()\n"
"assert item.GetItemType() == item.ITEM_TYPE_WEAPON and item.GetItemSubType() == item.WEAPON_SWORD\n"
"assert item.GetValue(3) > 0, 'min attack'"));
// Ground items (CPythonItem): the fake server put vnum 19 next to the actor at entergame (GC_ITEM_GROUND_ADD
// + GC_ITEM_OWNERSHIP). player.PickCloseItem finds it within __GetPickableDistance, sends CG_ITEM_PICKUP,
// and the server's GC_ITEM_GROUND_DEL removes it.
if (server)
{
CPythonItem& ground = CPythonItem::Instance();
TPixelPosition item_pos = {};
const char* owner = nullptr;
CHECK(ground.GetGroundItemPosition(FakeLoginServer::kGroundItemVID, &item_pos));
CHECK(ground.GetOwnership(FakeLoginServer::kGroundItemVID, &owner) && owner &&
std::string(owner) == FakeLoginServer::kCharacterName);
CHECK(ground.GetVirtualNumberOfGroundItem(FakeLoginServer::kGroundItemVID) == FakeLoginServer::kGroundItemVnum);
std::printf("port_login_flow_test: ground item at %.0f %.0f\n", item_pos.x, item_pos.y);
CHECK(py("import player\nplayer.PickCloseItem()"));
const std::string pickup = "game2:pickup " + std::to_string(FakeLoginServer::kGroundItemVID);
CHECK(pump_until(2, [&] { return server->Has(pickup); }));
CHECK(pump_until(2, [&] { return !ground.GetGroundItemPosition(FakeLoginServer::kGroundItemVID, &item_pos); }));
}
// 8. (批次 2V3) Click-to-move: a left click on the ground below the actor reaches game.GameWindow's
// OnMouseLeftButtonDown → player.SetMouseState(MBT_LEFT, MBS_PRESS); CPythonPlayer picks the ground
// through the cursor ray (CScreen::SetCursorPosition → CPythonBackground::GetPickingPoint) and the
// actor walks to it, sending CG_MOVE FUNC_MOVE, then FUNC_WAIT on arrival.
@@ -537,7 +564,8 @@ int main(int argc, char** argv)
if (e.rfind(prefix, 0) == 0)
want.push_back(e);
for (const auto& e : events)
if (e.rfind(prefix, 0) == 0 && e.find(":move ") == std::string::npos)
if (e.rfind(prefix, 0) == 0 && e.find(":move ") == std::string::npos &&
e.find(":pickup ") == std::string::npos)
got.push_back(e);
CHECK(want == got);
}