Inhaltsverzeichnis

RPG Mod

Mit dem Code auf dieser Seite kann man mit seinen Helden wie in einem RPG Spiel Erfahrung sammeln und im Level aufsteigen. Durch einen Levelaufstieg bekommen die Helden mehr Gesundheitspunkte, mehr Angriff und mehr Verteidigung. Außerdem können sie schneller ihre Spezialfähigkeiten einsetzen. Diese Mod wurde für den MP Modus entwickelt, funktioniert aber durchaus auch im Singleplayer.

Beschreibung der Änderungen

Heldenmenü

Wird ein Held markiert, öffnet sich über dem normalen Selektionsmenü ein weiteres Infofenster, in dem das aktuelle Level, die gesammelte Erfahrung (in Prozent bis zum nächsten Level), die Aktionspunkte sowie die vorhandenen Items zu sehen sind.

Erfahrung sammeln

Erfahrung sammelt ein Held, wenn in seiner Nähe eine feindliche Einheit durch die eigenen Einheiten vernichtet wird. Die Anzahl der Erfahrungspunkte richtet sich nach dem Typ des Gegners. Ein Hauptmann bringt z.B. mehr Erfahrung als ein einfacher Soldat. Gebäude bringen keine Erfahrung. Feindliche Helden geben je nach Level am meisten Erfahrungspunkte.

Befindet sich mehr als ein eigener Held in Reichweite, so wird die gewonnene Erfahrung zu gleichen Teilen auf alle Helden aufgeteilt. Tipp: Man kann einen schwachen Helden also schneller Erfahrung sammeln, wenn man die starken Helden aus den Kämpfen heraushält.

Levelaufstieg

Wenn ein Held genug Erfahrung gesammelt hat, steigt er eine Stufe auf. Die Erfahrung die dafür benötigt wird, steigt von Level zu Level. Mit anderen Worten wird ein Held mit niedrigem Level schneller aufsteigen, als ein Held mit hohem Level. Pro Level erhöht sich die Gesundheit, der Angriff, die Verteidigung und die Aktionspunkte.

Spezialfähigkeiten und Aktionspunkte

Der Einsatz der Spezialfähigkeiten hat sich gegenüber dem Originalspiel geändert. Die Fähigkeiten haben keinen Cooldown mehr, sondern verbrauchen die sogenannten Aktionspunkte. Aktionspunkte regenerieren sich (ähnlich wie die Gesundheit) mit der Zeit von selbst.

Items

Helden können nun Gegenstände tragen, die in speziellen Schatzkisten zu finden sind. Das können z.B. Heiltränke oder Rüstungen sein. Im SP Modus könnten die Helden auch Schlüssel oder andere Questgegenstände tragen. Leider gibt es noch keine Möglichkeit Items beliebig einzusetzen oder wegzuwerfen. Ein Heiltrank müsste also automatisch eingesetzt werden, wenn die Gesundheit des Helden sehr schwach ist.

Eigene Namen für die Helden

Im Multiplayer können den Helden über die S5Tools eigene Namen gegeben werden, die dann bei Info-Messages bei allen Spielern angezeigt werden.

