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>
This commit is contained in:
shenlei
2026-09-24 03:07:15 +09:00
co-authored by Claude Opus 5.5
parent 7358bf444a
commit 03de3f314a
25 changed files with 3944 additions and 328 deletions
+20 -1
View File
@@ -222,7 +222,10 @@ int main(int argc, char** argv)
CHECK(PackInitialize("pack"));
std::string error;
CHECK(PythonBoot::Start(stdlib.c_str(), &error));
const bool started = PythonBoot::Start(stdlib.c_str(), &error);
if (!started)
std::fprintf(stderr, "PythonBoot::Start: %s\n", error.c_str());
CHECK(started);
PythonBoot::SetUISize(800, 600);
const bool ran = PythonBoot::RunMainScript("", &error);
if (!ran)
@@ -316,6 +319,12 @@ int main(int argc, char** argv)
"_mob = nonplayer.GetMonsterName(101)\n"
"assert _mob, 'mob 101 has no name'\n"
"import sys\nsys.stderr.write('port_login_flow_test: mob 101 %s\\n' % _mob)"));
// CPythonSkill read skilldesc.txt and skilltable.txt (RegisterSkillDesc/RegisterSkillTable); skill 1's
// cool time is skilltable's constant "12".
CHECK(py("import skill\n"
"assert skill.GetSkillName(1, 0) == 'Three-Way Cut', skill.GetSkillName(1, 0)\n"
"assert skill.GetSkillType(1) == skill.SKILL_TYPE_ACTIVE, skill.GetSkillType(1)\n"
"assert skill.GetSkillCoolTime(1, 1.0) == 12, skill.GetSkillCoolTime(1, 1.0)"));
}
// 4-5. Start the game on the slot: DirectEnter, the second game login and the loading phase.
@@ -501,6 +510,16 @@ int main(int argc, char** argv)
"assert item.GetItemType() == item.ITEM_TYPE_WEAPON and item.GetItemSubType() == item.WEAPON_SWORD\n"
"assert item.GetValue(3) > 0, 'min attack'"));
// skill icons: RegisterSkillDesc loaded each skill's .sub icons; an active skill has one per grade
// (__RegisterGradeIconImage) and no plain icon, as in 40250. The icon instance handle survives the 64-bit
// round trip (Py_BuildPointer).
CHECK(py("import skill\n"
"assert skill.GetIconImage(1) == 0\n"
"assert skill.GetIconImageNew(1, 0) != 0, 'skill 1 has no grade icon'\n"
"_icon = skill.GetIconInstanceNew(1, 0)\n"
"assert _icon != 0\n"
"skill.DeleteIconInstance(_icon)"));
// miniMap module (PythonMiniMapModule.cpp): uiminimap.MiniMap created and showed it. CMapOutdoor is not
// ported, so CMapManager::IsMapOutdoor() is false and the atlas reports "not loaded" as 40250 does off an
// outdoor map.