• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Capturable Building System (League of Legends)

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Captureable Building System by The_Witcher

What is the Capture Building System?
The Capture Building System is based on the system which is used in the
popular online game League of Legends (Dominion Mode):
-When you right click a capturable building, your hero will charge and
release a beam which captures the building over time.
-If the building was already captured by someone else, you will neutralize
it before taking it for yourself.
-If you are interrupted by an attack, or because you gave an order, while capturing,
your unit will stop.
-If your building was under attack but couldn't be neutralized, it will regenerate.


Requirements:
-T32 by Jesus4Lyf
-PlayerColor Utils by The_Witcher (http://www.hiveworkshop.com/forums/submissions-414/playercolor-utils-209846/)
-Jass NewGen Pack and the newest Jasshelper
-Minor Jass knowledge


Methods & Functions:
capturableBuilding.create( unit, points )
CreateCapturableBuilding( unit, points ) <- simple Jass wrapper
makes the given unit capturable, while the "points" value represents the "points to capture"

set capturableBuilding.onCapture = yourFunc
will fire the given function when the building is fully captured
the given function MUST look like this:
yourfunc takes capturableBuilding captured returns nothing

will fire the given trigger when the building is fully captured


Members:
real points
real maxPoints
player owner

you can edit those meber's values using
set capturableBuilding.<yourMember> = <yourValue>



- Copy the T32, the Simple Bars and the PlayerColorUtils triggers into your map.
- Copy this trigger into your map.
- Copy the Capture Building Dummy ability into your map.
- Adjust the values of the ability and the globals below.


Remember to give credit when used ;)
Constructive critism is always welcome :D

The_Witcher

v.1.0: initial release
v.1.1: improved many things thanks to magtheridon, added a new library to handle bars


JASS:
library CaptureBuildingSystem requires T32, PlayerColorUtils
// Captureable Building System by The_Witcher
//
//  What is the Captureable Building System?
//      The Captureable Building System is based on the system which is used in the
//      popular online game League of Legends (Dominion Mode):
//      -When you right click a capturable building, your hero will charge and
//       release a beam which captures the building over time.
//      -If the building was already captured by someone else, you will neutralize
//       it before taking it for yourself.
//      -If you are interrupted by an attack, or because you gave an order, while capturing,
//       your unit will stop.
//      -If your building was under attack but couldn't be neutralized, it will regenerate.
//
//
//  Requirements:
//      -T32 by Jesus4Lyf
//      -PlayerColor Utils by The_Witcher
//      -simpleBar by The_Witcher
//      -Jass NewGen Pack and the newest Jasshelper
//      -Minor Jass knowledge 
//
//
//  Methods & Functions:
//      capturableBuilding.create(   unit,   points   )  
//      CreateCapturableBuilding(    unit,   points   )  <- simple Jass wrapper
//          makes the given unit capturable, while the "points" value represents the "points to capture"
//      
//      set capturableBuilding.onCapture = yourFunc
//          will fire the given function when the building is fully captured
//          the given function MUST look like this:
//          yourfunc    takes capturableBuilding captured returns nothing
//
//
//  Members:
//      real points
//      real maxPoints
//      player owner
//
//      you can edit those meber's values using   
//          set capturableBuilding.<yourMember> = <yourValue>
//
//
//  How to import:
//      - Copy the T32, the Simple Bars and the PlayerColorUtils triggers into your map.
//      - Copy this trigger into your map.
//      - Copy the Capture Building Dummy ability into your map.
//      - Adjust the values of the ability and the globals below.
//
//============================ Setup ========================================
    globals
        //If true, only heroes can capture buildings
        constant boolean HEROES_ONLY = true
        
        //If true, a bar will indicate the captureing progress
        constant boolean SHOW_BARS = true
        
        //If >0, every capturable building will be in a visible circle for all player, with the given radius
        constant real VISIBILITY_RADIUS = 800
        
        //The points a building regenerates if it could'nt be fully captured
        constant real REGENERATION_PER_SECOND = 1
        
        //The points every hero capture per second
        constant real POINTS_PER_SECOND = 5
        
        //The time in seconds a hero charges before capturing
        constant real CAPTURE_DELAY = 2
        
        //If true, every building will be paused while being captured (= no attack)
        constant boolean PAUSE_WHILE_CAPTUREING = true
        
        //The rawcode of the dummy ability (press Ctrl + D in the object editor)
        constant integer CAPTURE_ABILITY = 'A000'
        
        //The order id of the dummy ability
        constant string CAPTURE_ABILITY_ORDER = "channel"
        
        //The lightning effect, symbolising the capture process
        constant string LIGHTNING_EFFECT = "FORK"
        
        //The effect indicating that the charging time is over
        constant string BEGIN_CAPTURE_EFFECT = "Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl"
        
        //The effect indicating that a building is captured
        constant string FINISH_CAPTURE_EFFECT = "Abilities\\Spells\\Other\\Charm\\CharmTarget.mdl"
        
        //The length of the building points bar
        constant integer BAR_LENGTH = 50
        
        //The size of the building points bar
        constant real BAR_SIZE = 8
        
        //The owning player of neutral buildings (only change if needed!)
        constant player BUILDING_OWNER = Player(PLAYER_NEUTRAL_PASSIVE)
    endglobals
    
