Heldenfähigkeit: "Charge"

„Charge“ fügt Kerberos' „Aura der Furcht“ eine weitere Komponente hinzu:

20 Sekunden lang kann bis zu 5x ein Nahkämpfer aus dem Umkreis von Kerberos (max. Abstand 1500) einen gegnerischen Fernkämpfer anstürmen - der Nahkämpfer wird dabei sofort an die Position des Fernkämpfers versetzt.

Dieser Sturmangriff wird ausgelöst, wenn der feindliche Fernkämpfer den betreffenden eigenen Nahkämpfer aus einer Distanz von 400-2200 angreift. Pro Einsatz der Aura kann ein einzelner Trupp höchstens 1x „stürmen“.

Als Bonus wird die Erfahrung eines Trupps beim Stürmen auf 3 Sterne angehoben. Truppen, die vorher schon mehr Sterne hatten, behalten den alten Wert.

Anmerkungen:

function Heromods_ActivateCharge()
 
    Heromods_Charge = {
        -- ### Konstanten
        AURA_RANGE = 1500,
        MIN_RANGE = 400,
        MAX_RANGE = 2200,
        MAX_DURATION = 20,
        MAX_CHARGES = 5,
        MELEE_ENTITY_TYPES = {
            [Entities.PU_BattleSerf] = true,
            [Entities.PU_Hero5_Outlaw] = true,
            [Entities.CU_Barbarian_Hero_wolf] = true,
            [Entities.PU_LeaderHeavyCavalry1] = true,
            [Entities.PU_LeaderHeavyCavalry2] = true,
            [Entities.PU_LeaderPoleArm1] = true,
            [Entities.PU_LeaderPoleArm2] = true,
            [Entities.PU_LeaderPoleArm3] = true,
            [Entities.PU_LeaderPoleArm4] = true,
            [Entities.PU_LeaderSword1] = true,
            [Entities.PU_LeaderSword2] = true,
            [Entities.PU_LeaderSword3] = true,
            [Entities.PU_LeaderSword4] = true,
            [Entities.CU_VeteranCaptain] = true,
            [Entities.CU_VeteranLieutenant] = true,
            [Entities.CU_VeteranMajor] = true,
            [Entities.CU_Evil_LeaderBearman1] = true,
            [Entities.CU_BlackKnight_LeaderMace1] = true,
            [Entities.CU_Barbarian_LeaderClub1] = true,
            [Entities.CU_BanditLeaderSword1] = true,
            [Entities.CU_AggressiveWolf] = true,
        },
 
        -- ### Verwaltung
        activeForPlayer = {false, false, false, false, false, false, false, false},
        effectCounter = {0,0,0,0,0,0,0,0},
        chargesUsed = {0,0,0,0,0,0,0,0},
        chargedEntities = {{},{},{},{},{},{},{},{}},
        entityHurtTriggerID = 0,
    };
 
    GUIAction_Hero7Madness_OrigHeroMod = GUIAction_Hero7Madness;
    GUIAction_Hero7Madness = function()
        GUIAction_Hero7Madness_OrigHeroMod();
        local pID = GUI.GetPlayerID();
        GUI.PayTribute( 8, Heromods_Charge.tribut[pID] );
    end
 
    GUITooltip_NormalButton_OrigHeroMod = GUITooltip_NormalButton;
    GUITooltip_NormalButton = function( _wID, _keyBind )
        GUITooltip_NormalButton_OrigHeroMod( _wID, _keyBind );
        if _wID == "MenuHero7/command_madness" then
            XGUIEng.SetText( gvGUI_WidgetID.TooltipBottomText, "@color:220,10,30 Aura des Krieges @cr @color:255,255,255 Kerberos senkt die Widerstandskraft aller Feinde in seiner Umgebung und ermöglicht es verbündeten Nahkämpfern 20 Sek. lang, feindliche Fernkämpfer anzustürmen." );
        end
    end
 
    -- ###############################
    -- ### Tribute
    Heromods_Charge.tributCallback = {
        [1] = function()
            Heromods_Charge_ActivateForPlayer(1);
            Heromods_Charge.tribut[1] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[1] ) );
        end,
        [2] = function()
            Heromods_Charge_ActivateForPlayer(2);
            Heromods_Charge.tribut[1] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[2] ) );
        end,
        [3] = function()
            Heromods_Charge_ActivateForPlayer(3);
            Heromods_Charge.tribut[1] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[3] ) );
        end,
        [4] = function()
            Heromods_Charge_ActivateForPlayer(4);
            Heromods_Charge.tribut[1] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[4] ) );
        end,
        [5] = function()
            Heromods_Charge_ActivateForPlayer(5);
            Heromods_Charge.tribut[1] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[5] ) );
        end,
        [6] = function()
            Heromods_Charge_ActivateForPlayer(6);
            Heromods_Charge.tribut[1] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[6] ) );
        end,
        [7] = function()
            Heromods_Charge_ActivateForPlayer(7);
            Heromods_Charge.tribut[1] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[7] ) );
        end,
        [8] = function()
            Heromods_Charge_ActivateForPlayer(8);
            Heromods_Charge.tribut[1] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[8] ) );
        end,
    };
 
    Heromods_Charge.tribut = {
        [1] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[1] ) ),
        [2] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[2] ) ),
        [3] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[3] ) ),
        [4] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[4] ) ),
        [5] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[5] ) ),
        [6] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[6] ) ),
        [7] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[7] ) ),
        [8] = AddTribute( CreateATribute( 8, "", {Gold=0}, Heromods_Charge.tributCallback[8] ) ),
    };
 
    -- ###############################
    -- ### Comforts
    Heromods_Charge_GetHero = function(_pID)
        local found,heroID = Logic.GetPlayerEntities( _pID, Entities.CU_BlackKnight, 1 );
        if found == 0 then
            Message("DEBUG: Kein Kerberos fuer Spieler ".._pID.." gefunden!");
            return 0;
        else
            return heroID;
        end
    end
 
    Heromods_ChargeAbility = function( _charger, _target )
        if SoldierTypes[Logic.GetEntityType(_charger)] then
            _charger = SoldierGetLeaderEntityID(_charger);
        end
        assert(IsExisting(_charger));
 
        local eType = Logic.GetEntityType(_charger);
        local eName = GetEntityName(_charger);
        local pID = GetPlayer(_charger);
        assert(IsValidPlayerID[pID]);
        local oldPos = GetPosition(_charger);
        assert(IsValidPosition(oldPos));
        local newPos = GetPosition(_target);
        assert(IsValidPosition(newPos));
        local nSol = Logic.LeaderGetNumberOfSoldiers(_charger);
        assert(type(nSol) == "number");
        local ep = Logic.GetLeaderExperienceLevel(_charger);
        assert(type(ep) == "number");
        local hurt = Logic.GetEntityMaxHealth(_charger) - Logic.GetEntityHealth(_charger);
        assert(type(hurt) == "number");
        local selected = false;
        if pID == GUI.GetPlayerID() then
            local sels = {GUI.GetSelectedEntities()};
            for i,sel in ipairs(sels) do
                if sel == _charger then
                    selected = true;
                    break;
                end
            end
        end
 
        Logic.CreateEffect( GGL_Effects.FXCrushBuilding, oldPos.X, oldPos.Y );
        Logic.CreateEffect( GGL_Effects.FXCrushBuilding, newPos.X, newPos.Y );
        Effektlinie( oldPos.X, oldPos.Y, newPos.X, newPos.Y, GGL_Effects.FXDestroyTree, 150 );
        Logic.DestroyGroupByLeader(_charger);
        local newID = CreateArmyTroops( pID, newPos, eType, nSol, 1, math.max(ep-1, 1) );
 
        if selected then
            GUI.SelectEntity(newID);
        end
        if hurt>0 then
            Logic.HurtEntity( newID, hurt );
        end
        if type(eName) == "string" then
            SetEntityName(newID,eName);
        end
        Logic.GroupAttack( newID, _target );
        return newID;
    end
 
    Heromods_Charge_ActivateForPlayer = function(_pID)
        -- ### Einstellungen
        Heromods_Charge.activeForPlayer[_pID] = true;
        Heromods_Charge.effectCounter[_pID] = 0;
        Heromods_Charge.chargesUsed[_pID] = 0;
        Heromods_Charge.chargedEntities[_pID] = {};
 
        -- ### Jobs
        if JobIsRunning(Heromods_Charge.entityHurtTriggerID) == 0 then
            Heromods_Charge.entityHurtTriggerID = Trigger.RequestTrigger( Events.LOGIC_EVENT_ENTITY_HURT_ENTITY, "", "Heromods_Charge_EntityHurt", 1);
            Message("SPIELER ".._pID..": EFFEKT BEGINNT -> Trigger inaktiv, starten!");
        else
            Message("SPIELER ".._pID..": EFFEKT BEGINNT -> Trigger bereits aktiv!");
        end
        StartSimpleJob("Heromods_Charge_Job1");
    end
 
    Heromods_Charge_DeactivateForPlayer = function(_pID)
        -- ### Einstellungen
        Heromods_Charge.activeForPlayer[_pID] = false;
 
        -- ### Jobs
        local t = Heromods_Charge.activeForPlayer;
        if not (t[1] or t[2] or t[3] or t[4] or t[5] or t[6] or t[7] or t[8]) then
            EndJob( Heromods_Charge.entityHurtTriggerID ); -- solange kein Effekt laeuft, braucht der Hurt-Callback nicht aktiv zu sein
            Message("SPIELER ".._pID..": EFFEKT KLINGT AUS -> Trigger wird deaktiviert!");
        else
            Message("SPIELER ".._pID..": EFFEKT KLINGT AUS -> Trigger bleibt aktiv...");
        end
    end
 
    Heromods_Charge_JobTick = function(_pID)
        if not Heromods_Charge.activeForPlayer[1] then
            return true;
        else
            local count = Heromods_Charge.effectCounter[_pID];
            if (count < Heromods_Charge.MAX_DURATION) then
                local heroID = Heromods_Charge_GetHero(_pID);
                if IsAlive( heroID ) then
                    Message("Job Tick "..count.." fuer Spieler ".._pID);
                    Heromods_Charge.effectCounter[_pID] = count+1;
                    local pos = GetPosition( heroID );
                    -- Logic.CreateEffect( GGL_Effects.FXKerberosFear, pos.X, pos.Y );
                    -- Logic.CreateEffect( GGL_Effects.FXMaryDemoralize, pos.X, pos.Y );
                    Logic.CreateEffect( GGL_Effects.FXBuildingSmokeLarge, pos.X, pos.Y );
                    Logic.CreateEffect( GGL_Effects.FXCannonFire, pos.X, pos.Y );
                else
                    Heromods_Charge_DeactivateForPlayer(_pID);
                end
            else
                Heromods_Charge_DeactivateForPlayer(_pID);
            end
            return false;
        end
    end
 
    -- ###############################
    -- ### Jobs
    Heromods_Charge_Job1 = function()
        return Heromods_Charge_JobTick(1);
    end
    Heromods_Charge_Job2 = function()
        return Heromods_Charge_JobTick(2);
    end
    Heromods_Charge_Job3 = function()
        return Heromods_Charge_JobTick(3);
    end
    Heromods_Charge_Job4 = function()
        return Heromods_Charge_JobTick(4);
    end
    Heromods_Charge_Job5 = function()
        return Heromods_Charge_JobTick(5);
    end
    Heromods_Charge_Job6 = function()
        return Heromods_Charge_JobTick(6);
    end
    Heromods_Charge_Job7 = function()
        return Heromods_Charge_JobTick(7);
    end
    Heromods_Charge_Job8 = function()
        return Heromods_Charge_JobTick(8);
    end
 
    Heromods_Charge_EntityHurt = function()
        local attID = Event.GetEntityID1();
        local tarID = TroopGetLeaderEntityID( Event.GetEntityID2() );
 
        local eType = Logic.GetEntityType(tarID);
        if Heromods_Charge.MELEE_ENTITY_TYPES[eType] then
            local pID = GetPlayer(tarID);
            if Heromods_Charge.activeForPlayer[pID] then -- TODO: auch Verbündete ermöglichen
                if not Heromods_Charge.chargedEntities[pID][tarID] then
                    local nChargesUsed = Heromods_Charge.chargesUsed[pID];
                    if (nChargesUsed < Heromods_Charge.MAX_CHARGES) then
                        local heroID = Heromods_Charge_GetHero(pID);
                        if (GetDistance(tarID, heroID) <= Heromods_Charge.AURA_RANGE) then
                            local dist = GetDistance(attID, tarID);
                            if (dist >= Heromods_Charge.MIN_RANGE) and (dist <= Heromods_Charge.MAX_RANGE) then
                                local newID = Heromods_ChargeAbility( tarID, attID );
                                Heromods_Charge.chargesUsed[pID] = nChargesUsed+1;
                                Heromods_Charge.chargedEntities[pID][newID] = true;
                            end
                        else
                            -- Message("zu weit vom Helden weg.");
                        end
                    else
                        Heromods_Charge_DeactivateForPlayer(pID);
                        -- Message("charges verbraucht");
                    end
                else
                    -- Message("bereits gecharged: "..tarID);
                end
            else
                -- Message("nicht aktiv fuer Spieler: "..pID);
            end
        else
            -- Message("ungueltiger Typ: "..eType);
        end
    end
 
