• This is a read only backup of the old Emudevs forum. If you want to have anything removed, please message me on Discord: KittyKaev

Dynamic Gossip System (extension)

Grandelf

Esteemed Member
Dynamic Gossip System

General information
So I've been messing around with gossip, and came up with a system,
so you can dynamically build gossip menu's.

So what does this mean?
It means you can make entire gossip script, using no more than
a single table. This system isn't limited to teleporters,
you can basically script anything with it.

There's a few advantages by doing it this way, such as:
  • More efficient;
  • No more linking the menu id's to actions (intid's);
  • Automatic pagination (next and back buttons);
  • It's a lot clearer (in my opinion).

Script
The script: http://paste2.org/V931c9V5
You will have to save this script as an extension (.ext) file.
So for example
: dynamic_gossip.ext

I recreated an 'ancient' teleporter as an example, which
should give a pretty good idea of how it works.
Example script: http://paste2.org/MHsC7Zyt

How it works
You start by defining a table, in which you can add menu's, actions and checks.
Lets say we want a teleporter, which can teleport players to the faction cities.
We start by making the menu and the actions (teleport).
Code:
local locations = {
  ['Alliance Cities'] = {
    ['Stormwind City'] = function(player, object)
      player:Teleport(0, -8826.4091680, 629.565002, 94.078217, 3.954481);
    end,
    ['Darnassus'] = function(player, object)
      player:Teleport(1, 9951.950195, 2280.438965, 1341.394409, 1.577327);
    end
  },
  ['Horde Cities'] = {
    ['Orgrimmar'] = function(player, object)
      player:Teleport(1, 1556.017578, -4419.726563, 8.626983, 0.984890);
    end,
    ['Silvermoon City'] = function(player, object)
      player:Teleport(530, 10021.248047, -7014.779785, 49.717529, 3.998464);
    end
  }
};
As you can see, the key represents the menu name. The value can either represent,
another menu, an action or a check (which I will cover a bit later).
An action is defined, by adding a function. This function accepts two arguments,
player and the object (Creature, GameObject or Item). Both of them can be used.
A submenu is defined by creating a table instead of a function. In this table,
you can do as before and define yet another table or a function (action) (there is no limit).

You can also add checks to check if a player should be able to see the menu.
So if we only want horde members to see the horde cities and the alliance
members to see alliance cities, we can make a check for this.

Code:
local locations = {
  ['Alliance Cities'] = {
    ['Stormwind City'] = function(player)
      player:Teleport(0, -8826.4091680, 629.565002, 94.078217, 3.954481);
    end,
    ['Darnassus'] = function(player)
      player:Teleport(1, 9951.950195, 2280.438965, 1341.394409, 1.577327);
    end,
    -- Alliance only.
[COLOR="#FF0000"]    ['check'] = function(player, object)
      return player:GetTeam() == 0;
    end[/COLOR]
  },
  ['Horde Cities'] = {
    ['Orgrimmar'] = function(player)
      player:Teleport(1, 1556.017578, -4419.726563, 8.626983, 0.984890);
    end,
    ['Silvermoon City'] = function(player)
      player:Teleport(530, 10021.248047, -7014.779785, 49.717529, 3.998464);
    end,
    -- Horde only.
[COLOR="#FF0000"]    ['check'] = function(player, object)
      return player:GetTeam() == 1;
    end[/COLOR]
  }
};
As you can see, checks work exactly the same as actions. The only difference
is, is that the key has to be named 'check' and it has to return a boolean value.
True means the menu will show up, false means it won't.

Registering
Like normal gossip scripts, these scripts have to be register as well.
You can do this by calling one of these functions.
Code:
-- Table is the table that you created with all the gossip actions in it.
RegisterDynamicCreatureGossipEvent(creature_id, table);
RegisterDynamicItemGossipEvent(item_id, table);
RegisterDynamicGameObjectGossipEvent(gameobject_id, table);

More advanced topics