//==============================================================================   
//========================= System Code ========================================
//==============================================================================
    
    function CreateCapturableBuilding takes unit u, real pts returns capturableBuilding
        return capturableBuilding.create(u, pts)
    endfunction
    
    public function interface onCaptureFunc takes capturableBuilding captured returns nothing
    
    private struct captureUnitData
        unit u
        real time
        lightning light
        capturableBuilding toCapture
    endstruct
    
    struct capturableBuilding
        private static hashtable h
        private static location loc
        private static group dmgGroup
        private static capturableBuilding temp
        private static trigger onDamageTrig
        private player owningPlayer
        private player taker
        readonly unit u
        private group g
        private texttag info
        private real pts
        private real maxPts
        private simpleBar bar
        onCaptureFunc onCapture = 0
        
        private static method endCaptureForUnit takes unit u returns nothing
            local captureUnitData dat = LoadInteger(.h, GetHandleId(u), 1)
            call GroupRemoveUnit(dat.toCapture.g,u)
            call UnitRemoveAbility(u, CAPTURE_ABILITY)
            call SaveInteger(.h, GetHandleId(u), 1, 0)
            if dat.light != null then
                call DestroyLightning(dat.light)
            endif
            call dat.destroy()
            set u = null
        endmethod
        
        private static method periodEnum takes nothing returns nothing
            local unit u = GetEnumUnit()
            local captureUnitData dat = LoadInteger(.h, GetHandleId(u), 1)
            if (GetUnitCurrentOrder(u) != OrderId(CAPTURE_ABILITY_ORDER)) or ((IsUnitAlly(u, temp.owningPlayer) and temp.owningPlayer != BUILDING_OWNER)) then
                call .endCaptureForUnit(u)
            else
                if dat.time > 0 then
                    set dat.time = dat.time - T32_PERIOD
                    call SetLightningColor(dat.light, GetLightningColorR(dat.light), GetLightningColorG(dat.light), GetLightningColorB(dat.light), (1 - dat.time / CAPTURE_DELAY) * 0.6)
                    if dat.time <= 0 then
                        call DestroyEffect(AddSpecialEffect(BEGIN_CAPTURE_EFFECT, GetUnitX(u), GetUnitY(u)))
                        call SetLightningColor(dat.light, GetLightningColorR(dat.light), GetLightningColorG(dat.light), GetLightningColorB(dat.light), 1)
                    endif
                elseif temp.owningPlayer == BUILDING_OWNER and temp.taker == temp.owningPlayer then
                    set temp.taker = GetOwningPlayer(u)
                    set temp.bar.colorFull = GetPlayerColorString(temp.taker)
                else
                    if IsUnitAlly(u, temp.taker) then
                        set temp.pts = temp.pts + POINTS_PER_SECOND / T32_FPS
                    else
                        set temp.pts = temp.pts - POINTS_PER_SECOND / T32_FPS
                    endif
                endif
                if temp.pts < 0 then
                    set temp.pts = 0
                    set temp.owningPlayer = BUILDING_OWNER
                    set temp.taker = GetOwningPlayer(u)
                    set temp.bar.colorFull = GetPlayerColorString(temp.taker)
                    call SetUnitColor(temp.u, GetPlayerColor(BUILDING_OWNER))
                endif
            endif
            set u = null
        endmethod
        
        private static method endCapture takes nothing returns nothing
            call .endCaptureForUnit(GetEnumUnit())
        endmethod
        
        private method periodic takes nothing returns nothing
            set .temp = this
            if FirstOfGroup(.g) != null then
                call ForGroup(.g, function capturableBuilding.periodEnum)
            elseif .pts < .maxPts and .owningPlayer != BUILDING_OWNER then
                set .pts = .pts + REGENERATION_PER_SECOND / T32_FPS
                if .pts > .maxPts then
                    set .pts = .maxPts
                endif
            endif
            set .bar.progress = .pts/.maxPts
            if .pts > .maxPts then
                if .onCapture != 0 then
                    call onCapture.execute(this)
                endif
                call DestroyEffect(AddSpecialEffect(FINISH_CAPTURE_EFFECT,GetUnitX(.u),GetUnitY(.u)))
                call .stopPeriodic()
                set .pts = .maxPts
                call ForGroup(.g, function capturableBuilding.endCapture)
                call GroupClear(.g)
                set .owningPlayer = .taker
                call SetUnitColor(.u, GetPlayerColor(.taker))                
            endif
            if (IsUnitType(.u, UNIT_TYPE_DEAD) or GetUnitTypeId(.u) == 0 ) then
                call .stopPeriodic()
                call .destroy()
            endif
            if FirstOfGroup(.g) == null and PAUSE_WHILE_CAPTUREING and IsUnitPaused(.u) then
                call PauseUnit(.u,false)
            endif
        endmethod
        
        implement T32xs
        
        static method create takes unit u, real pts returns capturableBuilding
            local capturableBuilding this = capturableBuilding.allocate()
            local integer i = 0
            set .u = u
            if VISIBILITY_RADIUS > 0 then
                loop
                    call FogModifierStart(CreateFogModifierRadius(Player(i), FOG_OF_WAR_VISIBLE, GetUnitX(u), GetUnitY(u), VISIBILITY_RADIUS, true, false))
                    set i = i + 1
                    exitwhen i == 12
                endloop
            endif
            call SetUnitOwner(u, BUILDING_OWNER, true)
            set .owningPlayer = BUILDING_OWNER
            set .taker = owningPlayer
            set .g = CreateGroup()
            set .bar = 0
            if SHOW_BARS then
                set .bar = simpleBar.create(GetUnitX(u) - BAR_LENGTH * BAR_SIZE* 0.23, GetUnitY(u), 0, 0, BAR_SIZE, BAR_LENGTH, "","|cff949596")
            endif
            set .pts = 0
            set .maxPts = pts
            call SaveInteger(.h, GetHandleId(u), 0, this)
            return this
        endmethod
        
        private static method initTaking takes nothing returns boolean
            local unit u = GetOrderTargetUnit()
            local unit o = GetOrderedUnit()
            local capturableBuilding this = LoadInteger(.h, GetHandleId(u), 0)
            local captureUnitData dat
            if u != null and this != 0 and GetIssuedOrderId() != OrderId(CAPTURE_ABILITY_ORDER) then
                if IsUnitType(o, UNIT_TYPE_HERO) or not HEROES_ONLY then
                    set dat = captureUnitData.create()
                    set dat.toCapture = this
                    set dat.u = o
                    set dat.time = CAPTURE_DELAY+ 0.01
                    set dat.light = null
                    call SaveInteger(.h, GetHandleId(o), 1, dat)
                    call UnitAddAbility(o, CAPTURE_ABILITY)
                    call IssueTargetOrder(o, CAPTURE_ABILITY_ORDER, .u)
                endif
            endif
            set u = null
            set o = null
            return false
        endmethod
        
        private static method startTaking takes nothing returns boolean
            local unit t = GetSpellTargetUnit()
            local capturableBuilding this = LoadInteger(.h, GetHandleId(t), 0)
            local unit u = GetTriggerUnit()
            local captureUnitData dat = LoadInteger(.h, GetHandleId(u), 1)
            local real ux = GetUnitX(u)
            local real uy = GetUnitY(u)
            local real uz
            local real tx = GetUnitX(t)
            local real ty = GetUnitY(t)
            local real tz
            local player p = GetOwningPlayer(u)
            if GetSpellAbilityId() == CAPTURE_ABILITY then
                call .startPeriodic()
                if PAUSE_WHILE_CAPTUREING and not IsUnitPaused(t) then
                    call PauseUnit(t,true)
                endif
                call GroupAddUnit(.g, u)
                call MoveLocation(.loc, ux, uy)
                set uz = GetLocationZ(.loc)
                call MoveLocation(.loc, tx, ty)
                set tz = GetLocationZ(.loc)
                set dat.light = AddLightningEx(LIGHTNING_EFFECT, false, ux, uy, uz + 50, tx, ty, tz + 150)
                call SetLightningColor(dat.light, GetPlayerColorRedPercent(p), GetPlayerColorGreenPercent(p), GetPlayerColorBluePercent(p), 0)
                if not IsUnitInGroup(u,.dmgGroup) then
                    call TriggerRegisterUnitEvent(onDamageTrig, u, EVENT_UNIT_DAMAGED)
                    call GroupAddUnit(.dmgGroup,u)
                endif
            endif
            set u = null
            return false
        endmethod
        
        private static method abortTaking takes nothing returns boolean
            local unit u = GetTriggerUnit()
            local captureUnitData dat = LoadInteger(.h, GetHandleId(u), 1)
            local capturableBuilding this = dat.toCapture
            if dat != 0 then
                call .endCaptureForUnit(u)
            endif
            return false
        endmethod
        
        method operator owner= takes player p returns nothing
            set .owningPlayer = p
            set .taker = p
            call SetUnitColor(.u, GetPlayerColor(p))
        endmethod
        method operator owner takes nothing returns player
            return .owningPlayer
        endmethod
        
        method operator points= takes real r returns nothing
            set .pts = r
            if .pts > .maxPts then
                set .pts = .maxPts
            endif
            set .bar.progress = .pts/.maxPts
            call .bar.refresh()
        endmethod
        method operator points takes nothing returns real
            return .pts
        endmethod
        
        method operator maxPoints= takes real r returns nothing
            set .maxPts = r
            if .pts > .maxPts then
                set .pts = .maxPts
            endif
             set .bar.progress = .pts/.maxPts
            call .bar.refresh()
        endmethod
        method operator maxPoints takes nothing returns real
            return .maxPts
        endmethod
        
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            local trigger t2 = CreateTrigger()
            local integer i = 0
            loop
                call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
                call TriggerRegisterPlayerUnitEvent(t2, Player(i), EVENT_PLAYER_UNIT_SPELL_CHANNEL, null)
                set i = i + 1
                exitwhen i == 12
            endloop
            call TriggerAddCondition(t, Condition(function capturableBuilding.initTaking))
            call TriggerAddCondition(t2, Condition(function capturableBuilding.startTaking))
            set onDamageTrig = CreateTrigger()
            call TriggerAddCondition(onDamageTrig, Condition(function capturableBuilding.abortTaking))
            set .loc = Location(0, 0)
            set .dmgGroup = CreateGroup()
            set .h = InitHashtable()
        endmethod
    
    endstruct

