2V3-a: walk with the keyboard through the ported player chain
PythonPlayer, PythonPlayerInput{,Keyboard,Mouse}, PythonPlayerEventHandler,
PythonPlayerSkill, PythonPlayerModule and GameType are verbatim copies; the
`player` stub module and the pending PythonPlayer/GameType stand-ins are gone.
Unported callees (CPythonSkill, CItemData, CItemManager, CPythonItem, ...) are
pending stubs. 64-bit fixes: emotion icon handles use Py_BuildPointer, and
PyDict_Next takes Py_ssize_t.
The actor did not move because GrannyUpdateModelMatrix passed the model matrix
through. Walk/run motions use constant root-motion extraction
(AccumulationFlags=AccumulationExtracted, LoopTranslation e.g. (0,-300,0) for
the warrior run, same as the .msa Accumulation). libgr2 now reads both fields
from TrackGroups[0], and the runtime moves by LoopTranslation over each
control's local-clock span, blended by control weight. VDA extraction is not
implemented.
port.login_flow presses DIK_UP; the actor walks more than 100 cm, and the fake
server parses CG packets and sees CG_MOVE FUNC_MOVE, then FUNC_WAIT at the new
position. port.login_live against the real server walked 132 cm.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
157f7c3bb4
commit
ff0983d3c8
@@ -377,6 +377,42 @@ int main(int argc, char** argv)
|
||||
return false;
|
||||
};
|
||||
CHECK(pump_until(3, character_draw));
|
||||
|
||||
// 7. (批次 2V3) Keyboard movement through the original path: the key reaches game.GameWindow's
|
||||
// onPressKeyDict, MoveUp calls player.SetSingleDIKKeyState(DIK_UP, True), CPythonPlayer turns
|
||||
// it into NEW_MoveToDirection on the main instance, and CPythonPlayerEventHandler::OnMove sends
|
||||
// CG_MOVE FUNC_MOVE. Releasing the key stops the actor (OnStop: FUNC_WAIT at the new position).
|
||||
TPixelPosition start = {};
|
||||
if (main_instance)
|
||||
main_instance->NEW_GetPixelPosition(&start);
|
||||
auto walked = [&] {
|
||||
TPixelPosition now = {};
|
||||
main_instance->NEW_GetPixelPosition(&now);
|
||||
return std::hypot(now.x - start.x, now.y - start.y);
|
||||
};
|
||||
PythonBoot::UIKey(DIK_UP, true);
|
||||
CHECK(main_instance && pump_until(3, [&] { return walked() > 100.0f; }));
|
||||
PythonBoot::UIKey(DIK_UP, false);
|
||||
CHECK(main_instance && pump_until(2, [&] { return !main_instance->IsWalking(); }));
|
||||
const float distance = main_instance ? walked() : 0.0f;
|
||||
std::printf("port_login_flow_test: DIK_UP walked %.0f cm\n", distance);
|
||||
if (server)
|
||||
{
|
||||
// The fake server records "game2:move <func> <arg> <rot> <x> <y>"; x, y are the TPixelPosition
|
||||
// (y negated back, as SendCharacterStatePacket does) plus the map base the server gave.
|
||||
auto moves = [&] {
|
||||
std::vector<std::string> out;
|
||||
for (const auto& e : server->Events())
|
||||
if (e.rfind("game2:move ", 0) == 0)
|
||||
out.push_back(e);
|
||||
return out;
|
||||
};
|
||||
CHECK(pump_until(2, [&] { auto m = moves(); return m.size() >= 2 && m.back().rfind("game2:move 0 ", 0) == 0; }));
|
||||
const auto m = moves();
|
||||
CHECK(!m.empty() && m.front().rfind("game2:move 1 0 ", 0) == 0);
|
||||
for (const auto& e : m)
|
||||
std::printf("port_login_flow_test: %s\n", e.c_str());
|
||||
}
|
||||
}
|
||||
if (server)
|
||||
{
|
||||
@@ -394,7 +430,7 @@ 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)
|
||||
if (e.rfind(prefix, 0) == 0 && e.find(":move ") == std::string::npos)
|
||||
got.push_back(e);
|
||||
CHECK(want == got);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user