Een metin2 riemsysteem voegt een nieuwe uitrustingsslot (de belt slot) toe aan het uitrustingsvenster van de speler, plus een aparte kleine inventaris (de belt inventory) die opengaat zodra een riem is uitgerust. Je kunt er verbruiksvoorwerpen zoals drankjes of healthflessen in slepen voor snelle toegang tijdens het spelen, en het aantal bruikbare cellen groeit naarmate de riem wordt geüpgraded. In deze gids leg ik uit hoe ik een riem werkend krijg over drie lagen — item_proto, de server source en de client (Python UI).
De architectuur van het riemsysteem
De riem is geen enkel script; het werkt alleen als de drie lagen synchroon blijven. Ze moeten allemaal dezelfde constanten (het slotbereik) delen, anders "verdwijnen" voorwerpen of crasht de client:
- item_proto: definieert het type van het riemvoorwerp, de slot waarin het wordt uitgerust en hoeveel cellen het per grade ontgrendelt.
- Server source: een apart slotbereik voor de belt inventory, validatie van voorwerpplaatsing en de opslaglogica.
- Client (root/Python): de belt slot in het uitrustingsvenster en de grid-UI die opengaat wanneer een riem wordt gedragen.
In de meeste open-source releases zit het systeem achter een compileervlag, bijvoorbeeld ENABLE_BELT_INVENTORY_SYSTEM. Compileer je server en client niet met deze define ingeschakeld, dan komen de pakketstructuren niet overeen en worden spelers bij het inloggen losgekoppeld.
Een riemvoorwerp toevoegen aan item_proto
De riem is een eigen voorwerptype en wordt uitgerust in een speciale slot. Drie velden in de item_proto-vermelding zijn cruciaal: het type (ITEM_BELT), de draagvlag (WEARABLE_BELT) en in welke slot het past (WEAR_BELT). De grade (hoeveel cellen het ontgrendelt) wordt meestal opgeslagen in een waardeveld (value0):
-- als je de proto rechtstreeks in MySQL bewerkt is de logica:
-- type = ITEM_BELT (riemtype)
-- subtype = 0
-- wearflags = WEARABLE_BELT (draagbaar in de belt slot)
-- value0 = riemgrade (1,2,3,4 → ontgrendeld celniveau)
UPDATE item_proto
SET type = 'ITEM_BELT',
wearflags = 'WEARABLE_BELT',
value0 = 1 -- startgrade
WHERE vnum = 18000;
In Turkse/multi-source builds kunnen de velden ook via item_names.txt en item_proto.txt beheerd worden. Het kernpunt: het type van de riem is geen wapen of harnas maar een apart riemtype, zodat hij in de WEAR_BELT-slot past en de normale inventaris niet bezet. Hercompileer na het toevoegen de proto vanuit item_proto.txt (of werk de live-tabel bij) en herstart de game core.
Server source: het belt inventory-slotbereik
De belt inventory leeft in een apart adresbereik los van de normale inventaris en de uitrustingsslots. In de source wordt dit bereik met constanten gedefinieerd (bestand en namen verschillen per source, maar de logica is hetzelfde):
// in een header zoals service.h / item_length
#define BELT_INVENTORY_SLOT_START (INVENTORY_AND_EQUIP_SLOT_END)
#define BELT_INVENTORY_SLOT_COUNT 16
#define BELT_INVENTORY_SLOT_END (BELT_INVENTORY_SLOT_START + BELT_INVENTORY_SLOT_COUNT)
Een hulpfunctie die dit bereik herkent, wordt gebruikt bij de validatie van voorwerpplaatsing. Wanneer de game core beslist in welke cel een voorwerp mag, controleert hij of de slot binnen het riembereik valt:
bool CHARACTER::IsBeltInventorySlot(WORD wCell) const
{
return (wCell >= BELT_INVENTORY_SLOT_START &&
wCell < BELT_INVENTORY_SLOT_END);
}
// in de voorwerp-verplaatsvalidatie (char_item.cpp-achtig):
if (IsBeltInventorySlot(wDestCell))
{
// 1) Heeft de speler een riem uitgerust?
LPITEM belt = GetWear(WEAR_BELT);
if (!belt)
return false; // geen riem → cellen onbruikbaar
// 2) Is de doelcel ontgrendeld door de grade?
if (!IsValidBeltCell(belt, wDestCell))
return false; // kan niet in een vergrendelde cel
}
Beide controles zijn cruciaal: voorwerpen mogen niet in riemcellen wanneer geen riem wordt gedragen, en alleen de cellen die door de value0-grade van de riem zijn ontgrendeld mogen gebruikt worden. Sla je deze logica over, dan misbruiken spelers vergrendelde cellen voor gratis extra inventaris. Bij het afdoen van de riem moet de game core zijn voorwerpen terugplaatsen in de normale inventaris (of de actie weigeren als er geen ruimte is); anders worden de voorwerpen onbereikbaar.
Database en persistentie
Voorwerpen in de belt inventory worden net als elk ander voorwerp opgeslagen in de tabel player.item via de velden window en pos; alleen de pos-waarde valt binnen het riembereik. Let hier op Metin2's klassieke valkuil: zolang de speler online is, wordt de inventaris in het geheugen in de DB-cache-laag gehouden. Schrijf je riemvoorwerpen rechtstreeks naar MySQL, dan overschrijft de speler die bij het uitloggen met zijn geheugenkopie en zijn de voorwerpen weg. Voorwerpverplaatsingen moeten altijd via de game-core-API gaan, nooit via ruwe SQL. Het opslagschema verandert niet; het enige verschil is dat riemslots in het nieuwe pos-bereik worden geschreven.
Client: de belt slot en de grid-UI
Aan de clientkant zijn er twee toevoegingen. Ten eerste de belt slot in het uitrustingsvenster; ten tweede de belt inventory-grid die opengaat wanneer een riem wordt gedragen. In de Python UI-bestanden (uiInventory.py en het bijbehorende vensterscript) moet het slotbereik identiek aan de server worden gedefinieerd:
# in playerSettingModule / player.py
BELT_INVENTORY_SLOT_START = 0
BELT_INVENTORY_SLOT_COUNT = 16
BELT_INVENTORY_SLOT_END = BELT_INVENTORY_SLOT_START + BELT_INVENTORY_SLOT_COUNT
# toon/verberg de grid wanneer de riem is uitgerust
def OnEquipBelt(self, isEquipped):
if isEquipped:
self.beltInventoryGrid.Show()
else:
self.beltInventoryGrid.Hide()
Een zuinige regel voor de gridcellen: toon zoveel cellen "actief" als de grade van de riem (de waarde van de server) ontgrendelt, en de rest "vergrendeld". Slepen-en-neerzetten in vergrendelde cellen moet geblokkeerd worden. Vergeet ook niet de .tga-graphics van de UI en de celcoördinaten toe te voegen; de meeste sources bewaren die in een bestand belt_inventory_window.py. Tot slot is de open/dicht-animatie van de grid puur cosmetisch — voorwerpvalidatie gebeurt altijd op de server, vertrouw nooit de client.
De riem upgraden: cellen ontgrendelen
De aantrekkingskracht van de riem is dat hij upgradebaar is. De algemene aanpak is om de value0-grade van de riem te verhogen via een upgrade-NPC of een quest. Een eenvoudige grade-verhoging aan de questkant ziet er zo uit:
quest belt_upgrade begin
state start begin
when 18000.use begin
local belt = item.get_value(0) -- huidige grade
if belt >= 4 then
say("Je riem is al op de hoogste grade.")
return
end
-- controle van upgrademateriaal / yang hier
item.set_value(0, belt + 1)
say("Je riem is opgewaardeerd naar grade "..(belt+1).."!")
end
end
end
Wanneer de grade stijgt, telt de server meer riemcellen als "ontgrendeld" op basis van de nieuwe value0 van de uitgeruste riem en werkt de client bij. Houd het grade-maximum (4 in het voorbeeld) consistent zowel in de quest als in de IsValidBeltCell-logica van de source; als de twee verschillen, laat je óf cellen onnodig vergrendeld óf open je een misbruikbaar gat.
Veelgestelde vragen
De grid gaat open als ik een riem uitrust, maar voorwerpen verdwijnen bij het uitloggen — waarom?
De klassieke DB-cache-valkuil. Schrijf je riemvoorwerpen rechtstreeks naar MySQL, dan overschrijft de speler ze met zijn geheugenkopie bij het uitloggen. Verplaats voorwerpen altijd via de game-core-API en zorg dat de pos-waarde correct in het riembereik wordt geschreven.
Spelers kunnen riemcellen gebruiken zonder een riem te dragen.
De server-side GetWear(WEAR_BELT)-controle ontbreekt of is zwak. Valideer bij elke plaatsing in het riembereik eerst of een riem is uitgerust, daarna of de doelcel door de grade is ontgrendeld. Het slot aan de clientkant is alleen cosmetisch.
Ik heb het systeem ingeschakeld maar spelers worden bij het inloggen losgekoppeld.
Bijna altijd een mismatch in de ENABLE_BELT_INVENTORY_SYSTEM-vlag of de slotconstanten tussen server en client. Hercompileer beide met dezelfde definities; komen de pakketgroottes niet overeen, dan valt de sessie weg.
Wil je het riemsysteem netjes op je server geïnstalleerd hebben? Ik zet de belt inventory van begin tot eind op — van item_proto tot source- en clientintegratie, inclusief het stapsgewijs ontgrendelen van cellen. Laten we je project bespreken — neem contact met me op.