Jump to content

Winterfox

Members
  • Posts

    236
  • Joined

  • Last visited

  • Days Won

    18

Everything posted by Winterfox

  1. There are many places where people can get enchantments. You can find most by searching for "Enchant", "Enchants NPCs" or for the command "item_enchant" in the npc directory.
  2. Which version of Visual Studio? Is there also an Error message, or just the code? When exactly does this error pop up?
  3. This script will enable pvp when there is atleast one living mvp on a map. If the last mvp gets killed, it will announce the kill and end pvp until a mvp is spawned again. Optionally, you can disable the dynamic mobs option in monster.conf. This will result in pvp already being enabled on a living mvps map, when a player loads it. Otherwise, it might take a second to refresh, since the script has to check the newly loaded mobs first. The downside is of course that disabling dynamic mobs will take more ram. - script MVP_PVP_MODE FAKE_NPC,{ function mapHasAliveMVP; function mobHasMvpMode; OnInit: setarray(.mvp_maps$, "moc_pryd06","ra_fild03","ra_fild04","ve_fild01","ve_fild02", "lou_dun03","prt_maze03","abbey03", "gl_chyard","abyss_03","gef_dun02","gef_dun01","treasure02", "pay_fild10","gon_dun03","abbey02","xmas_fild01","ra_san05", "prt_sewb4","mosk_dun03","thor_v03","ama_dun03", "kh_dun02","ayo_dun02","niflheim","anthell02", "mjolnir_04","pay_dun04","gef_fild03","gef_fild10", "moc_pryd04","in_sphinx5","moc_fild17","ein_dun02","xmas_dun02", "beach_dun","thana_boss","tur_dun04","odin_tem03", "jupe_core","lhz_dun02"); freeloop(1); while(true) { for(.@i = 0; .@i < getarraysize(.mvp_maps$); .@i++) if(mapHasAliveMVP(.mvp_maps$[.@i])) pvpon(.mvp_maps$[.@i]); sleep(1000); } freeloop(0); end; OnNPCKillEvent: getunitdata(killedgid, .@data); .@map$ = mapid2name(.@data[UMOB_MAPID]); if(!mobHasMvpMode(.@data[UMOB_MODE]) || mapHasAliveMVP(.@map$)) end; announce(strcharinfo(0) + " killed " + strmobinfo(1, killedrid) + "!", bc_all); pvpoff(.@map$); end; function mapHasAliveMVP { .@map$ = getarg(0); getmapunits(BL_MOB, .@map$, .@mobs); for(.@i = 0; .@i < getarraysize(.@mobs); .@i++) { getunitdata(.@mobs[.@i], .@data); if(.@data[UMOB_HP] == 0 || !mobHasMvpMode(.@data[UMOB_MODE])) continue; return 1; } return 0; } function mobHasMvpMode { return getarg(0) & MD_MVP; } }
  4. This won't work as expected. The script is only run on onequip. So, the code you provide leads to the bonus3 only to be applied when the player has 500 zeny or more at the time he equips the item, but it doesn't check it on every autospell trigger. That means if mammonite consumes the last 500 zeny the following casts will still be done, since the check doesn't happen at the time of the casting. The solution would be either to instead of just providing a skill name, you could provide a script on each trigger like autobonus works or a parameter to tell the function it should check skill requirements before casting it. But those are things that need a source change. What could work but is kinda messy would be this: autobonus2("{ if(zeny >=500) bonus3(bAutoSpellWhenHit,\"MC_MAMMONITE\",5,5); }", 1000, 1000); I didn't test it but what it should do in theory is to check on each hit if the player has at least 500 zeny and if he does, apply the bonus for 1 second to check if it should trigger mammonite according to its chance. But this is very speculative. It could easily be that the bonus3 doesn't work on the attack that triggered the attachment via autobonus, but instead only on the successive attacks because of the way the state flow is organized.
  5. You should write a git issue to make the rathena team aware of that. https://github.com/rathena/rathena/issues
  6. The best solution I came up with would be to use autobonus2, it will add a script that will execute every time the player using the item is attacked, run a check if he is poisoned and if he is, it will apply a physical damage reduction by 20% for 10 seconds. I didn't test it, but it should look similar to this: autobonus2("{ if(checkoption2(1)) bonus bNoWeaponDamage, 20; }", 1000, 10000, BF_WEAPON|BF_MAGIC|BF_MISC);
  7. For the modification of Ragnarok itself, you will only need C++ and rAthena Script (the language NPCs are written in). For C++ you could get some books or look online for tutorials. As resources for rAthena Script, you have the documents I linked you in my previous post. Personally I think, the best way to learn a programming language is by reading how the language works and then using that basis to do own small projects and to read and modify other peoples programs to steady your knowledge. The headgear list is a list of existing headgear sprites in the game. It only shows a picture representation of the sprite from the front. A Client is a software that connects to another software to request things from it. The Server is a software that accepts connections and handles requests from Clients. (The Server >serves< the Clients.) It processes requests and sends back the answers to the Clients. An emulator is a software that tries to exactly reproduce the behavior of another software. In the Client Server sense it would be like this: If the Server isn't the original software, but behaves like the original in that it serves the same results to the Clients the original would, it is an emulation. The essentials are rAthena Full Client Unpacked and patched exe Translation Patch GRF Editor You go into your client's folder, search for the data.grf and open it. Then you will see a list of folders. From there, you can search for the files you are interested in. Sprite files end with .spr and are most of the time paired with an .act file. They base sprites are stored in data/sprite. Model files end with .rsm or rsm2. You can find them at data/model. Here you can see what most of the folders in the grf mean to help you navigate through it a bit better: https://github.com/rathena/rathena/wiki/Data-Folder
  8. Welcome Rhymaestro, if you want to modify Ragnarok to fit to your ideas, you have several options and things to learn. The simplest way to change the world itself is using scripting, it lets you do a lot of stuff, but based on how deep you want your changes to be, you will reach its limits relatively fast. This brings us to source modification, for which you will need C++, it will allow you far more agency but is also more complicated and harder to learn. Custom jobs for example would be a thing that you would implement via source modification. You will also need to grasp at least a bit of how the client works, where it gets the graphics and information from, so you can extend or modify things to your liking. Taking your custom job idea, you would need to change client files for the skill descriptions, for example. Many things you want to achieve will have been done in one way or another already. So searching the forum or checking the download section for scripts or source mods you can use as a base or learn from might be useful. You can also check the rAthena wiki at https://github.com/rathena/rathena/wiki which has a lot of guides on how to install rAthena and connect to it or how to achieve certain things you may want to do like adding custom items or mobs. Another helpful resource is the doc directory in the rAthena folder. You can find an online copy here: https://github.com/rathena/rathena/tree/master/doc it provides a lot of information regarding the structure of rAthena to make it clearer how it works. I think especially helpful are the script_commands.txt, which is a reference of all scripting commands rAthena provides and the source_doc.txt which explains how the servers communicate with each other, how the code is organized and what a lot of the acronyms often seen in the source code mean. Tool and resource wise, I can recommend you the tools from: Ai4Rei: https://nn.ai4rei.net/ (Check yourself which tools you might need) Tokei: https://rathena.org/board/files/file/2766-grf-editor/ (GRF Editor) llchrisll: https://github.com/llchrisll/ROenglishRE (English translation of the client) 4144: https://nemo.herc.ws/downloads/ (A good source for full clients, unpacked client exes, and you can get nemo patcher from there to patch your client) Feel free to ask any questions you have. I wish you the best and look forward to seeing how your project progresses.
  9. The easiest solution would be to remove the classes you don't want in the menu manually, for example if you want to remove ninja you would need to change it from: case Job_Novice: // First job change Job_Options(.@job_opt,Job_Swordman, Job_Mage, Job_Archer, Job_Acolyte, Job_Merchant, Job_Thief, Job_Super_Novice, Job_Taekwon, Job_Gunslinger, Job_Ninja); if( .BabyNovice ) Job_Options(.@job_opt, Job_Baby); break; to case Job_Novice: // First job change Job_Options(.@job_opt,Job_Swordman, Job_Mage, Job_Archer, Job_Acolyte, Job_Merchant, Job_Thief, Job_Super_Novice, Job_Taekwon, Job_Gunslinger); if( .BabyNovice ) Job_Options(.@job_opt, Job_Baby); break;
  10. You could modify it like this: At the OnInit on line 18 change: OnInit: disablenpc "The King#KoE"; disablenpc "Exit#KoE"; bindatcmd "koe", strnpcinfo(0)+"::OnCommand", 99,100; end; to OnInit: disablenpc "The King#KoE"; disablenpc "Exit#KoE"; bindatcmd "koe", strnpcinfo(0)+"::OnCommand", 99,100; setarray($@winner_items, 22783, 25, 22780, 25); // <<Item ID>, <Amount>>,... setarray($@loser_items, 22783, 10, 22780, 10); // <<Item ID>, <Amount>>,... end; and on line 87 change: guild_vs1,49,56,5 script Exit#KoE 1_M_BARD,{ mes "[Exit]"; mes "Here's the rewards."; mes "See ya!"; close2; warp "Save",0,0; if ( getcharid(2) == $koegid ) getitem 22783, 25; // Finale Token getitem 22780, 25; // Guild Weapon Box end; } to guild_vs1,49,56,5 script Exit#KoE 1_M_BARD,{ mes "[Exit]"; mes "Here's the rewards."; mes "See ya!"; close2; warp "Save",0,0; if(getcharid(2) == $koegid) copyarray(.@prizes[0], $@winner_items[0], getarraysize($@winner_items)); else copyarray(.@prizes[0], $@loser_item[0], getarraysize($@loser_items)); for(.@i = 0; .@i < getarraysize(.@prizes); .@i += 2) getitem(.@prizes[.@i], .@prizes[.@i + 1]); end; }
  11. That is because there is no config to disallow the first expanded classes. You can find the part where the menu is built for players with novice class on line 183. case Job_Novice: // First job change Job_Options(.@job_opt,Job_Swordman, Job_Mage, Job_Archer, Job_Acolyte, Job_Merchant, Job_Thief, Job_Super_Novice, Job_Taekwon, Job_Gunslinger, Job_Ninja); if( .BabyNovice ) Job_Options(.@job_opt, Job_Baby); break;
  12. Additional to what Rynbef posted, if you want to create a website you will also need HTML, CSS and if you want interactivity JavaScript.
  13. I think you should better create a post in the source request section. I think, to provide a good functionality that works like the original card deposit system, there are some code changes required. Here are some reasons that come to my mind: There isn't a way to apply bonuses permanently to a character outside from having something equipped. So the adventure book would need to be an equipment There is no system to put infinite items or cards into another item. Slots are limited to 4 server side since the client limits them to max 4 due to packet size anyway. Also, only the item bonus would be applied and the cards basically removed, since the slot holds an id of an item not a copy of the item you put in. There is no way to differentiate if a card or item should apply its deposited bonuses or its normal bonuses when put into the adventure book.
  14. The errormessage already tells you what is wrong. The Barters don't support more than 5 items. I am not sure why the limit is hard-coded to five, if it is because the original behavior is like this or because the client doesn't support more than five. What you could try though is to raise the limit and recompile rathena. You can find the definition of the limit in the mmo.hpp on line 117. #define MAX_BARTER_REQUIREMENTS 5
  15. You could combine bindatcmd and readbook. *bindatcmd "<command>","<NPC object name>::<event label>"{,<atcommand level>,<charcommand level>}; This command will bind a NPC event label to an atcommand. Upon execution of the atcommand, the user will invoke the NPC event label. Each atcommand is only allowed one binding. If you rebind, it will override the original binding. Note: The default level for atcommand is 0 while the default level for charcommand is 100. The following variables are set upon execution: .@atcmd_command$ = The name of the @command used. .@atcmd_parameters$[] = Array containing the given parameters, starting from an index of 0. .@atcmd_numparameters = The number of parameters defined. Example: When a user types the command "@test", an angel effect will be shown. - script atcmd_example -1,{ OnInit: bindatcmd "test",strnpcinfo(3) + "::OnAtcommand"; end; OnAtcommand: specialeffect2 EF_ANGEL2; end; } *readbook <book id>,<page>; This command will open a book item at the specified page.
  16. Why don't you simply try it before asking if it works? Everything inside the {} works like in any other script. So of course you can simply chain commands after each other.
  17. It needs to be ";" instead of ",". setitemscript(.@itemIds[.@i], "{ bonus(bMaxHP, 100000); bonus(bCritical, 100); }");
  18. You can use a floting npc. The npc doesn't need to be visible. You als should call the code using the OnInit label, so you don't have to call it manually. - script TestBonus FAKE_NPC,{ OnInit: setarray(.@itemIds[0], 440008, 1102); // ประกาศอาร์เรย์ itemIds ขนาด 2 for (.@i = 0; .@i < getarraysize(.@itemIds); .@i++) setitemscript(.@itemIds[.@i], "{ bonus(bMaxHP, 100000); }"); }
  19. That is wrong. You should take a better look at my example. setitemscript(.@itemIds[.@i], "{ bonus(bMaxHP, 100); }"); You need to add the {} and bonus is enough since you only need 1 value to set the max hp bonus.
  20. To shorten this code, you can use these commands: *getexp <base_exp>,<job_exp>{,<char_id>}; This command will give the invoking character a specified number of base and job experience points. Used for a quest reward. Negative values won't work. The EXP values are adjustted by 'quest_exp_rate' config value, VIP bonus, Guild Tax and EXP boost items such Battle Manual, Bubble Gum, or items that have SC_EXPBOOST or SC_ITEMBOOST. getexp 10000,5000; --------------------------------------- *getexp2 <base_exp>,<job_exp>{,<char_id>}; This command is safety version of 'set' command for BaseExp and JobExp. If using 'set' while the BaseExp or JobExp value is more than 2,147,483,647 (INT_MAX) will causing overflow error. Unlike 'getexp', this command ignores the adjustment factors! So using getexp2 you could shorten your function, while keeping the same effect as the original: function script F_Tower_Exp { setarray(.@bonusExp[1], 10, 20, 40, 80, 100); 'bonuzExp = .@bonusExp['level_mode]; getexp2(getmonsterinfo(killedrid, 3) * 'bonuzExp, getmonsterinfo(killedrid, 4) * 'bonuzExp)) return; }
  21. I am not 100% sure, but it seems that depends on your client version. I couldn't find any config in rAthena regarding the showing or not showing of the user count. I also couldn't find a patch for the client to enable it. Maybe you are lucky and someone else can tell you more.
  22. As per a user's request, I added the possibility to modify the chance to catch certain fish based on the rod used. sec_in02, 150, 165, 0 script Fishing Hole 10065,{ Fish: for(.@i = 0; .@i < .rods_size; .@i += .next_rod_step) { if(isequipped(.rods[.@i])) { .@rod_equipped = 1; .@rod_type = .@i; break; } } if(.@rod_equipped == 0 || countitem(.lure) == 0) { dispbottom("[Fishing] You need a Rod and Lure."); end; } specialeffect(EF_BUBBLE, "Fishing Hole"); soundeffect("fishingrod.wav", 0); dispbottom("[Fishing] Casting..."); .@fishing_cast_time = .default_fishing_cast_time; for(.@i = 0; .@i < .fishing_items_size; .@i += 2) if (isequipped(.fishing_items[.@i])) .@fishing_cast_time += .fishing_items[.@i + 1]; progressbar("ffffff", .@fishing_cast_time); delitem(.lure, 1); .@chance_sum = 0; cleararray(.@current_chances[0], 0, .chances_size); for(.@i = 0; .@i < .chances_size; .@i++) { .@modifier = .chances[.@i] * .rods[.@rod_type + (.@i + 1)]; .@current_chances[.@i] += .chances[.@i] * 100 + .@modifier; .@chance_sum += .@current_chances[.@i]; } .@pull = rand(1, .@chance_sum); .@catch = 0; for(.@i = 0; .@i < .chances_size; .@i++) { .@pull -= .@current_chances[.@i]; if(.@pull <= 0) { switch(.@i) { case 0: .@catch = .rare_catches[rand(.rare_catches_size)]; mapannounce(strcharinfo(3), strcharinfo(0) + " has caught a " + getitemname(.@catch) + "!", bc_map, "0x33CC00"); break; case 1: .@catch = .magic_fish; mapannounce(strcharinfo(3), strcharinfo(0) + " has caught a Magical Fish!", bc_map, "0xff77ff"); break; case 2: .@catch = .normal_catches[rand(.normal_catches_size)]; break; case 3: break; } break; } } if(.@catch == 0) { dispbottom("[Fishing] Nothing was caught..."); specialeffect2(EF_TEMP_FAIL); if(.auto_fail == 1) goto Fish; end; } getitem(.@catch, 1); specialeffect2(EF_TEMP_OK); if(.auto == 1) goto Fish; end; OnInit: // Fishing rod <id, rare, magic fish, normal, nothing> // + 1 = 1% increase // - 1 = 1% decrease setarray(.rods, 2764, 0, 0, 0, -25, 2763, 0, 0, 0, -50); // Fishing Lure .lure = 2775; // Auto-Fish .auto = 1; // Auto-Fish on Fail .auto_fail = 1; // Default Cast Time .default_fishing_cast_time = 15; // Chances for catch rarities: <rare>, <magic fish>, <normal>, <nothing> setarray(.chances[0], 5, 3, 55, 37); // Fishing Items setarray(.fishing_items, 2550, -2, 2443, -2, 2764, -3, 2775, -1); // Magic Fish .magic_fish = 6096; // Catches setarray(.normal_catches[0], 579, 908, 909, 963, 956, 6049, 918, 960, 910, 938, 624); // Rare Catches setarray(.rare_catches[0], 644, 603, 617); // CONFIG END // .chances_size = getarraysize(.chances); .rods_size = getarraysize(.rods); .fishing_items_size = getarraysize(.fishing_items); .normal_catches_size = getarraysize(.normal_catches); .rare_catches_size = getarraysize(.rare_catches); .next_rod_step = .chances_size + 1; } sec_in02,153,165,0 duplicate(Fishing Hole) Fishing Hole#1 10065 sec_in02,156,165,0 duplicate(Fishing Hole) Fishing Hole#2 10065 sec_in02,159,165,0 duplicate(Fishing Hole) Fishing Hole#3 10065 sec_in02,162,165,0 duplicate(Fishing Hole) Fishing Hole#4 10065 sec_in02,153,168,0 duplicate(Fishing Hole) Fishing Hole#5 10065 sec_in02,156,168,0 duplicate(Fishing Hole) Fishing Hole#6 10065 sec_in02,159,168,0 duplicate(Fishing Hole) Fishing Hole#7 10065 sec_in02,162,168,0 duplicate(Fishing Hole) Fishing Hole#8 10065 sec_in02,150,168,0 duplicate(Fishing Hole) Fishing Hole#9 10065 sec_in02,153,162,0 duplicate(Fishing Hole) Fishing Hole#10 10065 sec_in02,156,162,0 duplicate(Fishing Hole) Fishing Hole#11 10065 sec_in02,159,162,0 duplicate(Fishing Hole) Fishing Hole#12 10065 sec_in02,150,162,0 duplicate(Fishing Hole) Fishing Hole#14 10065 sec_in02,153,159,0 duplicate(Fishing Hole) Fishing Hole#15 10065 sec_in02,156,159,0 duplicate(Fishing Hole) Fishing Hole#16 10065 sec_in02,159,159,0 duplicate(Fishing Hole) Fishing Hole#17 10065 sec_in02,150,159,0 duplicate(Fishing Hole) Fishing Hole#18 10065 sec_in02,153,156,0 duplicate(Fishing Hole) Fishing Hole#19 10065 sec_in02,156,156,0 duplicate(Fishing Hole) Fishing Hole#20 10065 sec_in02,159,156,0 duplicate(Fishing Hole) Fishing Hole#21 10065 sec_in02,162,156,0 duplicate(Fishing Hole) Fishing Hole#22 10065 sec_in02,150,156,0 duplicate(Fishing Hole) Fishing Hole#23 10065
  23. I added that every item picked up from the mvp does expire and as long as you have one of the items in your inventory and are on the same map as the event mvp is, a buff is applied. There are a few things to note though: The script is really resource heavy due to 3 loops, of which 2 run as long as the mob lives and one that runs infinite. The buff will not immediately disappear once you leave the map or the item expires. It will run out with its normal expiration time. Since there is no way to make rentals drop on the floor, the picked up item gets deleted and replaced by a rental version, that means it will seem as if a player picked up 2 items, even though he will have only one in the inventory, of course. - script MVP_EVENT FAKE_NPC,{ OnInit: // Mobinfo .mob = 1159; .map$ = "morocc"; .x = 154; .y = 92; .respawn_time = 15; // Respawntime in minutes .percent = 10; // Item drop every x hp percent. // <x, y>.. setarray(.path, 158, 87, 162, 90, 166, 87, 161, 91, 156, 90); // Dropiteminfo .item = 6797; .repetition = 25; // How often the item drops per x hp percent .expiration_time = 15; // Expiration time in minutes // Buffinfo .skill_effect = 34; // The id of the buff skill .skill_effect_value = 0; // The value the skill effect shows, for example when the skill is heal. .status_effetc_type = SC_BLESSING; .status_ticks = 240000; .status_value1 = 10; .status_value2 = 0; .status_value3 = 0; .status_value4 = 0; // CONFIG END // .move_count = getarraysize(.path) - 2; donpcevent("MVP_EVENT::OnBuffcheck"); .respawn_time *= 1000 * 60; .expiration_time *= 60; OnSpawn: monster(.map$, .x, .y, strmobinfo(1, .mob), .mob, 1); .gid = $@mobid[0]; setunitdata(.gid, UMOB_DMOTION, 0); setunitdata(.gid, UMOB_MODE, MD_CANMOVE | MD_NORANDOMWALK | MD_MVP | MD_KNOCKBACKIMMUNE | MD_STATUSIMMUNE | MD_TELEPORTBLOCK); .curr_pointer = -2; donpcevent("MVP_EVENT::OnDestinationReached"); .@next_drop_percent = 100 - .percent; .@last_hp_percent = 100; freeloop(1); while(unitexists(.gid)) { getunitdata(.gid, .@mvp_data); .@curr_hp_percent = .@mvp_data[UMOB_HP] * 100 / .@mvp_data[UMOB_MAXHP]; if(.@curr_hp_percent == .@last_hp_percent) { sleep(100); continue; } if(.@curr_hp_percent > .@last_hp_percent) .@next_drop_percent = (.@curr_hp_percent / .percent) * .percent; .@last_hp_percent = .@curr_hp_percent; if(.@next_drop_percent >= .@curr_hp_percent) { donpcevent("MVP_EVENT::OnDrop"); .@next_drop_percent -= .percent; } sleep(100); } freeloop(0); sleep(.respawn_time); donpcevent("MVP_EVENT::OnSpawn"); end; OnDrop: for(.@i = 0; .@i < .repetition; .@i++) { getunitdata(.gid, .@mvp_data); makeitem(.item, 1, mapid2name(.@mvp_data[UMOB_MAPID]), .@mvp_data[UMOB_X], .@mvp_data[UMOB_Y], 1); sleep(100); } end; OnDestinationReached: if(!unitexists(.gid)) end; if(.move_count == .curr_pointer) .curr_pointer = 0; else .curr_pointer += 2; unitwalk(.gid, .path[.curr_pointer], .path[.curr_pointer + 1], "MVP_EVENT::OnDestinationReached"); end; OnBuffcheck: freeloop(1); while(true) { addrid(5, 0, .map$); .@item_count = countitem(.item); if(.@item_count > 0) { getinventorylist(getcharid(0)); for(.@i = 0; .@i < @inventorylist_count; .@i++) { if(@inventorylist_id[.@i] == .item && @inventorylist_expire[.@i] == 0) { delitemidx(@inventorylist_idx[.@i], @inventorylist_amount[.@i]); break; } } for(.@i = 0; .@i < .@item_count; .@i++) rentitem(.item, .expiration_time); } if(rentalcountitem(.item) > 0 && getstatus(.status_effetc_type) == 0) { sc_start4(.status_effetc_type, .status_ticks, .status_value1, .status_value2, .status_value3, .status_value4); skilleffect(.skill_effect, .skill_effect_value); } detachrid; sleep(100); } freeloop(0); }
×
×
  • Create New...