Jump to content

Syreldar

Premium
  • Posts

    1298
  • Joined

  • Last visited

  • Days Won

    38
  • Feedback

    100%

Everything posted by Syreldar

  1. Hello. 100% of the people I've seen coding dungeons or making time-based events of any sort handled `Time remaining` notices like this: This is not entirely their fault, sadly Webzen isn't exactly capable of setting a good example of how to do things right in Python, C++ and -of course- Lua. This way of doing things is very limited, initializing a new timer for every notice they wanna make is not a very efficient way of dealing with the matter. Naturally, you could implement the Official Zodiac's way of handling Time remaining, by overriding the Minimaps' interface with a new interface that has a built-in timer which works via python-quest communication, but shouldn't that be available for you, then you can do this: First things first, we'll need a single server timer, which will be of the loop type, and it will handle both the `Time remaining` notices and the actions for when the time expires. For simplicity purposes, I'll make an example for the dungeons. Now, we can either set up two full-scope variables, or defines: define TIME_OUT 3600 -- 1 hour define TIME_OUT_STEP 60 -- 1 minute Or, we can make an array that stores the information within a function, like this: function GetDungeonData() return { ["time_out"] = 3600, ["time_out_step"] = 60 }; end -- function Note that functions initialized inside quests are not global: they are only valid within their scope - the state they get initialized in. For this example, I'll make use of the first of the 2 methods I just showed you. Now, we got 2 elements: time_out, which is our time limit. time_out_step, which will be the interval in seconds in-between triggers of our server loop timer. We'll use them like this: if (TIME_OUT and TIME_OUT_STEP) then -- No reason to initialize anything if you don't want a time limit. server_loop_timer("dungeon_time_out_check", TIME_OUT_STEP, d.get_map_index()); notice(string.format("<Dungeon> Initialized. Time limit: %s!", get_time_format(TIME_OUT))) -- get_time_format - [Hidden Content] end -- if We have bound a timer to our dungeon instance and set it to trigger once every `time_out_step` seconds; This allows us to control the interval between `Time remaining` notices by changing the `time_out_step` element as we like. Now all that remains is to set our timer's trigger and make use of `time_out` and `time_out_step`, the full example code would look like this: define TIME_OUT 3600 -- 1 hour define TIME_OUT_STEP 60 -- 1 minute define DUNGEON_MAP_INDEX XXX quest dungeon begin state start begin when --[[]] with InDungeon(DUNGEON_MAP_INDEX) begin -- InDungeon - [Hidden Content] -- if (TIME_OUT and TIME_OUT_STEP) then -- No reason to initialize anything if you don't want a time limit. server_loop_timer("dungeon_time_out_check", TIME_OUT_STEP, d.get_map_index()); d.notice(string.format("<Dungeon> Initialized! Time limit: %s!", get_time_format(TIME_OUT))) -- get_time_format - [Hidden Content] end -- if end -- when when dungeon_time_out_check.server_timer begin -- Only refer to our personal dungeon instance's server timer. if (d.select(get_server_timer_arg())) then -- increase by TIME_OUT_STEP each trigger d.setf("seconds_passed", d.getf("seconds_passed") + TIME_OUT_STEP); -- If the flag equals or surpasses TIME_OUT after being increased, it means the time has expired. if (d.getf("seconds_passed") >= TIME_OUT) then -- Clear the timer as there's no more need for it to loop. clear_server_timer("dungeon_time_out_check", get_server_timer_arg()); d.notice("<Dungeon> Time expired.") d.exit_all(); else -- As long as the time hasn't expired, we just notify the dungeon about the Time remaining. d.notice(string.format("<Dungeon> Time remaining: %s.", get_time_format(TIME_OUT - d.getf("seconds_passed")))) -- get_time_format - [Hidden Content] end -- if/else end -- if end -- when end -- state end -- quest Alternatively, instead of using the `seconds_passed` flag like I'm doing (the way I showed you is more understandable and thus more proper since this is an example), you can initialize a single flag to register the starting time and make use of get_time() in between triggers. And there you have it, full control in a single timer for your time remaining notices.
  2. I'm not sure why you made this post, given you know exactly what the problem is and why it occurs. The monster doesn't spawn because your boss.txt file for that map is empty.. so fill it?
  3. Added Colors. [Hidden Content]
  4. Why? You check with the flag still no reason to use a query to check it.
  5. quest test begin state start begin when 10581.chat."Block Player" with pc.is_gm() begin say_event_title(string.format("%s:[ENTER]", mob_name(10581))) say("Type the name of the player whom you want to") say("lock out of accessing this map.[ENTER]") local player_name = input(); -- yield say_event_title(string.format("%s:[ENTER]", mob_name(10581))) local player_vid = find_pc_by_name(player_name); if (player_vid == 0) then return say_reward(string.format("The player `%s` is offline or in another core.", player_name)); end -- if local old_vid = pc.select(player_vid, player_vid); pc.setf("map_control", "cannot_enter", 1); pc.select(old_vid, old_vid); say("Operation complete.[ENTER]") end -- when end -- state end -- quest The quest is limited due to the fact that if the player is offline or in a separate core you won't be able to reach out to him to assign the flag. This can be solved via query, i'll give you a rough example of the code using @ martysama0134's mysql_direct_query function, you can find it here paired with other useful functions: [Hidden Content] Keep in mind that this code works but you'll still have to properly escape the string on the input in order to avoid possible sqli. local results, ret = mysql_direct_query(string.format("SELECT `id` FROM `player`.`player` WHERE `name` = '%s' LIMIT 1;", player_name)); if (results > 0) then mysql_direct_query(string.format("INSERT INTO `player`.`quest`(`dwPID`, `szName`, `szState`, `lValue`) VALUES (%d, 'map_control', 'cannot_enter', 1) ON DUPLICATE KEY UPDATE `lValue` = 1;", ret[1].id)) else say_reward(string.format("The player `%s` was not found.[ENTER]", player_name)) end -- if/else
  6. Why are you here asking for a "bug free world editor" when martysama's is literally the only version that gets actively maintained and updated? and why are you asking here for it instead of reporting the bug directly in his topic so he can acknowledge it and fix it? :smh:
  7. The mentioned table for reference: # ¿ëÈ¥¼® ¼Ó¼ºº¯°æ AddÈ®·ü Å×À̺í (¿¬°ü Å×À̺í : ApplyNumSettings) Group ChangeAttrStepTables { Group GRADE_MYTH { #--# STEP 0 1 2 3 STEP_LOWEST 500 400 8 1 STEP_LOW 500 400 16 5 STEP_MID 500 400 40 10 STEP_HIGH 500 400 100 30 STEP_HIGHEST 500 400 300 150 } } So the way it works is it renders that specific probability, x% more likely to happen. As Owsap mentioned, it is not supported by the system by default.
  8. Trash that makes 0 sense. That's what I think about it. They meant for when the player attacks other players while on a mount, so not if both the player and the victim are on a mount.
  9. ActorInstanceSync.cpp: Search for: if (IsResistFallen()) return; Move this check just above this line: if (!IsUsingSkill()) ActorInstanceBattle.cpp: (Fix by @ martysama0134) Search for: // VICTIM_COLLISION_TEST const D3DXVECTOR3& kVictimPos = rVictim.GetPosition(); rVictim.m_PhysicsObject.IncreaseExternalForce(kVictimPos, c_rAttackData.fExternalForce); //*nForceRatio/100.0f); // VICTIM_COLLISION_TEST_END Substitute with: // VICTIM_COLLISION_TEST const D3DXVECTOR3& kVictimPos = rVictim.GetPosition(); float fExternalForceRatio = 1.0f; if (rVictim.IsResistFallen()) fExternalForceRatio *= 0.75f; rVictim.m_PhysicsObject.IncreaseExternalForce(kVictimPos, c_rAttackData.fExternalForce * fExternalForceRatio); //*nForceRatio/100.0f); // VICTIM_COLLISION_TEST_END
  10. 1. Open ActorInstanceSync.cpp: Search for: if (IsResistFallen()) return; Take that snippet and move it just above this check: if (!IsUsingSkill()) 2. Open ActorInstanceBattle.cpp: (Fix by @ martysama0134) Search for: rVictim.m_PhysicsObject.IncreaseExternalForce(kVictimPos, c_rAttackData.fExternalForce); //*nForceRatio/100.0f); Substitute it with: float fExternalForceRatio = 1.0f; if (rVictim.IsResistFallen()) fExternalForceRatio *= 0.75f; rVictim.m_PhysicsObject.IncreaseExternalForce(kVictimPos, c_rAttackData.fExternalForce * fExternalForceRatio); //*nForceRatio/100.0f); This allows the Push distance to still be reduced on Mental Warriors, while allowing the __Push function's SetBlendingPosition() to be executed, fixing this little desync issue.
  11. Okay? How do you plan on blocking the player from actually hitting another player with those? Even assuming you wanted to apply a solution as dirty as "If they hit a player while on mount, they dismount", no such trigger exists by default in Metin2's sources. If you had it, you could write: quest mount_dismount_pvp begin state start begin when damage with npc.is_pc() begin if (pc.is_mount()) then pc.unmount(); end -- if pc.setqf("last_pc_damage_time", get_time()); end -- when end -- state end -- quest And then, in your mount quest: -- -- when MOUNTSEAL_VNUM.use begin local last_pc_damage_time = pc.getf("mount_dismount_pvp", "last_pc_damage_time"); local cur_time, time_to_wait = get_time(), 3; if (cur_time - last_pc_damage_time < time_to_wait) then return syschat(string.format("You gotta wait %d more seconds to mount after damaging another character.", last_pc_damage_time + time_to_wait - cur_time)); end -- if pc.mount(vnum, mount_duration); pc.mount_bonus(apply_id, apply_vnum, apply_duration) end -- when -- -- But this solution is so bad I don't even want to look at it.. and I wrote it, lol. Also, this doesn't prevent damage at all, it just prevents players being on a mount while hitting other players after the first hit.
  12. Hello. Here's a list of useful globals you can use in your quests. You can add them to your questlib.lua or make a separate *.lua file and load it via dofile(). [Hidden Content] 25/04/2023: Added SELECT_YES, SELECT_ENTER, SELECT_NO; Added EMPIRES; Added ARMED_HORSE_LEVEL, MILITARY_HORSE_LEVEL; Added STATUS_VIT, STATUS_INT, STATUS_STR, STATUS_DEX; Added ITEM_TYPES, ITEM_SUB_TYPES; Added AFFECT; Updated APPLY; Added JOB_TO_RACE, RACE_TO_JOB, RACE_TO_SEX; Changed RACE_NAME_LIST; Added PC_RACE_LIST, PC2_RACE_LIST, PC3_RACE_LIST; Added MALE_RACE_LIST, FEMALE_RACE_LIST; Added SEX_NAME_LIST Added STATUS_NAME_LIST; Added EMPIRE_NAME_LIST; 11/05/2023: Improvements to the overall structure of the globals. Added POINTS 31/08/2023: Structural update for ITEMS and DUNGEON_DATA.
  13. 25/6/2022: Added get_gold_format Added BuildSkillListOfLevel Minor fixes.
  14. Flags are always always initialized with value 0. So if you enter a state and you never modified that specific flag for that specific quest up until that moment, it'll have 0 as value.
  15. ..No matter how useless it is. It's still a shared free fix, why comment like that?
  16. Select the run/walk animation of your mount. Open it in granny Click "view in detail" Profit. granny_real32 Duration = MotionDuration granny_real32 LoopTranslation = Accumulation Update the relative .msa file if they don't match with the .gr2's data: MotionDuration 0.666667 Accumulation 0.00 -300.00 0.00 Make sure it's also correct in the share/data folder in your server. Make sure the value in the mob_proto column `folder` is also correct (or in the .txt if you're using .txt protos).
  17. I can assure you that if they can bug it you made a mistake in the quest. There's no reason to use C++ whatsoever.
  18. Care to expand on that? What do you mean it's not safe?
  19. local pc_data = {}; for _, pid in ipairs({party.get_member_pids()}) do q.begin_other_pc_block(pid); -- table.insert(pc_data, {["name"] = pc.get_name(), ["level"] = pc.get_level()}); -- q.end_other_pc_block(); end -- for for index, data in ipairs(pc_data) do say(string.format("%d. %s's level is %d.", index, data["name"], data["level"])) end -- for
  20. Starting now, I'll permanently offer a 5% Discount on my services to Metin2Dev VIPs.
  21. You can't take random launchers and use them, packets have to match. You have to compile your own launcher to match your server packets.
  22. Two things @ Owsap. 1. For sure an overlook, this should be self.newTooltip.AddItemData(itemIndex, metinSlot) 2. You forgot to tell them to add this line: self.newToolTip.SetPosition(15 + self.oldToolTip.GetWidth() + 45, 38) Before if localeInfo.IsARABIC():
×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.