- Joined
- Nov 18, 2014
- Messages
- 180
Oh damn, I always loved BFME cavalry mechanics (Still playing the AotR mod from times to times).
Will test it, hope it will give the same feeling
Will test it, hope it will give the same feeling

'o000' into your Object Editor:Trample_Config with the Map Initialization event, and set the configuration globals (see below).Trample_Config trigger, use Custom Script to set the following:
Trample Config

Events


Map initialization

Conditions

Actions


-------- /////////////////////// --------


Custom script: set TrampleSystem_AuraBuff = 'B000'


Custom script: set TrampleSystem_DecelBuff = 'B001'


Custom script: set TrampleSystem_StunAbility = 'A000'


Custom script: set TrampleSystem_DecelAbility = 'A002'


Custom script: set TrampleSystem_RevengeAbility = 'A003'


-------- /////////////////////// --------


Custom script: set TrampleSystem_AttackType = ATTACK_TYPE_SIEGE


Custom script: set TrampleSystem_DamageType = DAMAGE_TYPE_UNIVERSAL


Custom script: set TrampleSystem_RevengeAttack = ATTACK_TYPE_PIERCE


-------- /////////////////////// --------


Custom script: set TrampleSystem_RevengeMult = 3.00


Custom script: set TrampleSystem_NormalMult = 3.00


Custom script: set TrampleSystem_RevengeBaseMult = 1.00


-------- /////////////////////// --------


Custom script: set TrampleSystem_AngleLimit = 90.00


Custom script: set TrampleSystem_KB_DistMul = 1.00


Custom script: set TrampleSystem_KB_Time = 0.50


-------- /////////////////////// --------


Custom script: set TrampleSystem_DensityRadius = 200.00


Custom script: set TrampleSystem_DensityFactor = 0.15


Custom script: set TrampleSystem_KB_MinDistance = 64.00


-------- /////////////////////// --------


Custom script: call Tramp_SetThreshold('hkni', 2)


Custom script: call Tramp_SetThreshold('orai', 4)


-------- /////////////////////// --------


Custom script: set TrampleSystem_ImpactEffect = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"


-------- /////////////////////// --------


Custom script: if udg_GameTimer == null then


Custom script: set udg_GameTimer = CreateTimer()


Custom script: endif


