-
Posts
1298 -
Joined
-
Last visited
-
Days Won
38 -
Feedback
100%
Content Type
Forums
Store
Third Party - Providers Directory
Feature Plan
Release Notes
Docs
Events
Posts posted by Syreldar
-
-
That makes no sense. You're using the same global flag to save the player's id, meaning it will get overwritten every time a player opens the letter.
Also using a global flag, something which is used for events or global settings, to save a var for a player is something you totally should not do.
-
void CActorInstance::__ProcessDataAttackSuccess(const NRaceData::TAttackData& c_rAttackData, CActorInstance& rVictim, const D3DXVECTOR3& c_rv3Position, UINT uiSkill, BOOL isSendPacket)
else if (rVictim.IsStone() || rVictim.IsDoor()) { __HitStone(rVictim); }
void CActorInstance::__HitStone(CActorInstance& rVictim) { if (rVictim.IsStun()) { rVictim.Die(); } else { rVictim.__Shake(100); // This is what triggers the shake, make sure it's there. } }
- 1
- 1
-
9 minutes ago, dahlemm said:
owsap 525 euro xD
Quality always has a price. Alternatively you can get your files from kebabmmo for free, good luck.
- 2
-
9 hours ago, ShaRaP said:
Well, the rejoin seems to work fine.. so just fix the local coordinates?
-
Well, I told you to change the coordinates of a map, so both root/atlasinfo.txt (client) and mapname/Settings.txt (server) need to be edited.
-
This is not a quest issue.
There's a conflict of coordinates between DevilTower and Valley of Seungryong. This bug also exists in official servers, but it's irrelevant cause they don't have a Rejoin system within the DevilTower dungeon, thus never accessing that set of coordinates globally.
The conflict for the DevilTower becomes relevant at 7th, 8th and 9th floor cause they're placed in the utmost right section of the map, which, as you can see from the screenshot below, is partly inside the Valley's map sector.
The server simply doesn't know where to place you., because that global set of coordinates belongs to two different maps.
To fix the issue, you gotta move either the DevilTower or the Valley in different coordinates in the plane, be careful to choose a free and valid set of coordinates.
- 1
-
8 hours ago, VegaS™ said:
Guys, why are you always using quest flags? It's been 8 years since the source first appeared; we should avoid them because they're highly toxic; let's not use the database for everything when we already have everything in C++, it just needs to be properly accessed.
Hidden Contentint party_get_all_member_pids(lua_State* L) { auto ch = CQuestManager::instance().GetCurrentCharacterPtr(); if (!ch) return 0; auto pParty = ch->GetParty(); if (!pParty) return 0; const auto& memberMap = pParty->GetPartyMembers(); for (const auto& it : memberMap) lua_pushnumber(L, it.first); return static_cast<int>(memberMap.size()); } { "party_get_all_member_pids", party_get_all_member_pids },
-
- questlua_party.cpp
- party.h
Hidden Contentpublic: const TMemberMap& GetPartyMembers() const { return m_memberMap; }
How-To-Use:
if party.is_party() then local party_member_table = {party.party_get_all_member_pids()} local party_member_count = table.getn(party_member_table) -- Debug local party_member_string = table.concat(party_member_table, ", ") say(string.format("Party members: count(%d), pids(%s)", party_member_count, party_member_string)) end
Good. That fixes the first part of the issue: The party member pids not being taken into account for characters in different cores/channels.
However, that still won't do, it requires more work: Your solution assumes that the party stays valid through channels, but as I previously stated, when a group member logins in a different one they will be without a party in that channel, parties are channel-exclusive.
Screenshots for reference:
This means that they can just change channel, do whatever they want while eluding the party checks, then log back in the old one. With my solution they can't, because a flag stays valid between different channels.
Also, I have already stated that my solution was suboptimal, I simply shared my own approach while being informative about it.
That said, I believe your take towards questflags is a bit of a stretch: the game is built to handle hundreds of thousands of them active and get info on them at the same time with negligible impact on the server.
- 1
-
-
@ Owsap's OSF Project.
- 1
- 2
- 1
-
The quest by default has a check for GMs.
If you want to trigger it as GM (keep in mind that in TEST_SERVER everyone is considered a GM), remove the first condition, otherwise, everything works as it should.
- 1
-
Hello,
For context, this is what it's about: https://en-wiki.metin2.gameforge.com/index.php/Saturn_Encyclopedia_(L), for each one of them.
- 118
- 3
- 2
- 1
- 1
- 1
- 4
- 1
- 25
- 8
- 50
-
SELECT `locale_name`, `max_hp` FROM `player`.`mob_proto` WHERE `max_hp` = 0 AND (`type` = 0 OR `type` = 2);
This makes you see all the monsters and metins that have a bugged max_hp value in your proto, so you can fix them manually.
but, If you want to temporarily fix them all automatically, you can run this:
UPDATE `player`.`mob_proto` SET `max_hp` = 100 WHERE `max_hp` = 0 AND (`type` = 0 OR `type` = 2);
-
2 minutes ago, ReFresh said:
@SyreldarI'm not 100% sure, but in game is one fish & item, which can give you invisibility and it's probably using the AFF_INVISIBILITY too.
Yes, USE_INVISIBILITY. But I am pretty sure he also wanted that taken into account, after all taking damage and bouncing left and right while seeing no one hitting you looks pretty bad.
- 1
-
1 minute ago, ReFresh said:
@Rev Arena Don't forget to add a IsGM() check to have that working only for GameMasters.
AFF_INVISIBILITY Is the affect given by /inv, Ninjas have EUNHYUNG. So it's not necessary.
-
1. Clientside:
Srcs/Client/InstanceBase.cpp
bool CInstanceBase::IsAttackableInstance(CInstanceBase& rkInstVictim) { // else if (IsPC()) { if (rkInstVictim.IsStone()) return true; if (rkInstVictim.IsPC()) { // ADD This here: if (IsAffect(AFFECT_INVISIBILITY)) return false; // //
2. Serverside:
Srcs/game/src/pvp.cpp:
bool CPVPManager::CanAttack(LPCHARACTER pkChr, LPCHARACTER pkVictim) { // if (pkChr == pkVictim) // ³»°¡ ³¯ Ä¥¶ó°í ÇÏ³× -_- return false; if (pkVictim->IsNPC() && pkChr->IsNPC() && !pkChr->IsGuardNPC()) return false; // Add this here: if (pkChr->IsAffectFlag(AFF_INVISIBILITY) && pkVictim->IsPC()) return false; // //
-
36 minutes ago, WeedHex said:
Are you sure in official side it's like that?
Why would you care how is in official, if that's what he wants regardless?
53 minutes ago, ReFresh said:#ref
char_skill.cpp, search:
// 2. Shoot! if (f.GetVictim()) { ch->CreateFly(FLY_SKILL_MUYEONG, f.GetVictim()); ch->ComputeSkill(SKILL_MUYEONG, f.GetVictim()); }
Turn into:
// 2. Shoot! LPCHARACTER victim = f.GetVictim(); if (victim && !victim->IsAffectFlag(AFF_EUNHYUNG) && !victim->IsAffectFlag(AFF_INVISIBILITY) && !victim->IsAffectFlag(AFF_REVIVE_INVISIBLE))) { ch->CreateFly(FLY_SKILL_MUYEONG, victim); ch->ComputeSkill(SKILL_MUYEONG, victim); }
- 1
- 1
-
3 hours ago, Ezequiel G. said:
I tried to change the .dds textures of the armors from one client to another but they still look the same.
Because your first screen shows what it looks like on metin2 by default.
The screenshot with the Greatsword shows a modified version of the Hwang where only the main details are not set to opaque in the alpha channel.
This is the modified Hwang texture that you want:
- 3
- 2
-
g 385 387 1 1 0 0 15400s 100 1 2019
`g` means I'm spawning the vnum of a group, not a monster. So in this case I'm calling the vnum of the group associated with the Spider Queen.
`385` `387` are the local spawn coordinates of the group.
`1` `1` are the random values for the local spawn coordinates.
`0` is the Z section, you don't care about that.
the other `0` is the direction, since this is not an NPC, you don't care about this either.
`15400` is the number of seconds in between respawns since server start time. Change it as you like.
`100` is the spawn chance per tick.
`1` is the number of groups to spawn per tick.
and finally, `2019` is the vnum of the monster, or in this case the group, to spawn.
- 1
-
6 hours ago, PetePeter said:
Why not using Timestamp instead of increase a dungeon quest flag every time ?
d.setf("dungeon_time_out_end", get_global_time() + (TIME_OUT_STEP*60)) -- Should be `get_time() + TIME_OUT`. [note that get_global_time() and get_time() are the same.]
if (d.getf("dungeon_time_out_end") < get_global_time()) -- should be `<=`, if not, it'll run for an additional TIME_OUT_STEP tick if math.mod(TIME_OUT, TIME_OUT_STEP) == 0.
Yes, that's perfect. What you wrote also happens to be the method I use for my dungeons.
I applied two corrections to it, maybe it'll help you out.
Regarding your question, I wrote it with the incremental flag because while this way of writing it is more efficient, with the incremental flag it becomes easier to understand, I chose to do it because this is an example and as such it is meant to instruct people.
-
Why would you add a new source function for such a thing?
- 1
-
44 minutes ago, Tekanse said:
d.setf("seconds_passed", d.getf("seconds_passed") + TIME_OUT_STEP); if (d.getf("seconds_passed") == TIME_OUT) then
Hmm.
define TIME_OUT 4000 define TIME_OUT_STEP 55
Hmmmm.
This is a development forum, not a baby school, I have wrote an example, you obviously adapt the code to your needs.
use the >= operator if you need it.
45 minutes ago, Exygo said:or just be badass and send the timestamp cmdchat(string.format("commandName %d",get_time() + 3600))
and make an UI for it in client and use app.GetGlobalTimeStamp() for calculation. And also in that UI you can display tips E.G: "Defeat all the monsters in the floor to advance to level 3", "Unlock all the 6 seals in order to advance to level 4"
If by "BEING BADASS" you mean "copy literally what illiterates did for Zodiac", then sure. But I don't see how that's relevant for the topic.
- 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:
Spoilerwhen devilcatacomb_45m_left_timer.server_timer begin if d.select(get_server_timer_arg()) then d.notice(string.format(gameforge.devilcatacomb_zone._20_say, 45)) server_timer('devilcatacomb_30m_left_timer', 60 * 15, get_server_timer_arg()) end end when devilcatacomb_30m_left_timer.server_timer begin if d.select(get_server_timer_arg()) then d.notice(string.format(gameforge.devilcatacomb_zone._20_say, 30)) server_timer('devilcatacomb_15m_left_timer', 60 * 15, get_server_timer_arg()) end end when devilcatacomb_15m_left_timer.server_timer begin if d.select(get_server_timer_arg()) then d.notice(string.format(gameforge.devilcatacomb_zone._20_say, 15)) server_timer('devilcatacomb_5m_left_timer', 60 * 10, get_server_timer_arg()) end end when devilcatacomb_5m_left_timer.server_timer begin if d.select(get_server_timer_arg()) then d.notice(string.format(gameforge.devilcatacomb_zone._20_say, 5)) server_timer('devilcatacomb_1m_left_timer', 60 * 4, get_server_timer_arg()) end end when devilcatacomb_1m_left_timer.server_timer begin if d.select(get_server_timer_arg()) then d.notice(string.format(gameforge.devilcatacomb_zone._20_say, 1)) server_timer ("devilcatacomb_0m_left_timer", 60 * 1, get_server_timer_arg()) end end when devilcatacomb_0m_left_timer.server_timer begin if d.select(get_server_timer_arg()) then d.notice (gameforge.devilcatacomb_zone._210_dNotice) d.set_warp_location (65, 5914, 992) server_timer('devilcatacomb_exit_timer', 7, get_server_timer_arg()) end end
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 - https://metin2.dev/topic/15905-syreldars-quest-functions/ 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 - https://metin2.dev/topic/15905-syreldars-quest-functions/ -- 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 - https://metin2.dev/topic/15905-syreldars-quest-functions/ 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 - https://metin2.dev/topic/15905-syreldars-quest-functions/ 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.
- 3
- 2
- 5
-
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?
-
1 hour ago, iderx said:
Hey when i dropped the quest.lua file and pasted the text from questlib file to my questlib (i pasted it at the end of the file)
i get this in my server\Machine after typing ./qc quest.lua
FUNCTION GetYangReward(lv_next)
FUNCITON GetExpReward(current.mission,lv_next)
i dont know what to do..that's not an error.
-
Return ALL numbers from random function
in Community Support - Questions & Answers
Posted · Edited by Syreldar
The table_shuffle function from my functions file does what you want.