Lua-Code für die RPG Mod

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- Hero Level System v1.2
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function HeroLevelSystem_Init()
 
    gvHeroLevelSystem = {}
    gvHeroLevelSystem.Heroes = {}
    gvHeroLevelSystem.Entities = {}
    gvHeroLevelSystem.Config = {}
 
    --
    -- Hier können die Einstellungen geändert werden!
    --
 
    -- Maximal erreichbares Level
    gvHeroLevelSystem.Config.MaxLevel               = 10
 
    -- Berechnungsgrundlage für den Levelaufstieg
    gvHeroLevelSystem.Config.BaseLevelExperience    = 100
 
    -- Maximale Distanz zu einem Gegner, bei dem der Held die komplette
    -- Erfahrung bekommt. Ist er weiter weg, gibt es weniger Erfahrung, je
    -- größer die Entfernung zwischen Held und Feind ist
 
    gvHeroLevelSystem.Config.MaxDistance            = 2200
 
    -- Maximale Anzahl an Items die ein Held aufnehmen kann
    gvHeroLevelSystem.Config.MaxItems               = 3
 
    -- Funktion die die benötigte Erfahrung für das nächste Level berechnet
    -- Sollte polynomiell/exponentiell mit dem Level wachsen.
    -- Ein sehr nützliches Tool, um den asymptotischen Verlauf der Erfahrung
    -- abzuschätzen findet man unter: http://www.mathe-fa.de
    gvHeroLevelSystem.Config.NextLevel = function(_currentLevel)
 
        local neededExperience = gvHeroLevelSystem.Config.BaseLevelExperience
 
        -- Zum Beispiel: Polynom 2. Grades mit Base=100
        -- f(x) = Base * (1+x)^2 / 4
        -- f(1) = 100, f(9) = 2500, f(14) = 5000, f(19) = 10000
        neededExperience = neededExperience * (1 + _currentLevel)^2 / 4
 
        return neededExperience
 
    end
 
    -- Zusätliche Lebenspunkte pro Level (sparsam einsetzen!)
    gvHeroLevelSystem.Config.HealthPerLevel         = 100
    -- Zusätzliche Schadenpunkte pro Level
    -- Eine Lvl4 Kanone hat 75 Schaden, nur als Hinweis...
    gvHeroLevelSystem.Config.DamagePerLevel         = 8
    -- Zusätzliche Verteidigung pro Level
    -- Sollte auf keinen Fall zu hoch gewählt werden!
    gvHeroLevelSystem.Config.ArmorPerLevel          = 2
    -- Zusätzliche Aktionspunkte pro Level
    gvHeroLevelSystem.Config.ActionPointsPerLevel   = 50
 
    -- Dürfen Helden wiederbelebt werden?
    -- Im SP _muss_ das hier auf false gesetzt werden!
    gvHeroLevelSystem.Config.AllowRespawn           = true
    -- Zeit in Sekunden nach der ein Hero automatisch respawned
    gvHeroLevelSystem.Config.RespawnTime            = 45
    -- Anzahl Gold mit der man seinen Helden sofort wiederbeleben kann
    gvHeroLevelSystem.Config.RespawnGold            = 250
 
    -- Werden die Helden normal im MP gekauft?
    -- Wenn nein hier false setzen und jeden Helden mit
    -- HeroLevelSystem_NewHero(_id)
    -- in der OnGameStart() initialisieren. Für den SP Modus gilt das Gleiche!
    -- z.B. HeroLevelSystem_NewHero(GetEntityId("HeldVonSpieler1"))
    gvHeroLevelSystem.Config.BuyHeroes              = true
 
    --
    -- Ab hier nichts mehr ändern!
    --
 
    HeroLevelSystem_InitInterface()
 
    if gvHeroLevelSystem.Config.BuyHeroes then
        Trigger.RequestTrigger(Events.LOGIC_EVENT_ENTITY_CREATED, nil, "HeroLevelSystem_OnEntityCreated", 1)
    end
    Trigger.RequestTrigger(Events.LOGIC_EVENT_ENTITY_HURT_ENTITY, nil, "HeroLevelSystem_OnEntityHurtEntity", 1)
    Trigger.RequestTrigger(Events.LOGIC_EVENT_ENTITY_DESTROYED, nil, "HeroLevelSystem_OnEntityDestroyed", 1)
    Trigger.RequestTrigger(Events.LOGIC_EVENT_EVERY_SECOND, nil, "HeroLevelSystem_EverySecond", 1)
 
    -- Allow multiplayer tributes
    function GameCallback_FulfillTribute() return 1 end
 
end
 
