-
Posts
545 -
Joined
-
Last visited
-
Days Won
66
Community Answers
-
Tokei's post in [ERROR] disconnect packet version (p:0x70ee,1:19) unknown was marked as the answer
As others have pointed out, this is caused by the packet obfuscation not being set properly. You have to either
enable it on both the client and the emulator, using the same exact packet keys, or disable it on both end (not recommended in the long run, but it's easier to do when starting). To disable packet obfuscation:
Client side: Open NEMO and load the client. You'll find the "Disable packet obfuscation" patch that you must appl Server side: Open src\config\core.h and uncomment "#define PACKET_OBFUSCATION" so that you get //#define PACKET_OBFUSCATION Then recompile your server.
Make sure you've set the correct client date in src\common\mmo.h > #define PACKETVER 20151104 (or whatever client version you're using, but 55 should be around that area).
If you're using a client past 2015-11-04 and that you still can't connect, then it would mean you need a higher packet version (56 and above). Unfortunately, rAthena doesn't have those packets in their repo yet so you're out of luck! You'll have to use PEEK and figure those out.
-
Tokei's post in clientinfo.xml ERROR was marked as the answer
Heya, this error can only happen when you try to extract a file. This is very common for encrypted files (they cannot be extracted) but it can also happen if the file inside the GRF is broken. The thread will keep extracting the rest of the files (if you're trying to extract more than one) and only the last error will be shown. You'd have to give us more details on what you're doing here.
-
Tokei's post in Sprite (.spr) file structure was marked as the answer
You can check roBrowser's sprite loading; it's very clear and on point: https://github.com/vthibault/roBrowser/blob/master/src/Loaders/Sprite.js
-
Tokei's post in While loop inside instance causes map error on destroy was marked as the answer
Did you update your sources to the latest revision? This commit should have fixed your issue : https://github.com/rathena/rathena/commit/afa880ebd6bd55c2ecaa479cf00697db568ab353
(I'm unable to reproduce your problem.)
If that didn't work or if you're already fully up to date, could you provide a bigger script sample?
-
Tokei's post in Setunitdana make only one mob altered was marked as the answer
Heya, you have to set the properties for each mob:
deletearray [email protected][0],getarraysize([email protected]); monster "prontera",156,176,"Omega poring",1002,2; for ([email protected] = 0; [email protected] < getarraysize([email protected]); [email protected]++) { setunitdata [email protected][[email protected]],UMOB_MAXHP,(50*[email protected]); setunitdata [email protected][[email protected]],UMOB_HP,(50*[email protected]); setunitdata [email protected][[email protected]],UMOB_ATKMIN,300; setunitdata [email protected][[email protected]],UMOB_ATKMAX,300; } deletearray [email protected][0],getarraysize([email protected]); monster "prontera",156,176,"Fracus",1758,3; for ([email protected] = 0; [email protected] < getarraysize([email protected]); [email protected]++) { setunitdata [email protected][[email protected]],UMOB_MAXHP,(100*[email protected]); setunitdata [email protected][[email protected]],UMOB_HP,(100*[email protected]); setunitdata [email protected][[email protected]],UMOB_MODE,12433; }
-
Tokei's post in Script Optimization Request was marked as the answer
You can try the following:
- script fishvendor -1,{ [email protected]$ = "[Fish Vendor]"; if (checkweight(1201, 1) == 0) { mes [email protected]$, "You have too many items in your inventory!"; close; } mes [email protected]$, "Hi there, if you went fishing, I can buy your rare catch!"; next; mes [email protected]$, "If you caught a Shark, Whale, or Octopus, I can give you 10 Fish Market Receipt for each of them"; next; mes [email protected]$, "Here's the list of the fishes I want to buy"; next; setarray [email protected], 32189, 32195, 32191, 32190, 32180, 32181, 32182, 32183, 32186, 32187, 32188, 32192, 32193, 32184, 32185, 32194; setarray [email protected], 10, 10, 10, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1; setarray [email protected], 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 10, 10, 10; for ([email protected] = 0; [email protected] < getarraysize([email protected]); [email protected]++) { // Optional, but I usually make such items with blue links so that players click the right ones more easily if (countitem([email protected][[email protected]]) >= [email protected][[email protected]]) [email protected]$ = [email protected]$ + "^0000ff" + [email protected][[email protected]] + "x " + getitemname([email protected][[email protected]]) + "^000000:"; else [email protected]$ = [email protected]$ + [email protected][[email protected]] + "x " + getitemname([email protected][[email protected]]) + ":"; } [email protected] = select([email protected]$) - 1; if ([email protected] < 0 || [email protected] >= getarraysize([email protected])) end; [email protected] = [email protected][[email protected]]; [email protected] = [email protected][[email protected]]; if (countitem([email protected]) < [email protected]) { mes [email protected]$, "Sorry but you don't have enough " + getitemname([email protected]) + " with you"; close; } delitem [email protected], [email protected]; getitem 32196, [email protected][[email protected]]; mes [email protected]$, "Thanks! Here's your Fish Market Receipt!"; close; } You may want to double check the receipts to make sure all the values are right.
-
Tokei's post in ReqshadowFactor Error on client opening was marked as the answer
This is usually because you're using outdated lua files. The files you want to update are:
data\luafiles514\lua files\datainfo\shadowtable.lub
data\luafiles514\lua files\datainfo\shadowtable_f.lub
-
Tokei's post in Variable for each NPC [ random ] was marked as the answer
There are multiple ways to approach this. I personally find it easier to make a global variable containing all your values, and then having the NPCs access the array. For example:
prontera,151,183,0 script NPC1 77,{ dispbottom "Variable 1: " + $npc_values[0]; end; } prontera,153,183,0 script NPC2 77,{ dispbottom "Variable 2: " + $npc_values[1]; end; } prontera,155,183,0 script NPC3 77,{ dispbottom "Variable 3: " + $npc_values[2]; end; } prontera,157,183,0 script NPC4 77,{ dispbottom "Variable 4: " + $npc_values[3]; end; } - script yourscript_main -1,{ end; OnInit: cleararray $npc_values, 0, 50; setarray $npc_values, 1, 2, 3, 4; // Shuffle the array for ([email protected] = getarraysize($npc_values) - 1; [email protected] > 0; [email protected]) { [email protected] = rand([email protected] + 1); [email protected] = $npc_values[[email protected]]; $npc_values[[email protected]] = $npc_values[[email protected]]; $npc_values[[email protected]] = [email protected]; } end; }
This is handy if you plan on duplicating your NPCs, such as:
prontera,151,183,0 script NPC#0 77,{ [email protected] = atoi(strnpcinfo(2)); dispbottom "Variable " + ([email protected] + 1) + ": " + $npc_values[[email protected]]; end; } prontera,153,183,0 duplicate(NPC#0) NPC#1 77 prontera,155,183,0 duplicate(NPC#0) NPC#2 77 prontera,157,183,0 duplicate(NPC#0) NPC#3 77 - script yourscript_main -1,{ end; OnInit: cleararray $npc_values, 0, 50; setarray $npc_values, 1, 2, 3, 4; // Shuffle the array for ([email protected] = getarraysize($npc_values) - 1; [email protected] > 0; [email protected]) { [email protected] = rand([email protected] + 1); [email protected] = $npc_values[[email protected]]; $npc_values[[email protected]] = $npc_values[[email protected]]; $npc_values[[email protected]] = [email protected]; } end; }
If you plan on shuffling the arrays often, you can use the following custom script command:
// script.c BUILDIN_FUNC(shufflearray) { struct script_data* data; const char* name; struct map_session_data* sd = NULL; int array_size, i, j; int32 id; void * temp_val; // Copy-paste from getarraysize data = script_getdata(st, 2); if (!data_isreference(data)) { ShowError("buildin_shufflearray: not a variable\n"); script_reportdata(data); script_pushnil(st); st->state = END; return SCRIPT_CMD_FAILURE;// not a variable } name = reference_getname(data); id = reference_getid(data); if (not_server_variable(*name)) { sd = script_rid2sd(st); if (sd == NULL) return SCRIPT_CMD_SUCCESS;// no player attached } array_size = script_array_highest_key(st, sd, reference_getname(data), reference_getref(data)); // Start shuffling the array for (i = array_size - 1; i > 0; i--) { j = rand() % (i + 1); temp_val = get_val2(st, reference_uid(id, i), reference_getref(data)); script_removetop(st, -1, 0); set_reg(st, sd, reference_uid(id, i), name, get_val2(st, reference_uid(id, j), reference_getref(data)), reference_getref(data)); script_removetop(st, -1, 0); set_reg(st, sd, reference_uid(id, j), name, temp_val, reference_getref(data)); } return SCRIPT_CMD_SUCCESS; } // def BUILDIN_DEF(shufflearray,"r"), and use it as follow:
- script yourscript_main -1,{ end; OnInit: cleararray $npc_values, 0, 50; setarray $npc_values, 1, 2, 3, 4; shufflearray $npc_values; end; } If you need something more specific, you'll have to give us more details xD
-
Tokei's post in areakill command was marked as the answer
Well it's pretty straightforward:
//script.c static int buildin_areakill_sub(struct block_list *bl,va_list ap) { status_kill(bl); return 0; } BUILDIN_FUNC(areakill) { const char *mapname; int16 m; int x0, y0, x1, y1; mapname = script_getstr(st,2); if ((m = map_mapname2mapid(mapname)) < 0) return SCRIPT_CMD_FAILURE; x0 = script_getnum(st,3); y0 = script_getnum(st,4); x1 = script_getnum(st,5); y1 = script_getnum(st,6); map_foreachinarea(buildin_areakill_sub,m,x0,y0,x1,y1,BL_MOB); // If you want to kill players as well, add BL_PC: //map_foreachinarea(buildin_areakill_sub,m,x0,y0,x1,y1,BL_MOB|BL_PC); return SCRIPT_CMD_SUCCESS; } // def BUILDIN_DEF(areakill,"siiii"), I'm not sure if you wanted to kill mobs or players, or both.
-
Tokei's post in Online Guild Member was marked as the answer
It's easier to create custom script commands (it is more reliable than using SQL and it is faster). Plus, you'll be able to have more freedom if you want to add more guild options:
script.c /*========================================== *------------------------------------------*/ BUILDIN_FUNC(getguildinfo) { struct guild *g = NULL; int result = 0; if ((g = guild_search(script_getnum(st,2))) != NULL) { int type = script_getnum(st,3); switch(type) { case 0: result = g->guild_lv; break; case 1: result = g->connect_member; break; case 2: script_pushstrcopy(st, g->name); return SCRIPT_CMD_SUCCESS; //case 3: // etc... // break; default: ShowError("buildin_getguildinfo: unknown type '%d'.\n", type); script_pushint(st, 0); return SCRIPT_CMD_FAILURE; } } script_pushint(st, result); return SCRIPT_CMD_SUCCESS; } def method: BUILDIN_DEF(getguildinfo,"ii"),
Recompile your server, then your event would be:
OnPCLoadMapEvent: if (getmapflag(strcharinfo(3), mf_gvg_castle)) { [email protected]_id = getcharid(2); if (BaseLevel < 175 || [email protected]_id == 0 || getguildinfo([email protected]_id, 0) < 43 || getguildinfo([email protected]_id, 1) < 7) { dispbottom "[ Server ]: You don't have a guild or your Base Level isn't high enough.", 0x00ff00; warp "prontera", 155, 182; } } end;
-
Tokei's post in How to change folder and file to korea language was marked as the answer
Well, why do you need to change the file names to Korean? If you want to test locally in your client (using a data folder), it won't read Korean filenames. If you want to merge them in your GRF, GRF Editor will convert the names for you. So it's easier to not work in Korean.
-
Tokei's post in @changesex was marked as the answer
The command you're looking for is @changecharsex.
-
Tokei's post in How to create a new Mapflag? was marked as the answer
To make the mapflag usable with @mapflag, open atcommand.c, ACMD_FUNC(mapflag) and add it to the list of existing flags:
checkflag(nousecart); checkflag(noitemconsumption); checkflag(nosumstarmiracle); checkflag(nomineeffect); checkflag(nolockon); checkflag(notomb); checkflag(nocostume); checkflag(gvg_te); checkflag(gvg_te_castle); checkflag(newmapflag); checkflag(newmapflag2); ... setflag(nousecart); setflag(noitemconsumption); setflag(nosumstarmiracle); setflag(nomineeffect); setflag(nolockon); setflag(notomb); setflag(nocostume); setflag(gvg_te); setflag(gvg_te_castle); setflag(newmapflag); setflag(newmapflag2); To make the mapflag readable from a script, you'll have to add the flag as a constant first:
Go in script.c, search for "MF_NOTOMB" and add your flag at the end of the list, such as:
MF_NOTOMB, MF_SKILL_DAMAGE, //60 MF_NOCOSTUME, MF_GVG_TE_CASTLE, MF_GVG_TE, MF_NEWMAPFLAG = 100, // Custom flags MF_NEWMAPFLAG2, // Custom flags }; and then add it as a script constant. Open script_constants.h and add
export_constant(MF_NOTOMB); export_constant(MF_SKILL_DAMAGE); export_constant(MF_NOCOSTUME); export_constant(MF_GVG_TE_CASTLE); export_constant(MF_GVG_TE); export_constant(MF_NEWMAPFLAG); // Custom flags export_constant(MF_NEWMAPFLAG2); Now you'll have to add the mapflag to the map structure, so go in map.h:
unsigned nocostume : 1; // Disable costume sprites [Cydh] unsigned newmapflag : 1; // Custom flag unsigned newmapflag2 : 1; // Custom flag2 unsigned gvg_te : 1; // GVG WOE:TE. This was added as purpose to change 'gvg' for GVG TE, so item_noequp, skill_nocast exlude GVG TE maps from 'gvg' (flag &4) unsigned gvg_te_castle : 1; // GVG WOE:TE Castle #ifdef ADJUST_SKILL_DAMAGE unsigned skill_damage : 1; #endif } flag; You have to make it so that the script engine can read your flag, so go in npc.c and ctrl-f "nowarp":
else if (!strcmpi(w3,"noteleport")) map[m].flag.noteleport=state; else if (!strcmpi(w3,"nowarp")) map[m].flag.nowarp=state; else if (!strcmpi(w3,"newmapflag")) map[m].flag.newmapflag=state; else if (!strcmpi(w3,"newmapflag2")) map[m].flag.newmapflag2=state; else if (!strcmpi(w3,"nowarpto")) map[m].flag.nowarpto=state; You should now be done, but if you want to make your flag work with the getmapflag/setmapflag/removemapflag script commands, you'll have to edit script.c:
//BUILDIN_FUNC(getmapflag) case MF_NEWMAPFLAG: script_pushint(st,map[m].flag.newmapflag); break; //BUILDIN_FUNC(setmapflag) case MF_NEWMAPFLAG: map[m].flag.newmapflag = 1; break; //BUILDIN_FUNC(removemapflag) case MF_NEWMAPFLAG: map[m].flag.newmapflag = 0; break; Now you can use your mapflag in the source with (map[m].flag.newmapflag) { ... }
Good luck ;O!
-
Tokei's post in Where to go to modify skills? was marked as the answer
Envenom's element can be found in db/(pre-)re/skill_db.txt, look for TF_POISON.
52,-2,6,1,5,0,0,10,1,no,0,0,0,weapon,0,0x0, TF_POISON,Envenom The 5 is for poison.
As for the rest, you'll have to look up how it's been implemented. Start off by checking in skill.c and you'll see soon enough that the status is applied in skill_additional_effect. You're looking for:
case TF_POISON: case AS_SPLASHER: if(!sc_start2(src,bl,SC_POISON,(4*skill_lv+10),skill_lv,src->id,skill_get_time2(skill_id,skill_lv)) && sd && skill_id==TF_POISON ) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); break; The proc chance is at (4*skill_lv+10), which is out of 100. If you want to costumize TF_POISON only, you'll have to separate it from AS_SPLASHER since they share the same poison effect.
Battle formulas (or skill damage) are usually in battle.c. Searching for TF_POISON will get you to
// renewal: if(skill_id == TF_POISON) //Additional ATK from Envenom is treated as mastery type damage [helvetica] ATK_ADD(wd.masteryAtk, wd.masteryAtk2, 15 * skill_lv); // or pre-renewal: if (skill_id == TF_POISON) ATK_ADD(wd.damage, wd.damage2, 15 * skill_lv); So the damage of 15 * skill_lv can be changed there.
SP costs and other skill requirements are found in db/(pre-re)/skill_require_db.txt.
52,0,0,12,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //TF_POISON Poison will consume 12 SP as shown on the line above.
Cast times and delays are found in db/(pre-)re/skill_cast_db.txt :
//-- TF_POISON 52,0,0,0,0,15000:20000:25000:30000:35000:40000:45000:50000:55000:60000,0,0 So here it uses the 'duration2' field, which is then used in the function above 'skill_get_time2(skill_id,skill_lv)' to get the duration of the status on the player. You can get more info from the file's header.
Edit: oops, I missed the pre-renewal part! Sorry ;x! Most of the information still works though xD
-
Tokei's post in About random different item in a array was marked as the answer
Just wanted to point out that
[email protected] = rand(0, getarraysize([email protected]_ids) - 1); is the equivalent of
[email protected] = rand(getarraysize([email protected]_ids)); Except it's easier to read xD. Also, you should avoid using temporary char-bound variables in scripts (@item_id versus [email protected]_id), those are kept until the player logs out. The usage of "set" is also deprecated, you should be using 'variable = value;".
prontera,137,203,4 script TEST::KST01 834,{ setarray [email protected]_ids, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510; for ([email protected] = 0; [email protected] < 5; [email protected]++) { [email protected] = rand(getarraysize([email protected]_ids)); getitem [email protected]_ids[[email protected]], 1; deletearray [email protected]_ids[[email protected]], 1; } end; } -
Tokei's post in how to convert image by script runner in act editor? was marked as the answer
You can simply right-click the image and convert it:
I'm not sure what you're trying to do here.
Edit: The script up there will break the SPR format. I should really remake that tool's interface, was a bit messy at the time.
-
Tokei's post in Sprite different ingame and in actOR was marked as the answer
Headgears cannot have more than 1 layer ingame. You can use Scripts > Generate sprite from selection. This will create a new layer with all your other layers merged into one (this will, however, increase the size of your spr file by a lot, especially if you're using semi-transparent images). What most people would do is to make a single image from all the black balls and move them with photoshop (well, I'm assuming they're moving).
-
Tokei's post in Setunitdata Help was marked as the answer
The GID is not supposed to be 199 (GIDs are way higher); the monster function does not return a value by default either, so I'm assuming you made a custom command? Either way, the following should work properly:
monster([email protected]$,359,294,"",2337,1); [email protected] = [email protected][0]; setunitdata [email protected], 9, 0; setunitdata [email protected], 29, 1; unitskilluseid [email protected],353,10,1; -
Tokei's post in Team viewer for recompiling using cygwin was marked as the answer
As others have already told you, you're doing it wrong.
You're applying a Linux compilation guide on Windows, this won't work (well it will but it's definitely not recommended). If you want to run your server on a Windows environment, you will need to compile it with Visual Studio, not cygwin. All the information you need can be found here: https://rathena.org/wiki/Installation_on_Windows .
For the SQL installation part, you can use https://rathena.org/wiki/SQL_Installation#Windows .
This is usually to make a local server. If you plan on making a 'real server', you'll most likely want a host which is going to be using Linux and then you'll have to use the other appropriate guides.
-
Tokei's post in How can I reduce 1 from a value in SQL? was marked as the answer
You could use the following:
UPDATE `debt` SET `zeny` = `zeny` - 1 WHERE `zeny` > 0 -
Tokei's post in Map server keeps disconnecting me was marked as the answer
The most common issue for this is packet obfuscation; it is turned on by default on rAthena and your client probably has it turned off. The 'easy' solution is to disable packet obfuscation server side (from src/config/core.h - #define PACKET_OBFUSCATION). The recommended solution would be to set your packet keys in packet_db.txt with packet_keys_use and rediff your client with those packet keys that you used.
-
Tokei's post in How to Edit this script? was marked as the answer
If I understood this right, you want all gears to be upgraded to +4, expect for accessories and headgears?
prontera,150,150,3, script +10 Refiner 813,{ for ([email protected] = EQI_ARMOR; [email protected] <= EQI_SHOES; [email protected]++) { if (!getequipisequiped([email protected])) continue; [email protected] = 4 - getequiprefinerycnt([email protected]); if ([email protected] > 0) { successrefitem [email protected], [email protected]; } } end; } -
Tokei's post in Stuck on how to connect to my own server was marked as the answer
You could have kept your previous client, but yes, that works too. You can also keep 2013-08-14; the two clients are practically the same. Step 2 isn't needed right now. Add those patches (as well as the recommended ones) while you're in NEMO :
Read Data Folder First
Skip License Screen
Load Custom lua file instead of iteminfo.lub > change it to itemInfo.lua (this is required to avoid kRO updates erasing your own files)
All the others are really up to you and how you want your client to be.
Don't forget the missing font in the System folder (actually just copy the entire folder from the link above as well, for the System folder, that will make things easier). (And remove/rename that skin folder, took me quite some time to figure out why my client wasn't loading at all when I used this full client release from nickyzai.)
-
Tokei's post in saiyan aura sprite [advance] was marked as the answer
Hmmm, cut the image in 4 and add it to your sprite? Look at the attached file.
As for the TGA format, it should be forgotten ;x. The PNG format is much more convenient for semi-transparent images.
Edit : the quality issue you're getting is perhaps related to you using Paint? It doesn't support semi-transparent images.
aura.rar
-
Tokei's post in How packets works? How read it or write one new? was marked as the answer
Heya!
WFIFOHEAD(fd, slen); WFIFOW(fd,0)=0xb4; WFIFOW(fd,2)=slen; WFIFOL(fd,4)=npcid; memcpy((char*)WFIFOP(fd,8), mes, slen-8); WFIFOSET(fd,WFIFOW(fd,2)); Let's say you want to send the message "hello", slen being 5 + 9 = 14.
First call is obvious, sets the length of the packet.
WFIFOW means "Write a Word to the socket (a Word is 2 bytes - uint16) at position 0", giving you the following raw output so far :
0xb4 0x00 - ...
It is reversed because of the endianness, which is "always" in little-endian.
WFIFOW(fd,2), same as above, giving you (14 = 0x0E) :
0xb4 0x00 - 0x0E 0x00 - ...
WFIFOL means "Write a Long to the socket (a Long is 4 bytes - uint32) at position 4", giving you the following raw output so far (let's say the npcid is 297520349 = 0x11BBCCDD) :
0xb4 0x00 - 0x0E 0x00 - 0xDD 0xCC 0xBB 0x11 - ...
WFIFOP retrieves the char pointer for the writing socket buffer at the specified position. memcpy simply copies the content of mes ("hello") to that position. Giving you...
0xb4 0x00 - 0x0E 0x00 - 0xDD 0xCC 0xBB 0x11 - 0x48 0x65 0x6c 0x6c 0x6f 0x00
WFIFOSET sets the buffer to the correct position to send it (WFIFOW actually returns a pointer to the position, so in this case it's handy because it returns slen; then again it's a bit pointless, it could have just been "WFIFOSET(fd,slen);" and it would be clearer).
/// 00b4 <packet len>.W <npc id>.L <message>.?B
This means 0x00b4 is your packet id, followed by a Word (packet length), followed by a Long (npc id), followed by a variable number of bytes (your message).
0x00b4,-1
That means the size of the packet is unknown/variable (indeed, the message can be of any size).
0x00b5,6
That means the size of the packet is always 6 bytes. In this case, the first 2 bytes are the packet ID, the next 4 bytes are the npc id, or : 00b5 <npc id>.L
The packet field can be used for something like... 0x00b5,6,clif_scriptnext,2; - this can help you retrieve the positions more easily. For example :
packet_db[sd->packet_ver][RFIFOW(fd,0) /* packet id */].pos[0] // pos[0] here is the "2" you defined earlier. Some packet_ver defines different indexes for the same packet, that's why it's useful not to use pre-defined values.
To write a new packet, you have the right idea. Find a packet not used, go in a high range to avoid further conflicts. Also there's a packet limit, not really sure where it's defined on rAthena though.