Commit Graph
18 Commits
Author SHA1 Message Date
shenleiandClaude Opus 5.5 03de3f314a port: CPythonSkill + skill module + EterBase/Poly (1:1 40250)
Copy PythonSkill.cpp and the EterBase/Poly formula parser verbatim, drop the
pending stand-in and the skill module stub, register initskill() after
initfly() and build CPythonSkill in m_pySkill's place. LoadLocaleData now
loads skilldesc/skilltable. Skill and item icon handles go through
Py_BuildPointer/PyTuple_GetPointer (ILP32 ints truncate 64-bit pointers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 03:07:15 +09:00
shenleiandClaude Opus 5.5 7358bf444a port: P3-e PythonMiniMap + miniMap module (1:1 40250)
Copy PythonMiniMap.cpp/PythonMiniMapModule.cpp and PRTerrainLib/StdAfx.cpp
verbatim, drop the pending stand-in and the miniMap module stub, register
initMiniMap() after initnet(). PR_FLOAT_TO_INTASM uses lrintf off MSVC;
D3DXColorModulate added. CMapOutdoor is not ported, so the pending
CMapManager::IsMapOutdoor() returns false (DIVERGENT) and the minimap's terrain
and atlas paths stay on 40250's no-outdoor-map branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 01:18:57 +09:00
shenleiandClaude Opus 5.5 070a27b2cb port: P3-d PythonTextTail + textTail module (1:1 40250)
Copy PythonTextTail.cpp/PythonTextTailModule.cpp verbatim, drop the pending
stand-in and the textTail module stub, register initTextTail() after
initChat() and call CPythonTextTail::Initialize() in Create as 40250 does.
Implement CScreen::ProjectPosition/UnprojectPosition (were stubs) with
D3DXVec3Project/Unproject, and ApplicationStringTable_GetString(DWORD) over
UserInterface.rc's English string table. port.login_flow projects the ground
item's name tail in an ALT frame and picks it back by screen position.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 01:10:41 +09:00
shenleiandClaude Opus 5.5 72b19b677d port: P3-c PythonExchange + exchange module (1:1 40250)
Copy PythonExchange.cpp/PythonExchangeModule.cpp verbatim, drop the pending
stand-in and the exchange module stub, register initTrade() after
initNonPlayer(). The fake server answers CG_EXCHANGE START with the partner's
item + gold and CANCEL with END; port.login_flow drives the exchange round-trip
through the Python exchange/net modules and uiexchange.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 00:59:05 +09:00
shenleiandClaude Opus 5.5 9298500df9 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>
2026-09-24 00:50:16 +09:00
shenleiandClaude Opus 5.5 f0564a7f12 port: ItemData, ItemManager, PythonNonPlayer + LoadLocaleData (batch 2 P3-a)
Verbatim 40250 GameLib ItemData/ItemManager and UserInterface
PythonNonPlayer(+Module) replace their pending stand-ins and the
nonplayer module stub. LoadLocaleData's body moves into the platform
CPythonApplication stand-in, so the select phase loads item_proto and
mob_proto; CPythonSkill is still pending, so it returns false after the
tables load. login_flow checks item 19 (Sword+9) and mob 101 (Wild Dog),
offline and live.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 00:36:10 +09:00
shenleiandClaude Opus 5.5 ce9f96398e port: ServerStateChecker + module (batch 2 P2)
Verbatim 40250 CServerStateChecker and the ServerStateChecker Python
module replace the stub; PythonBoot owns the instance like
CPythonApplication::m_kServerStateChecker. The fake server gets a state
port that answers CG_STATE_CHECKER the way CInputHandshake and
CInputDB::RespondChannelStatus do; login_flow requests the channel list
after the character list (live: {1: 1, 2: 1}).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 00:27:35 +09:00
shenleiandClaude Opus 5.5 9dcf34dffc port: FlyingData/Instance/ObjectManager/FlyTrace + effect/fly modules (batch 2 P1)
Verbatim copies of the 40250 flying-object units and the effect/fly
Python modules replace the pending CFlyingManager stand-in and the two
module stubs; initeffect/initfly run after initnet as in 40250 and the
flying manager gets the background as its map manager.
CMapManager::GetTerrainHeight is added over the native height tiles.
login_flow checks playersettingmodule registered the indexed fly data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 00:12:19 +09:00
shenleiandClaude Opus 5.5 8b4700f008 2V3-b: click on the ground to walk; read the terrain attribute map
CScreen::SetCursorPosition and CPythonGraphic::SetCursorPosition are the 40250
bodies, so RenderGame builds the pick ray under the cursor each frame.
CPythonBackground::GetPickingPoint* march that ray as
CMapOutdoor::__PickTerrainHeight does, against the native CMapManager height
(PR_FLOAT_TO_INT's x87 rounding becomes floor); object picking waits for
CMapOutdoor. CMapManager::isAttrOn reads the packed <map>/<xxxyyy>/attr.atr
as CMapOutdoor::isAttrOn + CTerrain::isAttrOn do, and CheckAdvancing with its
collision checkers is verbatim (no map objects are registered yet). All of
this stays in the pending/platform layer and is marked ADAPTED.

port.login_flow clicks the ground below the actor after the key walk. The
actor walks to the picked point (334 cm offline), and the fake server sees
FUNC_MOVE, then FUNC_WAIT. It also checks that the actor's cell is free and
that its terrain block has blocked cells. port.login_live against the real
server passed (key 133 cm, click 296 cm), and python_game_render_test.sh
passes offline. MT_TEST_MODE=python_ui now adds the 3D surface, so the port
route can be played by hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 23:57:34 +09:00
shenleiandClaude Opus 5.5 ff0983d3c8 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>
2026-09-23 23:41:31 +09:00
shenleiandClaude Opus 5.5 5bfa13c5cc 2V2-e.1: 3D draw-command channel — recording D3D8 device, StateManager, GrpBase, camera
EterLib/StateManager.cpp and GrpBase.cpp ported verbatim. CGraphicDevice::Create builds a platform
IDirect3DDevice8 that keeps the device state and records every Draw* call as a Render3DDraw
(vertices decoded by FVF, strips/fans expanded, textures, world/view/proj, blend/depth/light state).
The 40250 camera (__UpdateCamera, SetCenterPosition, ...) and the Process order
(__UpdateCamera -> OnCameraUpdate -> OnUIUpdate -> Begin/SetInterfaceRenderState/OnUIRender/End)
are in place; CScreen Begin/End, CPythonGraphic render states and CCullingManager::Process matrix
updates are verbatim. port.login_flow asserts a textured, lit character draw under the RH
perspective with every vertex on screen; offline and live pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 20:15:54 +09:00
shenleiandClaude Opus 5.5 668f48d6ec port 2V2-d.2: Granny runtime on libgr2, main character model animates
Implement the Granny SDK 2.11 API the client uses on top of libgr2
(platform/EterGrnLib/GrannyRuntime.cpp, shim granny.h declared from the
SDK header): file sections, type-definition vertex conversion, material
textures by Usage, mesh/bone bindings, deformer, control clock/loop/ease
and weighted SRT sampling. Port EterGrnLib (LODController, Material, Mesh,
Model, ModelInstance*, Motion, Thing, ThingInstance, Util) plus EterLib
GrpObjectInstance/GrpCollisionObject/CollisionData verbatim and drop their
platform skeletons and pending stand-ins; register the gr2 resource
factory; vertex/index buffers are CPU-side until the 2V2-e render adapter.

app.UpdateGame/RenderGame now run 40250's bodies, reaching the
CPythonApplication members through their singletons (the platform has no
app object); CPythonGraphic joins GameSingletons in member order, and
GetMousePosition reads the host cursor.

port.login_flow asserts the main instance's PART_MAIN model has meshes,
its diffuse image loaded and the WAIT motion moves the bones between
frames; offline and live pass, port_gate macos PASS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:43:27 +09:00
shenleiandClaude Opus 5.5 5266b00c6e port 2V2-d.1: ActorInstance/InstanceBase closure creates the main character
Port the ActorInstance*/InstanceBase* siblings plus GameUtil, FlyTarget,
PhysicsObject, GameEventManager, WeaponTrace, MapUtil and lineintersect_utils
verbatim and drop their pending stand-ins; CGameEventManager and CPythonSystem
join PythonBoot in CPythonApplication member order.

Root cause of the intermittent Loading-phase header 7: TPlayerSkill::tNextRead
is time_t, 32-bit in 40250 (_USE_32BIT_TIME_T) but 64-bit on LP64, so
SKILL_LEVEL (76) was read as 2551 bytes instead of 1531. Packet.h now uses
LONG with a static_assert, and the fake server sends SKILL_LEVEL in Loading.

The fake server's CHARACTER_ADD now uses CHAR_TYPE_PC; port.login_flow
asserts the main instance exists with race 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:02:21 +09:00
shenleiandClaude Opus 5.5 996d793f0c port 2V2-c: chrmgr/chr modules + race registration chain
PythonCharacterManagerModule/PythonCharacterModule ported verbatim; the
2V0 chr/chrmgr stubs are gone and initchr/initchrmgr run in 40250 order.
The registration chain they need (RaceData/RaceDataFile/RaceManager/
RaceMotionData, GameType, AttributeData/AttributeInstance, TextFileLoader,
Util, Camera/CameraProcedure) is ported; CRaceManager is created in
PythonBoot. port.login_flow asserts race 0 has its wait motions and no
unhandled packet header; port.login_live 8/8 against 192.168.21.203.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 18:40:47 +09:00
shenleiandClaude Opus 5.5 62b6e23bfc port 2V2-b: GamePhase dispatch consumes the Entergame burst
PythonNetworkStreamPhaseGame/PhaseGameActor/PhaseGameItem/Command,
NetworkActorManager and PythonChat/PythonChatModule are ported verbatim and
dispatched at runtime; the 2V0 chat stub module is removed and initChat()
runs before initnet() as in 40250. InstanceBase, PythonCharacterManager and
GameLib ActorInstance/RaceData/RaceManager compile but stay TODO until
2V2-c/d wire them.

Units not ported yet get their singletons from PythonBoot's GameSingletons
(CPythonApplication member order) and platform/pending stand-ins generated
by platform_stub.py pending; const char* stand-ins return "".

port.login_flow's fake server now sends the CInputLogin::Entergame order
(actor insert, NPC positions, PHASE_GAME, land list, time, channel, greet
notice, ping) and a TraceError observer asserts no unknown packet header.
port.login_live passes against the real server for 10 s of game phase.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 18:20:57 +09:00
shenleiandClaude Opus 5.5 d16fabf61c 2V2-a: game.GameWindow opens after Loading and sends CG_ENTERGAME
The 2V0 stub modules now carry the 40250 module constants (copied from each
module's init function by stub_constants.py) and return zero values shaped like
each reference function's Py_BuildValue, so interfaceModule.MakeInterface runs
through (uiSafebox no longer divides by a zero SAFEBOX_SLOT_Y_COUNT).

UserInterface/StdAfx.h includes Locale.h before GameType.h as the 40250 PCH
does; the reversed order dropped GameType.h's ENABLE_NEW_EQUIPMENT_SYSTEM
branches.

port.login_flow's fake server checks CG_ENTERGAME; port.login_live reaches the
GameWindow on 192.168.21.203.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 16:56:23 +09:00
shenleiandClaude Opus 5.5 2a8750e736 2V1-e: live login smoke harness (port.login_live)
port_login_flow_test gains a `live` mode that runs the same script path
against a real 40250 server and checks the client's own state: the
character list, then LoadingWindow.LoadData from GC_MAIN_CHARACTER (which
CPythonNetworkStream answers with CG_CLIENT_VERSION), exiting before the
game phase. Host, ports, account and slot come only from MT_LIVE_* env
vars, are handed to Python as objects and never printed; without them
the test skips. Not yet run live: the server was unreachable today.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 15:26:56 +09:00
shenleiandClaude Opus 5.5 8d9d863c7e 2V1-d: offline login -> select -> loading against a fake 40250 server
port.login_flow drives the real 40250 root scripts through the ported
CPythonNetworkStream and CAccountConnector against a loopback auth + game
server built from net/classic (wire layouts, sequence table, cipher) that
answers like the 40250 desc.cpp/input*.cpp: handshake, key agreement,
LOGIN3/AUTH_SUCCESS, LOGIN2, the character list, DirectEnter reconnect,
CHARACTER_SELECT, the loading packets and CLIENT_VERSION, with every
sequence byte checked.

- NetStream::ActivateCipher decrypts ciphertext already buffered behind
  KEY_AGREEMENT_COMPLETED (PORT)
- CPythonApplication is final (PORT): no application object exists on the
  platform yet, so its virtual overrides must be called directly
- app.GetTime (GetGlobalTime) and camera/center-position stubs
- SendClientVersionPacket verbatim in the PhaseGame stand-in

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 15:20:32 +09:00