endlibrary

Keywords:
league of legends, lol, tower, capture, building, ctf, charge, system, bar, mui
Contents

Capturable Building System (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 16th Jan 2012 Bribe: The SimpleBars library should be its own resource, if you want to use it as a library. Otherwise, just inline it. The library itself needs a review on its own because it...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.

16th Jan 2012
Bribe: The SimpleBars library should be its own resource, if you want to use it as a library. Otherwise, just inline it. The library itself needs a review on its own because it could use some work.

JASS:
@static@ if SHOW_BARS then //Note the keyword "static" which I added. You should do this for all constant booleans.

JASS:
call TriggerRegisterUnitEvent(onDamageTrig, u, EVENT_UNIT_DAMAGED)

/*
This is going to need a damage detection system or a better method.
If a unit is removed from the game, the event persists, racking up the
handle count as the game goes on.
*/

JASS:
        private static method abortTaking takes nothing returns boolean
            @local unit u@ = GetTriggerUnit() //Forgot to null this

This could also take advantage of RegisterPlayerUnitEvent, because it's the one of the best resources around.
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
I don't think that the hashtable is needed. Just get an indexing system (AIDS/UnitIndexer; both of them is perfect, and you don't even need static ifs) and create a bunch of global arrays. Then instead of storing things in the hashtable, store them in the arrays using GetUnitUserData()

