otland / forgottenserver

A free and open-source MMORPG server emulator written in C++
https://otland.net
GNU General Public License v2.0
1.55k stars 1.04k forks source link

Move spells to scripts folder #4755

Open ranisalt opened 3 weeks ago

ranisalt commented 3 weeks ago

Pull Request Prelude

Changes Proposed

Moves player spells to data/scripts in order to reduce XML-coding. Monster spells, rune spells and house spells stay in XML for now :laughing:

EPuncker commented 3 weeks ago

nice, can we keep tab for indentation just to keep aligned with the other files? 😄 and also I see some spells were deleted accidentally? for example magic shield and cancel magic shield

ranisalt commented 3 weeks ago

nice, can we keep tab for indentation just to keep aligned with the other files?

It was formatted with luaformatter, I don't know why it used spaces but maybe we're missing a config file?

some spells were deleted accidentally? for example magic shield and cancel magic shield

oops

ranisalt commented 3 weeks ago

nice, can we keep tab for indentation just to keep aligned with the other files?

All files use spaces rather than tabs, are you sure about that? :thinking:

ranisalt commented 3 weeks ago

We can make loading not fail if XML is not present and drop the file altogether

ArturKnopik commented 3 weeks ago

We can make loading not fail if XML is not present and drop the file altogether

I we wont drop support for xml i preffer to keep this file with some samples (like revscript examples)

ranisalt commented 3 weeks ago

I we wont drop support for xml i preffer to keep this file with some samples (like revscript examples)

The goal is to drop XML

ArturKnopik commented 3 weeks ago

Droping XML support will break backward compatibility. As far as i know TFS want to be backward compat. PS: There is no automated script to convert all xml spells to revscript format.

ranisalt commented 3 weeks ago

Droping XML support will break backward compatibility. As far as i know TFS want to be backward compat.

It won't, we can supply a XML parser written in Lua that can be disabled at will. I will do that in a separate PR while disabling it in C++

There is no automated script to convert all xml spells to revscript format.

Indeed :laughing: I used this script written in Python to mass-convert them:

```python import xml.etree.ElementTree as ET from os import makedirs, path, unlink tree = ET.parse("spells.xml") root = tree.getroot() assert root.tag == "spells" for child in root: if "script" not in child.attrib: continue lines = [] for attrib in child.attrib: if attrib in ["script"]: continue match attrib.lower(): case "spellid": lines.append(f"spell:id({child.attrib[attrib]})") case "id" if child.tag == "rune": lines.append(f"spell:runeId({child.attrib[attrib]})") case "allowfaruse" if child.tag == "rune": if child.attrib[attrib] == "1": lines.append(f"spell:allowFarUse(true)") case "charges" if child.tag == "rune": lines.append(f"spell:charges({child.attrib[attrib]})") case "blocktype" if child.tag == "rune": match child.attrib[attrib]: case "all": lines.append(f"spell:isBlocking(true, true)") case "solid": lines.append(f"spell:isBlocking(true, false)") case "creature": lines.append(f"spell:isBlocking(false, true)") case "group": lines.append(f'spell:group("{child.attrib[attrib]}")') case "secondarygroup": lines = [line if not line.startswith("spell:group") else f'spell:group("{child.attrib["group"]}", "{child.attrib[attrib]}")' for line in lines] case "name": lines.append(f'spell:name("{child.attrib[attrib]}")') case "words": lines.append(f'spell:words("{child.attrib[attrib]}")') case "level" if child.tag == "spell": lines.append(f"spell:level({child.attrib[attrib]})") case "magiclevel" if child.tag == "spell": lines.append(f"spell:magicLevel({child.attrib[attrib]})") case "level" if child.tag == "rune": lines.append(f"spell:runeLevel({child.attrib[attrib]})") case "magiclevel" if child.tag == "rune": lines.append(f"spell:runeMagicLevel({child.attrib[attrib]})") case "mana": lines.append(f"spell:mana({child.attrib[attrib]})") case "soul": lines.append(f"spell:soul({child.attrib[attrib]})") case "premium": if child.attrib[attrib] == "0": lines.append(f"spell:isPremium(true)") case "direction": if child.attrib[attrib] == "1": lines.append(f"spell:needDirection(true)") case "aggressive": if child.attrib[attrib] == "0": lines.append(f"spell:isAggressive(false)") case "playernameparam": if child.attrib[attrib] == "1": lines.append(f"spell:hasPlayerNameParam(true)") case "params": if child.attrib[attrib] == "1": lines.append(f'spell:hasParams(true)') case "selftarget": if child.attrib[attrib] == "1": lines.append(f"spell:isSelfTarget(true)") case "pzlock" if child.tag == "rune": if child.attrib[attrib] == "1": lines.append(f"spell:isPzLocked(true)") case "range": lines.append(f"spell:range({child.attrib[attrib]})") case "castertargetordirection": if child.attrib[attrib] == "1": lines.append(f"spell:needCasterTargetOrDirection(true)") case "blockwalls": if child.attrib[attrib] == "1": lines.append(f"spell:blockWalls(true)") case "needtarget": if child.attrib[attrib] == "1": lines.append(f"spell:needTarget(true)") case "needweapon": if child.attrib[attrib] == "1": lines.append(f"spell:needWeapon(true)") case "cooldown": lines.append(f"spell:cooldown({child.attrib[attrib]})") case "groupcooldown": lines.append(f"spell:groupCooldown({child.attrib[attrib]})") case "secondarygroupcooldown": lines = [line if not line.startswith("spell:groupCooldown") else line[:-1] + f", {child.attrib[attrib]})" for line in lines] case "needlearn": if child.attrib[attrib] == "1": lines.append(f"spell:needLearn(true)") case _: print(f"Unknown attribute: {attrib}") with open(f"scripts/{child.attrib['script']}", "r") as f: spell = f.read().split("\n") voc_list = [] for voc in child: assert voc.tag == "vocation" voc_list.append((voc.attrib['name'].lower(), voc.attrib.get('showInDescription', '1') == '1')) if voc_list: lines.append(f'spell:vocation({", ".join(f'"{voc};true"' if show else f'"{voc}"' for voc, show in voc_list)})') for idx, line in enumerate(spell): if line.startswith("function onCastSpell"): break else: print(f"Spell {child.attrib['name']} does not have onCastSpell function") continue print(f"Converting spell {child.attrib['name']}") makedirs(f"../scripts/spells/{path.dirname(child.attrib['script'])}", exist_ok=True) with open(f"../scripts/spells/{child.attrib['script']}", "w") as f: f.write( "\n".join( [ *spell[:idx], f"local spell = Spell(SPELL_{child.tag.upper()})\n", *spell[idx:], *lines, "spell:register()", ] ) ) unlink(f"scripts/{child.attrib['script']}") ```
ArturKnopik commented 3 weeks ago

will be nice to add this script to repo for speedup migration from older version to latest