Tero Posted October 18, 2022 Group: Members Topic Count: 8 Topics Per Day: 0.01 Content Count: 65 Reputation: 131 Joined: 10/01/22 Last Seen: Monday at 05:23 AM Share Posted October 18, 2022 (edited) Someone on my server asked for this, and I saw that many people had been using an old script that doesn't work anymore, so I made a new one. If playing PRE-RE, you need to pull RE's costume headgears into your PRE-RE item database first. Open re's item-db and search for 19500, and copy all the items from there until 20499 into pre-re's item database, under costume system. This script also requires code changes to a few files, but they are purely additions, nothing needs to be changed. In script.cpp Near all of the other defs, you need this line: BUILDIN_DEF(getcostumeid,"i?"), You also need to add this function, anywhere in the class: /*========================================== * Returns the costume id associated with an equip slot, if one exists * return costume id number or -1 * getcostumeid(<equipment slot>{,<char_id>}) *------------------------------------------*/ BUILDIN_FUNC(getcostumeid) { int i, num; TBL_PC* sd; struct item_data* item; unsigned short returnid; if (!script_charid2sd(3, sd)) { script_pushint(st, -1); return SCRIPT_CMD_FAILURE; } num = script_getnum(st, 2); if (!equip_index_check(num)) { script_pushint(st, -1); return SCRIPT_CMD_SUCCESS; } // get inventory position of item i = pc_checkequip(sd, equip_bitmask[num]); if (i < 0) { script_pushint(st, -1); return SCRIPT_CMD_SUCCESS; } item = sd->inventory_data[i]; if (item != 0) { // search item db for an item with the same view id returnid = itemdb_findmatchinglook(item); script_pushint(st, returnid); } else script_pushint(st, -1); return SCRIPT_CMD_SUCCESS; } In itemdb.hpp: Add this near all the other method declarations: int itemdb_findmatchinglook(struct item_data* data); In itemdb.cpp: The solution you need here differs depending on the version of Rathena you have If you have newest Rathena, add this in the class somewhere: int itemdb_findmatchinglook(struct item_data* data) { for (auto &tmp_item : item_db) { std::shared_ptr<item_data> item = tmp_item.second; if (item->look == data->look && item->nameid > 19432 && item->nameid < 20500 && item->nameid != data->nameid) { return (int)item->nameid; } } return -1; } If you have an older version, add this somewhere in the class instead. static int itemdb_searchlook(DBKey key, DBData* data, va_list ap) { struct item_data* item = (struct item_data*)db_data2ptr(data), * tomatch, ** dst; tomatch = va_arg(ap, struct item_data*); dst = va_arg(ap, struct item_data**); //We want items that have the same look but not the same id if (dst != NULL && item->look == tomatch->look && item->nameid > 19432 && item->nameid < 20500 && item->nameid != tomatch->nameid) *dst = item; return 0; } int itemdb_findmatchinglook(struct item_data* data) { struct item_data* item = NULL; itemdb->foreach(itemdb, itemdb_searchlook, data, &item, NULL); if (item) { return (int)item->nameid; } else { return -1; } } Then just recompile and add the script to your npc folder and to script_athena.conf or any similar file. //===== rAthena Script ======================================= //= Costume Trader NPC //===== By: ================================================== //= Tero //===== Current Version: ===================================== //= 1.0 //===== Compatible With: ===================================== //= rAthena Project //===== Description: ========================================= //= Can trade headgears for costumes. //===== Additional Comments: ================================= //= //============================================================ prontera,145,211,4 script Costume Trader#prt 612,{ mes "[Costume Trader]"; mes "I'm the costume trader."; mes "I can trade your headgear for costume versions."; mes "These items don't affect your stats, so you can wear other headgear with them."; mes "Do you want to trade for a costume?"; next; switch(select("Trade for a costume:Cancel")) { case 1: setarray .@indices[1], EQI_HEAD_TOP, EQI_HEAD_MID, EQI_HEAD_LOW; for(.@i = 1; .@i<getarraysize(.@indices); ++.@i) { if(getequipisequiped(.@indices[.@i])) { .@menu$ = .@menu$ + F_getpositionname(.@indices[.@i]) + "-[" + getequipname(.@indices[.@i]) + "]"; .@equipped = 1; } .@menu$ = .@menu$ + ":"; } if (.@equipped == 0) { mes "[Costume Trader]"; mes "It doesn't seem you have anything you can trade."; close; } mes "[Costume Trader]"; mes "Here's what you're wearing right now."; next; .@part = .@indices[select(.@menu$)]; .@baseitemid = getequipid(.@part); .@costumeid = getcostumeid(.@part); if (.@costumeid < 19433 || .@costumeid > 20499 ) { mes "[Costume Trader]"; mes "Unfortunately, it doesn't seem like a costume exists for this item."; mes "I guess you'll just have to keep wearing it."; mes "I think it looks good on you at least."; close; } mes "[Costume Trader]"; mes "A costume is available for that item."; mes "If you trade, the costume will have no stats,"; mes "and any cards in it will be lost"; mes "Are you sure you want to trade?"; next; switch(select("Do the trade!:Cancel")) { case 1: // anti-hack if (callfunc("F_IsEquipIDHack", .@part, .@baseitemid)) { mes "[Costume Trader]"; emotion ET_FRET; mes "Wait a second..."; mes "Do you think I'm stupid?!"; mes "You switched the item while I wasn't looking! Get out of here!"; close; } delequip .@part; getitem .@costumeid, 1; mes "[Costume Trader]"; mes "Tada!"; mes "It's a shiny new costume item!"; mes "Aren't I amazing?"; mes "Come back again!"; close; case 2: mes "[Costume Trader]"; mes "That's too bad."; mes "But I understand you might be attached to it."; mes "Come back again."; close; } case 2: mes "[Costume Trader]"; mes "That's too bad."; mes "Collect more cute headgear and come back."; close; } } Enjoy costumes! Edited September 8, 2023 by Tero Adding support for new rathena. 1 Quote Link to comment Share on other sites More sharing options...
Tero Posted November 6, 2022 Group: Members Topic Count: 8 Topics Per Day: 0.01 Content Count: 65 Reputation: 131 Joined: 10/01/22 Last Seen: Monday at 05:23 AM Author Share Posted November 6, 2022 Additionally, while most headgear items are available as costumes, a few aren't. Should you find a headgear item you want that isn't available, it's easy to add it yourself. The code looks through item IDs 19433 to 20499 for costumes, so add your new items to gaps within this range to put them into the game. Let's take this example of the Monk Hat. It doesn't have a costume entry, so let's create one. If using item-db.txt: First, find another entry to use as a template. Let's use this one here for the Costume Angel Wing. Note that it's important to choose a headgear that uses the same slots as the item you want to create, or use SDE to update the slots for the item afterwards. 19529,C_Angelic_Chain,Costume Angel Wing,4,0,,0,,0,,0,0xFFFFFFFF,63,2,1024,,1,0,38,{},{},{} The costumes are all very similar, so we don't have to do much. The main thing we need to change is the View ID, which determines which sprite will be shown when wearing the costume. This is the last entry before the 3 sets of brackets. The Angel Wing has a view ID of 38. Now, we need to find the original entry for the Monk Hat. Here it is: 2251,Holy_Bonnet,Monk Hat,4,30000,,100,,5,,0,0x00008110,7,2,256,,0,1,35,{ bonus bMdef,3; },{},{} All we care about is the view ID, which is again before the 3 brackets. Its view ID is 35. Now, we just copy paste the Costume Angel Wing line, while making changes to the first number (the id), as well as the following two fields (the item's name) and changing the view ID, like so: 19434,C_Monk_Hat,Costume Monk Hat,4,0,,0,,0,,0,0xFFFFFFFF,63,2,1024,,0,0,35,{},{},{} Now we just paste that into the item-db and the Costume Monk Hat exists. If using item-db.yml The steps are the same if using the yaml version of the item-db, the formatting is just a little different. Again, let's find the Costume Angel wing to use as a template. - Id: 19529 AegisName: C_Angelic_Chain Name: Costume Angel Wing Type: Armor Locations: Costume_Head_Top: true ArmorLevel: 1 EquipLevelMin: 1 View: 38 Script: | bonus bUnbreakableHelm; bonus bVit,1; bonus bAgi,1; I note that apparently by default, this item had stats. I personally removed the stats from all costume items on my server, which is as easy as removing the "script" portion of this definition. Now, let's swap out the values for the Monk Hat, as above. - Id: 19434 AegisName: C_Monk_Hat Name: Costume Monk Hat Type: Armor Locations: Costume_Head_Top: true ArmorLevel: 1 EquipLevelMin: 1 View: 35 Paste this into item-db.yml and we're good to go. Updating the Description file: We're not quite done yet. While we can now trade for this item, it'll show up as "Unknown Item" due to the fact that it doesn't exist in iteminfo.lua. This is present in the System subfolder inside your client folder. There are entries for every item in the game. To start, let's copy the one for the non-costume Monk Hat. This is what my file has. [2251] = { unidentifiedDisplayName = "Hat", unidentifiedResourceName = "ĸ", unidentifiedDescriptionName = { "Unknown Item, can be identified by using a ^6666CCMagnifier^000000." }, identifiedDisplayName = "Monk Hat", identifiedResourceName = "¼ºÁ÷ÀÚÀǸðÀÚ", identifiedDescriptionName = { "A ceremonial hat worn by monks that contains a sacred force which offers protection from evil.", "Mdef +3", "Class:^6666CC Headgear^000000", "Defense:^0000FF 5^000000", "Position:^6666CC Upper^000000", "Weight:^009900 10^000000", "Jobs:^6666CC Acolyte^000000" }, slotCount = 0, ClassNum = 35 }, Now we just make a few small changes, by pulling some related values from another costume entry, to get something like this. [19434] = { unidentifiedDisplayName = "Hat", unidentifiedResourceName = "ĸ", unidentifiedDescriptionName = { "Unknown Item, can be identified by using a ^6666CCMagnifier^000000." }, identifiedDisplayName = "Costume Monk Hat", identifiedResourceName = "¼ºÁ÷ÀÚÀǸðÀÚ", identifiedDescriptionName = { "A ceremonial hat worn by monks that contains a sacred force which offers protection from evil.", "Class:^6666CC Costume^000000", "Position:^6666CC Upper^000000", "Weight:^009900 10^000000", "Jobs:^6666CC All^000000" }, slotCount = 0, ClassNum = 65 And we're done. The Monk Hat is now a fully functional costume item. You can use the same steps to add any other missing costume items that you come across. Quote Link to comment Share on other sites More sharing options...
playniks Posted August 12, 2023 Group: Members Topic Count: 14 Topics Per Day: 0.02 Content Count: 52 Reputation: 2 Joined: 02/23/23 Last Seen: Tuesday at 02:47 PM Share Posted August 12, 2023 On 10/18/2022 at 1:10 PM, Tero said: Someone on my server asked for this, and I saw that many people had been using an old script that doesn't work anymore, so I made a new one. If playing PRE-RE, you need to pull RE's costume headgears into your PRE-RE item database first. Open re's item-db and search for 19500, and copy all the items from there until 20499 into pre-re's item database, under costume system. This script also requires code changes to a few files, but they are purely additions, nothing needs to be changed. In script.cpp Near all of the other defs, you need this line: BUILDIN_DEF(getcostumeid,"i?"), You also need to add this function, anywhere in the class: /*========================================== * Returns the costume id associated with an equip slot, if one exists * return costume id number or -1 * getcostumeid(<equipment slot>{,<char_id>}) *------------------------------------------*/ BUILDIN_FUNC(getcostumeid) { int i, num; TBL_PC* sd; struct item_data* item; unsigned short returnid; if (!script_charid2sd(3, sd)) { script_pushint(st, -1); return SCRIPT_CMD_FAILURE; } num = script_getnum(st, 2); if (!equip_index_check(num)) { script_pushint(st, -1); return SCRIPT_CMD_SUCCESS; } // get inventory position of item i = pc_checkequip(sd, equip_bitmask[num]); if (i < 0) { script_pushint(st, -1); return SCRIPT_CMD_SUCCESS; } item = sd->inventory_data[i]; if (item != 0) { // search item db for an item with the same view id returnid = itemdb_findmatchinglook(item); script_pushint(st, returnid); } else script_pushint(st, -1); return SCRIPT_CMD_SUCCESS; } In itemdb.hpp: Add this near all the other method declarations: int itemdb_findmatchinglook(struct item_data* data); In itemdb.cpp: Add this somewhere in the class. static int itemdb_searchlook(DBKey key, DBData* data, va_list ap) { struct item_data* item = (struct item_data*)db_data2ptr(data), * tomatch, ** dst; tomatch = va_arg(ap, struct item_data*); dst = va_arg(ap, struct item_data**); //We want items that have the same look but not the same id if (dst != NULL && item->look == tomatch->look && item->nameid > 19432 && item->nameid < 20500 && item->nameid != tomatch->nameid) *dst = item; return 0; } int itemdb_findmatchinglook(struct item_data* data) { struct item_data* item = NULL; itemdb->foreach(itemdb, itemdb_searchlook, data, &item, NULL); if (item) { return (int)item->nameid; } else { return -1; } } Then just recompile and add the script to your npc folder and to script_athena.conf or any similar file. //===== rAthena Script ======================================= //= Costume Trader NPC //===== By: ================================================== //= Tero //===== Current Version: ===================================== //= 1.0 //===== Compatible With: ===================================== //= rAthena Project //===== Description: ========================================= //= Can trade headgears for costumes. //===== Additional Comments: ================================= //= //============================================================ prontera,145,211,4 script Costume Trader#prt 612,{ mes "[Costume Trader]"; mes "I'm the costume trader."; mes "I can trade your headgear for costume versions."; mes "These items don't affect your stats, so you can wear other headgear with them."; mes "Do you want to trade for a costume?"; next; switch(select("Trade for a costume:Cancel")) { case 1: setarray .@indices[1], EQI_HEAD_TOP, EQI_HEAD_MID, EQI_HEAD_LOW; for(.@i = 1; .@i<getarraysize(.@indices); ++.@i) { if(getequipisequiped(.@indices[.@i])) { .@menu$ = .@menu$ + F_getpositionname(.@indices[.@i]) + "-[" + getequipname(.@indices[.@i]) + "]"; .@equipped = 1; } .@menu$ = .@menu$ + ":"; } if (.@equipped == 0) { mes "[Costume Trader]"; mes "It doesn't seem you have anything you can trade."; close; } mes "[Costume Trader]"; mes "Here's what you're wearing right now."; next; .@part = .@indices[select(.@menu$)]; .@baseitemid = getequipid(.@part); .@costumeid = getcostumeid(.@part); if (.@costumeid < 19433 || .@costumeid > 20499 ) { mes "[Costume Trader]"; mes "Unfortunately, it doesn't seem like a costume exists for this item."; mes "I guess you'll just have to keep wearing it."; mes "I think it looks good on you at least."; close; } mes "[Costume Trader]"; mes "A costume is available for that item."; mes "If you trade, the costume will have no stats,"; mes "and any cards in it will be lost"; mes "Are you sure you want to trade?"; next; switch(select("Do the trade!:Cancel")) { case 1: // anti-hack if (callfunc("F_IsEquipIDHack", .@part, .@baseitemid)) { mes "[Costume Trader]"; emotion ET_FRET; mes "Wait a second..."; mes "Do you think I'm stupid?!"; mes "You switched the item while I wasn't looking! Get out of here!"; close; } delequip .@part; getitem .@costumeid, 1; mes "[Costume Trader]"; mes "Tada!"; mes "It's a shiny new costume item!"; mes "Aren't I amazing?"; mes "Come back again!"; close; case 2: mes "[Costume Trader]"; mes "That's too bad."; mes "But I understand you might be attached to it."; mes "Come back again."; close; } case 2: mes "[Costume Trader]"; mes "That's too bad."; mes "Collect more cute headgear and come back."; close; } } Enjoy costumes! Hi, there sir @Tero! Good day! Does this convert the item into costume while disabling its ""item script bonus"" but not removing its ""item sockets""? Quote Link to comment Share on other sites More sharing options...
Tero Posted August 14, 2023 Group: Members Topic Count: 8 Topics Per Day: 0.01 Content Count: 65 Reputation: 131 Joined: 10/01/22 Last Seen: Monday at 05:23 AM Author Share Posted August 14, 2023 The way it works on a technical level is that it trades your item for an item in the range of 19432-20500 that has an equivalent view id. Let's look at a quick example in SDE: As you can see, the view ID for both of these items is 169, and Costume Small Ribbons is between 19432-20500, so it gives you this item if you give it Small Ribbons. Note that it does not modify the contents of your item database in any way, so if you had a script attached to Costume Small Ribbons, it would execute, but I believe the default item DB does not have any scripts attached to costume headgear (you could always take a quick look through the Item DB just in case if you're worried). Quote Link to comment Share on other sites More sharing options...
tritonium Posted September 7, 2023 Group: Members Topic Count: 0 Topics Per Day: 0 Content Count: 1 Reputation: 1 Joined: 01/04/22 Last Seen: May 23, 2024 Share Posted September 7, 2023 On 10/18/2022 at 5:10 AM, Tero said: int itemdb_findmatchinglook(struct item_data* data) { struct item_data* item = NULL; itemdb->foreach(itemdb, itemdb_searchlook, data, &item, NULL); if (item) { return (int)item->nameid; } else { return -1; } } putting this in causes a compilation error with itemdb.cpp:3830:9: error: ‘itemdb’ was not declared in this scope; did you mean ‘item_db’? 3830 | itemdb->foreach(itemdb, itemdb_searchlook, data, &item, NULL); | ^~~~~~ | item_db trying that item_db doesn't have a foreach property and nothing else passed into the method does. I am on the latest rathena. Just want to see if I can get this to work since it looks like a cool idea. 1 Quote Link to comment Share on other sites More sharing options...
Tero Posted September 8, 2023 Group: Members Topic Count: 8 Topics Per Day: 0.01 Content Count: 65 Reputation: 131 Joined: 10/01/22 Last Seen: Monday at 05:23 AM Author Share Posted September 8, 2023 Thanks for finding that and testing out the potential solution. I've added the fix to the first post. Quote Link to comment Share on other sites More sharing options...
NimbleStorm Posted February 18, 2024 Group: Members Topic Count: 0 Topics Per Day: 0 Content Count: 4 Reputation: 1 Joined: 05/18/16 Last Seen: April 9 Share Posted February 18, 2024 Hi Tero, This quick & easy source mod came in quite handy along with the provided script! Thank you for your contribution. Kind regards, NimbleStorm. Quote Link to comment Share on other sites More sharing options...
Mister Noob Posted September 17, 2024 Group: Members Topic Count: 1 Topics Per Day: 0.00 Content Count: 9 Reputation: 0 Joined: 12/11/19 Last Seen: April 9 Share Posted September 17, 2024 What if I wanted an option to return the headgear to it's original form. Can you help with that @Tero? Thank you. I was trying to play with it but I can't figure it out Quote Link to comment Share on other sites More sharing options...
Tero Posted September 18, 2024 Group: Members Topic Count: 8 Topics Per Day: 0.01 Content Count: 65 Reputation: 131 Joined: 10/01/22 Last Seen: Monday at 05:23 AM Author Share Posted September 18, 2024 So, for a variety of reasons that's a bit harder. In theory it would basically just be the reverse operation, where you would try to find a matching item by look id that ISN'T within the rejoin where the costume items are, but there's some extra challenges here. So the basic gist of it would be you'd need a method that basically does something like this: int itemdb_findmatchingnoncostume(struct item_data* data) { for (auto &tmp_item : item_db) { std::shared_ptr<item_data> item = tmp_item.second; if (item->look == data->look && item->nameid < 19432 && item->nameid > 20500 && item->nameid != data->nameid) { return (int)item->nameid; } } return -1; } Then just add a new option in the script that starts with a costume item and calls that method to find the item that gives you. The challenge though is that while there's generally only a single costume item having a given look id, this is not the case with regular items. This is essentially the reason my implementation specifically doesn't support it. For example, consider a simple Helm. Both item ids 2228 and 2229 are called Helm and have look id 40. There's of course also Costume Helm, which is id 19526. Why are there two Helms? The first is a 0-slot helm, and the second is a 1-slot helm. The problem is, either helm will convert into Costume Helm, and the information about what slots it had is completely lost, so we don't know which one to choose when going back. You could simply always choose the first one in the DB (the solution above works this way), I think this will generally prevent abuse. Realistically, people probably aren't going to socket enchant an item, then turn it into a costume, so maybe returning the first item is fine. But by that same token, most items you get back from the transition probably won't be highly valuable items, so it may be a lot of work for a feature not too many people will actually use. Quote Link to comment Share on other sites More sharing options...
Mister Noob Posted September 23, 2024 Group: Members Topic Count: 1 Topics Per Day: 0.00 Content Count: 9 Reputation: 0 Joined: 12/11/19 Last Seen: April 9 Share Posted September 23, 2024 On 9/19/2024 at 1:32 AM, Tero said: Then just add a new option in the script that starts with a costume item and calls that method to find the item that gives you. Thank you for your reply. The solution you gave would work on my purpose since I'll just instead put a restriction to change the item to costume if they are trying to exchange a headgear with slot. Can you help me make a script for this part though? i'm trying to figure it out but I can't seem to make it work lol Quote Link to comment Share on other sites More sharing options...
Tero Posted September 24, 2024 Group: Members Topic Count: 8 Topics Per Day: 0.01 Content Count: 65 Reputation: 131 Joined: 10/01/22 Last Seen: Monday at 05:23 AM Author Share Posted September 24, 2024 I tried for like an hour, I couldn't get it working either. The issue was trying to list what costume items you have equipped, getequipisequiped doesn't seem to work properly if the slot is a costume slot. This is unchanged in latest rathena and I wasn't able to figure out why it's not returning the right values, so I'm stuck. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.