Jump to content

Release Dungeon with Quest


Zylon

Recommended Posts

Alternative download links → Alternate_1 or Alternate_2 or Alternate_3

 

I share it with you without support. If you need some support just add me on Discord with fees

YT:


Video is out to date, the actually Dungeon is reworked often as me.
You can use the free System in there. implementation duration ca. 1 hour without fixes. 
If you use some other files than me, you can get some problems.

Download for your own Risk and Waste of Time

 

Edited by Gurgarath
  • Metin2 Dev 8
  • Good 3
  • Love 5

Contact:

Zylon#9003

Link to comment
Share on other sites

  • Premium

Several heads-up, going from top to bottom of the file:

  1. The "enter" trigger is not what you think it is, it's useless in this case, you can remove it.
     
  2. Not sure what's the point of writing d.set_warp_location(DUNGEON_MAP_INDEX, 12319,13341), the correct way to use it is to define the coordinates of the map_index where you want the player to be sent to in response to a d.exit() call.
    For example, the devil tower uses it to send the player outside of the tower near the entry NPC, meanwhile you're using it with the same destination index as the dungeon itself.
     
  3. You use multiple d.set_minimap_dungeoninfo_status and d.spawn_mob calls in a single trigger, you can just make a function for the first one, and make a generic array for the second one which you can later use to spawn monster recursively via a simple for cycle, this way not only you avoid repetition but you also add a certain degree of customizability to the script.
     
  4. when 8005.kill with d.getf("deathhall_stage") == 1 begin 
    Here you didn't specify the dungeon instance in which the script should be triggered, this leads to errors if the monster is present in the normal world, and can also lead to conflict in case you use the same monster in multiple instances. I see you did it for some of the triggers that follow, but always make sure you leave none unchecked, there's a couple more triggers that need it.
     
  5. 		when 304.kill with d.getf("deathhall_stage") == 3 and death_hall.isInDungeon(DUNGEON_MAP_INDEX) begin
    			local count_pillar = d.getf("pillar_stone_flag") - 1
    			d.setf("pillar_stone_flag", count_pillar)
    			if contains({0, 100, 200, 300}, count_pillar) then
    				pc.give_item2(PILLAR_STONE, 1)
    				d.set_minimap_dungeoninfo_notice("Benutze den Schlüsselstein mit rechtsklick")
    			end
    		end

    Assuming contains() uses a for loop to check recursively, you have adopted an O(n) approach for no reason at all.
    I will provide two O(1) solutions for your learning:

    For the first one, we'll use a set data structure:

    when 304.kill with d.getf("deathhall_stage") == 3 and death_hall.isInDungeon(DUNGEON_MAP_INDEX) begin
        local count_pillar = d.getf("pillar_stone_flag") - 1;
        d.setf("pillar_stone_flag", count_pillar);
        local pillars = { [0] = true, [100] = true, [200] = true, [300] = true };
        if (pillars[count_pillar]) then
            pc.give_item2(PILLAR_STONE, 1);
            d.set_minimap_dungeoninfo_notice("Benutze den Schlüsselstein mit rechtsklick")
        end -- if
    end -- when

    For the second one, since these values are all multiples, we can use the math.mod func.

    when 304.kill with d.getf("deathhall_stage") == 3 and death_hall.isInDungeon(DUNGEON_MAP_INDEX) begin
        local count_pillar = d.getf("pillar_stone_flag") - 1;
        d.setf("pillar_stone_flag", count_pillar);
        if (math.mod(count_pillar, 100) == 0 and count_pillar <= 300) then
            pc.give_item2(PILLAR_STONE, 1);
            d.set_minimap_dungeoninfo_notice("Benutze den Schlüsselstein mit rechtsklick")
        end -- if
    end -- when

     

  6. We can see here another case of repetition:

    			if count == 4 then
    				d.set_minimap_dungeoninfo_gauge(0,0,0)
    				d.set_minimap_dungeoninfo_notice("Du hast einen Torschlüssel erhalten. Ziehe ihn aufs Tor.")
    				d.kill_all()
    				item.remove(PILLAR_STONE, 1)
    				pc.give_item2(DOOR_KEY, 1)
    				d.setf("door_key_flag", 1)
    			else
    				d.set_minimap_dungeoninfo_gauge(1,d.getf("stones_use_flag"),4)
    				d.set_minimap_dungeoninfo_notice("Vernichte solange Monster bis du einen Schlüsselstein droppst!")
    				item.remove(PILLAR_STONE, 1)
    				d.kill_all()
    				d.regen_file("data/zylon_dungeon/zylon_regen02.txt")
    				d.regen_file("data/zylon_dungeon/zylon_regen_s.txt")
    			end


    Move the calls you're doing in any case outside of the if blocks, like this:

    item.remove(PILLAR_STONE, 1)
    d.kill_all()
    if count == 4 then
        d.set_minimap_dungeoninfo_gauge(0,0,0)
        d.set_minimap_dungeoninfo_notice("Du hast einen Torschlüssel erhalten. Ziehe ihn aufs Tor.")
        pc.give_item2(DOOR_KEY, 1)
        d.setf("door_key_flag", 1)
    else
        d.set_minimap_dungeoninfo_gauge(1,d.getf("stones_use_flag"),4)
        d.set_minimap_dungeoninfo_notice("Vernichte solange Monster bis du einen Schlüsselstein droppst!")
        d.regen_file("data/zylon_dungeon/zylon_regen02.txt")
        d.regen_file("data/zylon_dungeon/zylon_regen_s.txt")
    end

     

  7. The following is another mistake many people make (also same mistake as .4):

    		when 8008.kill with d.getf("door_key_drop") == 1 begin
    			local pct = number(1,10)
    			if pct == 1 then
    				pc.give_item2(DOOR_KEY, 1)
    				d.set_minimap_dungeoninfo_notice("Du hast einen Torschlüssel erhalten. Ziehe ihn aufs Tor.")
    				d.setf("door_key_flag", 2)
    				clear_server_timer("time_out", get_server_timer_arg())
    			elseif pct != 1 then
    				d.regen_file("data/zylon_dungeon/stone_regen.txt")
    			end
    		end

    get_server_timer_arg() only works inside of a server_timer, not outside of it, so this code won't clear the time_out server_timer.
    When trying to clear a server_timer outside of another server_timer's scope, use d.get_map_index(), since the timer's arg will for sure be the instance's map_index itself.

     

  8. Not really an error but this will make any rejoin system not work, since you're clearing the dungeon and its flags, clearing flags is also pointless, see (.9) for an explanation:

    		when logout begin
    			if death_hall.isInDungeon() then
    				death_hall.clearDungeon()
    			end
    		end

     

  9. Again, the same error as before (potentially calling get_server_timer_arg() outside of a server_timer's scope) :

    		function clearDungeon()
    			d.setf("deathhall_stage", 0)
    			d.setf("boss_stage", 0)
    			d.setf("door_key_flag", 0)
    			d.setf("door_key_drop", 0)
    			d.setf("metinstone_count", 0)
    			d.setf("monster_count", 0)
    			d.setf("pillar_stone_flag", 0)
    			d.setf("stones_use_flag", 0)
    			d.set_minimap_dungeoninfo_gauge(0,0,0)
    			d.kill_all()
    			d.clear_regen()
    			clear_server_timer("dungeon_end", get_server_timer_arg())
    			clear_server_timer("time_out", get_server_timer_arg())
    		end

    the two clear_server_timer calls won't work when called outside of a server_timer, see the "logout" trigger. Use d.get_map_index() when calling the func outside of another server_timer's scope, clearing flags is also useless as I mentioned in (.8).

    Why is clearing flags/timers upon finishing a dungeon useless?:
    An abandoned instance won't be accessible to players until deleted (~5 minutes since last activity usually, check the dungeon_dead_event in dungeon.cpp), since d.new_jump/d.new_jump_party/d.new_jump_all will always create a new one.
    And, when deleted, the flags will reset automatically and the timers will also automatically clear themselves, meaning players will never be able to enter an instance with pre-set flags from a past user via d.new_jump/d.new_jump_party/d.new_jump_all.

    This practice of resetting flags/timers upon ending a dungeon is something we now only see in official quests (or from bad and clueless coders) and is likely practice from past times that is now obsolete.

Edited by Syreldar
  • Metin2 Dev 4
  • Confused 1
  • Good 4
  • Love 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

  • 2 weeks later...
  • Active Member

99.9% of developers don't know about bugs until they happen.

1. Be careful when logging in because if you change character, all the code will be executed again.

Your code:
 

when login or enter with death_hall.isInDungeon(DUNGEON_MAP_INDEX) begin
	...
	d.setf("deathhall_stage", 1)
end

New code:

when login or enter with death_hall.isInDungeon(DUNGEON_MAP_INDEX) and d.getf("deathhall_stage") == 0 begin
	...
	d.setf("deathhall_stage", 1)
end

In this way you ensure that the code is executed only once.

 

2. 

Another very common mistake is to put generic names in the server_timers, this causes conflicts because if you do another quest, it is very likely that you put the same name of the server_timer and it will be overwritten.

When you compile the quests, a directory tree is created where the parent is "object". The timers are stored there with the name. For example, the server_timer "dungeon_end" generates a folder object/dungeon_end/server_timer/death_hall.start
If you build another quest where there is a server_timer with the same name, it will be replaced. (Ymir bugs)

The solution is to put more detailed names, for example:

death_hall_dungeon_end

 

3. 

This code:

		when 304.kill with d.getf("deathhall_stage") == 2 and death_hall.isInDungeon(DUNGEON_MAP_INDEX) begin
			local count_monster = d.getf("monster_count") + 1
			d.setf("monster_count", count_monster)
			if count_monster > 0 then
				d.set_minimap_dungeoninfo_gauge(1, d.getf("monster_count"), 100)
				if count_monster == 100 then
					d.setf("monster_count", count_pillar)
					d.kill_all()
					d.set_minimap_dungeoninfo_stage(3,6)
					d.set_minimap_dungeoninfo_gauge(1,d.getf("stones_use_flag"),4)
					d.set_minimap_dungeoninfo_notice("Vernichte solange Monster bis du einen Schlüsselstein droppst!")
					d.regen_file("data/zylon_dungeon/zylon_regen_s.txt")
					d.regen_file("data/zylon_dungeon/zylon_regen02.txt")
					d.setf("deathhall_stage", 3)
					d.setf("pillar_stone_flag", 400)
				end
			end
		end

		when 304.kill with d.getf("deathhall_stage") == 3 and death_hall.isInDungeon(DUNGEON_MAP_INDEX) begin
			local count_pillar = d.getf("pillar_stone_flag") - 1
			d.setf("pillar_stone_flag", count_pillar)
			if contains({0, 100, 200, 300}, count_pillar) then
				pc.give_item2(PILLAR_STONE, 1)
				d.set_minimap_dungeoninfo_notice("Benutze den Schlüsselstein mit rechtsklick")
			end
		end

You must be careful because something similar happens here with the server_timers.
If you have two similar whens, probably only one of the two will be saved.
The solution is to put the blocks together.

New code:

		when 304.kill with death_hall.isInDungeon(DUNGEON_MAP_INDEX) begin
			if d.getf("deathhall_stage") == 2 then
				local count_monster = d.getf("monster_count") + 1
				d.setf("monster_count", count_monster)
				if count_monster > 0 then
					d.set_minimap_dungeoninfo_gauge(1, d.getf("monster_count"), 100)
					if count_monster == 100 then
						d.setf("monster_count", count_pillar)
						d.kill_all()
						d.set_minimap_dungeoninfo_stage(3,6)
						d.set_minimap_dungeoninfo_gauge(1,d.getf("stones_use_flag"),4)
						d.set_minimap_dungeoninfo_notice("Vernichte solange Monster bis du einen Schlüsselstein droppst!")
						d.regen_file("data/zylon_dungeon/zylon_regen_s.txt")
						d.regen_file("data/zylon_dungeon/zylon_regen02.txt")
						d.setf("deathhall_stage", 3)
						d.setf("pillar_stone_flag", 400)
					end
				end
			elseif d.getf("deathhall_stage") == 3 then
				local count_pillar = d.getf("pillar_stone_flag") - 1
				d.setf("pillar_stone_flag", count_pillar)
				if contains({0, 100, 200, 300}, count_pillar) then
					pc.give_item2(PILLAR_STONE, 1)
					d.set_minimap_dungeoninfo_notice("Benutze den Schlüsselstein mit rechtsklick")
				end
			end
		end

 

If your code works, that's fine, even if your code isn't the most optimal, however, it needs to be improved.
Along the way you will improve it.
I will complement with code that I would change.

1. 

Your code:

		function isInDungeon(idx)
			return pc.get_map_index() >= (idx * 10000) and pc.get_map_index() < ((idx+1) * 10000)
		end

New code:

		function isInDungeon()
			return pc.get_map_index() >= (DUNGEON_MAP_INDEX * 10000) and pc.get_map_index() < (DUNGEON_MAP_INDEX+1) * 10000
		end

I do this because it is not necessary to send the map index, since the dungeon is specified by calling the function with death_hall.isInDungeon().

2.

Your code:

		when 8008.kill with d.getf("door_key_drop") == 1 begin
			local pct = number(1,10)
			if pct == 1 then
				pc.give_item2(DOOR_KEY, 1)
				d.set_minimap_dungeoninfo_notice("Du hast einen Torschlüssel erhalten. Ziehe ihn aufs Tor.")
				d.setf("door_key_flag", 2)
				clear_server_timer("time_out", get_server_timer_arg())
			elseif pct != 1 then
				d.regen_file("data/zylon_dungeon/stone_regen.txt")
			end
		end

New code:

		when 8008.kill with death_hall.isInDungeon() and d.getf("door_key_drop") == 1 begin
			if number(1, 10) == 1 then --10%
				pc.give_item2(DOOR_KEY)
				d.set_minimap_dungeoninfo_notice("Du hast einen Torschlüssel erhalten. Ziehe ihn aufs Tor.")
				d.setf("door_key_flag", 2)
				clear_server_timer("death_hall_time_out", d.get_map_index())
			else
				d.regen_file("data/zylon_dungeon/stone_regen.txt")
			end
		end

3. I don't know if you know about the difference between item.remove and pc.remove_item.
item.remove removes the item's stack.
pc.remove_item removes the number of items from the vnum that you specify.

I always use pc.remove_item

 

Follow the advice of the experts and learn the codes of other quests (whether they are good or not so good), that will help you a lot.

As you gain expertise, you will find yourself in need of creating libraries.

I wish you the best of luck, that you do something great with your dungeon!

  • Metin2 Dev 2
  • Love 2
Link to comment
Share on other sites

  • 4 weeks later...

i get this client syserr

0320 12:45:38318 :: CMapBase::LoadProperty(FileName=zylon_dung_new\MapProperty.txt) - LoadMultipleTextData ERROR ÆÄÀÏÀÌ ¾øÀ» °¡´É¼ºÀÌ ¸¹½À´Ï´Ù.
0320 12:45:38318 :: CMapManager::LoadMap() Invalid Map Type
0320 12:45:38319 :: CPythonBackground::SelectViewDistanceNum(int eNum=0) mc_pcurEnvironmentData is NULL

Link to comment
Share on other sites

Announcements



  • Similar Content

  • Activity

    1. 13

      Metin2 Closed Beta Content (2003-2004)

    2. 25

      [SRC] Metin2 on LINUX - The Old Metin2 Project

    3. 2

      United/Club/Midgard serverfiles?

    4. 13

      Metin2 Closed Beta Content (2003-2004)

    5. 13

      Metin2 Closed Beta Content (2003-2004)

  • Recently Browsing

    • No registered users viewing this page.
×
×
  • 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.