9.7 KiB
Python Skill Conversion Tool - Current Status & Fixes Needed
Tool Location
e:\Projects\convert_skills.py
Current Status
✅ Working: Tool successfully converts C++ skill files to C# format ✅ Tested: Skill 390 converted successfully with no linter errors ⚠️ Issues: Some formatting issues need to be fixed (see below)
How to Use
Convert specific skills:
cd e:\Projects
python convert_skills.py 390,391,392,393
Convert all remaining skills:
cd e:\Projects
python convert_skills.py --all
Known Issues & Required Fixes
🔴 CRITICAL FIX 1: Calculate Method Formatting
Problem: Missing semicolons and improper spacing in Calculate method body
Current Output:
public void Calculate(Skill skill)
{
skill.GetPlayer ().SetDecmp (28)
skill.GetPlayer ().SetPray (1)
}
Should Be:
public void Calculate(Skill skill)
{
skill.GetPlayer().SetDecmp(28);
skill.GetPlayer().SetPray(1);
}
Fix Location: Line ~136-147 in generate_csharp_state method
Required Changes:
# Current code (BROKEN):
calculate_body = "\n " + "\n ".join(line.strip() for line in calc_content.split(';') if line.strip())
# Should be (FIXED):
lines = [line.strip() for line in calc_content.split(';') if line.strip()]
lines = [line + ';' if not line.endswith(';') else line for line in lines]
lines = [re.sub(r'\s+\(', '(', line) for line in lines] # Remove spaces before (
calculate_body = "\n " + "\n ".join(lines)
🔴 CRITICAL FIX 2: Remove Spaces Before Parentheses
Problem: Spaces before () in method calls
Examples:
skill.GetPlayer ()→ Should beskill.GetPlayer()skill.GetLevel ()→ Should beskill.GetLevel()skill.GetVictim ()→ Should beskill.GetVictim()
Fix Location: Multiple places - add global regex replacement
Required Changes: Add this to ALL C++ → C# conversion sections:
# After any C++ to C# conversion, add:
content = re.sub(r'\s+\(', '(', content)
Specific locations to add:
- In
generate_csharp_statemethod (line ~140) - In method body conversions (line ~380-400)
- In StateAttack/BlessMe conversions (line ~420-450)
🟡 IMPORTANT FIX 3: Float Suffix Consistency
Problem: Not all float values have f suffix
Examples:
0in float method → Should be0f1.8→ Should be1.8f125in GetMpcost → Should be125f
Fix Location: Line ~350-360 in method generation
Required Changes:
# Current:
if method_info['return_type'] == 'float' and not value.endswith('f') and '.' not in value:
value += 'f'
# Should be:
if method_info['return_type'] == 'float':
# Check if it's a numeric value
if re.match(r'^[\d.]+$', value.strip()) and not value.endswith('f'):
value = value.strip() + 'f'
# For expressions with parentheses, add f at the end
elif '(' in value and not value.endswith('f'):
value = value.strip() + 'f'
🟡 IMPORTANT FIX 4: Complex Expression Parsing
Problem: Complex expressions need better parsing
Example C++:
return (float) (skill->GetPlayer ()->GetRange () + 3 + 0.3 * skill->GetLevel ());
Current Output:
public float GetAttackdistance(Skill skill) => (float)(skill.GetPlayer ().GetRange () + 3 + 0.3 * skill.GetLevel ());
Should Be:
public float GetAttackdistance(Skill skill) => (float)(skill.GetPlayer().GetRange() + 3 + 0.3 * skill.GetLevel());
Fix: Apply space removal regex to all expressions
🟢 MINOR FIX 5: StateAttack/BlessMe Body Conversion
Problem: Complex method bodies need better formatting
Fix Location: Line ~420-450
Required Changes:
# Add after body conversion:
body_lines = []
for line in body.split('\n'):
line = line.strip()
if line and 'return' not in line:
# Remove spaces before parentheses
line = re.sub(r'\s+\(', '(', line)
# Ensure semicolon at end
if not line.endswith(';'):
line += ';'
body_lines.append(line)
Complete Fix Implementation
Here's the complete fixed version of the critical sections:
Fixed generate_csharp_state method (line ~90-150):
def generate_csharp_state(self, state_data: Dict) -> str:
"""Generate C# code for a state class."""
state_num = state_data['num']
body = state_data['body']
# Parse all methods from state body
methods = {}
method_patterns = {
'GetTime': r'int\s+GetTime\s*\([^)]*\)\s*const\s*\{[^}]*return\s+(\d+)',
'Quit': r'bool\s+Quit\s*\([^)]*\)\s*const\s*\{[^}]*return\s+(false|true|\d+)',
'Loop': r'bool\s+Loop\s*\([^)]*\)\s*const\s*\{[^}]*return\s+(false|true|\d+)',
'Bypass': r'bool\s+Bypass\s*\([^)]*\)\s*const\s*\{[^}]*return\s+(false|true|\d+)',
'Interrupt': r'bool\s+Interrupt\s*\([^)]*\)\s*const\s*\{[^}]*return\s+(false|true|\d+)',
'Cancel': r'bool\s+Cancel\s*\([^)]*\)\s*const\s*\{[^}]*return\s+(false|true|\d+)',
'Skip': r'bool\s+Skip\s*\([^)]*\)\s*const\s*\{[^}]*return\s+(false|true|\d+)',
}
for method_name, pattern in method_patterns.items():
match = re.search(pattern, body, re.DOTALL)
if match:
value = match.group(1)
if method_name != 'GetTime':
if value == '0':
value = 'false'
elif value == '1' or value == 'true':
value = 'true'
elif value == 'false':
value = 'false'
methods[method_name] = value
# Extract Calculate method body
calc_match = re.search(r'void\s+Calculate\s*\([^)]*\)\s*const\s*\{(.*?)\}', body, re.DOTALL)
calculate_body = ""
if calc_match:
calc_content = calc_match.group(1).strip()
if calc_content:
# Convert C++ to C#
calc_content = calc_content.replace('->', '.')
# Remove spaces before parentheses
calc_content = re.sub(r'\s+\(', '(', calc_content)
# Split by semicolons and process each line
lines = [line.strip() for line in calc_content.split(';') if line.strip()]
# Add semicolons back
lines = [line + ';' if not line.endswith(';') else line for line in lines]
# Proper indentation
calculate_body = "\n " + "\n ".join(lines)
code = f"""#if SKILL_SERVER
public class State{state_num} : SkillStub.State
{{
public int GetTime(Skill skill) => {methods.get('GetTime', '0')};
public bool Quit(Skill skill) => {methods.get('Quit', 'false')};
public bool Loop(Skill skill) => {methods.get('Loop', 'false')};
public bool Bypass(Skill skill) => {methods.get('Bypass', 'false')};
public void Calculate(Skill skill)
{{{calculate_body if calculate_body else ' '}
}}
public bool Interrupt(Skill skill) => {methods.get('Interrupt', 'false')};
public bool Cancel(Skill skill) => {methods.get('Cancel', 'false')};
public bool Skip(Skill skill) => {methods.get('Skip', 'false')};
}}
#endif
"""
return code
Fixed method value processing (add around line ~350):
# For all method values, remove spaces before parentheses
value = re.sub(r'\s+\(', '(', value)
# For float methods, ensure f suffix
if method_info['return_type'] == 'float':
# If it's a simple number, add f
if re.match(r'^[\d.]+$', value.strip()) and not value.endswith('f'):
value = value.strip() + 'f'
Fixed StateAttack/BlessMe conversion (around line ~420):
elif method_name in ['StateAttack', 'BlessMe']:
# Parse body lines
body_lines = []
for line in body.split('\n'):
line = line.strip()
if line and 'return' not in line:
# Remove spaces before parentheses
line = re.sub(r'\s+\(', '(', line)
# Convert 1.0 to 1.0f
line = re.sub(r'(\d+\.\d+)(?!f)', r'\1f', line)
# Ensure semicolon
if not line.endswith(';'):
line += ';'
body_lines.append(line)
server_methods_code += f" public bool {method_name}(Skill skill)\n {{\n"
for line in body_lines:
server_methods_code += f" {line}\n"
server_methods_code += f" return true;\n }}\n"
Testing Checklist
After applying fixes, test with:
# Test single skill
python convert_skills.py 390
# Check output
# 1. Open skill390.cs
# 2. Verify Calculate method has proper semicolons and indentation
# 3. Verify no spaces before parentheses: skill.GetPlayer() not skill.GetPlayer ()
# 4. Verify all float values have f suffix
# 5. Run linter - should be 0 errors
# If all good, proceed with batch
python convert_skills.py 390,391,392,393,394,395,396,397
Next Steps
- ✅ Apply all fixes above to
convert_skills.py - ✅ Test on skill 390 again
- ✅ Verify linter shows 0 errors
- ✅ Run on batch 390-397 (8 skills)
- ✅ Verify all 8 skills compile with no errors
- ✅ Run on all remaining skills with
--allflag - ✅ Final verification and linter check
Skills Remaining to Convert
Total: ~200+ skills
Priority batches:
- 390-439 (50 skills) - Current focus
- 440-491 (52 skills)
- All other ranges listed in SKILL_CONVERSION_INSTRUCTIONS.md
Success Criteria
✅ All converted skills have:
- No linter errors
- Proper semicolons in Calculate methods
- No spaces before parentheses
- Float values with
fsuffix - Proper indentation (4 spaces per level)
- Matching pattern with existing skills (skill65.cs, skill374.cs, etc.)