You also don't have to inline TriggerRegisterAnyUnitEventBJ(); it's only use is adding to map size.
 
In your onInit method, you're doing 2 iterations over the same indices.
You can combine those loops and just add an extra local trigger.
OR, you can use TriggerRegisterAnyUnitEventBJ

The Bar handling should be done in an extra library that gets implemented through optional textmacros (The user might not want them ;P)

The hashtables could be replaced with Tables (Makes things more readable)
You're going to need 4 or 5 Tables here.
Or, better yet, you could use a TableArray (Set it to TableArray[5] on map initialization)

In the "startTaking" method, GetSpellTargetUnit and other functions that are being called repeatedly should be cached into locals for efficiency.

Your event Handler should allow the user to simply pass a function (code) and a struct instance instead of a trigger and an instance. (Sort of like SpellEffectEvent, RegisterPlayerUnitEvent, OrderEvent, etc..)

This way, you don't have to bother yourself with all the triggers.

You'd have to store the code in the form of boolexprs (Filter(code))

For efficient code/boolexpr executing/evaluating, you can use FireCode (in my signature)

I figured it's best to blame one system on how functions are being executed rather than blaming every other system that needs to execute functions :p

edit

Also, if you're up for it, could you convert this system so that it uses CTL instead of T32?

There are a lot of things that could be improved here and there, but with all those hashtable functions, the code's really hard to look at :x