Checks
Adding a check to a single gossip item is a bit different.
Rather than just defining the check, you have to wrap the
action and the check in one table.
You can also add multipile checks, by wrapping all the checks
in one table.
So if we want the player to be at least level 20 but lower than level 60
before he can teleport to orgrimmar, this is what it would look like:
Code:
local locations = {
  ['Alliance Cities'] = {
    ['Stormwind City'] = function(player)
      player:Teleport(0, -8826.4091680, 629.565002, 94.078217, 3.954481);
    end,
    ['Darnassus'] = function(player)
      player:Teleport(1, 9951.950195, 2280.438965, 1341.394409, 1.577327);
    end,
    -- Alliance only.
    ['check'] = function(player)
      return player:GetTeam() == 0;
    end
  },
  ['Horde Cities'] = {
    ['Orgrimmar'] = [COLOR="#FF0000"]{[/COLOR]
      function(player)
        player:Teleport(1, 1556.017578, -4419.726563, 8.626983, 0.984890);
      end,
[COLOR="#FF0000"]      ['check'] = {
        function(player)
          return player:GetLevel() >= 20;
        end,
        function(player)
          return player:GetLevel() <= 60;
        end[/COLOR]
      [COLOR="#FF0000"]}[/COLOR]
    [COLOR="#FF0000"]}[/COLOR],
    ['Silvermoon City'] = function(player)
      player:Teleport(530, 10021.248047, -7014.779785, 49.717529, 3.998464);
    end,
    -- Horde only.
    ['check'] = function(player)
      return player:GetTeam() == 1;
    end
  }
};
Of course this check could easiliy be rewritten to:
Code:
function(player)
  return player:GetLevel() >= 20 and player:GetLevel() <= 60;
end

Pagination
By default, back and next button are on, and the system will automatically
generate a next button after every 10 gossip items.
It is possible to manipulate these settings by setting two fields.

'_next', which can either be a number (the number of items before a next button is generated)
or the boolean value false (in which case there won't be any next buttons).
'_back', which can only be a boolean value, indicating whether or not to show back buttons.
These have to be added to the gossip table.
An example of this can be seen in the teleporter script.

Finally

I tried to keep the explanation short, so it may not answer every question.
So if you have a question, feel free to ask!

Grandelf.
 
Last edited:

Kaev

Super Moderator
Thanks for sharing!

A question: Can't we just do this instead of using "checks"?
These checks are probably fine for huge lists (like a teleporter :p ) where many things need the same condition, but i don't see a reason why someone should use these for single points.

Code:
local locations = {
  ['Alliance Cities'] = {
    ['Stormwind City'] = function(player)
      if return player:GetTeam() == 0 then
        player:Teleport(0, -8826.4091680, 629.565002, 94.078217, 3.954481);
      end
    end,
    ['Darnassus'] = function(player)
      if return player:GetTeam() == 0 then
        player:Teleport(1, 9951.950195, 2280.438965, 1341.394409, 1.577327);
      end
    end,
  },
  ['Horde Cities'] = {
    ['Orgrimmar'] = {
      function(player)
        if player:GetLevel() >= 20 and player:GetLevel() <= 60 then
          player:Teleport(1, 1556.017578, -4419.726563, 8.626983, 0.984890);
        end
      end,
      }
    }
    ['Silvermoon City'] = function(player)
      if player:GetTeam() == 1 then
        player:Teleport(530, 10021.248047, -7014.779785, 49.717529, 3.998464);
      end
    end,
  }
};
 

Grandelf

Esteemed Member
Well you can do it like that, however doing it like that will result
in the option still being visible. When you click it though,
the gossip menu will just close without teleporting you.

The reason why you have to explicity tell the system that the specified
function is a check, is that there is no other way to check if a condition
is met before the actual menu's are shown.
Calling a function with an inline check when the menu is shown, would teleport the player
to unwanted places.

However, a check like that is 2 lines, when adding a check this way is 3 lines.
Code:
['check'] = function(player)
  return player:GetLevel() <= 60;
end
You could make this shorter by making a closure.
Code:
local function make_level_check(player, level)
  return function(player, level)
    return player:GetLevel() <= level;
  end
end

local locations = {
  ['Alliance Cities'] = {
    ['Stormwind City'] = {
      function(player)
        player:Teleport(0, -8826.4091680, 629.565002, 94.078217, 3.954481);
      end,
      ['check'] = make_level_check(player, 60)
    },
    ['check'] = function (player)
      return player:GetTeam() == 0;
    end
  }
}
 
Last edited:

Kaev

Super Moderator
Well you can do it like that, however doing it like that will result
in the option still being visible. When you click it though,
the gossip menu will just close without teleporting you.

The reason why you have to explicity tell the system that the specified
function is a check, is that there is no other way to check if a condition
is met or not before the actual menu's are shown.
Calling that function when the menu is shown, would teleport the player
to unwanted places.

Haven't thought about that, that's actually a pretty good idea. Thanks for your answer. :)
 
Top