Jump to content

Headgear to Costume Headgear Trader


Tero

Recommended Posts


  • Group:  Members
  • Topic Count:  8
  • Topics Per Day:  0.01
  • Content Count:  58
  • Reputation:   112
  • Joined:  10/01/22
  • Last Seen:  

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 by Tero
Adding support for new rathena.
  • Like 1
Link to comment
Share on other sites

  • 3 weeks later...

  • Group:  Members
  • Topic Count:  8
  • Topics Per Day:  0.01
  • Content Count:  58
  • Reputation:   112
  • Joined:  10/01/22
  • Last Seen:  

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.

CostumeMonkHat.png.75fe5436b0763a7da6ecc26ed820dd54.png

Link to comment
Share on other sites

  • 9 months later...

  • Group:  Members
  • Topic Count:  12
  • Topics Per Day:  0.03
  • Content Count:  44
  • Reputation:   2
  • Joined:  02/23/23
  • Last Seen:  

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""?

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  8
  • Topics Per Day:  0.01
  • Content Count:  58
  • Reputation:   112
  • Joined:  10/01/22
  • Last Seen:  

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:

 

image.thumb.png.ab375d426b803df6eb833af72623b201.png

 

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).

Link to comment
Share on other sites

  • 4 weeks later...

  • Group:  Members
  • Topic Count:  0
  • Topics Per Day:  0
  • Content Count:  1
  • Reputation:   1
  • Joined:  01/04/22
  • Last Seen:  

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.

  • Upvote 1
Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  8
  • Topics Per Day:  0.01
  • Content Count:  58
  • Reputation:   112
  • Joined:  10/01/22
  • Last Seen:  

Thanks for finding that and testing out the potential solution.  I've added the fix to the first post.

Link to comment
Share on other sites

  • 5 months later...

  • Group:  Members
  • Topic Count:  0
  • Topics Per Day:  0
  • Content Count:  4
  • Reputation:   1
  • Joined:  05/18/16
  • Last Seen:  

Hi Tero,

This quick & easy source mod came in quite handy along with the provided script! Thank you for your contribution.

Kind regards,
NimbleStorm.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...