You should link us to PlayerColorUtils. (It's infamous, unlike AIDS, TimerUtils, UnitIndexer and all those other systems used by practically everyone modding warcraft III)
 
In your onInit method, you're doing 2 iterations over the same indices.
You can combine those loops and just add an extra local trigger.
OR, you can use TriggerRegisterAnyUnitEventBJ

The Bar handling should be done in an extra library that gets implemented through optional textmacros (The user might not want them ;P)

The hashtables could be replaced with Tables (Makes things more readable)
You're going to need 4 or 5 Tables here.
Or, better yet, you could use a TableArray (Set it to TableArray[5] on map initialization)

In the "startTaking" method, GetSpellTargetUnit and other functions that are being called repeatedly should be cached into locals for efficiency.

Your event Handler should allow the user to simply pass a function (code) and a struct instance instead of a trigger and an instance. (Sort of like SpellEffectEvent, RegisterPlayerUnitEvent, OrderEvent, etc..)

This way, you don't have to bother yourself with all the triggers.

You'd have to store the code in the form of boolexprs (Filter(code))

For efficient code/boolexpr executing/evaluating, you can use FireCode (in my signature)

I figured it's best to blame one system on how functions are being executed rather than blaming every other system that needs to execute functions :p

edit

Also, if you're up for it, could you convert this system so that it uses CTL instead of T32?

There are a lot of things that could be improved here and there, but with all those hashtable functions, the code's really hard to look at :x

You should link us to PlayerColorUtils. (It's infamous, unlike AIDS, TimerUtils, UnitIndexer and all those other systems used by practically everyone modding warcraft III)
Thanks for your fast and useful reply!
I will improve my system a lot in the next days thanks to your hints ;)

I will think about it, but atm I'm not going to handle the bars seperately, because players won't have any indication of how far they captured the building, so they are essential...

I won't convert my system to use CTL cause i simply prefere T32 and it's easier to implement!

I will link to the other ressources in the next update ;)

Thanks again!
 
Level 20
Joined
Jul 12, 2010
Messages
1,717
hmm...for some reason i can't open the map...?
anyway i play LoL and i could point out some things you forgot to mention...
If you are interrupted by an attack, or because you gave an order, while capturing,
your unit will stop
after that happens you have to w8 a fixxed amount of time,about 3 seconds(you could make this customizable) before you can attempt to recapture the tower
When you right click a capturable building, your hero will charge and
release a beam which captures the building over time.
normal units like minions will attack the tower and can also capture it(not sure if you included that since i didn't try it yet)

plus when a hero tries to capture the tower, when he begins capturing the tower will stop attacking nearby enemy units but when minions attack the tower, the tower attacks them and at the same time deals AoE damage around it that damages only minions

well that's all for now...i hope i can test the map soon...good luck;)
 
Just stick with the hashtable witcher, its more reliable and easier for people to work with.

no point in over complicating something or breaking what isn't broken.

Yes I will keep on using hashtables since tables are just wrappers (so theres no speed improvement)...

UPDATE: fixed the issues magtheridon mentioned and added a simpleBars library for bar handling.

@xorkatoss
will be included in the next update!
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
Tables are not used to gain speed, but to reduce the amount of hashtables needed. Less handle -> more speed.

Without Table I would have to create like 20 or even more hashtables on my map, but with Table I need only one beautiful snippet.

And why are hashtables easier to use for people? Guess writing this:

JASS:
call SaveInt(thistype.hash,GetUnitUserData(this.caster),SPELL_LEVEL_ID,5)//Considering you have some sort of an indexing system

is easier than writing this:
JASS:
set thistype.levels[GetUnitUserData(this.caster)]=5
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Table is a good thing to reduce handle count and keep the hashtable 255 limit low, and is a good feeling to use it because you really maximize the potential of your hashtable (I always feel like I am wasting one when I only use the "0" key of the hashtable, for example).

However, using a hashtable instead of a Table is not really a problem unless you are creating hashtables dynamically, which I have only ever seen Nestharus do and he had a very good reason for doing so. Other than that, if it's only one hashtable per trigger and you have more than 255 triggers your map that will also break it. But that many trigger is insane, and if a mapmaker is considering something so epic then yeah (s)he should consider using Table as a resource.
 
Top