end
 
 
 
-- ##################################
-- ### Comforts
-- ##################################
function IsValidPosition( _position )
    if ( type(_position) == "table" ) then
        if ( type(_position.X) == "number" ) and ( type(_position.Y) == "number" ) then
            local x,y = Logic.WorldGetSize();
            if ( (_position.X <= x+100) and (_position.X >= 0) ) and ( (_position.Y <= y+100) and (_position.Y >= 0) ) then
                return true;
            end
        end
    end
    return false;
end
 
IsValidPlayerID = {1,2,3,4,5,6,7,8}
 
function GetDistance(_pos1,_pos2)
    if (type(_pos1) == "string") or (type(_pos1) == "number") then
        _pos1 = GetPosition(_pos1);
    end
    assert(type(_pos1) == "table");
    if (type(_pos2) == "string") or (type(_pos2) == "number") then
        _pos2 = GetPosition(_pos2);
    end
    assert(type(_pos2) == "table");
    local xDistance = (_pos1.X - _pos2.X);
    local yDistance = (_pos1.Y - _pos2.Y);
    return math.sqrt((xDistance^2) + (yDistance^2));
end
 
SoldierTypes = { -- Alle Soldaten-EntityTypes mit zugehörigem Leader-Typ
    [Entities.CU_BanditSoldierBow1] = Entities.CU_BanditLeaderBow1,
    [Entities.CU_BanditSoldierSword1] = Entities.CU_BanditLeaderSword1,
    [Entities.CU_BanditSoldierSword2] = Entities.CU_BanditLeaderSword2,
    [Entities.CU_Barbarian_SoldierClub1] = Entities.CU_Barbarian_LeaderClub1,
    [Entities.CU_Barbarian_SoldierClub2] = Entities.CU_Barbarian_LeaderClub2,
    [Entities.CU_BlackKnight_SoldierMace1] = Entities.CU_BlackKnight_LeaderMace1,
    [Entities.CU_BlackKnight_SoldierMace2] = Entities.CU_BlackKnight_LeaderMace2,
    [Entities.CU_Evil_SoldierBearman1] = Entities.CU_Evil_LeaderBearman1,
    [Entities.CU_Evil_SoldierSkirmisher1] = Entities.CU_Evil_LeaderSkirmisher1,
    [Entities.PU_SoldierBow1] = Entities.PU_LeaderBow1,
    [Entities.PU_SoldierBow2] = Entities.PU_LeaderBow2,
    [Entities.PU_SoldierBow3] = Entities.PU_LeaderBow3,
    [Entities.PU_SoldierBow4] = Entities.PU_LeaderBow4,
    [Entities.PU_SoldierCavalry1] = Entities.PU_LeaderCavalry1,
    [Entities.PU_SoldierCavalry2] = Entities.PU_LeaderCavalry2,
    [Entities.PU_SoldierHeavyCavalry1] = Entities.PU_LeaderHeavyCavalry1,
    [Entities.PU_SoldierHeavyCavalry2] = Entities.PU_LeaderHeavyCavalry2,
    [Entities.PU_SoldierPoleArm1] = Entities.PU_LeaderPoleArm1,
    [Entities.PU_SoldierPoleArm2] = Entities.PU_LeaderPoleArm2,
    [Entities.PU_SoldierPoleArm3] = Entities.PU_LeaderPoleArm3,
    [Entities.PU_SoldierPoleArm4] = Entities.PU_LeaderPoleArm4,
    [Entities.PU_SoldierRifle1] = Entities.PU_LeaderRifle1,
    [Entities.PU_SoldierRifle2] = Entities.PU_LeaderRifle2,
    [Entities.PU_SoldierSword1] = Entities.PU_LeaderSword1,
    [Entities.PU_SoldierSword2] = Entities.PU_LeaderSword2,
    [Entities.PU_SoldierSword3] = Entities.PU_LeaderSword3,
    [Entities.PU_SoldierSword4] = Entities.PU_LeaderSword4,
};
 