--------------------------------------------------------------------------------
-- Creates a new hero table
--------------------------------------------------------------------------------
function HeroLevelSystem_NewHero(_id, _level)
 
    local Hero = {}
 
    if not _level then
        _level = 1
    end
    assert(_level > 0, "Hero level must be greater zero")
 
    ----------------------------------------------------------------------------
    -- Set default hero values
    ----------------------------------------------------------------------------
    function Hero:Hero(_id, _level)
 
        self.ID                 = _id
        self.Player             = GetPlayer(_id)
        self.Level              = 1
        self.Experience         = 0
        self.IsDead             = false
        self.Type               = Logic.GetEntityType(self.ID)
        self.MaxHealth          = 500 + (_level - 1) * gvHeroLevelSystem.Config.HealthPerLevel
        self.Health             = self.MaxHealth
        self.MaxActionPoints    = 200 + (_level - 1) * gvHeroLevelSystem.Config.ActionPointsPerLevel
        self.ActionPoints       = self.MaxActionPoints
        self.BaseArmor          = Logic.GetEntityArmor(_id)
        self.ExtraArmor         = (_level - 1) * gvHeroLevelSystem.Config.ArmorPerLevel
        self.BaseDamage         = Logic.GetEntityDamage(_id)
        self.ExtraDamage        = (_level - 1) * gvHeroLevelSystem.Config.DamagePerLevel
 
        self.Items              = {}
        self.FoundItemRemainingTime = 0
 
        -- Get the hero name from the stringtable - can be also a custom name
        local HeroName = XGUIEng.GetStringTableText("Names/" .. Logic.GetEntityTypeName(self.Type))
        -- Get the player color
        local R, G, B = GUI.GetPlayerColor(self.Player)
        -- Color the hero name :)
        self.Name = "@color:" .. R .. "," .. G .. "," .. B .. " " .. HeroName .. " @color:255,255,255 "
 
        -- Prevent the game from hurt our hero - we will do it self
        MakeInvulnerable(self.ID)
 
        return self
 
    end
 
    ----------------------------------------------------------------------------
    -- The hero gains some experience; called when an enemy died
    ----------------------------------------------------------------------------
    function Hero:TakeExperience(_xp)
 
        -- Hero is dead at the moment - sorry no xp!
        if self.IsDead then
            return
        end
 
        -- Maximum level reached - no xp anymore
        if self.Level >= gvHeroLevelSystem.Config.MaxLevel then
            return
        end
 
        -- Increase xp
        self.Experience = self.Experience + _xp
 
        -- Enough xp for the next level - level up!
        if self.Experience >= gvHeroLevelSystem.Config.NextLevel(self.Level) then
            self:LevelUp()
        end
 
    end
 
    ----------------------------------------------------------------------------
    -- The hero gets a level up :)
    ----------------------------------------------------------------------------
    function Hero:LevelUp()
 
        -- Decrease experience
        self.Experience = math.max(0, self.Experience - gvHeroLevelSystem.Config.NextLevel(self.Level))
        -- Increase level
        self.Level = self.Level + 1
 
        -- Increase (maximum) health values
        self.MaxHealth = self.MaxHealth + gvHeroLevelSystem.Config.HealthPerLevel
        self.Health    = math.min(self.MaxHealth, self.Health + gvHeroLevelSystem.Config.HealthPerLevel)
 
        -- Increase maximum action points
        self.MaxActionPoints = self.MaxActionPoints + gvHeroLevelSystem.Config.ActionPointsPerLevel
 
        -- Increase damage and armor values
        self.ExtraDamage = self.ExtraDamage + gvHeroLevelSystem.Config.DamagePerLevel
        self.ExtraArmor  = self.ExtraArmor + gvHeroLevelSystem.Config.ArmorPerLevel
 
        -- Inform the player(s) about this event
        HeroLevelSystem_Chat(self.Name .. " hat Stufe " .. self.Level .. " erreicht!", self.Player)
 
        -- Play a sound feedback
        if GUI.GetPlayerID() == self.Player then
            Sound.PlayGUISound(Sounds.OnKlick_Select_dario, 0)
        end
 
    end
 
    ----------------------------------------------------------------------------
    -- This function is called when someone hurts the hero
    ----------------------------------------------------------------------------
    function Hero:TakeDamage(_attackerDamage)
 
        -- Just decrease the internal health here
        local hitpointsToHurt = math.max(0, _attackerDamage - (self.BaseArmor + self.ExtraArmor)) + GetRandom(3) + 1
        self.Health = self.Health - hitpointsToHurt
 
    end
 
    ----------------------------------------------------------------------------
    -- This function is called when the hero hurts another entity
    ----------------------------------------------------------------------------
    function Hero:HurtEntity(_entity)
 
        -- Target is already dead or a building (no extra damage against buildings - inba;))
        if IsDead(_entity) and Logic.IsBuilding(_entity) == 1 then
            return
        end
 
        -- Target is a leader - hurt the soldiers first
        if Logic.IsLeader(_entity) == 1 then
            local soldiersList = {Logic.GetSoldiersAttachedToLeader(_entity)}
            if soldiersList[1] > 0 then
                _entity = soldiersList[2]
            end
        end
 
        local hitpointsToHurt = self.ExtraDamage
        local targetHitpoints = Logic.GetEntityHealth(_entity)
 
        -- Give full damage against soldiers, cause they never receive damage by the game
        if Logic.IsEntityInCategory(_entity, EntityCategories.Soldier) == 1 then
            hitpointsToHurt = hitpointsToHurt + self.BaseDamage + GetRandom(4)
        end
 
        -- Hurt the target
        Logic.HurtEntity(_entity, hitpointsToHurt)
        -- Remember the last attacker of the target
        gvHeroLevelSystem.Entities[_entity] = self.ID
 
    end
 
    ----------------------------------------------------------------------------
    -- Update the entity health bar on the 3D gamearea
    ----------------------------------------------------------------------------
    function Hero:Update3DHealthBar()
 
        local maxHealth = HeroLevelSystem_GetEntityMaxHealth(self.ID)
        local health = HeroLevelSystem_GetEntityHealth(self.ID)
        local delta = (maxHealth * self.Health / self.MaxHealth) - health
 
        MakeVulnerable(self.ID)
        if delta > 0 then
            Logic.HealEntity(self.ID, delta)
        else
            Logic.HurtEntity(self.ID, -delta)
        end
        MakeInvulnerable(self.ID)
 
    end
 
    ----------------------------------------------------------------------------
    -- Update hero status values
    ----------------------------------------------------------------------------
    function Hero:UpdateStatusValues()
 
        -- Every two seconds
        if Counter.Tick2("Hero" .. self.ID .. "_UpdateStatusValues", 2) then
            -- Heal
            self.Health = math.min(self.MaxHealth, self.Health + 5 + GetRandom(6))
            -- Refresh action points
            self.ActionPoints = math.min(self.MaxActionPoints, self.ActionPoints + 4 + GetRandom(math.floor(self.Level / 2)))
        end
 
        -- Perform item effects
        for _, ItemID in self.Items do
            Item = HeroItems_GetByID(ItemID)
            if Item.EffectEverySecond ~= nil then
                Item.EffectEverySecond(self, ItemID)
            end
        end
 
        -- Check for performed abilities
        for _, Ability in Abilities do
            if Logic.HeroIsAbilitySupported(self.ID, Ability) == 1 then
                local RechargeTime = Logic.HeroGetAbilityRechargeTime(self.ID, Ability)
 
                -- Check if an ability was performed and decrease the action points
                if Logic.HeroGetAbiltityChargeSeconds(self.ID, Ability) < RechargeTime then
                    self.ActionPoints = self.ActionPoints - RechargeTime
                end
 
                -- Set the ability charge seconds to the maximum
                Logic.HeroSetAbilityChargeSeconds(self.ID, Ability, RechargeTime)
            end
        end
 
    end
 
    ----------------------------------------------------------------------------
    -- Create the respawn tribute and countdown when the hero died
    ----------------------------------------------------------------------------
    function Hero:InitRespawn()
 
        if gvHeroLevelSystem.Config.AllowRespawn and self.IsDead then
            -- Pay Gold and resurrect the hero immediately
            self.TributeID = AddTribute{
                playerId = self.Player,
                text = self.Name .. " fuer " .. gvHeroLevelSystem.Config.RespawnGold .. " Gold wiederbeleben.",
                cost = {Gold = gvHeroLevelSystem.Config.RespawnGold},
                Callback = function()
                    self:Respawn()
                end
            }
 
            -- The hero always respawns after a certain time
            self.RespawnCountdown = StartCountdown(gvHeroLevelSystem.Config.RespawnTime, function()
                self:Respawn()
            end)
 
            return true
        else
            return false
        end
 
    end
 
    ----------------------------------------------------------------------------
    -- Destroy the respawn countdown/tribute
    ----------------------------------------------------------------------------
    function Hero:StopRespawn()
 
        if gvHeroLevelSystem.Config.AllowRespawn and not self.IsDead then
            -- Tribute
            Logic.RemoveTribute(self.Player, self.TributeID)
            -- Countdown
            StopCountdown(self.RespawnCountdown)
        end
 
    end
 
    ----------------------------------------------------------------------------
    -- This function is called every second
    ----------------------------------------------------------------------------
    function Hero:EverySecond()
 
        if not self.IsDead then
            self.IsDead = IsDead(self.ID)
            -- The hero died
            if self.IsDead then
                -- Show a message
                HeroLevelSystem_Chat(self.Name .. " (Stufe " .. self.Level .. ") ist gefallen.", self.Player)
                -- Callback
                HeroLevelSystem_OnEntityDestroyed(self.ID)
                -- Init respawn
                self:InitRespawn()
 
            -- Hero is still alive
            else
                -- Update status values
                self:UpdateStatusValues()
 
                -- Update helathbar
                self:Update3DHealthBar()
            end
        else
            self.IsDead = IsDead(self.ID)
            -- Hero was resurrected by friendly units
            if not self.IsDead then
                -- Update all status values once before we set the default values after resurrection
                self:UpdateStatusValues()
 
                -- Give him back some health...
                self.Health = math.floor(self.MaxHealth / 2)
                -- ...and action points
                self.ActionPoints = math.floor(self.MaxActionPoints / 3)
                -- Stop respawn
                self:StopRespawn()
            end
        end
 
    end
 
    ----------------------------------------------------------------------------
    -- Resurrect the hero by the system
    ----------------------------------------------------------------------------
    function Hero:Respawn()
 
        -- This might happen, when the hero drowned. No solution yet :(
        if not IsExisting(self.ID) then
            return
        end
 
        -- Drop an item
        self:DropItem()
 
        -- Respawn in front of the HQ or, if non exists, at the last position
        local position = GetPosition(self.ID)
        for i = 3, 1, -1 do
            local count, id = Logic.GetPlayerEntities(self.Player, Entities["PB_Headquarters"..i], 1)
            if count > 0 then
                position = KreisPosition(id, 1000, 0)
            end
        end
 
        -- Remove the dead body
        local oldID = self.ID
        DestroyEntity(self.ID)
 
        -- Create a new hero of the same type
        self.ID = CreateEntity(self.Player, self.Type, position)
        -- Full health now
        self.Health = self.MaxHealth
 
        -- Update hero table
        gvHeroLevelSystem.Heroes[self.ID] = gvHeroLevelSystem.Heroes[oldID]
        gvHeroLevelSystem.Heroes[oldID] = nil
 
    end
 
    ----------------------------------------------------------------------------
    -- Give an item to the hero
    ----------------------------------------------------------------------------
    function Hero:GiveItem(_ItemID)
 
        -- The hero can't carry one more item, maximum reached
        if table.getn(self.Items) >= gvHeroLevelSystem.Config.MaxItems then
            Message(self.Name .. " kann nicht noch mehr Items mehr tragen!")
            return false
        end
 
        local Item = HeroItems_GetByID(_ItemID)
 
        -- Check if the item can be carried
        if Item.Unique then
            for _, ItemID in self.Items do
                if ItemID == _ItemID then
                    -- Hero has this item already
                    Message(self.Name .. " hat dieses Item bereits!")
                    return false
                end
            end
        end
 
        -- Perform item effect
        if Item.EffectOnReceipt ~= nil then
            Item.EffectOnReceipt(self, _ItemID)
        end
 
        -- Add the item to the list
        table.insert(self.Items, _ItemID)
        return true
 
    end
 
    ----------------------------------------------------------------------------
    -- Remove an item from the hero
    ----------------------------------------------------------------------------
    function Hero:RemoveItem(_ItemID)
 
        for i, ItemID in self.Items do
            if ItemID == _ItemID then
                Item = HeroItems_GetByID(_ItemID)
 
                -- Perform item effect
                if Item.EffectOnDrop ~= nil then
                    Item.EffectOnDrop(self, _ItemID)
                end
 
                table.remove(self.Items, i)
                return
            end
        end
 
    end
 
    ----------------------------------------------------------------------------
    -- Drops an item on the gamearea
    ----------------------------------------------------------------------------
    function Hero:DropItem(_ItemID)
 
        local NumberOfItems = table.getn(self.Items)
 
        -- Hero has no items
        if NumberOfItems == 0 then
            return
        end
 
        -- Drop a random item if no ID given
        if not _ItemID then
            local i = GetRandom(NumberOfItems) + 1
            _ItemID = self.Items[i].ID
        end
 
        -- Drop the item near the hero
        local Position = GetPosition(self.ID)
        HeroItems_CreateChest(Position, _ItemID)
 
        self:RemoveItem(_ItemID)
 
    end
 
 
    gvHeroLevelSystem.Heroes[_id] = Hero:Hero(_id, _level)
 
end
 
--------------------------------------------------------------------------------
-- Returns the hero table of the given id or false if the id was not found
--------------------------------------------------------------------------------
function HeroLevelSystem_GetHero(_HeroID)
 
    return gvHeroLevelSystem.Heroes[_HeroID] or false
 
end
 
--------------------------------------------------------------------------------
-- Returns the number of heroes of a player and a table with all his heroes
--------------------------------------------------------------------------------
function HeroLevelSystem_GetHeroesOfPlayer(_Player)
 
    local Heroes = {}
    for HeroID, Hero in pairs(gvHeroLevelSystem.Heroes) do
        if Hero.Player == _Player then
            table.insert(Heroes, Hero)
        end
    end
 
    return table.getn(Heroes), Heroes
 
end
 
--------------------------------------------------------------------------------
-- Will be called when an entity was created
--------------------------------------------------------------------------------
function HeroLevelSystem_OnEntityCreated()
 
    local EntityID = Event.GetEntityID()
    if Logic.IsHero(EntityID) == 1 then
        HeroLevelSystem_NewHero(EntityID)
    end
 
end
 
--------------------------------------------------------------------------------
-- This function is called when an entity hits another entity
--------------------------------------------------------------------------------
function HeroLevelSystem_DelayOneTurn(_Attacker, _Target)
 
    local Hero = HeroLevelSystem_GetHero(_Attacker)
    if Hero then
        Hero:HurtEntity(_Target)
    end
 
    Hero = HeroLevelSystem_GetHero(_Target)
    if Hero then
        Hero:TakeDamage(Logic.GetEntityDamage(_Attacker))
    end
 
    return true
 
end
 
function HeroLevelSystem_OnEntityHurtEntity()
 
    local Attacker, Target = Event.GetEntityID1(), Event.GetEntityID2()
 
    if Logic.IsLeader(Target) == 1 then
        local soldiersList = {Logic.GetSoldiersAttachedToLeader(Target)}
        for i = 2, soldiersList[1] + 1 do
            gvHeroLevelSystem.Entities[soldiersList[i]] = Attacker
        end
    end
    gvHeroLevelSystem.Entities[Target] = Attacker
 
    Trigger.RequestTrigger(Events.LOGIC_EVENT_EVERY_TURN, nil, "HeroLevelSystem_DelayOneTurn", 1, nil, {Attacker, Target})
 
end
 
--------------------------------------------------------------------------------
-- This function is called when an entity was destroyed or a hero died
--------------------------------------------------------------------------------
function HeroLevelSystem_OnEntityDestroyed(_entity)
 
    if not _entity then
        _entity = Event.GetEntityID()
 
        if Logic.IsHero(_entity) == 1 then
            return
        end
    end
 
    local attacker = gvHeroLevelSystem.Entities[_entity]
    if not IsValid(attacker) then
        return
    end
 
    local xp = 0
    if Logic.IsSerf(_entity) == 1 then
        xp = 5
 
    elseif Logic.IsBuilding(entity) == 1 then
        return
 
    elseif Logic.IsEntityInCategory(_entity, EntityCategories.Military) == 0 then
        return
 
    elseif Logic.IsEntityInCategory(_entity, EntityCategories.Hero) == 1 then
        local Hero = HeroLevelSystem_GetHero(_entity)
        if Hero then
            xp = Hero.Level * 50
        else
            xp = 100
        end
 
    elseif Logic.IsEntityInCategory(_entity, EntityCategories.Cannon) == 1 then
        xp = 20
 
    elseif Logic.IsEntityInCategory(_entity, EntityCategories.Leader) == 1 then
        xp = 30
 
    elseif Logic.IsEntityInCategory(_entity, EntityCategories.Soldier) == 1 then
        xp = 15
    end
 
    local Player = GetPlayer(attacker)
    local Position = GetPosition(_entity)
 
    -- Get all near heroes in range
    local NearHeroes = {}
    for p = 1, gvMission.HumanPlayers do
        -- Must be an enemy, of course
        if Logic.GetDiplomacyState(Player, p) == Diplomacy.Hostile then
            -- Is a hero of this player in range?
            if Logic.IsPlayerEntityOfCategoryInArea(p, Position.X, Position.Y, gvHeroLevelSystem.Config.MaxDistance, "Hero") == 1 then
                -- Yes, get all heroes and find out which heroes are near the target
                local Count, Heroes = HeroLevelSystem_GetHeroesOfPlayer(p)
                for _, Hero in Heroes do
                    if IsNear(_entity, Hero.ID, gvHeroLevelSystem.Config.MaxDistance) then
                        table.insert(NearHeroes, Hero)
                    end
                end
            end
        end
    end
 
    -- For all near heroes: XP and gold, shared with all other near heroes
    NumberOfNearHeroes = table.getn(NearHeroes)
    for _, Hero in NearHeroes do
        -- Give experience
        Hero:TakeExperience(math.floor(xp / NumberOfNearHeroes + 0.5))
    end
 
    gvHeroLevelSystem.Entities[_entity] = nil
 
end
 
--------------------------------------------------------------------------------
-- This function is called every second
--------------------------------------------------------------------------------
function HeroLevelSystem_EverySecond()
 
    for id, hero in pairs(gvHeroLevelSystem.Heroes) do
        hero:EverySecond()
    end
 
    -- Item chest quest
    if HeroItems_EverySecond ~= nil then
        HeroItems_EverySecond()
    end
 
end
 
--------------------------------------------------------------------------------
-- Initialise the user interface
--------------------------------------------------------------------------------
function HeroLevelSystem_InitInterface()
 
    ----------------------------------------------------------------------------
    -- This shows the real maximum of health of a hero
    ----------------------------------------------------------------------------
    HeroLevelSystem_GetEntityMaxHealth = Logic.GetEntityMaxHealth
    Logic.GetEntityMaxHealth = function(_EntityID)
 
        local Hero = HeroLevelSystem_GetHero(_EntityID)
        if Hero then
            -- Additional hero information
            HeroLevelSystem_DetailsHero(_EntityID)
 
            return Hero.MaxHealth
        else
            return HeroLevelSystem_GetEntityMaxHealth(_EntityID)
        end
 
    end
 
    ----------------------------------------------------------------------------
    -- This shows the real current health of a hero
    ----------------------------------------------------------------------------
    HeroLevelSystem_GetEntityHealth = Logic.GetEntityHealth
    Logic.GetEntityHealth = function(_EntityID)
 
        local Hero = HeroLevelSystem_GetHero(_EntityID)
        if Hero then
            return Hero.Health
        else
            return HeroLevelSystem_GetEntityHealth(_EntityID)
        end
 
    end
 
    ----------------------------------------------------------------------------
    -- This shows the real damage amount of a hero
    ----------------------------------------------------------------------------
    HeroLevelSystem_GetEntityDamage = Logic.GetEntityDamage
    Logic.GetEntityDamage = function(_EntityID)
 
        local Hero = HeroLevelSystem_GetHero(_EntityID)
        if Hero then
            return Hero.BaseDamage + Hero.ExtraDamage
        else
            return HeroLevelSystem_GetEntityDamage(_EntityID)
        end
 
    end
 
    ----------------------------------------------------------------------------
    -- This shows the real armor amount of a hero
    ----------------------------------------------------------------------------
    HeroLevelSystem_GetEntityArmor = Logic.GetEntityArmor
    Logic.GetEntityArmor = function(_EntityID)
 
        local Hero = HeroLevelSystem_GetHero(_EntityID)
        if Hero then
            return Hero.BaseArmor + Hero.ExtraArmor
        else
            return HeroLevelSystem_GetEntityArmor(_EntityID)
        end
 
    end
 
    ----------------------------------------------------------------------------
    -- Update the hero details widget
    ----------------------------------------------------------------------------
    HeroLevelSystem_DetailsHero = function(_EntityID)
 
        local Hero = HeroLevelSystem_GetHero(_EntityID)
    	if not Hero then
            XGUIEng.ShowWidget(XGUIEng.GetWidgetID("TooltipBottom"), 0)
            return
        end
 
        -- Hero name and level
        local Text = Hero.Name .. " (Stufe " .. Hero.Level
 
        -- Experience
        if Hero.Level < gvHeroLevelSystem.Config.MaxLevel then
            local Experience = math.floor(Hero.Experience / gvHeroLevelSystem.Config.NextLevel(Hero.Level) * 100)
            Text = Text .. " => Stufe " .. (Hero.Level + 1) .. ": " .. Experience .. "%"
        end
 
        Text = Text .. ")"
 
        -- ActionPoints
        Text = Text .. " @cr Aktionspunkte: " .. Hero.ActionPoints .. "/" .. Hero.MaxActionPoints
 
        -- Items
        local NumberOfItems = table.getn(Hero.Items)
        if NumberOfItems > 0 then
            Text = Text .. " @cr Items: "
 
            local i = 1
            for _, ItemID in Hero.Items do
                local Item = HeroItems_GetByID(ItemID)
 
                Text = Text .. Item.Name
 
                if i ~= NumberOfItems then
                    Text = Text .. ", "
                end
                i = i + 1
            end
 
        end
 
        -- Open item chest
        if Hero.FoundItemRemainingTime ~= 0 then
            Text = Text .. " @cr Kiste mit " .. Hero.FoundItem.Name .. " wird geoeffnet..." .. Hero.FoundItemRemainingTime
        end
 
        -- Update Tooltip text
        XGUIEng.ShowWidget(XGUIEng.GetWidgetID("TooltipBottom"), 1)
 
        XGUIEng.SetText(gvGUI_WidgetID.TooltipBottomCosts, "")
        XGUIEng.SetText(gvGUI_WidgetID.TooltipBottomText, Text)
        XGUIEng.SetText(gvGUI_WidgetID.TooltipBottomShortCut, "")
 
    end
 
    ----------------------------------------------------------------------------
    -- Show or hide the tooltip widget with the hero details when the selection changed
    ----------------------------------------------------------------------------
    HeroLevelSystem_SelectionChanged = GameCallback_GUI_SelectionChanged
    GameCallback_GUI_SelectionChanged = function()
        HeroLevelSystem_SelectionChanged()
        HeroLevelSystem_DetailsHero(GUI.GetSelectedEntity())
    end
 
    ----------------------------------------------------------------------------
    -- Enable or disable the ability buttons in subject to the available action points
    ----------------------------------------------------------------------------
    HeroLevelSystem_HeroAbility = GUIUpdate_HeroAbility
    GUIUpdate_HeroAbility = function(_ability, _button)
 
    	local HeroID = HeroSelection_GetCurrentSelectedHeroID()
    	local Hero = HeroLevelSystem_GetHero(HeroID)
 
    	if Hero then
            local CurrentWidgetID = XGUIEng.GetCurrentWidgetID()
    	    XGUIEng.SetMaterialColor(CurrentWidgetID, 1, 0, 0, 0, 0)
 
            local NeededActionPoints = Logic.HeroGetAbilityRechargeTime(HeroID, _ability)
        	if Hero.ActionPoints >= NeededActionPoints then
        		XGUIEng.DisableButton(_button, 0)
        	end
        	if Hero.ActionPoints < NeededActionPoints then
        		XGUIEng.DisableButton(_button, 1)
        	end
        else
            HeroLevelSystem_HeroAbility(_ability, _button)
        end
 
    end
 
    ----------------------------------------------------------------------------
    -- Use the chatsystem when network exists. So everyone will see the custom hero name :)
    ----------------------------------------------------------------------------
    if XNetwork and XNetwork.Manager_DoesExist() == 1 then
         HeroLevelSystem_Chat = function(_message, _player)
            if _player == GUI.GetPlayerID() then
                XNetwork.Chat_SendMessageToAll(_message)
            end
        end
    else
        HeroLevelSystem_Chat = Message
    end
 
