Jump to content

Tokei

Members
  • Posts

    666
  • Joined

  • Last visited

  • Days Won

    91

Everything posted by Tokei

  1. There are no easy ways of doing this, OnPCDieEvent is annoying to use. I would try the following instead: 1@tower,0,0,0 script #ins_die_event_sub -1,{ end; OnPartyCheck: 'player_alive = 0; getpartymember 'party_id, 2; for (.@i = 0; .@i < $@partymembercount && !'player_alive; .@i++) { if (isloggedin($@partymemberaid[.@i]) && $@partymemberaid[.@i] != 'ins_rid) { // Logged out players are still active if (attachrid($@partymemberaid[.@i])) { if (HP > 0 && strcharinfo(3) == instance_mapname("1@tower")) { 'player_alive++; } detachrid(); } } } // Run code back on the instance if ('player_alive == 0) { announce "All players have died.", 0; } end; } - script #ins_die_event -1,{ end; OnPCDieEvent: OnPCLogoutEvent: if (strcharinfo(3) == instance_mapname("1@tower")) { // Do not perform the party checks here directly, this script // is never attached to the instance. 'party_id = getcharid(1); 'ins_rid = getattachedrid(); donpcevent instance_npcname("#ins_die_event_sub") + "::OnPartyCheck"; } end; } There are few things you have to be very careful with such scripts. OnPCDieEvent is a global event label which is not attached to your instance nor is it duplicated. All players who die on your server will trigger this event label, hence why you have to check if the player has the instance (and why you should not put the NPC in the instance map). The usage of a donpcevent is to force the script to run back on the instance after it's detached from the players' checks. You also cannot rely on $@partymembercount alone since some members could be offline in the party. Players can also leave the party or logout, which you have to take into account.
  2. Heya, you have to set the properties for each mob: deletearray $@mobid[0],getarraysize($@mobid); monster "prontera",156,176,"Omega poring",1002,2; for (.@i = 0; .@i < getarraysize($@mobid); .@i++) { setunitdata $@mobid[.@i],UMOB_MAXHP,(50*.@mp); setunitdata $@mobid[.@i],UMOB_HP,(50*.@mp); setunitdata $@mobid[.@i],UMOB_ATKMIN,300; setunitdata $@mobid[.@i],UMOB_ATKMAX,300; } deletearray $@mobid[0],getarraysize($@mobid); monster "prontera",156,176,"Fracus",1758,3; for (.@i = 0; .@i < getarraysize($@mobid); .@i++) { setunitdata $@mobid[.@i],UMOB_MAXHP,(100*.@mp); setunitdata $@mobid[.@i],UMOB_HP,(100*.@mp); setunitdata $@mobid[.@i],UMOB_MODE,12433; }
  3. You can try the following: - script fishvendor -1,{ .@n$ = "[Fish Vendor]"; if (checkweight(1201, 1) == 0) { mes .@n$, "You have too many items in your inventory!"; close; } mes .@n$, "Hi there, if you went fishing, I can buy your rare catch!"; next; mes .@n$, "If you caught a Shark, Whale, or Octopus, I can give you 10 Fish Market Receipt for each of them"; next; mes .@n$, "Here's the list of the fishes I want to buy"; next; setarray .@items, 32189, 32195, 32191, 32190, 32180, 32181, 32182, 32183, 32186, 32187, 32188, 32192, 32193, 32184, 32185, 32194; setarray .@amounts, 10, 10, 10, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1; setarray .@rewards, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 10, 10, 10; for (.@i = 0; .@i < getarraysize(.@items); .@i++) { // Optional, but I usually make such items with blue links so that players click the right ones more easily if (countitem(.@items[.@i]) >= .@amounts[.@i]) .@menu$ = .@menu$ + "^0000ff" + .@amounts[.@i] + "x " + getitemname(.@items[.@i]) + "^000000:"; else .@menu$ = .@menu$ + .@amounts[.@i] + "x " + getitemname(.@items[.@i]) + ":"; } .@opt = select(.@menu$) - 1; if (.@opt < 0 || .@opt >= getarraysize(.@items)) end; .@item = .@items[.@opt]; .@amount = .@amounts[.@opt]; if (countitem(.@item) < .@amount) { mes .@n$, "Sorry but you don't have enough " + getitemname(.@item) + " with you"; close; } delitem .@item, .@amount; getitem 32196, .@rewards[.@opt]; mes .@n$, "Thanks! Here's your Fish Market Receipt!"; close; } You may want to double check the receipts to make sure all the values are right.
  4. You're attaching a timer to a player, that's where the issue is. A player cannot run two scripts at the same time, so it stops the first script (the one where the player talks to an NPC with a dialogue, for instance) by adding a close button. Then it attaches the player on your OnTimer60000 script event. You'll have to change your approach.
  5. @xtian711 The link you're looking for is http://www.mediafire.com/?aflylbhblrzpz0h , it is public.
  6. 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
  7. You can use the mediafire link : http://www.mediafire.com/?aflylbhblrzpz0h
  8. There's a changelog in the page: Then again, there are way more changes than that, 1.7.5 was quite a while ago. I only put those I remembered and uploaded it to fix the broken link. Nothing major was changed though.
  9. Hmmm weird! Reuploaded.
  10. @Emistry I wrote this script as a generic example. When you convert the NPCs to instance NPCs, you just change the global variables to instance variables. I usually put mine as "$et_<name_here>" for endless tower, then replace all to " 'et_". It's faster for me when I want to test a specific part of an instance @@. But... we don't know what Hijirikawa is going to use that for. I do want the variable to be accessible from all the NPCs though; if you aren't using duplicates (the OP could be using 4 completely different NPCs with no code being shared at all), then NPC variables won't do. Well, I made a "setvariableofnpc" command which would solve that particular issue. With that said though, my solution works in all scenarios, even if you change your code midway for another approach. Skrom's algorithm is also in n²; it works fine with 4 NPCs, but it can get very slow if you plan on duplicating more of them. There's a lot of repeated code being ran here too (sorry >.<'). Though, if you really want to use NPC variables, I don't think you can do it another way (so I still wouldn't go for it on my end xD).
  11. @Skorm It really depends on what your goal is; I stay away from npc variables as much as possible for a few reasons. I'm usually scripting for instances and NPC variables won't work as expected in such a case (the variables will be shared across instances if you use duplicates). I personally don't like having the OnInit label on duplicate NPCs either since the event gets called multiple times; that's what you're relying on here though xD. I usually end up adding code in the init part and I don't want it to be repeated everytime a duplicate NPC gets loaded. And... you have to be careful while using strnpcinfo if you're having a floating base NPC, such as: - script NPC_base -1,{ .@id = atoi(strnpcinfo(2)); dispbottom "Variable " + (.@id + 1) + ": " + .npc_variables[.@id]; end; OnInit: .@len = getarraysize(.npc_variables); .npc_variables[.@len] = atoi(strnpcinfo(2)) + 1; //... } prontera,151,183,0 duplicate(NPC_base) NPC#0 77 prontera,153,183,0 duplicate(NPC_base) NPC#1 77 prontera,155,183,0 duplicate(NPC_base) NPC#2 77 prontera,157,183,0 duplicate(NPC_base) NPC#3 77 Also, in that script, you'll end up with OnInit being called 5 times (that's not the case in your sample, but that's just a mistake I don't want to have to deal with if I use floating NPCs). Anyway! It's just a preference for me. I make less mistakes that way and it's pretty much an habit now ;P.
  12. 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 (.@i = getarraysize($npc_values) - 1; .@i > 0; .@i--) { .@j = rand(.@i + 1); .@temp = $npc_values[.@i]; $npc_values[.@i] = $npc_values[.@j]; $npc_values[.@j] = .@temp; } end; } This is handy if you plan on duplicating your NPCs, such as: prontera,151,183,0 script NPC#0 77,{ .@id = atoi(strnpcinfo(2)); dispbottom "Variable " + (.@id + 1) + ": " + $npc_values[.@id]; 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 (.@i = getarraysize($npc_values) - 1; .@i > 0; .@i--) { .@j = rand(.@i + 1); .@temp = $npc_values[.@i]; $npc_values[.@i] = $npc_values[.@j]; $npc_values[.@j] = .@temp; } 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
  13. Welp, the issue is somewhere else then. Check the level requirement, the upper field, the item type or the location.
  14. Use setarray .@maps$, "prontera", "geffen", "izlude"; // An array of maps. .@map$ = .@maps$[rand(getarraysize(.@maps$))]; monster .@map$,0,0,"--ja--",1115,1,"Event_Spawn::OnEvent"; // The monster. for a random map.
  15. Packet length 19 is the wanttoconnection packet. The issue is (almost always) caused by issues with packet obfuscation. Make sure you've recompiled your server and that you've disabled the feature on your client when you diffed it. Edit: That's the easy solution, the ideal one would be to turn on the packet obfuscation, set custom keys and change them in your executable.
  16. Those are in hexadecimal numbers, you can't just add them together like that. Open your windows calculator and use view > programmer. Select hex and then add those values together. You'll end up with 0x03A00000.
  17. 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.
  18. 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)) { .@guild_id = getcharid(2); if (BaseLevel < 175 || .@guild_id == 0 || getguildinfo(.@guild_id, 0) < 43 || getguildinfo(.@guild_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;
  19. 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.
  20. You can try this script: function script DivToDecimal { .@x = getarg(0); .@y = getarg(1); .@precision = 2; // The amount of digits to keep after the dot .@mod = pow(10, .@precision); .@left = .@x / .@y; .@right = (.@x * .@mod / .@y) % .@mod; .@result$ = "" + .@left; if (!.@right) return .@result$; // Removes trailing zeroes, for example: // With .@zeroes set to 0, 5 / 2 will output 2.5 // With .@zeroes set to 1, 5 / 2 will output 2.50 .@zeroes = 0; for (.@i = 0; .@i < .@precision; .@i++) { .@digit = .@right % 10; .@right = .@right / 10; if (!.@zeroes) { if (.@digit == 0) continue; .@output$ = insertchar(.@output$, .@digit + "", 0); .@zeroes = 1; } else { .@output$ = insertchar(.@output$, .@digit + "", 0); } } return .@result$ + "." + .@output$; } prontera,154,161,0 script TestNPC 567,{ dispbottom "Your number is: " + callfunc("DivToDecimal", 5, 2); end; } Be careful with overflows if your numbers are too high (this script breaks easily, but for simple divisions it will work out fine). Edit: If you must store the result of your division in a variable, you'll have to do some dirty tricks, for example: Something = 5 * 100 / 2; //... dispbottom "There are " + callfunc("DivToDecimal", Something, 100);
  21. Tokei

    @changesex

    The command you're looking for is @changecharsex.
  22. 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!
  23. 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
  24. This feature is for other purposes! It will not create a thor patch. The problem is that your file names are not in the proper encoding. Download GRF Editor, go in File > New > New Thor. Click on 'root' > Container options (on the right side panel) > Set the Patching mode to Merge into GRF. Then drag and drop your files, save, and you should be able to patch properly. I'm guessing you're using Korean based file names, so you might want to change the encoding to Korean (Tools > Settings > General > Display encoding). Though this part doesn't change anything other than the language displayed.
×
×
  • Create New...