function SoldierGetLeaderEntityID(_solID)
    local leadType = SoldierTypes[Logic.GetEntityType(_solID)]
    if leadType then
        local pID = GetPlayer(_solID)
        local pos = GetPosition(_solID)
        local t = {Logic.GetPlayerEntitiesInArea( pID, leadType, pos.X, pos.Y, 2000, 48 )}
        for i = 2,t[1]+1 do
            local leader = t[i];
            local soldiers = {Logic.GetSoldiersAttachedToLeader(leader)}
            if soldiers[1] then
                for j = 2,soldiers[1]+1 do
                    if soldiers[j] == _solID then
                        return leader,soldiers[2];
                    end
                end
            end
        end
    end
    return 0;
end
 
function TroopGetLeaderEntityID( _eID )
    if Logic.IsLeader(_eID) == 1 then
        return _eID;
    end
    return SoldierGetLeaderEntityID(_eID);
end
 
function Effektlinie( _Ax, _Ay, _Bx, _By, _effekt, _periode)
    assert(type(_Ax) == "number")
    assert(type(_Ay) == "number")
    assert(type(_Bx) == "number")
    assert(type(_By) == "number")
    local startX = _Bx
    local startY = _By
    local _Cx = (_Ax - _Bx)
    local _Cy = (_Ay - _By)
    if (_Cx < 0) and (_Cy < 0) then
        local loX = -_Cx
        local loY = -_Cy
        if (loX > loY) then
            Anzahl = math.floor ((loX / _periode) + 0.5)
        else
            Anzahl = math.floor ((loY / _periode) + 0.5)
        end
    elseif (_Cx < 0) then
        local loX = -_Cx
        local loY = _Cy
        if (loX > loY) then
            Anzahl = math.floor ((loX / _periode) + 0.5)
        else
            Anzahl = math.floor ((loY / _periode) + 0.5)
        end
    elseif (_Cy < 0) then
        local loX = _Cx
        local loY = -_Cy
        if (loX > loY) then
            Anzahl = math.floor ((loX / _periode) + 0.5)
        else
            Anzahl = math.floor ((loY / _periode) + 0.5)
        end
    else
        local loX = _Cx
        local loY = _Cy
        if (loX > loY) then
            Anzahl = math.floor ((loX / _periode) + 0.5)
        else
            Anzahl = math.floor ((loY / _periode) + 0.5)
        end
    end
    _Cx = _Cx / Anzahl
    _Cy = _Cy / Anzahl
    if (_Cx >= 0) and (_Cy >= 0) then
        while (_Bx <= _Ax) and (_By <= _Ay) do
            _Ax = _Ax - _Cx
            _Ay = _Ay - _Cy
            Logic.CreateEffect( _effekt, _Ax, _Ay, 1 )
        end
    elseif (_Cx >= 0) and (_Cy <= 0) then
        _Cy = -_Cy
        while (_Bx <= _Ax) and (_By >= _Ay) do
            _Ax = _Ax - _Cx
            _Ay = _Ay + _Cy
            Logic.CreateEffect( _effekt, _Ax, _Ay, 1 )
        end
    elseif (_Cx <= 0) and (_Cy >= 0) then
        _Cx = -_Cx
        while (_Bx >= _Ax) and (_By <= _Ay) do
            _Ax = _Ax + _Cx
            _Ay = _Ay - _Cy
            Logic.CreateEffect( _effekt, _Ax, _Ay, 1 )
        end
    elseif (_Cx <= 0) and (_Cy <= 0) then
        _Cx = -_Cx
        _Cy = -_Cy
        while (_Bx >= _Ax) and (_By >= _Ay) do
            _Ax = _Ax + _Cx
            _Ay = _Ay + _Cy
            Logic.CreateEffect( _effekt, _Ax, _Ay, 1 )
        end
    end
end
 
function CreateArmyTroops( _player, _position, _leaderType, _numberOfSoldiers, _troops, _experience, _move )
    local army = {
        player     = _player,
    }
    local tD = {
        maxNumberOfSoldiers = _numberOfSoldiers,
        minNumberOfSoldiers = 0,
        experiencePoints    = _experience,
        leaderType          = _leaderType,
        position            = _position,
    }
    for i = 1,_troops do
        army[i] = CreateTroop( army , tD )
    end
    if _move ~= nil then
        for i = 1,_troops do
            Move( army[i], _move )
        end
    end
    return unpack(army);
end