end

Code für Items

--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- HeroItems.lua
-- Functions to create items and to control the item-chests.
-- Defines also some basic items.
--
-- Item table description:
--  .ID                     Internal name of the item
--  .Name                   The displayed name of the item
--  .Unique                 Set to true if the hero can carray only one item of
--                          this type (optional)
--  .Probability            The chance of the apperance of this item.
--                          Value must be in range [0; 1]
--  .EffectOnReceipt        A callback function that defines the behaviour of
--                          the item after receipt (optional)
--  .EffectEverySecond      A callback function that defines the behaviour of
--                          the item every second
--  .EffectOnDrop           A callback function that defines the behaviour of
--                          the item when it is dropped
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
HeroItems = {}
 
gvHeroItems_List = {}
gvHeroItems_Chests = {}
 
function HeroItems_GetByID(_ItemID)
    return gvHeroItems_List[_ItemID]
end
 
--------------------------------------------------------------------------------
-- Create a new item
--------------------------------------------------------------------------------
function HeroItems_Create(_ItemDescription)
 
    table.insert(gvHeroItems_List, _ItemDescription)
    HeroItems[_ItemDescription.ID] = table.getn(gvHeroItems_List)
 
end
 
--------------------------------------------------------------------------------
-- Create a chest with an item
--------------------------------------------------------------------------------
function HeroItems_CreateChest(_Position, _ItemID)
 
    local ChestID = CreateEntity(0, Entities.XD_ChestClose, _Position)
    gvHeroItems_Chests[ChestID] = _ItemID
 
    return ChestID
 
