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