Jump to content

Tokei

Members
  • Posts

    666
  • Joined

  • Last visited

  • Days Won

    91

Posts posted by Tokei

  1. Heya,

    Hmmm, first of all, you can't convert a RSM2 file to RSM just by changing its extension. GRF Editor shows the model properly because it reads the file header and sees it as a RSM2 file regardless of the extension. You need to either actually downgrade the models or simply update your client to a more recent version. As for ba_lost working using this method, that is just because the client works the same; it reads the header of the file, not the extension.I don't know if 2019-06-26's client is able to read RSM2 files or not. If we assume it does, then your issue is with the RSW map format, you'd have to downgrade it (which is fairly straightforward to do).

    Updating the client is the easier option either way though, and a long term one at that. You'll have to do it sooner or later and then it will just work out by itself:

    image.png.998fc54198c302627003c84dffe9d990.png

    (I need to update GRF Editor to read the newest RSW format in nif_dun02 map apparently... not that anything has really changed.)

    Best of luck!

    • Like 1
  2. Well in that case it'd probably be best to limit the amount of announces to 1 per account. You'd avoid a whole bunch of issues with that. The following resolves your server restart issues, though I'm... too lazy to make a GM handling of those announces.

    prontera,187,210,3	script	Broadcaster#1::BC	894,{
    	.@npcname$ = "^FF9300 Broadcaster ^000000";
    	.@header$ = "[^0000ff" + .@npcname$ + "^000000]";
    
    	mes .@header$;
    	mes "Hi, I'm the Broadcaster.";
    	mes "I can Broadcast a message for you.";
    	mes " ";
    	mes " ";
    	mes " ";
    	mes "It costs ^ff0000" + .broadcastfee + "^000000 zeny.";
    	next;
    	mes .@header$;
    	mes "Would you like to Broadcast?";
    	next;
    	
    	switch (select("Yes:Nevermind:Auto-broadcast:")) {
    		case 1:
    			if (Broadcast > gettimetick(2)) {
    				mes .@header$;
    				mes "Sorry you have to wait for 1 min.";
    				close;
    			}
    			
    			if (Zeny < .broadcastfee) {
    				goto L_NotEnoughZeny;
    			}
    			
    			mes .@header$;
    			mes "Please input your message.";
    			next;
    			input .@broadcast$;
    			Zeny -= .broadcastfee;
    			announce "Shout from " + strcharinfo(0) + ": " + .@broadcast$ + "", 0, 0x5AFF00; // Edit 5AFF00 for color code HTML Color Code
    			Broadcast = gettimetick(2) + 60; //Timer 60 = 1 minute/s
    			dispbottom "Broadcaster: Please wait for 1min until next broadcast to avoid flooding.";
    			end;
    		case 2:
    			mes .@header$;
    			mes "Suit yourself.";
    			close;
    		case 3:
    			mes .@header$;
    			mes "Hi, I can automatically broadcast messages for you!";
    			mes "It will cost you ^ff0000" + .auto_broadcastfee + "^000000 zeny per broadcast.";
    			next;
    			
    			.@aid = getcharid(3);
    			
    			switch(select("Proceed:Check status:Exit")) {
    				case 1:
    					if ($bc_announces_delay[.@aid]) {
    						mes .@header$;
    						mes "You already have an auto announce for this account.";
    						close;
    					}
    					
    					mes .@header$;
    					mes "Please input your message.";
    					next;
    					input .@broadcast$;
    					mes .@header$;
    					mes "How many times do you want to broadcast?";
    					mes "Min: 1";
    					mes "Max: 100";
    					next;
    					input .@repeat, 1, 100;
    					
    					if (.@repeat < 1 || .@repeat > 100) {
    						mes .@header$;
    						mes "Suit yourself.";
    						close;
    					}
    					
    					.@delay = 3;
    					//mes .@header$;
    					//mes "Delay between announces?";
    					//mes "Min: 3";
    					//mes "Max: 20";
    					//next;
    					//input .@delay, 3, 20;
    					//
    					//if (.@delay < 3 || .@delay > 20) {
    					//	mes .@header$;
    					//	mes "Suit yourself.";
    					//	close;
    					//}
    					
    					.@cost = .auto_broadcastfee * .@repeat;
    					
    					mes .@header$;
    					mes "You want to broadcast:";
    					mes "^ff0000" + .@broadcast$ + "^000000";
    					mes "Every 3 minutes for ^00ff00" + .@repeat + "^000000 time(s)?";
    					mes "It will cost you a total of ^ff0000" + .@cost + "^000000 zeny.";
    					next;
    					
    					switch(select("Proceed:Cancel")) {
    						case 2:
    							mes .@header$;
    							mes "Suit yourself.";
    							close;
    					}
    					
    					if (Zeny < .@cost) {
    						goto L_NotEnoughZeny;
    					}
    					
    					Zeny -= .@cost;
    					
    					$bc_announces_timer[.@aid] = 0;
    					$bc_announces_repeat[.@aid] = .@repeat;
    					$bc_announces_delay[.@aid] = .@delay * 60;
    					$bc_announces_mes$[.@aid] = "Shout from " + strcharinfo(0) + ": " + .@broadcast$;
    					$bc_announces_aid2idx[.@aid] = $bc_announces_count;
    					$bc_announces_idx2aid[$bc_announces_count] = .@aid;
    					$bc_announces_count++;
    					close;
    				case 2:
    					if ($bc_announces_delay[.@aid] == 0) {
    						mes .@header$;
    						mes "You currently have no auto announces.";
    						close;
    					}
    					
    					mes .@header$;
    					mes "Your current announce is as follow:";
    					mes "^ff0000" + $bc_announces_mes$[.@aid] + "^000000";
    					mes "It will be announced again in " + $bc_announces_timer[.@aid] + " second(s).";
    					mes "It will repeat " + $bc_announces_repeat[.@aid] + " more time(s).";
    					next;
    					
    					switch(select("Okay:Cancel announce:")) {
    						case 1:	
    							mes .@header$;
    							mes "...";
    							close;
    						case 2:
    							mes .@header$;
    							mes "Your zeny will not be refunded, are you sure you want to cancel?";
    							next;
    							
    							switch(select("No:Yes")) {
    								case 1:
    									mes .@header$;
    									mes "Suit yourself.";
    									close;
    							}
    							
    							if ($bc_announces_delay[.@aid] == 0) {	// It already ended?
    								end;
    							}
    							
    							callsub L_RemoveAutoAnnounce, .@aid;
    							mes .@header$;
    							mes "All done.";
    							close;
    					}
    					
    					end;
    				case 3:
    					mes .@header$;
    					mes "Suit yourself.";
    					close;
    				case 4:
    					
    					close;
    			}
    			
    			close;
    	}
    	
    	end;
    L_NotEnoughZeny:
    	mes .@header$;
    	mes "You don't have enough zeny.";
    	close;
    OnTimer1000:
    	freeloop(1);
    	
    	for (.@i = 0; .@i < $bc_announces_count; .@i++) {
    		.@aid = $bc_announces_idx2aid[.@i];
    		$bc_announces_timer[.@aid]--;
    		
    		if ($bc_announces_timer[.@aid] <= 0 && .@didannounce == false) {
    			announce $bc_announces_mes$[.@aid], 0, 0x5AFF00;
    			
    			$bc_announces_repeat[.@aid]--;
    			$bc_announces_timer[.@aid] = $bc_announces_delay[.@aid];
    			
    			if ($bc_announces_repeat[.@aid] <= 0) {
    				callsub L_RemoveAutoAnnounce, .@aid;
    				.@i--;
    			}
    			
    			.@didannounce = true;	// Prevents overlapping of announces
    		}
    	}
    	
    	freeloop(0);
    	initnpctimer;
    	end;
    L_RemoveAutoAnnounce:
    	.@aid = getarg(0);
    	.@idx = $bc_announces_aid2idx[.@aid];
    	
    	$bc_announces_timer[.@aid] = 0;
    	$bc_announces_repeat[.@aid] = 0;
    	$bc_announces_delay[.@aid] = 0;
    	$bc_announces_mes$[.@aid] = "";
    	
    	.@last_aid = $bc_announces_idx2aid[$bc_announces_count - 1];
    	$bc_announces_idx2aid[.@idx] = .@last_aid;
    	$bc_announces_idx2aid[$bc_announces_count - 1] = 0;
    	$bc_announces_aid2idx[.@last_aid] = .@idx;
    	$bc_announces_count--;
    	return;
    OnInit:
    	.broadcastfee = 3000000;
    	.auto_broadcastfee = 500000;
    	initnpctimer;
    	end;
    }

     

    • Like 1
  3. Your initial script has some oddities (regarding variable scope). Hmm, I see a few things you'd probably to worry about with the proposed solution:

    • A player can do multiple auto announces.
    • There is no way of tracking the active announcements.
    • Once the auto announce is bought, it is fully detached from the player and cannot be linked back to him.
    • The announces will get lost on script reload/server reboots. You'd probably want to use global arrays and have the announces displayed with a OnTimer1000 checks instead.

    With that being said, the following is probably what you're looking for. I personally prefer to have the loop on a separate event, but the end result is very similar in this case.

    prontera,187,210,3	script	Broadcaster#1::BC	894,{
    	.@npcname$ = "^FF9300 Broadcaster ^000000";
    	.@header$ = "[^0000ff" + .@npcname$ + "^000000]";
    
    	mes .@header$;
    	mes "Hi, I'm the Broadcaster.";
    	mes "I can Broadcast a message for you.";
    	mes " ";
    	mes " ";
    	mes " ";
    	mes "It costs ^ff0000" + .broadcastfee + "^000000 zeny.";
    	next;
    	mes .@header$;
    	mes "Would you like to Broadcast?";
    	next;
    	
    	switch (select("Yes:Nevermind:Auto-broadcast")) {
    		case 1:
    			if (Broadcast > gettimetick(2)) {
    				mes .@header$;
    				mes "Sorry you have to wait for 1 min.";
    				close;
    			}
    			
    			if (Zeny < .broadcastfee) {
    				goto L_NotEnoughZeny;
    			}
    			
    			mes .@header$;
    			mes "Please input your message.";
    			next;
    			input .@broadcast$;
    			Zeny -= .broadcastfee;
    			announce "Shout from " + strcharinfo(0) + ": " + .@broadcast$ + "", 0, 0x5AFF00; // Edit 5AFF00 for color code HTML Color Code
    			Broadcast = gettimetick(2) + 60; //Timer 60 = 1 minute/s
    			dispbottom "Broadcaster: Please wait for 1min until next broadcast to avoid flooding.";
    			end;
    		case 2:
    			mes .@header$;
    			mes "Suit yourself.";
    			close;
    		case 3:
    			mes .@header$;
    			mes "Hi, I can automatically broadcast messages for you!";
    			mes "It will cost you ^ff0000" + .auto_broadcastfee + "^000000 zeny per broadcast.";
    			next;
    			
    			switch(select("Proceed:Check status:Exit")) {
    				case 1:
    					mes .@header$;
    					mes "Please input your message.";
    					next;
    					input .@broadcast$;
    					mes .@header$;
    					mes "How many times do you want to broadcast?";
    					mes "Min: 1";
    					mes "Max: 100";
    					next;
    					input .@repeat, 1, 100;
    					
    					if (.@repeat < 1 || .@repeat > 100) {
    						mes .@header$;
    						mes "Suit yourself.";
    						close;
    					}
    					
    					.@cost = .auto_broadcastfee * .@repeat;
    					
    					mes .@header$;
    					mes "You want to broadcast:";
    					mes "^ff0000" + .@broadcast$ + "^000000";
    					mes "Every 3 minutes for ^00ff00" + .@repeat + "^000000 time(s)?";
    					mes "It will cost you a total of ^ff0000" + .@cost + "^000000 zeny.";
    					next;
    					
    					switch(select("Proceed:Cancel")) {
    						case 2:
    							mes .@header$;
    							mes "Suit yourself.";
    							close;
    					}
    					
    					if (Zeny < .@cost) {
    						goto L_NotEnoughZeny;
    					}
    					
    					Zeny -= .@cost;
    					$@bc_announce$ = "Shout from " + strcharinfo(0) + ": " + .@broadcast$;
    					$@repeat = .@repeat;
    					donpcevent strnpcinfo(3) + "::OnAutoAnnounce";
    					close;
    				case 2:
    					// ??
    					end;
    				case 3:
    					mes .@header$;
    					mes "Suit yourself.";
    					close;
    			}
    			
    			close;
    	}
    	
    	end;
    L_NotEnoughZeny:
    	mes .@header$;
    	mes "You don't have enough zeny.";
    	close;
    OnAutoAnnounce:
    	.@repeat = $@repeat;
    	.@broadcast$ = $@bc_announce$;
    	
    	while (.@repeat > 0) {
    		announce .@broadcast$, 0, 0x5AFF00; // Edit 5AFF00 for color code HTML Color Code
    		sleep 180000;
    		.@repeat--;
    	}
    	
    	end;
    OnInit:
    	.broadcastfee = 3000000;
    	.auto_broadcastfee = 500000;
    	end;
    }

     

    • Love 1
    • Like 1
  4. Heya,

    You can use mob_drop.txt in your db folder, and make drops protected from steal:

    // Monster Drop Database
    // Add drop item to monster
    //
    // Structure:
    // <mobid>,<itemid>,<rate>{,<randopt_groupid>,<flag>}
    //
    // <mobid>  : Monster ID. See db/[pre-]re/mob_db.txt
    // <itemid> : Item ID.
    // <rate>   : 1 = 0.01%
    //            100 = 1%
    //            10000 = 100%
    //            Just like rate in mob_db.txt, adjusted by battle_config.
    //            To remove original drop from monster, use 0 as rate.
    // Optional:
    // <randopt_groupid> : If set, the dropped item will be modified by Random Option Group based on db/[pre-]re/item_randomopt_group.txt
    // <flag>            : 1 - The item is protected from steal.
    //                     2 - As MVP Reward
    
    1063,1102,100,RDMOPTG_None,1

     

  5. 5 hours ago, Almond Snicker said:

    Hi @Tokei

    I need to fix this item or delete for me to extract to a folder for AndRO.
    Can you please help me determine how to locate this korean in english?
    I gave it a shot by typing 뮜?. in the collection tab but I can't locate it. (or maybe because my raw folder is in english)

    image.png.473f0535ea724354505fd7fdd97c9f86.png
     

    ? is used for regex searches, such as 

    *col??ction*

    That's why you can't do a search for this symbol. Try searching for this instead:

    뮜*
    
    Or
    
    *collection\뮜*

     

  6. 13 hours ago, Vyserion said:

    Hello there!

      I applied this patch and everything seems to work perfectly fine, however, when I want to make a random group to apply different values, it would just apply the first one of each group (RDMOPT_DAMAGE_PROPERTY_GROUND_USER in this case), for example:

    
    RDMOPTG_Crimson_Weapon_3,1,RDMOPT_DAMAGE_PROPERTY_GROUND_USER,5,0,10
    RDMOPTG_Crimson_Weapon_3,1,RDMOPT_DAMAGE_PROPERTY_NOTHING_TARGET,5,0,10
    RDMOPTG_Crimson_Weapon_3,1,RDMOPT_DAMAGE_PROPERTY_WATER_TARGET,5,0,10
    RDMOPTG_Crimson_Weapon_3,1,RDMOPT_DAMAGE_PROPERTY_FIRE_USER,5,0,10

    For this specific group, it is intended that one of these four values would apply randomly to an specific weapon, but, after applying the patch, the first in these lines will only apply. The other three effects are not being taken into consideration when dropping the weapon.

    Does someone have any idea what might be causing this or any work-around?

    Well the group is being chosen there:

    int rnd_value = rnd() % (randomopt_group->total_weight);

    Using a debugger would show you where the issue is. Otherwise, you can add debug lines, I suppose:

    			int rnd_value = rnd() % (randomopt_group->total_weight);
    			int total = 0;
    			int j;
    			ShowDebug("itemdb_add_randomopt: total_weight '%d', total '%d', rnd_value '%d'\n", randomopt_group->total_weight, randomopt_group->total, rnd_value);

     

    If the total_weight isn't 4, then it wasn't read properly and something's missing near

    	g->total_weight += rate;
    	g->total++;

     

  7. What Kreustoo said will fix most of your issues. For example:

    prontera,156,174,3	script	Event Warper	77,{
    	if (!$arenac_gate) {
    		mes "The event is closed.";
    		end;
    	}
    	
    	mes "Do you want to go in?";
    	next;
    	
    	switch(select("Yes.:No.:")) {
    		case 1:
    			// This is where you need to add your checks
    			if (!$arenac_gate) {
    				mes "The event is closed.";
    				end;
    			}
    			
    			warp "que_qaru01", 0, 0;
    			end;
    	}
    	
    	end;
    }

    However this is not perfect either. A player could still enter after the event is closed because there is a delay between warping a player on the map and the player landing on the map. That delay is the time the player takes to load the map (which can vary greatly  depending of the player).

    What you could do is add an "OnTouch" area where players are warped. For instance:

    que_qaru01,155,155,3	script	#event_check	-1,2,2,{
    	end;
    OnTouch:
    	if (!$arenac_gate) {
    		dispbottom "You've entered the event too late, sorry!";
    		warp "prontera", 160, 184;
    		end;
    	}
    	
    	end;
    }

     

    Another approach is to disable the entrance warper NPC a good minute prior to your event starting. Then you start your event with some dialogues, for a good minute as well. Then you start your actual event. This ensures that players who are loading slowly get on the map in time for the main event. The previous solution is safer, though.

    As for players getting items that they shouldn't, you could add checks there as well. Not sure how your script works though, hard to say if that's feasible or not in your case.

    	if (!$arenac_gate) {
    		dispbottom "The event ended, you can't get rewards!";
    		warp "prontera", 160, 184;
    		end;
    	}
    	
    	getitem 501, 10;
    	...

     

  8. Heya,

    Could you show an example of the issue? It's not normal for the color to change in-game as it would indicate a new unknown behavior with indexed images in the client (and that's not very likely). If you do not want to share the file in public, please send it privately to me.

    Are you building the sprite file from multiple BMP files in a folder? If so, that would be the problem as you have different palettes in each one of your BMPs. You cannot do that as it's not really possible to mix palettes. If you use Act Editor, it should give you a warning that the palettes aren't matching and it will attempt to fix the issue for you by converting the image to something as close as possible as your image. Depending of the option you select, you can retain 100% of the image quality. If you choose to find the closest color in the sprite palette instead, then it will end up picking #001000 as it's the closest color match to #001603.

    Ideally you want to stick with one palette and not change colors in your BMP files though. You can also choose to convert the image to Bgra32 directly and that will solve the issue, with compression loss however.

  9. Heya,

    Where to start... "sd" is a variable. What you're attempting to do is similar to...

    int x;
    int y;
    
    y = x + 5;

    It's impossible to assign a value to y if you don't know what the value of x is first. In your code, sd is the variable, but it is never really assigned. It is set to NULL as default (which is good practice), but NULL doesn't mean anything. If you look more closely, the value is actually assigned with "sd = map_charid2sd(script_getnum(st, 3))". It is commented with the "//" in front of it; you need to remove all of those "//" in front of the code.

    The "write access violation" comes from you attempting to do sd->guild_invite = guild_id, which in reality corresponds to NULL->guild_invite = guild_id. Obviously NULL doesn't "exist" and you can't assign guild_id to nothing. It's called a violation because you do not have the rights to write to NULL (to keep things simple anyway).

    sd isn't a cache, sd is the "map session data". It's what holds the information about your player's current session. You should not be doing queries to your database to update your character (unless your server is offline), that will not work and it's a bad approach. I don't know what you're trying to update though.

  10. Heya,

    Your main issue is that you're using "@rdice" instead of ".@rdice". The dot in front of the variable is for npc variables, while no dot is for a player variable.

    As a... recommendation, you should be using a donpcevent "NPCName::OnStart" after the close2 and move all your code there instead. You're trying to run the rest of the script on the player and that's not necessary here. Since the player was no longer attached to the script or was offline, the script failed to get the variable from the player.

  11. 3 hours ago, redv1per said:

    i have problem here, only 1 map is error like in pict. And when finished, maps.grf output size is 1kb. any suggest?

    im using grf editor 1.8.2.4

    Only one error will be displayed even if there are more; in your case, the maps are simply encrypted so it's skipping all of them. Hence the 1kb maps.grf output file.

  12. 12 hours ago, Lelouch vi Britannia said:

    @Tokei I found this after viewing TB_Layer_Priority.lub from kRO data.grf

    Im using
    Product version : 1.8.3.4
    Assembly version : 1.5.3.3063

    Heya,

    I'll have a look later; the function itself can be decompiled from the  labels:

    -- Function #0
    GetLayerPriority = function(direction, layer, acc_id)
    	local Items_List = TB_Layer_Priority.Items_List
    	if nil ~= Items_List then
    		local Item_Tb = Items_List[acc_id]
    		if nil ~= Item_Tb then
    			if nil ~= Item_Tb.Direction and nil ~= Item_Tb.Direction[direction] then
    				return Item_Tb.Direction[direction]
    			elseif nil ~= Item_Tb.Default then
    				return Item_Tb.Default
    			end
    		end
    	end
    	if 2 == layer then
    		return TB_Layer_Priority.Default_Bottom
    	elseif 3 == layer then
    		return TB_Layer_Priority.Default_Top
    	elseif 4 == layer then
    		return TB_Layer_Priority.Default_Mid
    	elseif 8 == layer then
    		return TB_Layer_Priority.Default_Robe
    	end
    	return -1
    end

     

    • Like 1
  13. 1 hour ago, Zandriarth said:

    Sorry to revive an old reply here. But I've been wondering if there was a way to make the flat maps without changing the textures of the ground itself?

    Try unchecking "Change the ground textures for custom ones".

    • Upvote 1
  14. Hmm, your base is different than rAthena (you do not have the int64 script update applied). There's a couple of mistakes with how you assigned the integers, replace your two functions with this instead:

    /**
    * Read Item Random Option Group from db file
    * @author [Cydh]
    **/
    static bool itemdb_read_randomopt_group(char* str[], int columns, int current) {
    	int id = 0;
    	int randid = 0;
    	unsigned short rate = (unsigned short)strtoul(str[1], NULL, 10);
    	struct s_random_opt_group *g = NULL;
    
    	if (!script_get_constant(str[0], &id)) {
    		// autogenerate constant, it gets annoying to define it everytime
    		script_set_constant(trim(str[0]), add_str(trim(str[0])), false, false);
    
    		if (!script_get_constant(trim(str[0]), &id)) {	// Should never happen?
    			ShowError("itemdb_read_randomopt_group: Invalid ID for Random Option Group '%s'.\n", str[0]);
    			return false;
    		}
    	}
    
    	if (!script_get_constant(str[2], &randid) || !itemdb_randomopt_exists(randid)) {
    		ShowError("itemdb_read_randomopt_group: Invalid random group id '%s'!\n", str[2]);
    		return false;
    	}
    
    	if (!(g = (struct s_random_opt_group *)uidb_get(itemdb_randomopt_group, id))) {
    		CREATE(g, struct s_random_opt_group, 1);
    		g->total = 0;
    		g->entries = NULL;
    		g->total_weight = 0;
    		uidb_put(itemdb_randomopt_group, id, g);
    	}
    
    	RECREATE(g->entries, struct s_random_opt_group_entry, g->total + 1);
    
    	memset(&g->entries[g->total], 0, sizeof(g->entries[g->total]));
    
    	g->entries[g->total].option.id = randid;
    	g->entries[g->total].option.value = (short)strtoul(str[3], NULL, 10);
    	g->entries[g->total].option.param = (char)strtoul(str[4], NULL, 10);
    	g->entries[g->total].weight = rate;
    	if (columns > 5) {
    		g->entries[g->total].max_value = max((short)strtoul(str[5], NULL, 10), g->entries[g->total].option.value);
    	}
    
    	g->total_weight += rate;
    	g->total++;
    	return true;
    }
    
    /**
    * Read Item Random Option Group from db file
    **/
    static bool itemdb_read_randomopt_equip(char* str[], int columns, int current) {
    	unsigned short nameid;
    	int group_id = -1;
    	struct item_data *item = NULL;
    	int i;
    
    	nameid = atoi(str[0]);
    
    	for (i = 1; i < columns; i++) {
    		if (ISDIGIT(str[i][0])) {
    			group_id = (int)strtoul(str[i], NULL, 10);
    		}
    		else {
    			if (!script_get_constant(trim(str[i]), &group_id)) {
    				ShowError("itemdb_read_randopt_equip: Invalid constant '%s'!\n", str[i]);
    				return false;
    			}
    		}
    
    		if ((item = itemdb_exists(nameid)) == NULL) {
    			ShowWarning("itemdb_read_randopt_equip: Invalid item ID %hu.\n", nameid);
    			return false;
    		}
    
    		if (!itemdb_randomopt_group_exists(group_id)) {
    			ShowWarning("itemdb_read_randopt_equip: Invalid random option group ID %hu.\n", group_id);
    			return false;
    		}
    
    		item->randomopt_groupid[i - 1] = group_id;
    	}
    
    	return true;
    }

     

  15. @mawjustin Hmmm, you'd get this error because the group IDs aren't defined. That... shouldn't be the case, though. The constant for your group ID, "7934", does seem to exist. Can you show me both of your itemdb_read_randomopt_group and itemdb_read_randomopt_equip functions, in itemdb.cpp? Also please show me one item you made in item_randoopt_equips.txt with one of the group from item_randoption_group.txt that is used. The error is somewhat vague.

  16. Heya,

    If you want to achieve that behavior, you'll have to change a whole bunch of code. There was an earlier version of the system which greatly simplified the randomness of random option enchants. The system would rely on 3 files:

    db/re/item_randomopt_db.txt (remains the same)
    db/re/item_randoption_equips.txt (this replaces the db/re/mob_drops.txt file, the random options are instead attached to the weapon itself, rather than the mob the weapon drops from)
    db/re/item_randoption_group.txt (remains mostly the same, except you add another optional column for the max value of the field)

    item_randoption_equips.txt would look as below. You first define the random options for the specific weapons as such:

    // Items Random Option Database	
    //
    // Structure of Database:
    // Item_ID,RandomOption_GroupId1{,RandomOption_GroupId2,RandomOption_GroupId3,RandomOption_GroupId4,RandomOption_GroupId5}
    
    28705,RDMOPTG_Crimson_Weapon_1,RDMOPTG_Crimson_Weapon_2,RDMOPTG_Crimson_Weapon_3
    13327,RDMOPTG_Crimson_Weapon_1
    28604,RDMOPTG_Crimson_Weapon_1
    28007,RDMOPTG_Crimson_Weapon_1
    1839,RDMOPTG_Crimson_Weapon_1
    1498,RDMOPTG_Crimson_Weapon_1
    16040,RDMOPTG_Crimson_Weapon_1
    13454,RDMOPTG_Crimson_Weapon_1
    1443,RDMOPTG_Crimson_Weapon_1
    28106,RDMOPTG_Crimson_Weapon_1
    21015,RDMOPTG_Crimson_Weapon_1
    1939,RDMOPTG_Crimson_Weapon_1
    1995,RDMOPTG_Crimson_Weapon_1

    The random options for each groups are defined in item_randoption_group.txt. In the sample below, you'll notice a new colum for the 2nd and 3rd random options I chose, which will make the value vary between 30~50 for the earth resistance and 5~10 for... the others.

    // Items Random Option Database	
    //
    // Structure of Database:
    // Group_ID,rate,randopt_id1,randopt_value1,randopt_param1
    
    // Crimson Weapon
    RDMOPTG_Crimson_Weapon_1,1,RDMOPT_WEAPON_ATTR_NOTHING,0,0
    RDMOPTG_Crimson_Weapon_1,1,RDMOPT_WEAPON_ATTR_WATER,0,0
    RDMOPTG_Crimson_Weapon_1,1,RDMOPT_WEAPON_ATTR_GROUND,0,0
    RDMOPTG_Crimson_Weapon_1,1,RDMOPT_WEAPON_ATTR_FIRE,0,0
    RDMOPTG_Crimson_Weapon_1,1,RDMOPT_WEAPON_ATTR_WIND,0,0
    RDMOPTG_Crimson_Weapon_1,1,RDMOPT_WEAPON_ATTR_SAINT,0,0
    
    RDMOPTG_Crimson_Weapon_2,1,RDMOPT_ATTR_TOLERACE_GROUND,30,0,50
    
    RDMOPTG_Crimson_Weapon_3,1,RDMOPT_DAMAGE_PROPERTY_GROUND_USER,5,0,10
    RDMOPTG_Crimson_Weapon_3,1,RDMOPT_DAMAGE_PROPERTY_NOTHING_TARGET,5,0,10
    RDMOPTG_Crimson_Weapon_3,1,RDMOPT_DAMAGE_PROPERTY_WATER_TARGET,5,0,10
    RDMOPTG_Crimson_Weapon_3,1,RDMOPT_DAMAGE_PROPERTY_FIRE_USER,5,0,10

    As for the source change, you can apply the diff below and do some tests with it (I didn't test it too seriously, it worked well enough for what I believe your request was though). The current weight/ratio system used... doesn't make any sense, so I made my own.

    diff --git a/db/re/item_randomopt_group.txt b/db/re/item_randomopt_group.txt
    index 6d9b9929a..05d579f9a 100644
    --- a/db/re/item_randomopt_group.txt
    +++ b/db/re/item_randomopt_group.txt
    @@ -1,10 +1,16 @@
     // <randopt_groupid>,<rate>,<randopt_id1>,<randopt_value1>,<randopt_param1>{,<randopt_id2>,<randopt_value2>,<randopt_param2>,<randopt_id3>,<randopt_value3>,<randopt_param3>,<randopt_id4>,<randopt_value4>,<randopt_param4>,<randopt_id5>,<randopt_value5>,<randopt_param5>}
     
     // Crimson Weapon
    -RDMOPTG_Crimson_Weapon,1,RDMOPT_WEAPON_ATTR_NOTHING,0,0
    -RDMOPTG_Crimson_Weapon,1,RDMOPT_WEAPON_ATTR_WATER,0,0
    -RDMOPTG_Crimson_Weapon,1,RDMOPT_WEAPON_ATTR_GROUND,0,0
    -RDMOPTG_Crimson_Weapon,1,RDMOPT_WEAPON_ATTR_FIRE,0,0
    -RDMOPTG_Crimson_Weapon,1,RDMOPT_WEAPON_ATTR_WIND,0,0
    -RDMOPTG_Crimson_Weapon,1,RDMOPT_WEAPON_ATTR_SAINT,0,0
    -RDMOPTG_Crimson_Weapon,1,RDMOPT_WEAPON_ATTR_DARKNESS,0,0
    +RDMOPTG_Crimson_Weapon_1,1,RDMOPT_WEAPON_ATTR_NOTHING,0,0
    +RDMOPTG_Crimson_Weapon_1,1,RDMOPT_WEAPON_ATTR_WATER,0,0
    +RDMOPTG_Crimson_Weapon_1,1,RDMOPT_WEAPON_ATTR_GROUND,0,0
    +RDMOPTG_Crimson_Weapon_1,1,RDMOPT_WEAPON_ATTR_FIRE,0,0
    +RDMOPTG_Crimson_Weapon_1,1,RDMOPT_WEAPON_ATTR_WIND,0,0
    +RDMOPTG_Crimson_Weapon_1,1,RDMOPT_WEAPON_ATTR_SAINT,0,0
    +
    +RDMOPTG_Crimson_Weapon_2,1,RDMOPT_ATTR_TOLERACE_GROUND,30,0,50
    +
    +RDMOPTG_Crimson_Weapon_3,1,RDMOPT_DAMAGE_PROPERTY_GROUND_USER,5,0,10
    +RDMOPTG_Crimson_Weapon_3,1,RDMOPT_DAMAGE_PROPERTY_NOTHING_TARGET,5,0,10
    +RDMOPTG_Crimson_Weapon_3,1,RDMOPT_DAMAGE_PROPERTY_WATER_TARGET,5,0,10
    +RDMOPTG_Crimson_Weapon_3,1,RDMOPT_DAMAGE_PROPERTY_FIRE_USER,5,0,10
    \ No newline at end of file
    diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp
    index 266f37526..b0c6874e3 100644
    --- a/src/map/atcommand.cpp
    +++ b/src/map/atcommand.cpp
    @@ -1403,6 +1403,9 @@ ACMD_FUNC(item)
     				item_tmp.nameid = item_id;
     				item_tmp.identify = 1;
     				item_tmp.bound = bound;
    +
    +				itemdb_add_randomopt(&item_tmp);
    +
     				if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND)))
     					clif_additem(sd, 0, 0, flag);
     			}
    diff --git a/src/map/itemdb.cpp b/src/map/itemdb.cpp
    index 56ced4d34..f7f606c4e 100644
    --- a/src/map/itemdb.cpp
    +++ b/src/map/itemdb.cpp
    @@ -1783,53 +1783,138 @@ struct s_random_opt_group *itemdb_randomopt_group_exists(int id) {
     static bool itemdb_read_randomopt_group(char* str[], int columns, int current) {
     	int64 id_tmp;
     	int id = 0;
    -	int i;
    +	int64 randid_tmp;
    +	int randid = 0;
     	unsigned short rate = (unsigned short)strtoul(str[1], NULL, 10);
     	struct s_random_opt_group *g = NULL;
     
     	if (!script_get_constant(str[0], &id_tmp)) {
    -		ShowError("itemdb_read_randomopt_group: Invalid ID for Random Option Group '%s'.\n", str[0]);
    -		return false;
    +		// autogenerate constant, it gets annoying to define it everytime
    +		script_set_constant(trim(str[0]), add_str(trim(str[0])), false, false);
    +		
    +		if (!script_get_constant(trim(str[0]), &id_tmp)) {	// Should never happen?
    +			ShowError("itemdb_read_randomopt_group: Invalid ID for Random Option Group '%s'.\n", str[0]);
    +			return false;
    +		}
     	}
     
    -	id = static_cast<int>(id_tmp);
    -
    -	if ((columns-2)%3 != 0) {
    -		ShowError("itemdb_read_randomopt_group: Invalid column entries '%d'.\n", columns);
    +	if (!script_get_constant(str[2], &randid_tmp) || ((randid = static_cast<int>(randid_tmp)) && !itemdb_randomopt_exists(randid))) {
    +		ShowError("itemdb_read_randomopt_group: Invalid random group id '%s'!\n", str[2]);
     		return false;
     	}
     
    +	id = static_cast<int>(id_tmp);
    +
     	if (!(g = (struct s_random_opt_group *)uidb_get(itemdb_randomopt_group, id))) {
     		CREATE(g, struct s_random_opt_group, 1);
    -		g->id = id;
     		g->total = 0;
     		g->entries = NULL;
    -		uidb_put(itemdb_randomopt_group, g->id, g);
    +		g->total_weight = 0;
    +		uidb_put(itemdb_randomopt_group, id, g);
     	}
     
    -	RECREATE(g->entries, struct s_random_opt_group_entry, g->total + rate);
    +	RECREATE(g->entries, struct s_random_opt_group_entry, g->total + 1);
     
    -	for (i = g->total; i < (g->total + rate); i++) {
    -		int j, k;
    -		memset(&g->entries[i].option, 0, sizeof(g->entries[i].option));
    -		for (j = 0, k = 2; k < columns && j < MAX_ITEM_RDM_OPT; k+=3) {
    -			int64 randid_tmp;
    -			int randid = 0;
    +	memset(&g->entries[g->total], 0, sizeof(g->entries[g->total]));
    +	
    +	g->entries[g->total].option.id = randid;
    +	g->entries[g->total].option.value = (short)strtoul(str[3], NULL, 10);
    +	g->entries[g->total].option.param = (char)strtoul(str[4], NULL, 10);
    +	g->entries[g->total].weight = rate;
     
    -			if (!script_get_constant(str[k], &randid_tmp) || ((randid = static_cast<int>(randid_tmp)) && !itemdb_randomopt_exists(randid))) {
    -				ShowError("itemdb_read_randomopt_group: Invalid random group id '%s' in column %d!\n", str[k], k+1);
    -				continue;
    +	if (columns > 5) {
    +		g->entries[g->total].max_value = max((short)strtoul(str[5], NULL, 10), g->entries[g->total].option.value);
    +	}
    +
    +	g->total_weight += rate;
    +	g->total++;
    +	return true;
    +}
    +
    +/**
    +* Read Item Random Option Group from db file
    +**/
    +static bool itemdb_read_randomopt_equip(char* str[], int columns, int current) {
    +	unsigned short nameid;
    +	int64 group_id_tmp;
    +	int group_id = -1;
    +	struct item_data *item = NULL;
    +	int i;
    +
    +	nameid = atoi(str[0]);
    +
    +	for (i = 1; i < columns; i++) {
    +		if (ISDIGIT(str[i][0])) {
    +			group_id = (int)strtoul(str[i], NULL, 10); 
    +		}
    +		else {
    +			if (!script_get_constant(trim(str[i]), &group_id_tmp)) {
    +				ShowError("itemdb_read_randopt_equip: Invalid constant '%s'!\n", str[i]);
    +				return false;
     			}
    -			g->entries[i].option[j].id = randid;
    -			g->entries[i].option[j].value = (short)strtoul(str[k+1], NULL, 10);
    -			g->entries[i].option[j].param = (char)strtoul(str[k+2], NULL, 10);
    -			j++;
    +
    +			group_id = static_cast<int>(group_id_tmp);
    +		}
    +
    +		if ((item = itemdb_exists(nameid)) == NULL) {
    +			ShowWarning("itemdb_read_randopt_equip: Invalid item ID %hu.\n", nameid);
    +			return false;
     		}
    +
    +		if (!itemdb_randomopt_group_exists(group_id)) {
    +			ShowWarning("itemdb_read_randopt_equip: Invalid random option group ID %hu.\n", group_id);
    +			return false;
    +		}
    +
    +		item->randomopt_groupid[i - 1] = group_id;
     	}
    -	g->total += rate;
    +
     	return true;
     }
     
    +/**
    +* Sets random options to a weapon, if it has any.
    +**/
    +void itemdb_add_randomopt(struct item *it) {
    +	struct item_data *id;
    +	struct s_random_opt_group *randomopt_group;
    +	int i;
    +
    +	if (!it || !it->nameid || (id = itemdb_exists(it->nameid)) == NULL) {
    +		return;
    +	}
    +
    +	for (i = 0; i < MAX_ITEM_RDM_OPT; i++) {
    +		if (id->randomopt_groupid[i] && ((randomopt_group = itemdb_randomopt_group_exists(id->randomopt_groupid[i])) != NULL)) {
    +			int rnd_value = rnd() % (randomopt_group->total_weight);
    +			int total = 0;
    +			int j;
    +
    +			for (j = 0; j < randomopt_group->total; j++) {
    +				total += randomopt_group->entries[j].weight;
    +
    +				if (rnd_value < total) {
    +					it->option[i].id = randomopt_group->entries[j].option.id;
    +
    +					if (randomopt_group->entries[j].max_value > 0 && randomopt_group->entries[j].max_value != randomopt_group->entries[j].option.value) {
    +						int max = randomopt_group->entries[j].max_value;
    +						int min = randomopt_group->entries[j].option.value;
    +						
    +						it->option[i].value = rnd() % (max - min + 1) + min;
    +					}
    +					else {
    +						it->option[i].value = randomopt_group->entries[j].option.value;
    +					}
    +
    +					it->option[i].param = randomopt_group->entries[j].option.param;
    +					break;
    +				}
    +			}
    +		}
    +	}
    +}
    +
     /**
     * Read all item-related databases
     */
    @@ -1881,7 +1966,8 @@ static void itemdb_read(void) {
     		sv_readdb(dbsubpath2, "item_delay.txt",         ',', 2, 3, -1, &itemdb_read_itemdelay, i > 0);
     		sv_readdb(dbsubpath2, "item_buyingstore.txt",   ',', 1, 1, -1, &itemdb_read_buyingstore, i > 0);
     		sv_readdb(dbsubpath2, "item_flag.txt",          ',', 2, 2, -1, &itemdb_read_flag, i > 0);
    -		sv_readdb(dbsubpath2, "item_randomopt_group.txt", ',', 5, 2+5*MAX_ITEM_RDM_OPT, -1, &itemdb_read_randomopt_group, i > 0);
    +		sv_readdb(dbsubpath2, "item_randomopt_group.txt", ',', 5, 6, -1, &itemdb_read_randomopt_group, i > 0);
    +		sv_readdb(dbsubpath2, "item_randomopt_equips.txt",',', 2, 1 + MAX_ITEM_RDM_OPT, -1, &itemdb_read_randomopt_equip, i > 0);
     		aFree(dbsubpath1);
     		aFree(dbsubpath2);
     	}
    diff --git a/src/map/itemdb.hpp b/src/map/itemdb.hpp
    index 546d4ef14..af460a82c 100644
    --- a/src/map/itemdb.hpp
    +++ b/src/map/itemdb.hpp
    @@ -865,6 +865,7 @@ struct item_data
     
     	bool isStackable();
     	int inventorySlotNeeded(int quantity);
    +	unsigned int randomopt_groupid[MAX_ITEM_RDM_OPT];
     };
     
     // Struct for item random option [Secret]
    @@ -882,14 +883,16 @@ enum Random_Option_Group {
     
     /// Struct for random option group entry
     struct s_random_opt_group_entry {
    -	struct s_item_randomoption option[MAX_ITEM_RDM_OPT];
    +	struct s_item_randomoption option;
    +	short max_value;
    +	int weight;
     };
     
     /// Struct for Random Option Group
     struct s_random_opt_group {
    -	uint8 id;
     	struct s_random_opt_group_entry *entries;
     	uint16 total;
    +	int total_weight;
     };
     
     struct item_data* itemdb_searchname(const char *name);
    @@ -964,6 +967,7 @@ bool itemdb_parse_roulette_db(void);
     
     struct s_random_opt_data *itemdb_randomopt_exists(short id);
     struct s_random_opt_group *itemdb_randomopt_group_exists(int id);
    +void itemdb_add_randomopt(struct item *it);
     
     void itemdb_reload(void);
     
    diff --git a/src/map/mob.cpp b/src/map/mob.cpp
    index f48c4d942..37e016f65 100644
    --- a/src/map/mob.cpp
    +++ b/src/map/mob.cpp
    @@ -2119,7 +2119,7 @@ static struct item_drop* mob_setdropitem(struct s_mob_drop *mobdrop, int qty, un
     	drop->item_data.nameid = mobdrop->nameid;
     	drop->item_data.amount = qty;
     	drop->item_data.identify = itemdb_isidentified(mobdrop->nameid);
    -	mob_setdropitem_option(&drop->item_data, mobdrop);
    +	itemdb_add_randomopt(&drop->item_data);
     	drop->mob_id = mob_id;
     	drop->next = NULL;
     	return drop;
    @@ -2931,7 +2931,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
     					intif_broadcast(message,strlen(message)+1,BC_DEFAULT);
     				}
     
    -				mob_setdropitem_option(&item, &mdrop[i]);
    +				itemdb_add_randomopt(&item);
     
     				if((temp = pc_additem(mvp_sd,&item,1,LOG_TYPE_PICKDROP_PLAYER)) != 0) {
     					clif_additem(mvp_sd,0,0,temp);
    diff --git a/src/map/pc.cpp b/src/map/pc.cpp
    index 8f609f505..ce4eb55e3 100755
    --- a/src/map/pc.cpp
    +++ b/src/map/pc.cpp
    @@ -5632,7 +5632,7 @@ bool pc_steal_item(struct map_session_data *sd,struct block_list *bl, uint16 ski
     	tmp_item.nameid = itemid;
     	tmp_item.amount = 1;
     	tmp_item.identify = itemdb_isidentified(itemid);
    -	mob_setdropitem_option(&tmp_item, &md->db->dropitem[i]);
    +	itemdb_add_randomopt(&tmp_item);
     	flag = pc_additem(sd,&tmp_item,1,LOG_TYPE_PICKDROP_PLAYER);
     
     	//TODO: Should we disable stealing when the item you stole couldn't be added to your inventory? Perhaps players will figure out a way to exploit this behaviour otherwise?
    diff --git a/src/map/script.cpp b/src/map/script.cpp
    index 5af99c5a5..f99bff45e 100644
    --- a/src/map/script.cpp
    +++ b/src/map/script.cpp
    @@ -7438,6 +7438,8 @@ BUILDIN_FUNC(getitem)
     		// if not pet egg
     		if (!pet_create_egg(sd, nameid))
     		{
    +			itemdb_add_randomopt(&it);
    +
     			if ((flag = pc_additem(sd, &it, get_count, LOG_TYPE_SCRIPT)))
     			{
     				clif_additem(sd, 0, 0, flag);

    Of course, this system isn't compatible with the default one on rAthena, so you'll have to modify your current random option weapons if you have any. Goodluck!

     

    option.diff

  17. Heya,

    Don't use timers that run scripts on players (you should avoid using the addtimer command entirely). The reason for that is you're attempting to run two scripts at once, and the script engine doesn't support that very well. Set the timer as a local variable instead:

    	mes "type anything in 5 seconds";
    	.@limit = gettimetick(0) + 5000;
    	input(.@text);
    	clear();
    	
    	if (gettimetick(0) >= .@limit) {
    		mes "time over";
    		close;
    	}
    	
    	mes "you did it";
    	close;

     

    • Upvote 1
    • MVP 1
  18. 24 minutes ago, Nuck said:
    
    #Log : Batch file detected an invalid encoding, changing command line arguments encoding to 1252
    #Error : An exception has been thrown
    #Error : Given command line : .\GrfCL.exe  -encoding 949 -open C:/patch/example.grf -extractFiles "" ""
    #Error : Command being executed : -encoding  <949>
    #Exception : The handle is invalid.

    Still don't works

    Looks like only the first command is execute

    Hmm, well the following command worked fine on Windows:

    GrfCL.exe -encoding 949 -open "C:\Program Files (x86)\GRF Editor\test.grf" -extractFiles "" ""

    Perhaps you do not have the 949 codepage on your machine. You could test by opening GrfCL and typing the encoding command directly:

    .\GrfCL.exe
    Commands> encoding 949
    #Log : Extraction and files added will now use this encoding : ks_c_5601-1987

    If you get an error with the above, you'll have to use a different encoding or keep it as the default one.

  19. 11 hours ago, Nuck said:

    Hey, can help me please

     

    I trying execute cli with just one line command, with -enc but don't work

     

    works fine:

    
    #Log : Batch file detected an invalid encoding, changing command line arguments encoding to 1252
    #Log : Opened GRF C:/patch/example.grf
    #Error : An exception has been thrown
    #Error : Given command line : .\GrfCL.exe  -open C:/patch/example.grf -extractFiles "" ""
    #Error : Command being executed : -extractFiles  <> <>
    #Exception : ??? ???????.

     

    don't work:

    
    #Log : Batch file detected an invalid encoding, changing command line arguments encoding to 1252
    #Error : An exception has been thrown
    #Error : Given command line : .\GrfCL.exe  -encode 949 -open C:/patch/example.grf -extractFiles "" ""
    #Error : Command being executed : -encode  <949>
    #Exception : 'C:\patch\949' ??? ?? ? ????.

     

    thx

    The command you're looking for is

    -encoding 949

     

  20. 3 hours ago, Igniz said:

    Hello, it seems that using certain 2018 clients (tested with 20180621 and 0620) the name of the custom DLL at the encripting function is ignored. It always read cps.dll despite of the custom name of the dll.

    Thanks for your awesome tool, hope this get fixed.

    The "cps.dll" string is there twice in the client executable, the tool only expects one. So you need to change one of them manually before using the tool, or you can just keep cps.dll as the name of the file.

  21. 34 minutes ago, IsabelaFernandez said:

    http://prntscr.com/peydl0

     

    Always gives error when trying to open the ItemInfo file from the system folder ... I would like to decompile this iteminfo: 

    iteminfo_sak.lubUnavailable

     

    iteminfo_sak.lub 4.83 MB · 1 download

    You are trying to open the file as a GRF, that will cause you errors. You have to open an already existing GRF or make a new one, then add the iteminfo and click on it to decompile it. It's a GRF tool, it's able to read and decompile lub files as a preview feature, but it's not its main purpose.

    • Upvote 1
    • MVP 1
×
×
  • Create New...