end
 
--------------------------------------------------------------------------------
-- Remove an item chest from the map
--------------------------------------------------------------------------------
function HeroItems_RemoveChest(_ChestID)
 
    DestroyEntity(_ChestID)
    table.remove(gvHeroItems_Chests, _ChestID)
 
end
 
--------------------------------------------------------------------------------
-- Return a random item
--------------------------------------------------------------------------------
function HeroItems_GetRandom()
 
    local n = table.getn(gvHeroItems_List)
    local ItemID, Item, p
 
    repeat
        ItemID = 1 + GetRandom(n)
        Item = HeroItems_GetByID(ItemID)
        p = GetRandom(101) / 100.0
    until p <= Item.Probability
 
    return ItemID
 
end
 
--------------------------------------------------------------------------------
-- This function handles the opening of the item chests and is called every second
--------------------------------------------------------------------------------
function HeroItems_EverySecond()
 
    -- For all heroes...
    for _, Hero in gvHeroLevelSystem.Heroes do
        -- ...and all chest
        for ChestID, ItemID in gvHeroItems_Chests do
 
            local Item = HeroItems_GetByID(ItemID)
            local NeededTime = 6
            local CounterName = "Hero" .. Hero.ID .. "Chest" .. ChestID
 
            -- Create a counter
            if not Counter.IsValid(CounterName) then
                Counter.SetLimit(CounterName, NeededTime)
            end
 
            -- Check if a hero is near this chest
            if IsNear(Hero.ID, ChestID, 250) and not Hero.IsDead then
                -- Need five seconds to open the chest
                if Counter.Tick(CounterName) then
                    -- Hero has taken this item, remove the chest and give a sound feedback
                    if Hero:GiveItem(ItemID) then
                        Hero.FoundItemRemainingTime = 0
 
                        -- Remove the chest
                        HeroItems_RemoveChest(ChestID)
 
                        Message(Item.Name .. " @color:255,255,255 gefunden!")
                        Sound.PlayGUISound(Sounds.OnKlick_Select_erec, 0)
                    end
 
                -- Show item description and the remaining time that is needed to open the chest
                else
                    Hero.FoundItemRemainingTime = NeededTime - Counter.GetTick(CounterName)
                    Hero.FoundItem = Item
                end
 
                -- Ignore all other chests; a hero can only open one chest at the same time
                break
 
            -- The hero is not near this chest
            else
                Counter.Reset(CounterName)
                Hero.FoundItemRemainingTime = 0
            end
 
        end
    end
 
