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:
co-authored by
Claude Opus 5.5
parent
f0564a7f12
commit
9298500df9
@@ -550,6 +550,19 @@ bool FakeLoginServer::ServeGame(Connection& c, int index)
|
||||
GCChatHead chat = {HDR_GC_CHAT, static_cast<std::uint16_t>(sizeof(GCChatHead) + sizeof(greet)), 2 /* CHAT_TYPE_NOTICE */, 0, 1};
|
||||
if (!c.Send(chat) || !c.Send(greet, sizeof(greet)))
|
||||
return Fail(name + ": send greet notice");
|
||||
// CItem::EncodeInsertPacket for an owned item in view: GC_ITEM_GROUND_ADD, then GC_ITEM_OWNERSHIP.
|
||||
GCItemGroundAdd ground = zeroed<GCItemGroundAdd>();
|
||||
ground.header = HDR_GC_ITEM_GROUND_ADD;
|
||||
ground.x = kGroundItemX;
|
||||
ground.y = kGroundItemY;
|
||||
ground.vid = kGroundItemVID;
|
||||
ground.vnum = kGroundItemVnum;
|
||||
GCItemOwnership ownership = zeroed<GCItemOwnership>();
|
||||
ownership.header = HDR_GC_ITEM_OWNERSHIP;
|
||||
ownership.vid = kGroundItemVID;
|
||||
std::strncpy(ownership.owner, kCharacterName, sizeof(ownership.owner) - 1);
|
||||
if (!c.Send(ground) || !c.Send(ownership))
|
||||
return Fail(name + ": send ground item");
|
||||
Event(name + ":burst_sent");
|
||||
|
||||
// A ping after the burst proves that GamePhase consumed every packet above and can reply.
|
||||
@@ -593,6 +606,16 @@ bool FakeLoginServer::ServeGame(Connection& c, int index)
|
||||
return Fail(name + ": SEQUENCE mismatch on CG header " + std::to_string(header));
|
||||
c.sequence = (c.sequence + 1) % SEQUENCE_TABLE_SIZE;
|
||||
}
|
||||
// CInputMain::ItemPickup → CHARACTER::PickupItem → CItem::RemoveFromGround → EncodeRemovePacket.
|
||||
if (header == HDR_CG_ITEM_PICKUP)
|
||||
{
|
||||
CGItemPickup pickup;
|
||||
std::memcpy(&pickup, packet, sizeof(pickup));
|
||||
Event(name + ":pickup " + std::to_string(pickup.vid));
|
||||
GCItemGroundDel del = {HDR_GC_ITEM_GROUND_DEL, pickup.vid};
|
||||
if (pickup.vid == kGroundItemVID && !c.Send(del))
|
||||
return Fail(name + ": send GC_ITEM_GROUND_DEL");
|
||||
}
|
||||
if (header == HDR_CG_MOVE)
|
||||
{
|
||||
CGMove move;
|
||||
|
||||
@@ -21,6 +21,10 @@ public:
|
||||
static constexpr std::uint32_t kLoginKey = 0x13572468;
|
||||
static constexpr std::uint32_t kMainVID = 0x00012345;
|
||||
static constexpr std::int32_t kX = 469300, kY = 964200;
|
||||
// A ground item in view at entergame (CItem::EncodeInsertPacket): vnum 19, owned by the character.
|
||||
static constexpr std::uint32_t kGroundItemVID = 0x00054321;
|
||||
static constexpr std::uint32_t kGroundItemVnum = 19;
|
||||
static constexpr std::int32_t kGroundItemX = kX + 150, kGroundItemY = kY;
|
||||
|
||||
~FakeLoginServer();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user