Jump to content

GmOcean

Community Contributors
  • Content Count

    371
  • Joined

  • Last visited

  • Days Won

    8

Posts posted by GmOcean


  1. @ShogS - This has already been explained. While IP address would work in most situations, a group of computers who use the same router will have the same IP address. Therefore they'd be excluded even if they were legitimate players on separate comps. Which is why a unique MacAddress would be ideal.

     

    So, your only bet here is to either disable the use of dual clients OR allow multiple users through IP address. Sorry, no real work around on this.

     

    Edit:

    Turns out a src edit was made to allow the grabbing of MacAddress. I don't know if it still works though.

    http://herc.ws/board/topic/4643-mac-ip-address/


  2. HWID? Hardware ID? Even if the client did transfer this information, you'd have to use a seperate patcher client to gather the information send it to a SQL database like FluxCP that your server would then be able to access. But on top of all that, it's not reliable. HWID is changed every few days (10 i think) and after every reboot of the computer. While they may be unique to each instance one is created, they get changed far too frequently to be of much use.

     

    Besides, I wouldn't even be able to begin explaining how to obtain a person's HWID through a program without the user's consent. I believe that would be a direct violation of Microsoft's Terms & Conditions D: best we not travel that road.


  3. So is it only that part?

     COMMON_OBJ = $(addprefix obj_all/, $(COMMON_SHARED_OBJ)              console.o core.o malloc.o socket.o harmonycore.o)

    Or does the map/makein file fail as well?

     MAP_C = atcommand.c battle.c battleground.c buyingstore.c chat.c chrif.c         clif.c date.c duel.c elemental.c guild.c harmony.c homunculus.c HPMmap.c          instance.c intif.c irc-bot.c itemdb.c log.c mail.c map.c mapreg_sql.c          mercenary.c mob.c npc.c npc_chat.c party.c path.c pc.c pc_groups.c          pet.c quest.c script.c searchstore.c skill.c status.c storage.c /////// MAP_H = atcommand.h battle.h battleground.h buyingstore.h chat.h chrif.h         clif.h date.h duel.h elemental.h guild.h harmony.h homunculus.h HPMmap.h          instance.h intif.h irc-bot.h itemdb.h log.h mail.h map.h mapreg.h          mercenary.h mob.h npc.h packets.h packets_struct.h party.h path.h          pc.h pc_groups.h pet.h quest.h script.h searchstore.h skill.h 

    If everything works as it is, then perhaps the patch only works for Hercules as it was back in May 2014, when this patch was released.


  4. While the initial idea seems fun, there isn't much room for interaction. You'd probably be better off giving them 4 movements, and each one having a less success chance then the other.

    e.g:

    First move = 100% Success

    Second Move = 70% Success

    Third Move = 50% Success

    Fourth Move = 20% Success.

     

    Then if all 4 moves are successful, give them the prize, otherwise fail. Perhaps even add a small animation for when it does grab something, and when it fails to grab something.

    I'm not going to write this one though, it doesn't " appeal " to my interests enough D:, but those are my thoughts on the script.


  5. The patch already has those inside,

    +	if ( !map->list[sd->bl.m].flag.town ) {+		clif->message( fd, "You can only use @market in a town.");+		return false;+	}+	if ( map->getcell( sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOCHAT ) ) {+		clif->message( fd, "You cannot use @market in this area.");+		return false;+	}+	if ( pc_isdead(sd) ) {+		clif->message( fd, "You can't create a Market clone while you are dead." );+		return false;+	}+	if ( npc->isnear( &sd->bl ) ) {+		clif->message( fd, "You can't create a Market clone too near to an npc." );+		return false;+	}

  6. Lol, yeah. I'm using C++ 2010 @.@; As for, memset,  I don't actually mind setting all the information myself, since this makes it easier for me to understand lol. I may not have been able to complete this with a successful creation ( set my first src creation too high xD ). But I did learn quite a bit, sadly I kept hitting an unbreakable client wall >.>;

     

    Edit:

    Okay, so I've decided to go with a small work around that IS working for now.

    Again this is the code block I'm currently using:

     

    NPC.C

    int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) {	double z;	int i,w,new_/*, skill_idx = skill->get_index(MC_DISCOUNT)*/; //Currently removing Discount modifier.	struct npc_data *nd;	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;	}		z = 0;	w = 0;	new_ = 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( nd->master_nd ) {// Script-controlled shops decide by themselves, what can be sold and at what price.			continue;		}		switch( pc->checkadditem(sd,nameid,amount) ) {			case ADDITEM_NEW:				new_++;				break;							case ADDITEM_OVERAMOUNT:				return 2;		}				value = sd->inventory_data[idx]->value_buy;/*// Currently Removing Discount Modifier	value = pc->modifybuyvalue(sd, sd->inventory_data[idx]->value_buy);*/		z+= (double)value*amount;		w+= itemdb_weight(nameid) * amount;	}// TODO - Change this to npc->buybacklist_sub to control how it works with a shop attached to an NPC	if( nd->master_nd ) { // Script-controlled shops		return npc->selllist_sub(sd, n, item_list, nd->master_nd);	}		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 = 0; //Normally can't even sell rental items.		it.bound = 0; // Normally can't even sell bound items.		// pc->additem function		pc->additem(sd, &it, amount, LOG_TYPE_NPC);	}	return 0;}

    CLIF.C

    /// Presents list of items, that can be bought back from an NPC shop (ZC_PC_SELL_ITEMLIST)./// 00c7 <packet len>.W { <index>.W <price>.L <overcharge price>.L }*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_sell;			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));}

    Also CLIF.C

    clif->buybacklist = clif_buybacklist;

     

     

     

    However, currently I'm stuck at an issue, where the code block in npc.c replaces the original. I've tried creating my own block, but can't find the src where it points to it. For now I'm assuming that clicking the SELL button in a shop will always default to: npc_selllist

     

    So I decided to try checking for a variable the player may have set, but I don't think i'm doing it right, here's my code with the changes:

     

    /// Player item selling to npc shop.////// @param item_list 'n' pairs <index,amount>/// @return result code for clif->parse_NpcSellListSendint 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	char buyback[20]; //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( 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( 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 = 0;			it.bound = sd->status.inventory[idx].bound;			// pc->additem function			pc->additem(sd, &it, amount, LOG_TYPE_NPC);		}	} 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]));			}		}		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);	// 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);		}	}	}	return 0;}

     

     

    Lastly the final problems i've arrived at is this:

    1. I don't know how to set the variable to 0 from within the src code. I've tried looking at *set in script.c but I don't think I can apply those methods here.

    2. As of right now, I have a script that gives players an item for 5seconds. However, if they don't " buy " those items before they expire, then it'll disappear from the shop window when attempting to interact with it. My guess is this is because it no longer exists in your inventory so the shop says it no longer exists. But I think this can be fixed if I can store that information in a separate array. Just I don't know how to make one in src.

     

    3. If #2 isn't fixed with a separate array, then I need to do proper clean up, by increasing the expiration time and deleting the items should they click cancel. So, how do I tell if a player clicked cancel or not? I'd assume it'd be like *close or *close2 but I can't find it at all.

     

    Edit 2:

    Okay, i've got MOST of this all done now, i'd say I'm about 75-80% completely done with adding this in. I just need to know how to check to see if the player is currently using the shop.

    The reason is because, inorder to access the buyback feature, a variable is set to 1 " from an NPC ". Should the player complete the transaction by buying something, the src sets it to 0.

    BUT should they click cancel or close the shop from any other means then the variable is set to 1 still. I need to put the NPC on a loop, to check if they are using the NPC or not. Then thusly set to 0 when they aren't.


  7. Okay, thank you Annieruru, that worked. At least I would go as far to say that I was able to finally make it give me the items displayed in the list. Now to tackle the issue of whether or not shops can indeed display data from a sql table. Here's the code block I used so far:

     

     

    //Custom Sell List to return items instead of take items/// Player item selling to npc shop.////// @param item_list 'n' pairs <index,amount>/// @return result code for clif->parse_NpcSellListSendint npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) {	double z;	int i,w,new_, skill_idx = skill->get_index(MC_DISCOUNT);	struct npc_data *nd;	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;	}		z = 0;	w = 0;	new_ = 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( nd->master_nd ) {// Script-controlled shops decide by themselves, what can be sold and at what price.			continue;		}		switch( pc->checkadditem(sd,nameid,amount) ) {			case ADDITEM_NEW:				new_++;				break;							case ADDITEM_OVERAMOUNT:				return 2;		}				value = sd->inventory_data[idx]->value_buy;//		value = pc->modifybuyvalue(sd, sd->inventory_data[idx]->value_buy);		z+= (double)value*amount;		w+= itemdb_weight(nameid) * amount;	}	if( nd->master_nd ) { // Script-controlled shops		return npc->selllist_sub(sd, n, item_list, nd->master_nd);	}		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;		it.bound = sd->status.inventory[idx].bound;		// pc->additem function		pc->additem(sd, &it, amount, LOG_TYPE_NPC);	}	return 0;}

    Also, I know I left the ' skill_idx = get_count(MC_DISCOUNT) ' in there even though it's currently not being used, since I set the val to the item's data val. Just didn't feel like removing it yet xD haven't decided whether or not to leave it as an option when attempting to buy back.

    Interesting enough though, if I don't specify whether or not the item is bound or has an expire time, the shop will give me a Bound Expiring item. When attempting to Right-Click the item to view it, it'll result in a client crash. O.o perhaps that's an underlying issue in itself.

     

    I'll post back with an update regarding my sql attempts @.@; (Hope it works).

     

     

    Edit: Okay, I couldn't figure out how to make it work with SQL. Mainly because of what you said before Annieruru regarding WFIFOW(fd,4+c*10)=i+2; calling from inventory. I can't seem to overwrite that data to sql even if I were to manually set the data, the client still tries to pull inventory information. Regarding this, I know that MAX_INVENTORY is 100 ( by default ), I was thinking maybe there is a way to create a hidden 10+ slots that we can set ourselves, and actually give the players items in those slots only and then have the client only pull data from those last 10 slots since it would be inventory space. But, I came to the realization that it would be where you ended up... too many remaining issues, about whether or not a player closes shop, drops/deletes item, sells/buys, stores in cart etc....

     

    Edit 2:

    However, your @marketclone & Your post here got me thinking. This all maybe possible using vending. If an NPC can have a chat window, then surely it must be able to have a vending window and a cart, the only real issue I can come across with this is, limiting what player can access that vending window.


  8. Bumping this if I may.....

     

     

    I've tried a few other things to get this to work, and failed sadly. Such as using map->charid2sd to try and obtain the players id, and thusly trying pc->additem(sd, sd->stautus.inventory[idx].nameid, etc...)

    ultimately everything i've tried failed, because I don't know how to obtain and set the information I need to make it add items in a way that getitem2 does.

     

    I'm thinking maybe I'll have to incorporate both script & src to make this work. But first there is the issue of making the shop generate an item list from sql data. Is this even possible? If not let me know, so this can be
    scrapped already lol.

    The reason I say this, is because, if a shop can indeed form a list of items to sell/buy from a sql table, then I can use the OnBuyItem/OnSellItem labels, and do the item calculation
    there without the need to figure out pc->additem function in src. But I have no idea where to even begin when trying to pull data from a sql table let alone store it using src. I've tried looking at *query_sql in script.c but it just left me even more confused lol.


  9. No, I didn't forget, just didn't know if these created mobs/clones would be able to function the same. Wasn't sure if there was a limitation put on them in the src which prevented it from being able to do whatever lol. But thanks for the info, gonna go run some tests now =p


  10. When creating this " clone ", is it possible to obtain it's GID? Because if so, you'd be able to command it like any normal mob right? I'm interested in this, because then I'd be able to attach the clones to a script and control them to fulfill my every whim.... Mainly I wanna use it in combination with my campfire system lol, eliminating my need for npc duplicates.


  11. mapname,x,y,z	script	npc_name	sprite_id,{mes "What would you like to do?";menu "Place a bet",L_Place,"Claim Winnings",L_Claim;L_Place:next;mes "Bets are placed in the form of RedPotions.";mes "How much would you like to bet?";switch( "1 Potion(s):5 Potion(s):10 Potion(s)" ) {	case 1:		if( !countitem(501) ){ .@i = 1; break; }		.potionbets += 1;		delitem 501, 1;		break;	case 2:		if( countitem(501) < 5 ){ .@i = 1; break; }		.potionbets += 5;		delitem 501,5;		break;	case 3:		if( countitem(501) < 10 ){ .@i = 1; break; }		.potionbets += 10;		delitem 501,10;		break;}if( .@i ){ next; mes "You don't have enough potions"; close; }next;mes "Bet placed successfully.";close;L_Claim:next;if( .winnerid != getcharid(3) ) { mes "You aren't the winner"; close; }getitem 501,.potionbets;.potionbets = 0;mes "Congratulations, here is your reward";close;}

     

    I won't go into detail on how to make this work, other than, .winnerid needs to be set to the account id of the winner of said game/event. This is also an overly simplified version using somewhat very basic scripting techniques to make it easier to understand.


  12. Okay after " experimenting " with this a bit, it seems slightly plausible, but only through the use of a modified SELL window. This means that the player will ultimately have to click " sell ", but we could feed it data to have it return items instead.

    Here's what I did so far:

     

     

    clif.h

    	void (*buylist) (struct map_session_data *sd, struct npc_data *nd);	void (*buybacklist) (struct map_session_data *sd); // BuyBack window list	void (*selllist) (struct map_session_data *sd);
    	void (*pNpcBuyListSend) (int fd, struct map_session_data* sd);	void (*pNpcBuyBackListSend) (int fd, struct map_session_data* sd); // Buybacklist check	void (*pNpcSellListSend) (int fd,struct map_session_data *sd);

     

    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] )		{			if( !itemdb_cansell(&sd->status.inventory[i], pc_get_group_level(sd)) )				continue;			if( sd->status.inventory[i].expire_time )				continue; // Cannot Sell Rental Items 			if( sd->status.inventory[i].bound && !pc_can_give_bound_items(sd))				continue; // Don't allow sale of bound items			val=sd->inventory_data[i]->value_sell;			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);			c++;		}	}	WFIFOW(fd,2)=c*10+4;	WFIFOSET(fd,WFIFOW(fd,2));}
    /// 00c9 <packet len>.W { <index>.W <amount>.W }*void clif_parse_NpcBuyBackListSend(int fd,struct map_session_data *sd){	int fail=0,n;	unsigned short *item_list;	n = (RFIFOW(fd,2)-4) /4;	item_list = (unsigned short*)RFIFOP(fd,4);	if (sd->state.trading || !sd->npc_shopid)		fail = 1;	else		fail = npc->selllist(sd,n,item_list);	sd->npc_shopid = 0; //Clear shop data.	clif->npc_sell_result(sd, fail);}
    	clif->buylist = clif_buylist;	clif->buybacklist = clif_buybacklist;	clif->selllist = clif_selllist;

     

    npc.c

    //Custom Sell List to return items instead of take items/// Player item selling to npc shop.////// @param item_list 'n' pairs <index,amount>/// @return result code for clif->parse_NpcSellListSendint npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) {	double z;	int i,w,new_, skill_idx = skill->get_index(MC_DISCOUNT);	struct npc_data *nd;	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;	}		z = 0;	w = 0;	new_ = 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( nd->master_nd ) {// Script-controlled shops decide by themselves, what can be sold and at what price.			continue;		}		switch( pc->checkadditem(sd,nameid,amount) ) {			case ADDITEM_NEW:				new_++;				break;							case ADDITEM_OVERAMOUNT:				return 2;		}				value = sd->inventory_data[idx]->value_buy;//		value = pc->modifybuyvalue(sd, sd->inventory_data[idx]->value_buy);		z+= (double)value*amount;		w+= itemdb_weight(nameid) * amount;	}	if( nd->master_nd ) { // Script-controlled shops		return npc->selllist_sub(sd, n, item_list, nd->master_nd);	}		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);	// delete items	for( i = 0; i < n; i++ ) {		int nameid, 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]));			}		} */ // Can't Buy pets with data (Don't wanna bother making this work, so it'll just be a fresh egg).				//Add pc->additem function	}	return 0;}

     

    Right now, the main issue I'm having is the pc->additem function. If I take the code snippet from buylist, it does indeed add items, but it claims an unknown ID was used, so it creates a dummy item. I don't know how to make it grab the itemdata *item information from my inventory, without using sd->inventory_data[idx]. And obviously that won't work since I'm trying to feed it map_session data :/ (Why won't you just eat what I give you xD).

     

     

     

    Edit:

     

    Also, I know this is like 100% copy paste code, but I was able to replace the " buy feature " completely with the sell feature, since really the client just has buttons, the src does calculations, so from pressing BUY a brought up the window to sell items from my inventory. And it worked when attempting to sell.

     

    However, the moment I tried to change it to creating a list of items to buy from my inventory it got very buggy. Items appeared as apples that ranged from -300mill to +300mill zeny, but when bought only gave me a magnifying glass ( was talking to a tool dealer located in prt_in ), for each item.

     

    Knowing this, I believe we could do the following:

    1. Add a new option to *callshop

    *callshop "<name>",<option>;These are a series of commands used to create dynamic shops. The callshop function calls an invisible shop (view -1) as if the player clicked on it.For the options on callShop:	0 = The normal window (buy, sell and cancel)	1 = The buy window	2 = The sell window        3 = Buyback window

    2. Then by modifying this:

    int npc_buysellsel(struct map_session_data* sd, int id, int type) 

    we can make it call our new clif->buybacklist

    3. Do the following changes I made above ( of course with code from someone who can actually code LOL ).

     

    4. Finally, see if it's possible for WFIFOW(fd,0)=0xc7; to display items listed from a different source then my inventory.

     

    5. Find a way to have the client display BUY buttons instead of SELL buttons ( Seems like a minor issue to me ).

    *Note - I can't remember if when replacing the clif->buylist function with clif->selllist  if it showed buy or sell buttons. All I know is, when prompted either ' Buy/Sell/Cancel ' it would bring up the window and display my inventory information.

     


  13. I thought so lol. Didn't know client doesn't send Mac address though. I may be wrong ( I gotta stop saying this cuz I've been been wrong a lot lately when I say say this...) but I do believe the client may actually send the Mac address, atleast from what I remember openkore needed certain info to work, and one of the ways to get it was a packet sniffer such as wpe. This would usually give info needed, some of which included what think was the Mac address when recording yourself logging in. But, again I'm probably wrong on this.


  14. If clif_selllist uses information from our inventory, that's completely voluntary on our part right? Could we not just " change " that 1 line and make it show something else? Then IF that is possible, (while being somewhat a little annoying) you can leave it as a sell button but send a buy packet instead D:. Or am I just jumping the gun and am overestimating the power of src code lol xD


  15. Not sure how to bypass 32k heal cap atm, but uhh, I think ad.damage stands for attack-data damage. It's used for like every skill, 1 int to rule them all. But don't worry, each skill sets it's own variation of it so other skills won't be affected.


  16. Edit: Hiding the post in a spoiler since this is solved already.

    But here is the patch in case anyone wants to try and use it: buybackfeatureV0.1.patch

     

    Don't forget to add this table to your sql database.

    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`));

    .... Maybe in the future i'll see about just placing this in SRC release D: till then, I feel it's not quite up-to par to be a release lol.

     

    Currently NPC's can only display a normal item in the BUY window, meaning it's currently not possible to buy upgraded/carded items through a normal NPC shop without doing something like a dynamic shop.
    However, the client is capable of displaying the item's true information when attempting to sell to an npc.
     
    So, really I'm trying to figure out how to get the client to display the items in the shop windows, if I can find that then, it might be possible to create something like a buyback system on the src side rather than the script side, by creating a sql log that tracks when players sell items, and storing that information.
    Only the last 10-20items will need to be stored, after that we just replace the list as we go. We could also maybe store this information on the server's ram as an alternative so that it wipes when the player logs rather than on a semi-permanent sql log.
     

     

    void clif_buylist(struct map_session_data *sd, struct npc_data *nd) {	struct npc_item_list *shop = NULL;	unsigned short shop_size = 0;	int fd,i,c;	nullpo_retv(sd);	nullpo_retv(nd);	if( nd->subtype == SCRIPT ) {		shop = nd->u.scr.shop->item;		shop_size = nd->u.scr.shop->items;	} else {		shop = nd->u.shop.shop_item;		shop_size = nd->u.shop.count;	}	fd = sd->fd;	WFIFOHEAD(fd, 4 + shop_size * 11);	WFIFOW(fd,0) = 0xc6;	c = 0;	for( i = 0; i < shop_size; i++ ) {		if( shop[i].nameid ) {			struct item_data* id = itemdb->exists(shop[i].nameid);			int val = shop[i].value;			if( id == NULL )				continue;			WFIFOL(fd, 4+c*11) = val;			WFIFOL(fd, 8+c*11) = pc->modifybuyvalue(sd,val);			WFIFOB(fd,12+c*11) = itemtype(id->type);			WFIFOW(fd,13+c*11) = ( id->view_id > 0 ) ? id->view_id : id->nameid;			c++;		}	}	WFIFOW(fd,2) = 4 + c*11;	WFIFOSET(fd,WFIFOW(fd,2));}

    Currently that snippet code block just searchs if the item is an actual item, where as the sell_list uses our inventory:

    void clif_selllist(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] )		{			if( !itemdb_cansell(&sd->status.inventory[i], pc_get_group_level(sd)) )				continue;			if( sd->status.inventory[i].expire_time )				continue; // Cannot Sell Rental Items 			if( sd->status.inventory[i].bound && !pc_can_give_bound_items(sd))				continue; // Don't allow sale of bound items			val=sd->inventory_data[i]->value_sell;			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);			c++;		}	}	WFIFOW(fd,2)=c*10+4;	WFIFOSET(fd,WFIFOW(fd,2));}

     


    Seeing as how this is possible to have the npc display the information, how can I feed it information stored from a sql-table or temp-stored ram (preferred since this usually wipes on player logout), and would I need to make a new npc function in npc.c that tells the server what type of BUY npc it is (I'm assuming yes, since we did this for different type of shop npcs already like MARKET and CASHSHOP).
     
    Edit:
    Ultimately I suck at all things src related in RO, since I have like 1% knowledge in C. But I'm trying to learn, and I've been wanting this in RO for years now, figure I'd do my part and make an attempt instead of just waiting around being a leech !!. So help me out if you can, and let me know if it's not possible xD. Thanks everyone.

     

     


  17. Yeah, this currently isn't possible nor do I think will it ever be possible. But if your not too concerned about the NPC itself, you can simulate this with instances.

     

    I've experimented with it pretty recently, I created an instance version of prontera and it's surrounding fields, then I added in all the NPCs. After which, I added in 2 more NPCs that are only apart of the instance. So from this, you could make that instance of the map only available to players level 1-254. Then do some clean up and make them go back to the normal map where the entire server population can co-exist lol.

     

    Otherwise, you better off just making the NPC ignore the unqualified players.


  18. But the problem with that is, multiple players in the same household will show up as 1 IP address, therefore they'll be counted as invalid. Since we don't want to wrongfully discredit those players we need to check the MacAddress of the computers since those are never the same as another computer. Unless there is something I'm missing from your sql query, because I don't see how comparing IP address's alone can confirm dual clienting :/


  19. Actually it's fine the way it is. While someone's server may not have in herently 50-100 people, it does on the other hand offer players to make multiple accounts and dual client. Not to mention 99% of all servers have increase rates. This means that they'd far more easily obtain the levels/items required. If anything those limits need to be increased to support the likely outcome that one's server becomes populated.

     

    But seeing as how most people remove those quests from servers, better to just leave it as is, and those who want to make changes should learn to script.

×
×
  • Create New...

Important Information

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