Jump to content

GmOcean

Community Contributors
  • Content Count

    371
  • Joined

  • Last visited

  • Days Won

    8

Posts posted by GmOcean


  1. I believe there was a client patch released a while ago that disables those messages, since it's in the official client. Coding the 0exp and 0drop wouldn't be that hard to do. Just add it as a permission and change the player's permission until they logout.


  2. Yeah, but any time a player receives damage, the client needs to update that information. By default it uses those clif->hp to do those updates, if I were to add the variable checks to those clif's it would interfere with the proper update because it would push for SP update instead. Also, it's better to use custom Clifs for modifications like this so as to not completely interfere with the way existing code works.

     

    Edit: Now that I re-think about it, you could be right about just adding the check to clif->hp, but since creating new clifs doesn't impact the way it works I'll use that since it makes things easier to read when looking at the code as a whole.


  3. It'd cause issues down the line with battle.c for use with skills that do HP/SP ratios to calculate damage. And yeah, @showsp just switches the variable. Once this issue of updating properly is fixed, i'll just create a custom state and switch that around so as not to rely on player variables. Also i believe a player's state is reset upon relogging which is what i'd like to have this do without the need of OnPCLoginEvent.


  4. I've been asking for assistance quite a bit lately, forgive me xD

     

    Okay, let's start off with, I know there currently isn't any packets that support the display of party member's SP bar(blue). So I decided to settle with copying the clif->hpdisplay and making SP versions, to just update the HP with SP status information.

    This is all done by performing a check of a player's variable and if yes update with SP if no, update with HP. So far it works.... but backwards @.@;

     

    What I want it to do is, if a player turns @showsp on. Then the HP bars of his/her party members will be updated with SP information. Then when a party member who has @showsp off, will only see HP bars with HP information. That means, even the HP of the player using @showsp on.

     

    But, what I managed to do was this:

    1. Player A uses @showsp on.

    2. Player B uses @showsp off.

    3. Player A uses a skill.

    4. Player B see's his HP bar drop. Pressing Alt+Z will also display the same information. (This is the problem. Player B should only see the HP information).

    5. Player B uses a skill.

    6. Player A doesn't see a change in the bar because Player B isn't showing SP. (Again this is a problem. Player A should see an update because he is requesting to see SP information).

     

    I don't know where I went wrong with this patch, but i'll post what changes I made.

    ( Don't mind the buybacklist clif added in there. It is completely irrelevant to this, and also doesn't interfere with it =P )

    showsp.patch


  5. This isn't a fix for issues regarding adding items into the game incorrectly, but rather it's a bit of code added in, that creates dummy information for items that just don't exist. For instance creating an item in itemdb2.conf but not adding any information on the client side. This will let the server give you an item, without having a sprite, since it will default to the apple, and call it unknown item and such.


  6. Okay, so we all know that if we obtain an item that doesn't exist, it gives us a " dummy " item.

    However, through all my attempts I was never able to change that information.

    void create_dummy_data(void){	memset(&itemdb->dummy, 0, sizeof(struct item_data));	itemdb->dummy.nameid=500;	itemdb->dummy.weight=1;	itemdb->dummy.value_sell=1;	itemdb->dummy.type=IT_ETC; //Etc item	safestrncpy(itemdb->dummy.name,"UNKNOWN_ITEM",sizeof(itemdb->dummy.name));	safestrncpy(itemdb->dummy.jname,"UNKNOWN_ITEM",sizeof(itemdb->dummy.jname));	itemdb->dummy.view_id=UNKNOWN_ITEM_ID;}

    essentially, that holds all the information of the dummy item. But even if I change the name of that dummy item, it doesn't update to what I set it as. It either becomes, Apple or Unknown Item. It doesn't even have the _ in its name.

     

    So is there something I'm missing from all of this? Or is it hardcoded else where? Because, theoretically configuring this data and all sources that control it's creation, dynamic item creation should be possible. Unless I'm COMPLETELY missing something of importance here. Any thoughts?


  7. Well, this program works fine for me. Don't seem to have any of the issues other people are having. I just 2 questions though....

    1. Any idea when you might be feeling upto releasing the packet to change name colors?

    and

    2. If you managed to find those, then perhaps you'd be able to find the one that allows the client to change the color of item names. (The one that happens when using 3-4cards on items, binding items, etc....).

     

    Other than that, I love the simplicity this program adds when diffing clients. It was always a hassle having to find the .diff file for your specific client and modify it to only patch certain things. But NEMO makes it 100million times easier. Absolutely love it. Made upgrading my client from 2010 -> 2013 -> 2014 very easy and issue free.


  8. Default Kafra already does this. Here is a small sample of when trying to open storage.

    // Accessing Normal Storage (Skipped if accessing Storage from Guild castle)	if (getarg(0) != 2) {		// Consume "Free Ticket for Kafra Storage" if available.		if (countitem(Cargo_Free_Ticket)) {			delitem Cargo_Free_Ticket, 1;		} else {			if (Zeny < getarg(1)) {				mes getarg(3);				// Niflheim Specific Message				if (getarg(2) == 1) {					percentheal -50,-50;					mes "^666666Zeeeeeny...";					mes "M-more z-zeny...!";					mes "N-neeed 150... zeny...";					mes "Ergh! T-taking bl-blood~!^000000";					return;				}				// Standard Message				mes "I'm sorry, but you don't";				mes "have enough zeny to use";				mes "the Storage Service. Our";				mes "Storage access fee is "+getarg(1)+" zeny.";				return;			}			Zeny -= getarg(1);			if (getarg(2) != 6)				RESRVPTS += getarg(1) / 5;		}	}

    If you look closely you'll see this:

    		// Consume "Free Ticket for Kafra Storage" if available.		if (countitem(Cargo_Free_Ticket)) {			delitem Cargo_Free_Ticket, 1;

    That tells it to take a ticket instead of paying zeny, should they have 1 in their inventory.


  9. There already programs / bots that can answer reCaptcha successfully 100% of the time. All openkore needs to do is borrow that code or hook into those programs. Very easily done for a group of people dedicated to making these bots work.


  10. Oh? I like the sound of this.  I never really used this because it was limited to ratio drop rate. But with a fixed rate option that'd be useful, I could remove the use of the scripts I use to simulate it.

    But, I feel that this would still be missing 1 more optional field: Mapname.

    So that we can specify What Item, and what rate, that rate, the monster, and the map that it should only affect.

     

    To use the same example of above:

    909,0,100,1002 // Jellopies from ALL Porings will drop with a 1x ratio drop rate.

    909,1,100,1002, pay_fild04 // Jellopies from Porings on the map pay_fild04 will drop with a 0.1% fixed drop rate.


  11. Okay, what it does is this:

    1. Any items you sell to an NPC ( A normal NPC doesn't support NPCs used through CallShop ), will get added to a SQL table.

    2. When you use CallShop npc_name, 3; it will give those items back to you with an expiration date and bind them to your player.

    3. It will then open a Sell window (sadly buy windows don't support detailed items) and then create a list of items based off of those that are both BOUND to your character and have an expiration time. ( Currently not possible unless you use my command ).

    4. After that, you just choose the items you want to buy, and click SELL ( yeah lol, I know T.T ), and it'll give you those items back at the cost of the zeny.

    5. The items you don't buy, will expire. And the items you did buy will be removed from the SQL table OR updated to show the new amount.

     

    Lastly, the items you buy will be a permanent version, unbound and with no expiration time. If you use the sample script and leave the logout_clear, then it will delete all records of that character from the SQL table. ( Most MMO's do this so I like it xD ).

     

    Edit: I'll post back with ScreenShots when I get time... For now DINNER !!


  12. You could edit:

    /// Response to the death/system menu (CZ_RESTART)./// 00b2 <type>.B/// type:///     0 = restart (respawn)///     1 = char-select (disconnect)void clif_parse_Restart(int fd, struct map_session_data *sd) {	switch(RFIFOB(fd,2)) {		case 0x00:			pc->respawn(sd,CLR_OUTSIGHT);			break;		case 0x01:			/*	Rovert's Prevent logout option - Fixed [Valaris]	*/			if( !sd->sc.data[SC_CLOAKING] && !sd->sc.data[SC_HIDING] && !sd->sc.data[SC_CHASEWALK] && !sd->sc.data[SC_CLOAKINGEXCEED] && !sd->sc.data[SC__INVISIBILITY] &&				(!battle_config.prevent_logout || DIFF_TICK(timer->gettick(), sd->canlog_tick) > battle_config.prevent_logout) )			{	//Send to char-server for character selection.				chrif->charselectreq(sd, session[fd]->client_addr);			} else {				clif->disconnect_ack(sd, 1);			}			break;	}}

    To just not work in BG's or resurrect you at that spot.

    void clif_parse_Restart(int fd, struct map_session_data *sd) {	switch(RFIFOB(fd,2)) {		case 0x00:			if( sd->bg_id ) {				if( !pc_isdead(sd) )					return;				status->revive(&sd->bl, 100, 100);				clif->skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1);				break;			} else {			pc->respawn(sd,CLR_OUTSIGHT);			break;			}		case 0x01:			/*	Rovert's Prevent logout option - Fixed [Valaris]	*/			if( !sd->sc.data[SC_CLOAKING] && !sd->sc.data[SC_HIDING] && !sd->sc.data[SC_CHASEWALK] && !sd->sc.data[SC_CLOAKINGEXCEED] && !sd->sc.data[SC__INVISIBILITY] &&				(!battle_config.prevent_logout || DIFF_TICK(timer->gettick(), sd->canlog_tick) > battle_config.prevent_logout) )			{	//Send to char-server for character selection.				chrif->charselectreq(sd, session[fd]->client_addr);			} else {				clif->disconnect_ack(sd, 1);			}			break;	}}

    You just gotta add the 10second timer there so if they do CLICK the button, it'll need to wait 10seconds D:


  13. Okay, this is officially done. Unless of course you see something else that needs to get done or should be changed? At any rate, thank you for all your help Annieruru @.@; I absolutely could not have done this without your help. And definitely not within the time that I did do this in, despite how long it took lmao. Though, since this seems like a rigged patch to make something work, I don't think I'll be making an official src release for it. Perhaps I'll just let the src code sit here D:

     

    Especially since there are a few hiccups that need to be fixed. Such as the issues you ran into when you tried a few years ago:

    Inventory space, weight, drop item on floor... etc...

     

    But these can all be handled through the script side.


  14. See the First post to get the patch file.

     

    *Hiding this post in spoiler since patch is uploaded. But gonna keep this information here*

     

    D: I think it's a well timed attempt for my first ever real source mod lol. But yeah, it's taking a lot longer than I thought, but that's mainly due to me wanting it all to be source side. Honestly, where it is at, I can easily just remove the limitations I put on it in the source, and allow it to function with the OnSellItem and OnBuyItem labels, and do the bulky half of the job with script. As it would be A LOT easier. But, I feel that it'd be a half-lazy mod if I did it like that :/.

     

    Also, if I remove the 10 item limit the SQL script will run twice as fast, if not 3x faster. Since it won't have to do a double check and just insert. I'm thinking this might be the better idea, since I can limit the items given back on the script side. But the main reason I added the limit to begin with is, when giving the items to the player, their inventory could be nearly full, don't wanna give them 100 items if they can only hold like 20 lol. I'll post back with a few changes. It'll probably the last modification I do to it. After that, it'll be up to you or whomever wishes to use it to make the changes they want to it.

     

    Edit:

    My bad lol, I completely forgot to post back @.@ haha.

    CLIF.C

    void clif_buybacklist(struct map_session_data *sd){	int fd,i,c=0,val;	nullpo_retv(sd);	fd=sd->fd;	WFIFOHEAD(fd, MAX_INVENTORY * 10 + 4);	WFIFOW(fd,0)=0xc7;	for( i = 0; i < MAX_INVENTORY; i++ )	{		if( sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] && sd->status.inventory[i].expire_time && sd->status.inventory[i].bound )		{			if( !itemdb_cansell(&sd->status.inventory[i], pc_get_group_level(sd)) )				continue;						//This allows the selling(Buyback) of items that have both an expiration time and are bound to the player ONLY. (Normally not possible).						val=sd->inventory_data[i]->value_buy;			if( val < 0 )				continue;			WFIFOW(fd,4+c*10)=i+2;			WFIFOL(fd,6+c*10)=val;			WFIFOL(fd,10+c*10)=pc->modifysellvalue(sd,val); //Can I remove this if my npc.c data doesn't use it?			c++;		}	}	WFIFOW(fd,2)=c*10+4;	WFIFOSET(fd,WFIFOW(fd,2));}
    clif->buybacklist = clif_buybacklist;

     

     

    NPC.C

    int npc_buysellsel(struct map_session_data* sd, int id, int type) {	struct npc_data *nd;	nullpo_retr(1, sd);	if ((nd = npc->checknear(sd,map->id2bl(id))) == NULL)		return 1;	if ( nd->subtype != SHOP && !(nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->items) ) {				if( nd->subtype == SCRIPT )			ShowError("npc_buysellsel: trader '%s' has no shop list!n",nd->exname);		else			ShowError("npc_buysellsel: no such shop npc %d (%s)n",id,nd->exname);		 		if (sd->npc_id == id)			sd->npc_id = 0;		return 1;	}    	if (nd->option & OPTION_INVISIBLE) // can't buy if npc is not visible (hack?)		return 1;		if( nd->class_ < 0 && !sd->state.callshop ) {// not called through a script and is not a visible NPC so an invalid call		return 1;	}	// reset the callshop state for future calls	sd->state.callshop = 0;	sd->npc_shopid = id;	if (type==0) {		clif->buylist(sd,nd);	} 	if (type==1) {		clif->selllist(sd);	} 	if (type==2) {		clif->buybacklist(sd);		pc->setregistry(sd, script->add_str( "buyback" ), 1); // Set buyback player variable to 1 to trigger buyback feature in shops.	}		return 0;}
    int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) {	double z;	int i,skill_t, skill_idx = skill->get_index(MC_OVERCHARGE);	struct npc_data *nd;	int w, new_; //BuyBack Feature	nullpo_retr(1, sd);	nullpo_retr(1, item_list);	if( ( nd = npc->checknear(sd, map->id2bl(sd->npc_shopid)) ) == NULL ) {		return 1;	}	if( nd->subtype != SHOP ) {		if( !(nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->type == NST_ZENY) )			return 1;	}	if( pc_readglobalreg( sd, script->add_str( "buyback" ) ) ) {		w = 0;		new_ = 0;	}	z = 0;	// verify the sell list	for( i = 0; i < n; i++ ) {		int nameid, amount, idx, value;		idx    = item_list[i*2]-2;		amount = item_list[i*2+1];		if( idx >= MAX_INVENTORY || idx < 0 || amount < 0 ) {			return 1;		}		nameid = sd->status.inventory[idx].nameid;		if( !nameid || !sd->inventory_data[idx] || sd->status.inventory[idx].amount < amount ) {			return 1;		}		if( !pc_readglobalreg( sd, script->add_str( "buyback" ) ) ) { //Buyback feature doesn't support script-controlled shops. This is intended.			if( nd->master_nd ) {// Script-controlled shops decide by themselves, what can be sold and at what price.				continue;			}		}		if( pc_readglobalreg( sd, script->add_str( "buyback" ) ) ) {			value = sd->inventory_data[idx]->value_buy; //No Discount when buying back.			w+= itemdb_weight(nameid) * amount;		} else {			value = pc->modifysellvalue(sd, sd->inventory_data[idx]->value_sell);		}		z+= (double)value*amount;	}	if( !pc_readglobalreg( sd, script->add_str( "buyback" ) ) ) { //Only works if NOT BuyBack Feature this is intended.		if( nd->master_nd ) { // Script-controlled shops			return npc->selllist_sub(sd, n, item_list, nd->master_nd);		}	}	if( pc_readglobalreg( sd, script->add_str( "buyback" ) ) ) {				if( z > (double)sd->status.zeny )			return 1; // Not Enough Zeny		if( w + sd->weight > sd->max_weight )			return 2; // Too heavy		if( pc->inventoryblank(sd) < new_ )			return 3; // Not enough space to store items	pc->payzeny(sd,(int)z,LOG_TYPE_NPC, NULL);		// add items		for( i = 0; i < n; i++ ) {			int amount, idx;			struct item it;			idx    = item_list[i*2]-2;			amount = item_list[i*2+1];			it.nameid = sd->status.inventory[idx].nameid;			it.amount = sd->status.inventory[idx].amount;			it.equip = 0; //Don't equip.			it.identify = sd->status.inventory[idx].identify;			it.refine = sd->status.inventory[idx].refine;			it.attribute = sd->status.inventory[idx].attribute;			it.card[0] = sd->status.inventory[idx].card[0];			it.card[1] = sd->status.inventory[idx].card[1];			it.card[2] = sd->status.inventory[idx].card[2];			it.card[3] = sd->status.inventory[idx].card[3];			it.expire_time = sd->status.inventory[idx].expire_time; // Normally can't sell Rental Items.			it.bound = sd->status.inventory[idx].bound; // Normally can't sell Bound Items.			//Do the below so player has enough inventory space. (Probably not needed since we do the check above).			if( it.amount == amount ) { // Check to see if they are buying ALL of that item. If so, DELETE entry from SQL table.                if( SQL->Query( map->mysql_handle, "DELETE FROM buyback WHERE char_id = %d AND nameid = %d AND amount = %d AND identify = %d AND refine = %d AND attribute = %d AND card1 = %d AND card2 = %d AND card3 = %d AND card4 = %d AND bound = %d AND expire_time = %d",                    sd->status.char_id,                    it.nameid,//sd->status.inventory[idx].nameid,                    it.amount,//sd->status.inventory[idx].amount,                    it.identify,//sd->status.inventory[idx].identify,                    it.refine,//sd->status.inventory[idx].refine,                    it.attribute,//sd->status.inventory[idx].attribute,                    it.card[0],//sd->status.inventory[idx].card[1],                    it.card[1],//sd->status.inventory[idx].card[2],                    it.card[2],//sd->status.inventory[idx].card[3],                    it.card[3],//sd->status.inventory[idx].card[4],                    0, // Should be 0 due to being unable to sell Bound Items normally                    0 // Should be 0 due to being unable to sell Rental Items normally                    ) == SQL_ERROR ) { Sql_ShowDebug( map->mysql_handle ); }            } else { // If they did not buy ALL of the item, UPDATE SQL table to display new amount.                if( SQL->Query( map->mysql_handle, "UPDATE buyback SET amount = %d WHERE char_id = %d AND nameid = %d AND amount = %d AND identify = %d AND refine = %d AND attribute = %d AND card1 = %d AND card2 = %d AND card3 = %d AND card4 = %d AND bound = %d AND expire_time = %d",                    amount,                    sd->status.char_id,                    it.nameid,//sd->status.inventory[idx].nameid,                    it.amount,//sd->status.inventory[idx].amount,                    it.identify,//sd->status.inventory[idx].identify,                    it.refine,//sd->status.inventory[idx].refine,                    it.attribute,//sd->status.inventory[idx].attribute,                    it.card[0],//sd->status.inventory[idx].card[1],                    it.card[1],//sd->status.inventory[idx].card[2],                    it.card[2],//sd->status.inventory[idx].card[3],                    it.card[3],//sd->status.inventory[idx].card[4],                    0, // Should be 0 due to being unable to sell Bound Items normally                    0 // Should be 0 due to being unable to sell Rental Items normally                    ) == SQL_ERROR ) { Sql_ShowDebug( map->mysql_handle ); }            }			pc->delitem(sd, idx, amount, 0, 6, LOG_TYPE_NPC); //Delete the Item With Expiration & Bound first.			it.expire_time = it.bound = 0; //Set Expiration and Bound to 0			pc->additem(sd, &it, amount, LOG_TYPE_NPC); // Add the item to inventory without Expiration and Bound		}	} else {	// delete items	for( i = 0; i < n; i++ ) {		int amount, idx; 		idx    = item_list[i*2]-2;		amount = item_list[i*2+1];		if( sd->inventory_data[idx]->type == IT_PETEGG && sd->status.inventory[idx].card[0] == CARD0_PET ) {			if( pet->search_petDB_index(sd->status.inventory[idx].nameid, PET_EGG) >= 0 ) {				intif->delete_petdata(MakeDWord(sd->status.inventory[idx].card[1], sd->status.inventory[idx].card[2]));			}		}		//Store the Item into a SQL Table for SQL to use. (Need to figure out how to limit to last 10).		if( SQL->Query( map->mysql_handle, "INSERT INTO buyback ( char_id, nameid, amount, identify, refine, attribute, card1, card2, card3, card4, bound, expire_time ) VALUES ( %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d )",			sd->status.char_id,			sd->status.inventory[idx].nameid,			sd->status.inventory[idx].amount,			sd->status.inventory[idx].identify,			sd->status.inventory[idx].refine,			sd->status.inventory[idx].attribute,			sd->status.inventory[idx].card[0],			sd->status.inventory[idx].card[1],			sd->status.inventory[idx].card[2],			sd->status.inventory[idx].card[3],			sd->status.inventory[idx].bound,			sd->status.inventory[idx].expire_time			) == SQL_ERROR ) { Sql_ShowDebug( map->mysql_handle ); }		pc->delitem(sd, idx, amount, 0, 6, LOG_TYPE_NPC);	}	if( z > MAX_ZENY )		z = MAX_ZENY;	pc->getzeny(sd, (int)z, LOG_TYPE_NPC, NULL);	if( !pc_readglobalreg( sd, script->add_str( "buyback" ) ) ) { //BuyBack feature doesn't support shop EXP gain of any kind. This is an intended feature.		// custom merchant shop exp bonus		if( battle_config.shop_exp > 0 && z > 0 && ( skill_t = pc->checkskill2(sd,skill_idx) ) > 0) {			if( sd->status.skill[skill_idx].flag >= SKILL_FLAG_REPLACED_LV_0 )				skill_t = sd->status.skill[skill_idx].flag - SKILL_FLAG_REPLACED_LV_0;			if( skill_t > 0 ) {				z = z * (double)skill_t * (double)battle_config.shop_exp/10000.;				if( z < 1 )					z = 1;				pc->gainexp(sd, NULL, 0, (int)z, false);				}			}		}	}	pc->setregistry(sd, script->add_str( "buyback" ), 0); // Set buyback player variable to 0 to reset whether or not they are using a buyback shop	return 0;}

     

     

    SCRIPT.C

    /*========================================== * getbuybackitem * getbbitem *------------------------------------------*/BUILDIN(getbuybackitem) {	int nameid,amount,i,flag = 0, offset = 1;	int iden,ref,attr,c1,c2,c3,c4, bound = 0;	int expire;	TBL_PC *sd;	bound = script_getnum(st,11);	if( bound < IBT_MIN || bound > IBT_MAX ) { //Not a correct bound type		ShowError("script_getitembound2: Not a correct bound type! Type=%dn",bound);		return false;	}	if( script_hasdata(st,12+offset) )		sd=map->id2sd(script_getnum(st,12+offset)); // <Account ID>	else		sd=script->rid2sd(st); // Attached player	if( sd == NULL ) // no target		return true;	if( script_isstringtype(st, 2) ) {		const char *name = script_getstr(st, 2);		struct item_data *item_data = itemdb->search_name(name);		if( item_data )			nameid=item_data->nameid;		else			nameid=UNKNOWN_ITEM_ID;	} else {		nameid = script_getnum(st, 2);	}	amount=script_getnum(st,3);	iden=script_getnum(st,4);	ref=script_getnum(st,5);	attr=script_getnum(st,6);	c1=(short)script_getnum(st,7);	c2=(short)script_getnum(st,8);	c3=(short)script_getnum(st,9);	c4=(short)script_getnum(st,10);	expire = script_getnum(st,12);	if (bound && (itemdb_type(nameid) == IT_PETEGG || itemdb_type(nameid) == IT_PETARMOR)) {		ShowError("script_getitembound2: can't bind a pet egg/armor! Type=%dn",bound);		return false;	}	if(nameid<0) { // Invalide nameid		nameid = -nameid;		flag = 1;	}	if(nameid > 0) {		struct item item_tmp;		struct item_data *item_data = itemdb->exists(nameid);		int get_count;		memset(&item_tmp,0,sizeof(item_tmp));		if (item_data == NULL)			return -1;		if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR) {			if(ref > MAX_REFINE) ref = MAX_REFINE;		}		else if(item_data->type==IT_PETEGG) {			iden = 1;			ref = 0;		}		else {			iden = 1;			ref = attr = 0;		}		item_tmp.nameid=nameid;		if(!flag)			item_tmp.identify=iden;		else if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR)			item_tmp.identify=0;		item_tmp.refine=ref;		item_tmp.attribute=attr;		item_tmp.bound=(unsigned char)bound;		item_tmp.card[0]=(short)c1;		item_tmp.card[1]=(short)c2;		item_tmp.card[2]=(short)c3;		item_tmp.card[3]=(short)c4;		item_tmp.expire_time=(unsigned int)(time(NULL) + expire);		//Check if it's stackable.		if (!itemdb->isstackable(nameid))			get_count = 1;		else			get_count = amount;		for (i = 0; i < amount; i += get_count) {			// if not pet egg			if (!pet->create_egg(sd, nameid)) {				if ((flag = pc->additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT))) {					clif->additem(sd, 0, 0, flag);					if( pc->candrop(sd,&item_tmp) )						map->addflooritem(&item_tmp,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);				}			}		}	}	return true;}
    BUILDIN_DEF(getbuybackitem, "viiiiiiiiii?"),BUILDIN_DEF2(getbuybackitem,"getbbitem","viiiiiiiiii?"),
    /*========================================== * delbuybackitem * delbbitem *------------------------------------------*/BUILDIN(delbuybackitem) {	TBL_PC *sd;	struct item it;	if( script_hasdata(st,13) ) {		int account_id = script_getnum(st,13);		sd = map->id2sd(account_id); // <account id>		if( sd == NULL ) {			ShowError("script:delitem2: player not found (AID=%d).n", account_id);			st->state = END;			return false;		}	}	else	{		sd = script->rid2sd(st);// attached player		if( sd == NULL )			return true;	}	if( script_isstringtype(st, 2) ) {		const char* item_name = script_getstr(st, 2);		struct item_data* id = itemdb->search_name(item_name);		if( id == NULL ) {			ShowError("script:delitem2: unknown item "%s".n", item_name);			st->state = END;			return false;		}		it.nameid = id->nameid;// "<item name>"	} else {		it.nameid = script_getnum(st, 2);// <item id>		if( !itemdb->exists( it.nameid ) ) {			ShowError("script:delitem: unknown item "%d".n", it.nameid);			st->state = END;			return false;		}	}	it.amount=script_getnum(st,3);	it.identify=script_getnum(st,4);	it.refine=script_getnum(st,5);	it.attribute=script_getnum(st,6);	it.card[0]=(short)script_getnum(st,7);	it.card[1]=(short)script_getnum(st,8);	it.card[2]=(short)script_getnum(st,9);	it.card[3]=(short)script_getnum(st,10);	it.bound = script_getnum(st,11);	it.expire_time = script_getnum(st,12);	if( it.amount <= 0 )		return true;// nothing to do	if( script->buildin_delitem_search(sd, &it, true) )	{// success		return true;	}	ShowError("script:delitem2: failed to delete %d items (AID=%d item_id=%d).n", it.amount, sd->status.account_id, it.nameid);	st->state = END;	clif->scriptclose(sd, st->oid);	return false;}
    BUILDIN_DEF(delbuybackitem, "viiiiiiiiii?"),BUILDIN_DEF2(delbuybackitem,"delbbitem","viiiiiiiiii?"),

     

     

     

    SAMPLE SCRIPT

    -	shop	BuyBack	-1,501:100prontera,154,176,4	script	BuyBackShop	123,{if( gettimetick(2) < @buybackshopdelay ) { dispbottom "You can not use this yet. Please wait, "+ (gettimetick(2) - @buybackshopdelay) +" more second(s)"; end; }if( select( "BuyBack Shop:Cancel" ) == 2 ) { close; }.@size = query_sql( "SELECT * FROM buyback WHERE char_id = "+ getcharid(0) +"", @row, @char_id, @id, @amt, @iden, @ref, @attr, @c1, @c2, @c3, @c4, @bound, @expire );if( !.@size ){ dispbottom "You haven't sold anything recently."; close; }@buybackshopdelay = gettimetick(2) + .expire;for( .@i = 0; .@i < getarraysize(@id); .@i++ ) {// Give player last sold items and bind them with a 30second expiration time.	getbuybackitem @id[.@i],@amt[.@i],@iden[.@i],@ref[.@i],@attr[.@i],@c1[.@i],@c2[.@i],@c3[.@i],@c4[.@i],.bound,.expire;//	dispbottom ""+ @id[.@i] +"";	}deletearray @id[0];callshop "BuyBack",3; end;OnInit:.bound = 4; //Type of Binding..expire = 15; // Time in seconds buyback shop returns item to player..logout_clear = 1; // 0 = Disable.  1 = Delete all buyback data upon character logout.end;OnPCLoginEvent:buyback = 0;end;OnPCLogoutEvent:if( .logout_clear ) { query_sql( "DELETE FROM buyback WHERE char_id = "+ getcharid(0) +"" ); }end;}

     


    SQL TABLE

    CREATE TABLE `hercules_mainsql`.`buyback` (  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,  `char_id` INT(11) UNSIGNED NOT NULL,  `nameid` INT(11) UNSIGNED NOT NULL DEFAULT 501,  `amount` INT(11) UNSIGNED NOT NULL DEFAULT 1,  `identify` INT(11) UNSIGNED NOT NULL DEFAULT 1,  `refine` INT(11) UNSIGNED NOT NULL DEFAULT 0,  `attribute` INT(11) UNSIGNED NOT NULL DEFAULT 0,  `card1` INT(11) UNSIGNED NOT NULL DEFAULT 0,  `card2` INT(11) UNSIGNED NOT NULL DEFAULT 0,  `card3` INT(11) UNSIGNED NOT NULL DEFAULT 0,  `card4` INT(11) UNSIGNED NOT NULL DEFAULT 0,  `bound` INT(11) UNSIGNED NOT NULL DEFAULT 0,  `expire_time` INT(11) UNSIGNED NOT NULL DEFAULT 0,  PRIMARY KEY (`id`));

     

     

    Edit 2:

    There is a couple small bugs I've noticed.

    1. If a player "Splits" the amount of items he/she buys back the code is unable to detect it and will delete the entry from the SQL database regardless. *Fixed by adding an if( X == X ) statement.

    2. Because I use the commands to give players the items with both bound and expire, the shop will give multiple items upon clicking close/cancel in the given time frames. *Fixed by adding shop use timer in script.

     

     


  15. Wait, what? How does the src code for your modified waiting room help me? I understand, that it goes to SRC then SRC tells it to go back to script, which will then run w/e, but i'm trying to eliminate the need of an npc script as much as possible.

     

    I was thinking maybe instead of running the Query everytime an item is sold for each and every item, maybe I could just run a smaller query for just the last 10 items.

    int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) {	double z;	int i,skill_t, skill_idx = skill->get_index(MC_OVERCHARGE);	struct npc_data *nd;	int w, new_; //BuyBack Feature	uint64 last_sold; //BuyBack Feature	nullpo_retr(1, sd);	nullpo_retr(1, item_list);	if( ( nd = npc->checknear(sd, map->id2bl(sd->npc_shopid)) ) == NULL ) {		return 1;	}	if( nd->subtype != SHOP ) {		if( !(nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->type == NST_ZENY) )			return 1;	}	if( pc_readglobalreg( sd, script->add_str( "buyback" ) ) ) {		w = 0;		new_ = 0;	}	z = 0;	// verify the sell list	for( i = 0; i < n; i++ ) {		int nameid, amount, idx, value;		idx    = item_list[i*2]-2;		amount = item_list[i*2+1];		if( idx >= MAX_INVENTORY || idx < 0 || amount < 0 ) {			return 1;		}		nameid = sd->status.inventory[idx].nameid;		if( !nameid || !sd->inventory_data[idx] || sd->status.inventory[idx].amount < amount ) {			return 1;		}		if( !pc_readglobalreg( sd, script->add_str( "buyback" ) ) ) { //Buyback feature doesn't support script-controlled shops.			if( nd->master_nd ) {// Script-controlled shops decide by themselves, what can be sold and at what price.				continue;			}		}		if( pc_readglobalreg( sd, script->add_str( "buyback" )  ) ) { //BuyBack Feature			value = sd->inventory_data[idx]->value_buy; //No Discount when buying back.			w+= itemdb_weight(nameid) * amount;		} else {			value = pc->modifysellvalue(sd, sd->inventory_data[idx]->value_sell);		}		z+= (double)value*amount;	}	if( !pc_readglobalreg( sd, script->add_str( "buyback" )  ) ) { //Only works if NOT BuyBack Feature. (Currently not supported until official works).		if( nd->master_nd ) { // Script-controlled shops			return npc->selllist_sub(sd, n, item_list, nd->master_nd);		}	}	if( pc_readglobalreg( sd, script->add_str( "buyback" ) ) ) { //BuyBack Feature				if( z > (double)sd->status.zeny )			return 1; // Not Enough Zeny		if( w + sd->weight > sd->max_weight )			return 2; // Too heavy		if( pc->inventoryblank(sd) < new_ )			return 3; // Not enough space to store items	pc->payzeny(sd,(int)z,LOG_TYPE_NPC, NULL);		// add items		for( i = 0; i < n; i++ ) {			int amount, idx;			struct item it;			idx    = item_list[i*2]-2;			amount = item_list[i*2+1];			it.nameid = sd->status.inventory[idx].nameid;			it.amount = sd->status.inventory[idx].amount;			it.equip = 0; //Don't equip.			it.identify = sd->status.inventory[idx].identify;			it.refine = sd->status.inventory[idx].refine;			it.attribute = sd->status.inventory[idx].attribute;			it.card[0] = sd->status.inventory[idx].card[0];			it.card[1] = sd->status.inventory[idx].card[1];			it.card[2] = sd->status.inventory[idx].card[2];			it.card[3] = sd->status.inventory[idx].card[3];			it.expire_time = sd->status.inventory[idx].expire_time; // Normally can't sell Rental Items.			it.bound = sd->status.inventory[idx].bound; // Normally can't sell Bound Items.			//Do the below so player has enough inventory space. (Probably not needed since we do the check above).			pc->delitem(sd, idx, amount, 0, 6, LOG_TYPE_NPC); //Delete the Item With Expiration & Bound first.			if( SQL->Query( map->mysql_handle, "DELETE FROM buyback WHERE char_id = %d AND nameid = %d AND amount = %d AND identify = %d AND refine = %d AND attribute = %d AND card1 = %d AND card2 = %d AND card3 = %d AND card4 = %d AND bound = %d AND expire_time = %d",				sd->status.char_id,				sd->status.inventory[idx].nameid,				sd->status.inventory[idx].amount,				sd->status.inventory[idx].identify,				sd->status.inventory[idx].refine,				sd->status.inventory[idx].attribute,				sd->status.inventory[idx].card[0],				sd->status.inventory[idx].card[1],				sd->status.inventory[idx].card[2],				sd->status.inventory[idx].card[3],				sd->status.inventory[idx].bound,				sd->status.inventory[idx].expire_time				) == SQL_ERROR ) { Sql_ShowDebug( map->mysql_handle ); }			it.expire_time = it.bound = 0; //Set Expiration and Bound to 0			pc->additem(sd, &it, amount, LOG_TYPE_NPC); // Add the item to inventory without Expiration and Bound		}	} else {	//Run SQL->Query to obtain number of rows player has, then we adjust id to +1 and then to 1 if above 10.	if( SQL->Query( map->mysql_handle, "SELECT * FROM buyback WHERE char_id = %d", sd->status.char_id ) == SQL_ERROR ) {		Sql_ShowDebug( map->mysql_handle );	}	else { 		last_sold = SQL->NumRows( map->mysql_handle );  //Set ID to number of rows pulled from SQL.	}	// delete items	for( i = 0; i < n; i++ ) {		int amount, idx; 		idx    = item_list[i*2]-2;		amount = item_list[i*2+1];		last_sold++; // +1 From the information gathered above.		if( last_sold > 10 ){ last_sold = 1; } //Reset to 1 to keep buyback items to 10.		if( sd->inventory_data[idx]->type == IT_PETEGG && sd->status.inventory[idx].card[0] == CARD0_PET ) {			if( pet->search_petDB_index(sd->status.inventory[idx].nameid, PET_EGG) >= 0 ) {				intif->delete_petdata(MakeDWord(sd->status.inventory[idx].card[1], sd->status.inventory[idx].card[2]));			}		}		// Attempt to delete the previous information if it exists, and then insert the new information in its place.		if( SQL->Query( map->mysql_handle, "DELETE FROM buyback WHERE char_id = %d AND last_sold = %d", sd->status.char_id, last_sold ) == SQL_ERROR )			continue;		//Store the Item into a SQL Table for SQL to use. (Need to figure out how to limit to last 10).		if( SQL->Query( map->mysql_handle, "INSERT INTO buyback (char_id, nameid, amount, identify, refine, attribute, card1, card2, card3, card4, bound, expire_time, last_sold) VALUES ( %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d )",			sd->status.char_id,			sd->status.inventory[idx].nameid,			sd->status.inventory[idx].amount,			sd->status.inventory[idx].identify,			sd->status.inventory[idx].refine,			sd->status.inventory[idx].attribute,			sd->status.inventory[idx].card[1],			sd->status.inventory[idx].card[2],			sd->status.inventory[idx].card[3],			sd->status.inventory[idx].card[4],			sd->status.inventory[idx].bound,			sd->status.inventory[idx].expire_time,			last_sold			) == SQL_ERROR ) { Sql_ShowDebug( map->mysql_handle ); }		else { pc->delitem(sd, idx, amount, 0, 6, LOG_TYPE_NPC); }	}	if( z > MAX_ZENY )		z = MAX_ZENY;	pc->getzeny(sd, (int)z, LOG_TYPE_NPC, NULL);	if( !pc_readglobalreg( sd, script->add_str( "buyback" ) ) ) { //BuyBack feature doesn't support shop EXP gain of any kind. This is an intended feature.		// custom merchant shop exp bonus		if( battle_config.shop_exp > 0 && z > 0 && ( skill_t = pc->checkskill2(sd,skill_idx) ) > 0) {			if( sd->status.skill[skill_idx].flag >= SKILL_FLAG_REPLACED_LV_0 )				skill_t = sd->status.skill[skill_idx].flag - SKILL_FLAG_REPLACED_LV_0;			if( skill_t > 0 ) {				z = z * (double)skill_t * (double)battle_config.shop_exp/10000.;				if( z < 1 )					z = 1;				pc->gainexp(sd, NULL, 0, (int)z, false);				}			}		}	}	pc->setregistry(sd, script->add_str( "buyback" ), 0); // Set buyback player variable to 0 to reset whether or not they are using a buyback shop	return 0;}

     

    That's what I have so far. Don't mind the sql query right before pc->additem, i'm still trying to get that one to work... Can't figure out why it won't work OR a more effective method.

    So would it be possible to take the sql query section of this and make it run for only the last 10 items?

    for( i = 0; i < n; i++ ) {		int amount, idx; 		idx    = item_list[i*2]-2;		amount = item_list[i*2+1];		last_sold++; // +1 From the information gathered above.		if( last_sold > 10 ){ last_sold = 1; } //Reset to 1 to keep buyback items to 10.		// Attempt to delete the previous information if it exists, and then insert the new information in its place.		if( SQL->Query( map->mysql_handle, "DELETE FROM buyback WHERE char_id = %d AND last_sold = %d", sd->status.char_id, last_sold ) == SQL_ERROR )			continue;		//Store the Item into a SQL Table for SQL to use. (Need to figure out how to limit to last 10).		if( SQL->Query( map->mysql_handle, "INSERT INTO buyback (char_id, nameid, amount, identify, refine, attribute, card1, card2, card3, card4, bound, expire_time, last_sold) VALUES ( %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d )",			sd->status.char_id,			sd->status.inventory[idx].nameid,			sd->status.inventory[idx].amount,			sd->status.inventory[idx].identify,			sd->status.inventory[idx].refine,			sd->status.inventory[idx].attribute,			sd->status.inventory[idx].card[1],			sd->status.inventory[idx].card[2],			sd->status.inventory[idx].card[3],			sd->status.inventory[idx].card[4],			sd->status.inventory[idx].bound,			sd->status.inventory[idx].expire_time,			last_sold			) == SQL_ERROR ) { Sql_ShowDebug( map->mysql_handle ); }		else { continue; }	}

    Obviously, I would need to get the final result of the loop run on the delete, but I wouldn't know where that is, because of how the loop is. for( i = 0; i < n; i++ ) i guess I should say, I don't know what the Value of n is. I can't seem to find it anywhere other than it's an int. Does that give it an inherent value of 32758 (or the number thats close to that lol)?


  16. Okay, I made a few changes to it and it works, but it seems like whenever a player sells the items to the shop, which is then updated/inserted to the sql table from within the source that individual player lags anywhere from 1-5seconds depending on the amount of items they are selling. On average 100 separate items results in 3-5second delay.

    However this delay only applies to the character taking action, everything else will update as normal.

    Obviously it's because I'm running 2 queries per item, since update on duplicate key won't work for my particular situation. (it does now indeed work though).

    Is there anyway I can have the source code run the sql script as if it were being run in the background, like how npc scripts do?


  17. This topic is getting further and further away from Script Requests but w/e. You can't just apply a patch to a client as easily as you might think. Your best options have been presented before you. Your going to have to pick one or none at all.


  18. Well, I've managed to make this come a far way, but I'm having a slight issue with SQL atm, and I don't know why it's doing this. Heres the code i'm currently using:

    		// Attempt to delete the previous information if it exists, and then insert the new information in its place.		if( SQL->Query( map->mysql_handle, "DELETE FROM buyback WHERE id = %d AND char_id = %d", id, sd->status.char_id ) == SQL_ERROR )			continue;		//Store the Item into a SQL Table for SQL to use. (Need to figure out how to limit to last 10).		if( SQL->Query( map->mysql_handle, "INSERT INTO buyback (id, char_id, nameid, amount, identify, refine, attribute, card1, card2, card3, card4, bound, expire_time) VALUES ( %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d )", 			id, //ID used to limit last sold to 10 items. (Can be modified below).			sd->status.char_id, 			sd->status.inventory[idx].nameid, 			sd->status.inventory[idx].amount, 			sd->status.inventory[idx].identify, 			sd->status.inventory[idx].refine, 			sd->status.inventory[idx].attribute, 			sd->status.inventory[idx].card[1], 			sd->status.inventory[idx].card[2], 			sd->status.inventory[idx].card[3], 			sd->status.inventory[idx].card[4], 			sd->status.inventory[idx].bound, 			sd->status.inventory[idx].expire_time 			) == SQL_ERROR ) { Sql_ShowDebug( map->mysql_handle ); }

    You'll see that in the above, I delete a row if it already exists, and then insert new information back in. I do it this way, because it seems that Herc doesn't support " ON DUPLICATE KEY UPDATE X = %d ", atleast all my attempts were fails with that.

     

    But the issue I'm having is that it's not updating the information in the correct columns. Namely it SKIPS the char_id column, and adds all the information 1 column over as you'll see below.

    id, char_id, nameid, amount, identify, refine, attribute, card1, card2, card3, card4, bound, expire_time'1', '0', '150001', '511', '1', '1', '0', '0', '0', '0', '0', '0', '0'

    It's completely beyond my reasoning atm. It was working fine BEFORE I added in the DELETE query, but now it's acting weird... any solutions?


  19. If they are giving you 000000000000000 mac address, then chances are they are running the client program through a sand box. Doing so will create a virtual machine that cannot be detected by 90% of client programs that try to prevent dual clienting.

     

    In fact, I've personally used sandboxing to dual client on servers that don't support it, using the client they give. But, this is a rare situation as most players of RO wouldn't think of it unless they've heard of it before.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.