end

Beispiele für Items

--------------------------------------------------------------------------------
-- Define some basic items
--------------------------------------------------------------------------------
 
-- Ring of regeneration
HeroItems_Create{
    ID      = "RingOfRegeneration",
    Name    = "Ring der Regeneration",
    Unique  = true,
    Probability = 0.05,
 
    EffectEverySecond = function(_Hero)
        _Hero.Heath = math.min(_Hero.MaxHealth, _Hero.Health + 1 + GetRandom(3))
    end,
}
 
-- Heavy Armor
HeroItems_Create{
    ID      = "HeavyArmor",
    Name    = "Schwere Ruestung",
    Unique  = true,
    Probability = 0.10,
 
    EffectOnReceipt = function(_Hero, _ItemID)
        _Hero.ExtraArmor = _Hero.ExtraArmor + 2
    end,
 
    EffectOnDrop = function(_Hero, _ItemID)
        _Hero.ExtraArmor = _Hero.ExtraArmor - 2
    end,
}
 
-- Small Heal Potion
HeroItems_Create{
    ID      = "SmallHealPotion",
    Name    = "Schwacher Heiltrank",
    Probability = 0.75,
 
    EffectEverySecond = function(_Hero, _ItemID)
        if _Hero.Health <= 25 then
            _Hero.Health = math.min(_Hero.MaxHealth, _Hero.Health + 100)
            _Hero:RemoveItem(_ItemID)
        end
    end,
}