Jump to content

Dynamically handle `Time remaining` notices in Quests


Recommended Posts

  • Premium

Hello.

100% of the people I've seen coding dungeons or making time-based events of any sort handled `Time remaining` notices like this:
 

Spoiler
		when 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:

  1. time_out, which is our time limit.
  2. 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.

Edited by Syreldar
  • Metin2 Dev 3
  • Good 2
  • Love 5

 

"Nothing's free in this life.

Ignorant people have an obligation to make up for their ignorance by paying those who help them.

Either you got the brains or cash, if you lack both you're useless."

Syreldar

Link to comment
Share on other sites

  • Active Member

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"

Link to comment
Share on other sites

  • Active Member

just replace 

if (d.getf("seconds_passed") == TIME_OUT) then

with

if (d.getf("seconds_passed") >= TIME_OUT) then

in case someone puts TIME_OUT 4000 and TIME_OUT_STEP 55 the timer would not stop

Link to comment
Share on other sites

  • Premium
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.

  • kekw 1

 

"Nothing's free in this life.

Ignorant people have an obligation to make up for their ignorance by paying those who help them.

Either you got the brains or cash, if you lack both you're useless."

Syreldar

Link to comment
Share on other sites

Why not using Timestamp instead of increase a dungeon quest flag every time ?

 

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.setf("dungeon_time_out_end", get_global_time() + (TIME_OUT_STEP*60))
				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
				if (d.getf("dungeon_time_out_end") < get_global_time()) 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(d.getf("dungeon_time_out_end") - get_global_time()))) -- get_time_format - https://metin2.dev/topic/15905-syreldars-quest-functions/
				end -- if/else
			end -- if
		end -- when
	end -- state
end -- quest

 

Edited by PetePeter
  • Good 1
Link to comment
Share on other sites

  • Premium
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.

Edited by Syreldar

 

"Nothing's free in this life.

Ignorant people have an obligation to make up for their ignorance by paying those who help them.

Either you got the brains or cash, if you lack both you're useless."

Syreldar

Link to comment
Share on other sites

Announcements



×
×
  • 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.