Custom script: call TimerStart(udg_GameTimer, 100000.00, false, null)
library TrampleSystem requires DamageEngine
/*
* ==============================================================================
* Trample System 2.0
* by Artaros13 & the.Axolotl
* ==============================================================================
* REQUIREMENTS (all must be in your map):
* - Unit Indexer (Bribe)
* - Damage Engine 5.A.0.0+ (Bribe)
* - Knockback 2.5D (Bribe)
* - Is Unit Moving (Bribe)
* ==============================================================================
*/
globals
public integer AuraBuff = 0
public integer DecelBuff = 0
public integer StunAbility = 0
public integer DecelAbility = 0
public integer RevengeAbility = 0
public attacktype AttackType = ATTACK_TYPE_SIEGE
public damagetype DamageType = DAMAGE_TYPE_UNIVERSAL
public attacktype RevengeAttack = ATTACK_TYPE_PIERCE
public real RevengeMult = 3.0
public real NormalMult = 3.0
public real RevengeBaseMult = 1.0
public real AngleLimit = 90.0
public real KB_DistMul = 1.0
public real KB_Time = 0.50
public real DensityRadius = 200.0
public real DensityFactor = 0.15
public real KB_MinDistance = 64.0
// Dust / impact effect set in Trample_Config
public string ImpactEffect = ""
private hashtable hash = InitHashtable()
private unit array pool
private integer poolSize = 0
private constant integer DUMMY_ID = 'o000'
private constant integer MAX_POOL_SIZE = 12
private constant real ANTISPAM_COOLDOWN = 0.80
endglobals
//==============================================================================
// API: per‑unit‑type threshold
//==============================================================================
function Tramp_SetThreshold takes integer unitType, integer maxHits returns nothing
call SaveInteger(hash, unitType, 0, maxHits)
endfunction
//==============================================================================
// Helper: full weapon damage
//==============================================================================
private function GetUnitWeaponDamage takes unit u returns real
return BlzGetUnitBaseDamage(u, 0) + I2R(BlzGetUnitDiceNumber(u, 0)) * I2R(BlzGetUnitDiceSides(u, 0))
endfunction
//==============================================================================
// Frontal cone check
//==============================================================================
private function IsInFrontalCone takes unit source, unit target returns boolean
local real angle = bj_RADTODEG * Atan2(GetUnitY(target) - GetUnitY(source), GetUnitX(target) - GetUnitX(source))
local real diff = RAbsBJ(angle - GetUnitFacing(source))
if diff > 180.0 then
set diff = 360.0 - diff
endif
return diff <= (AngleLimit * 0.5)
endfunction
//==============================================================================
// Dummy recycling (fully MUI‑safe)
//==============================================================================
private function AcquireDummy takes player p, real x, real y, real face returns unit
local unit u
if poolSize > 0 then
set poolSize = poolSize - 1
set u = pool[poolSize]
call SetUnitOwner(u, p, false)
call SetUnitX(u, x)
call SetUnitY(u, y)
call SetUnitFacing(u, face)
call ShowUnit(u, true)
else
set u = CreateUnit(p, DUMMY_ID, x, y, face)
call UnitAddAbility(u, 'Aloc')
call SetUnitPathing(u, false)
endif
return u
endfunction
private function ReleaseDummy takes unit u returns nothing
call ShowUnit(u, false)
call SetUnitX(u, 0.0)
call SetUnitY(u, 0.0)
if poolSize < MAX_POOL_SIZE then
set pool[poolSize] = u
set poolSize = poolSize + 1
else
call RemoveUnit(u)
endif
endfunction
private function OnReleaseCallback takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit u = LoadUnitHandle(hash, GetHandleId(t), 2)
if u != null then
call UnitRemoveAbility(u, StunAbility)
call UnitRemoveAbility(u, DecelAbility)
call ReleaseDummy(u)
endif
call DestroyTimer(t)
set t = null
endfunction
private function DelayedRelease takes unit u returns nothing
local timer t = CreateTimer()
call SaveUnitHandle(hash, GetHandleId(t), 2, u)
call TimerStart(t, 0.00, false, function OnReleaseCallback)
set t = null
endfunction
//==============================================================================
// Core trample hit
//==============================================================================
private function PerformHit takes unit source, unit target, boolean revenge returns nothing
local integer srcId = GetUnitUserData(source)
local integer targId = GetUnitUserData(target)
local integer hits = LoadInteger(hash, srcId, 0) + 1
local integer threshold = LoadInteger(hash, GetUnitTypeId(source), 0)
local real srcX = GetUnitX(source)
local real srcY = GetUnitY(source)
local real face = GetUnitFacing(source)
local real dmg
local unit dummy
local unit u
local group g
local integer nearbyCount
local real kbDist
if GetUnitState(target, UNIT_STATE_LIFE) <= 0.405 then
return
endif
call SaveInteger(hash, srcId, 0, hits)
// --- Damage ---
if revenge then
set dmg = GetUnitWeaponDamage(target) * RevengeMult
call UnitDamageTarget(target, source, dmg, false, false, RevengeAttack, DamageType, null)
set dmg = GetUnitWeaponDamage(source) * RevengeBaseMult
else
set dmg = GetUnitWeaponDamage(source) * NormalMult
endif
call UnitDamageTarget(source, target, dmg, false, false, AttackType, DamageType, null)
// --- Density‑based knockback (manual enemy count) ---
set g = CreateGroup()
call GroupEnumUnitsInRange(g, srcX, srcY, DensityRadius, null)
call GroupRemoveUnit(g, source)
set nearbyCount = 0
loop
set u = FirstOfGroup(g)
exitwhen u == null
call GroupRemoveUnit(g, u)
if IsUnitEnemy(u, GetOwningPlayer(source)) and GetUnitState(u, UNIT_STATE_LIFE) > 0.405 and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
set nearbyCount = nearbyCount + 1
endif
endloop
call DestroyGroup(g)
set g = null
set u = null
set kbDist = GetUnitMoveSpeed(source) * KB_DistMul / (1.0 + DensityFactor * nearbyCount)
if kbDist < KB_MinDistance then
set kbDist = KB_MinDistance
endif
// Fully reset all knockback globals
set udg_Knockback2DAngle = face
set udg_Knockback2DDistance = kbDist
set udg_Knockback2DTime = KB_Time
set udg_Knockback2DUnit = target
set udg_Knockback2DAmphibious = false
set udg_Knockback2DBounces = false
set udg_Knockback2DCollision = 16.00
set udg_Knockback2DDestRadius = 128.00
set udg_Knockback2DKillTrees = false
set udg_Knockback2DPause = true
set udg_Knockback2DSimple = true
set udg_Knockback2DOverride = true
call TriggerExecute(gg_trg_Knockback_2D)
// --- Impact effect (dust) ---
if ImpactEffect != "" then
call DestroyEffect(AddSpecialEffectTarget(ImpactEffect, target, "origin"))
endif
// --- Stun ---
set dummy = AcquireDummy(GetOwningPlayer(source), srcX, srcY, face)
call UnitAddAbility(dummy, StunAbility)
call IssueTargetOrder(dummy, "thunderbolt", target)
call DelayedRelease(dummy)
// --- Deceleration ---
if threshold > 0 and hits >= threshold then
set dummy = AcquireDummy(GetOwningPlayer(source), srcX, srcY, face)
call UnitAddAbility(dummy, DecelAbility)
call IssueTargetOrder(dummy, "slow", source)
call DelayedRelease(dummy)
call RemoveSavedInteger(hash, srcId, 0)
endif
// --- Anti‑spam timestamp ---
call SaveReal(hash, targId, 1, TimerGetElapsed(udg_GameTimer) + ANTISPAM_COOLDOWN)
endfunction
//==============================================================================
// Event: Pre‑damage (aura tick)
//==============================================================================
private function OnPreDamage takes nothing returns boolean
local unit src = udg_DamageEventSource
local unit tgt = udg_DamageEventTarget
local integer id = GetUnitUserData(src)
if udg_DamageEventAmount >= 1.00 then
return false
endif
if GetUnitAbilityLevel(src, AuraBuff) == 0 or not udg_UnitMoving[id] then
return false
endif
if GetUnitAbilityLevel(tgt, 'BPSE') > 0 then
return false
endif
if GetUnitAbilityLevel(tgt, 'A001') > 0 then
return false
endif
// If the target's owner cannot see the attacker (Wind Walk/Invisibility), trample fails.
if IsUnitInvisible(src, GetOwningPlayer(tgt)) then
return false
endif
if GetUnitAbilityLevel(src, DecelBuff) > 0 then
return false
endif
if IsUnitType(tgt, UNIT_TYPE_STRUCTURE) or IsUnitType(tgt, UNIT_TYPE_MECHANICAL) then
return false
endif
if not IsInFrontalCone(src, tgt) then
return false
endif
if LoadReal(hash, GetUnitUserData(tgt), 1) > TimerGetElapsed(udg_GameTimer) then
return false
endif
call PerformHit(src, tgt, GetUnitAbilityLevel(tgt, RevengeAbility) > 0)
return false
endfunction
//==============================================================================
// Reset hit counter when unit stops moving OR loses deceleration buff
//==============================================================================
private function OnUnitStop takes nothing returns boolean
local integer id = udg_UDex
local unit u = udg_UDexUnits[id]
// Safety check for null units to prevent thread crashes
if u == null or not udg_UnitMoving[id] or GetUnitAbilityLevel(u, DecelBuff) == 0 then
call RemoveSavedInteger(hash, id, 0)
endif
set u = null
return false
endfunction
//==============================================================================
// Initialization
//==============================================================================
private function InitTrample takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
call TriggerRegisterVariableEvent(t, "udg_PreDamageEvent", EQUAL, 0.00)
call TriggerAddCondition(t, Condition(function OnPreDamage))
set t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitMovingEvent", EQUAL, 2.00)
call TriggerAddCondition(t, Condition(function OnUnitStop))
loop
exitwhen i >= 5
set pool[i] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, 0.0, 0.0, 0.0)
call UnitAddAbility(pool[i], 'Aloc')
call SetUnitPathing(pool[i], false)
call ShowUnit(pool[i], false)
set i = i + 1
endloop
set poolSize = 5
endfunction
private struct SafeInit extends array
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(), 0.10, false, function InitTrample)
endmethod
endstruct
endlibrary
v1.0:Initial release.v1.1:- Fixed leaks- Improved frontal cone logic- Stabilized timer behaviorv2.0:*Preserved original gameplay mechanics while improving technical implementation- Converted system to vJASS structure- Improved performance and execution safety- Added density-based knockback scaling- Implemented anti-spam protection- Added dummy recycling system (no leaks)- Added configurable impact effect- Refined filtering (structures/mechanical excluded)- General code cleanup and optimization-Ignores targets that are invisible to the attackerV2.01-Added a filter so that units with the Charge ability can't be affected by the Trample effect
