Name | Type | is_array | initial_value |
abilFly | abilcode | No | |
abilMeld | abilcode | No | |
abilSelect | abilcode | No | |
AllowAll | boolean | No | |
AllowMultiple | boolean | No | |
AllPlayers | integer | No | 29 |
confDelay | real | No | 2.00 |
confDuration | real | No | 1.50 |
confHeight | real | No | 350.00 |
confIncrement | real | No | 0.03 |
confOpacity | integer | No | 150 |
EffectString | string | No | |
Players | integer | No | 29 |
Race | race | No | |
Sound | sound | No | |
UnitType | unitcode | No |
//===========================================================================
//
// Revivable Unit System v1.3.3b
// by loktar
// -------------------------------------------------------------------------
// * Toggle any unit type per player or for all players
// * Option to allow normal training for other players
// * Option to show a configurable dissipate effect on death (default)
// * Show a configurable revive effect
// * Revivable unit types are limited to one unit (optional)
// -------------------------------------------------------------------------
//
// -------
// * API *
// -------
// * SetTypeRevivable(integer unitTypeId, integer playerNumber)
// - Make a unit type revivable for player(s).
// -- playerNumber: ALL_P = all players
// -- Players not set through this function will be unable to build the unit by default
// -- Returns true on success, false on failure
//
// * UnsetTypeRevivable(integer unitTypeId, integer playerNumber)
// - Make a unit type not revivable for player(s).
// -- playerNumber: ALL_P = all players
// -- Returns true on success, false on failure
//
// * AllowNormalTraining(integer unitTypeId, boolean allow)
// - Allow all other players to build the unit normally
// -- Returns true on success, false on failure
//
// * AllowMultiple(nteger unitTypeId, integer playerNumber, boolean allow)
// - Allow revivable unit to be trained more than once for player
// -- Returns true on success, false on failure
//
// * SetTypeHasDissipate(integer unitTypeId, boolean hasDiss)
// - Enable usage of model's dissipate animation
// -- Returns true on success, false on failure
//
// !! Note that you still have to add the unit type to the Altar (or any other building) !!
//
// ----------------------------------------------
// **** Look at the triggers for examples ****
// ----------------------------------------------
//
// -----------------
// * Test Map Info *
// -----------------
// * Chat commands:
// - "kill footman", "kill grunt", "kill ghoul", "kill archer", "kill stalker", "kill priest", "kill hero"
//
// * Use the "warpten" cheat to speed up training
//
//===========================================================================
library RevivableUnits initializer InitRevivable
globals
//revivable unit storage
private integer array revTypes
private boolean array revPlayers
private integer array revAllowAll
private boolean array revHasDissipate
private integer revSize = 0
private constant integer ANY_P = -1
private constant integer ALL_P = bj_MAX_PLAYER_SLOTS
private constant integer INDEX_P = ALL_P + 1
//dissipating unit storage
private hashtable dTable = InitHashtable()
//sounds & effects
private sound array dSound[12]
private sound array rSound[12]
private string array dEffect[12]
private string array rEffect[12]
//configure dissipate effect
private real dissDelay = 3 //delay after start of death animation before dissipating
private integer dissOpacity = 200 //starting opacity after ^
private real dissDuration = 2 //duration of the dissipate effect
private real dissIncrement = 0.03 //timer duration for the fade effect
private real dissHeight = 530 //flyheight
private integer dissOpacityIncr = R2I(I2R(dissOpacity)*dissIncrement/dissDuration)
//abilities
private integer abilFly = 'Arav'
private integer abilSelect = 'Aloc'
private integer array abilMeld
private integer abilMeldSize = 0
endglobals
//===========================================================================
// "Global" functions
//===========================================================================
//===========================================================================
// Get Player Color
//===========================================================================
private function GetPlayerColorString takes integer playerId returns string
if(playerId == 0) then
return "00ff0303"
elseif(playerId == 1) then
return "000042ff"
elseif(playerId == 2) then
return "001ce6b9"
elseif(playerId == 3) then
return "00540081"
elseif(playerId == 4) then
return "00fffc00"
elseif(playerId == 5) then
return "00fe8a0e"
elseif(playerId == 6) then
return "0020c000"
elseif(playerId == 7) then
return "00e55bb0"
elseif(playerId == 8) then
return "00959697"
elseif(playerId == 9) then
return "007ebff1"
elseif(playerId == 10) then
return "00106246"
elseif(playerId == 11) then
return "004e2a04"
elseif(playerId == 12) then
return "009b0000"
elseif(playerId == 13) then
return "000000c3"
elseif(playerId == 14) then
return "0000eaff"
elseif(playerId == 15) then
return "00be00fe"
elseif(playerId == 16) then
return "00ebcd87"
elseif(playerId == 17) then
return "00f8a48b"
elseif(playerId == 18) then
return "00bfff80"
elseif(playerId == 19) then
return "00dcb9eb"
elseif(playerId == 20) then
return "00282828"
elseif(playerId == 21) then
return "00ebf0ff"
elseif(playerId == 22) then
return "0000781e"
elseif(playerId == 23) then
return "00a46f33"
else
return "00000000"
endif
endfunction
//===========================================================================
// Check if unit type is already in array, else return revSize (size of revTypes)
//===========================================================================
private function GetTypeIndex takes integer wTypeId returns integer
local integer iType = 0
loop
exitwhen (iType >= revSize or revTypes[iType] == wTypeId)
set iType = iType + 1
endloop
if(iType > revSize) then
return revSize
else
return iType
endif
endfunction
//===========================================================================
// Check if unit type is revivable for players (ALL_P = all players; ANY_P = any player)
//===========================================================================
private function IsTypeRevivable takes integer wTypeId, integer wPlayers returns boolean
local integer iType = GetTypeIndex(wTypeId)
local integer iPlayer
//Type is present
if(wPlayers >= ANY_P and wPlayers <= ALL_P and iType < revSize) then
//All players set -> always return true
if(revPlayers[iType*INDEX_P+ALL_P]) then
return true
//Check for ANY player
elseif(wPlayers == ANY_P) then
set iPlayer = 0
loop
if(revPlayers[iType*INDEX_P+iPlayer]) then
return true //player found
endif
set iPlayer = iPlayer + 1
exitwhen iPlayer >= ALL_P
endloop
return false //no player found
//Check for specific player
else
return revPlayers[iType*INDEX_P+wPlayers]
endif
endif
return false
endfunction
//===========================================================================
// End "Global" functions
//===========================================================================
//===========================================================================
// Configuration functions
//===========================================================================
//===========================================================================
// Configure dissipate effect
//===========================================================================
function ConfigureDissipate takes real wDelay, integer wOpac, real wDur, real wIncr, real wHeight returns boolean
if(wDelay < 0 or wOpac < 0 or wOpac > 255 or wDur < 0 or wIncr < 0.01 or wIncr > wDur or wHeight < 0 or wHeight > 1000) then
return false
endif
if(wDelay != null) then
set dissDelay = wDelay
endif
if(wOpac != null) then
set dissOpacity = wOpac
endif
if(wDur != null) then
set dissDuration = wDur
endif
if(wIncr != null) then
set dissIncrement = wIncr
endif
if(wHeight != null) then
set dissHeight = wHeight
endif
set dissOpacityIncr = R2I(I2R(dissOpacity)*dissIncrement/dissDuration)
return true
endfunction
//===========================================================================
// Configure dissipate sound & effect
//===========================================================================
function ConfigureDissSFX takes race wRace, string wEffect, sound wSound returns nothing
local integer dRace = GetHandleId(wRace)
set dEffect[dRace] = wEffect
set dSound[dRace] = wSound
endfunction
//===========================================================================
// Configure revive sound & effect
//===========================================================================
function ConfigureRevSFX takes race wRace, string wEffect, sound wSound returns nothing
local integer rRace = GetHandleId(wRace)
set rEffect[rRace] = wEffect
set rSound[rRace] = wSound
endfunction
//===========================================================================
// Set Flying ability
//===========================================================================
function SetFlyAbil takes integer newAbilFly returns nothing
set abilFly = newAbilFly
endfunction
//===========================================================================
// Set Locus ability (unselectable)
//===========================================================================
function SetSelectAbil takes integer newAbilSelect returns nothing
set abilSelect = newAbilSelect
endfunction
//===========================================================================
// Add meld ability
//===========================================================================
function RemoveAbilOnDeath takes integer newAbilMeld returns boolean
local integer abilMeldIndex = 0
loop
exitwhen abilMeldIndex >= abilMeldSize
if(abilMeld[abilMeldIndex] == newAbilMeld) then
return false
endif
set abilMeldIndex = abilMeldIndex + 1
endloop
set abilMeld[abilMeldSize] = newAbilMeld
set abilMeldSize = abilMeldSize + 1
return true
endfunction
//===========================================================================
// Allow players who don't have unit type as revivable to train it normally
//===========================================================================
function AllowNormalTraining takes integer wTypeId, boolean allow returns boolean
local integer iType = GetTypeIndex(wTypeId)
local integer iPlayer
//Type is present
if(iType < revSize) then
//Set in array
if(allow) then
set revAllowAll[iType] = -1
else
set revAllowAll[iType] = 0
endif
//Not all players set -> need to set tech
if(not revPlayers[iType*INDEX_P+ALL_P]) then
set iPlayer = 0
loop
if(not revPlayers[iType*INDEX_P+iPlayer]) then
call SetPlayerTechMaxAllowed(Player(iPlayer), wTypeId, revAllowAll[iType])
endif
set iPlayer = iPlayer + 1
exitwhen iPlayer >= ALL_P
endloop
endif
return true
endif
return false
endfunction
//===========================================================================
// Allow more than one unit to be trained
//===========================================================================
function AllowMultiple takes integer wTypeId, integer wPlayers, boolean allow returns boolean
local integer iType = GetTypeIndex(wTypeId)
local integer iPlayer
local integer techMax
local boolean anyChange
//Valid player and type is present
if(wPlayers >= 0 and wPlayers <= ALL_P and iType < revSize) then
if(allow) then
set techMax = -1
else
set techMax = 1
endif
//Only one player to set
if(wPlayers != ALL_P) then
// Type is revivable for player
if(IsTypeRevivable(wTypeId, wPlayers)) then
call SetPlayerTechMaxAllowed(Player(wPlayers), wTypeId, techMax)
return true
endif
else
set anyChange = false
set iPlayer = 0
loop
// Type is revivable for player
if(IsTypeRevivable(wTypeId, iPlayer)) then
call SetPlayerTechMaxAllowed(Player(iPlayer), wTypeId, techMax)
set anyChange = true
endif
set iPlayer = iPlayer + 1
exitwhen iPlayer >= ALL_P
endloop
return anyChange
endif
endif
return false
endfunction
//===========================================================================
// Set unit type has dissipate animation
//===========================================================================
function SetTypeHasDissipate takes integer wTypeId, boolean hasDiss returns boolean
local integer iType = GetTypeIndex(wTypeId)
if(iType < revSize) then
set revHasDissipate[iType] = hasDiss
return true
endif
return false
endfunction
//===========================================================================
// Make unit type revivable for players (ALL_P = all players; players 0-27)
//===========================================================================
function SetTypeRevivable takes integer wTypeId, integer wPlayers returns boolean
local integer iType
local integer iPlayer
local integer iRevPlayers
local integer revAllow
//If type is already revivable, no need to proceed
if(wPlayers >= 0 and wPlayers <= ALL_P and not IsTypeRevivable(wTypeId, wPlayers)) then
set iType = GetTypeIndex(wTypeId)
//Unit-type present & only one player to set
if(iType < revSize and wPlayers != ALL_P) then
set revPlayers[iType*INDEX_P+wPlayers] = true
call SetPlayerTechMaxAllowed(Player(wPlayers), wTypeId, 1)
else
//New type -> store in revTypes
if(iType == revSize) then
set revTypes[revSize] = wTypeId
set revAllowAll[revSize] = 0 //don't allow other players to build unit by default
set revHasDissipate[revSize] = false //set no dissipate anim by default
set revSize = revSize + 1
endif
//All players -> store in revPlayers
if(wPlayers == ALL_P) then
set revPlayers[iType*INDEX_P+ALL_P] = true
endif
//Store settings in revPlayers & Set Tech
set iPlayer = 0
loop
set iRevPlayers = iType*INDEX_P+iPlayer
if(wPlayers == iPlayer or wPlayers == ALL_P or revPlayers[iRevPlayers]) then
set revPlayers[iRevPlayers] = true
set revAllow = 1
else
set revPlayers[iRevPlayers] = false
set revAllow = revAllowAll[iType]
endif
call SetPlayerTechMaxAllowed(Player(iPlayer), wTypeId, revAllow)
set iPlayer = iPlayer + 1
exitwhen iPlayer >= ALL_P
endloop
endif
return true
endif
return false
endfunction
//===========================================================================
// Make unit type unrevivable for players (ALL_P = all players; players 0-27)
//===========================================================================
function UnsetTypeRevivable takes integer wTypeId, integer wPlayers returns boolean
local integer iType
local integer iPlayer
//If type is not revivable, no need to proceed
if(IsTypeRevivable(wTypeId, wPlayers) or (wPlayers == ALL_P and IsTypeRevivable(wTypeId, ANY_P))) then
set iType = GetTypeIndex(wTypeId)
//"All players" has to be unset in all cases
set revPlayers[iType*INDEX_P+ALL_P] = false
//Only one player to unset
if(wPlayers != ALL_P) then
set revPlayers[iType*INDEX_P+wPlayers] = false
call SetPlayerTechMaxAllowed(Player(wPlayers), wTypeId, revAllowAll[iType])
//All players to unset
else
set iPlayer = 0
loop
set revPlayers[iType*INDEX_P+iPlayer] = false
call SetPlayerTechMaxAllowed(Player(iPlayer), wTypeId, revAllowAll[iType])
set iPlayer = iPlayer + 1
exitwhen iPlayer >= ALL_P
endloop
endif
return true
endif
return false
endfunction
//===========================================================================
// End Configuration functions
//===========================================================================
//===========================================================================
// Dissipate Effect
//===========================================================================
private function Dissipate_Fade takes nothing returns nothing
local timer dTimer = GetExpiredTimer()
local integer dTimerId = GetHandleId(dTimer)
local unit dUnit = LoadUnitHandle(dTable, dTimerId, 'unit')
local integer opacity = LoadInteger(dTable, dTimerId, 'opac')
//Fading continues
if(opacity > dissOpacityIncr) then
set opacity = opacity - dissOpacityIncr
call SetUnitVertexColor(dUnit, 255, 255, 255, opacity)
call SaveInteger(dTable, dTimerId, 'opac', opacity)
//Fading complete
else
call RemoveUnit(dUnit)
call DestroyTimer(dTimer)
call FlushChildHashtable(dTable, dTimerId)
endif
//Clean up
set dTimer = null
set dUnit = null
endfunction
private function Dissipate_Effect takes nothing returns nothing
local timer dTimer = GetExpiredTimer()
local integer dTimerId = GetHandleId(dTimer)
local unit dUnit = LoadUnitHandle(dTable, dTimerId, 'unit')
local boolean hasDiss = revHasDissipate[GetTypeIndex(GetUnitTypeId(dUnit))]
local integer dRace = GetHandleId(GetUnitRace(dUnit))
local player dOwner = GetOwningPlayer(dUnit)
local player localPlayer = GetLocalPlayer()
local force dAllies = CreateForce()
local sound dSoundTemp = null
//Set up player allies Force
call ForceEnumAllies(dAllies, dOwner, null)
//Display death message to owner and allies
if(GetPlayerTechMaxAllowed(dOwner, GetUnitTypeId(dUnit)) == 1 and IsPlayerInForce(localPlayer, dAllies)) then
call DisplayTextToPlayer(localPlayer, 0, 0, ("|c" + GetPlayerColorString(GetPlayerId(dOwner)) + GetUnitName(dUnit) + "|r has fallen."))
endif
//Start fading
call SaveInteger(dTable, dTimerId, 'opac', dissOpacity)
call SetUnitVertexColor(dUnit, 255, 255, 255, dissOpacity)
call TimerStart(dTimer, dissIncrement, true, function Dissipate_Fade)
//Lift unit
if(hasDiss) then
call QueueUnitAnimation(dUnit, "Dissipate")
else
if UnitAddAbility(dUnit, abilFly) then
call UnitRemoveAbility(dUnit, abilFly)
endif
call SetUnitFlyHeight(dUnit, dissHeight, dissHeight/dissDuration)
//Play dissipate sound
if(dSound[dRace] != null) then
set dSoundTemp = dSound[dRace]
call AttachSoundToUnit(dSoundTemp, dUnit)
call StartSound(dSoundTemp)
set dSoundTemp = null
endif
endif
//Create dissipate effect
if(dEffect[dRace] != null and dEffect[dRace] != "") then
call DestroyEffect(AddSpecialEffectTarget(dEffect[dRace], dUnit, "origin"))
endif
//Clean up
set dUnit = null
set dTimer = null
call DestroyForce(dAllies)
set dAllies = null
endfunction
private function Dissipate_Death takes nothing returns boolean
local unit dyingUnit = GetDyingUnit()
local integer dyingTypeId = GetUnitTypeId(dyingUnit)
local player dyingOwner = GetOwningPlayer(dyingUnit)
local unit dUnit = null
local timer dTimer = null
local integer abilMeldIndex
if(IsTypeRevivable(dyingTypeId, GetPlayerId(dyingOwner))) then
set dTimer = CreateTimer()
//Replace dying unit
call ShowUnit(dyingUnit, false)
set dUnit = CreateUnit(dyingOwner, dyingTypeId, GetUnitX(dyingUnit), GetUnitY(dyingUnit), GetUnitFacing(dyingUnit))
call PauseUnit(dUnit, true) //pause unit to prevent automatic orders (eg attack enemies)
call UnitAddAbility(dUnit, abilSelect) //make unit unselectable
//Remove meld abilities
set abilMeldIndex = 0
loop
exitwhen abilMeldIndex >= abilMeldSize
call UnitRemoveAbility(dUnit, abilMeld[abilMeldIndex])
set abilMeldIndex = abilMeldIndex + 1
endloop
//Play death animation
call SetUnitAnimation(dUnit, "Death")
//Store unit & wait for death animation to finish
call SaveUnitHandle(dTable, GetHandleId(dTimer), 'unit', dUnit)
call TimerStart(dTimer, dissDelay, false, function Dissipate_Effect)
//Clean up
set dUnit = null
set dTimer = null
endif
//Clean up
set dyingUnit = null
return true
endfunction
//===========================================================================
// End Dissipate Effect
//===========================================================================
//===========================================================================
// Revive Effect
//===========================================================================
private function Revive_Effect takes nothing returns boolean
local unit rUnit = GetTrainedUnit()
local integer rRace
local sound rSoundTemp = null
if(IsTypeRevivable(GetUnitTypeId(rUnit), GetPlayerId(GetOwningPlayer(rUnit)))) then
set rRace = GetHandleId(GetUnitRace(rUnit))
if(rEffect[rRace] != null and rEffect[rRace] != "") then
call DestroyEffect(AddSpecialEffectTarget(rEffect[rRace], rUnit, "origin"))
endif
if(rSound[rRace] != null) then
set rSoundTemp = rSound[rRace]
call AttachSoundToUnit(rSoundTemp, rUnit)
call StartSound(rSoundTemp)
set rSoundTemp = null
endif
endif
//Cleanup
set rUnit = null
return true
endfunction
//===========================================================================
// Set up triggers
//===========================================================================
private function InitRevivable takes nothing returns nothing
local trigger trg_Dissipate = CreateTrigger()
local trigger trg_Revive = CreateTrigger()
local integer iPlayer = 0
local integer iSFX = 1
local string dSoundString
local string dSoundLabelString
local player regPlayer
//Set sounds & effects arrays defaults
//Set existing revive effects
set rEffect[1] = "Abilities\\Spells\\Human\\ReviveHuman\\ReviveHuman.mdl"
set rEffect[2] = "Abilities\\Spells\\Orc\\ReviveOrc\\ReviveOrc.mdl"
set rEffect[3] = "Abilities\\Spells\\Undead\\ReviveUndead\\ReviveUndead.mdl"
set rEffect[4] = "Abilities\\Spells\\NightElf\\ReviveNightElf\\ReviveNightElf.mdl"
set rEffect[5] = "Abilities\\Spells\\Demon\\ReviveDemon\\ReviveDemon.mdl"
//DEMON rEffect has no sound
set rSound[5] = CreateSound("Abilities\\Spells\\Human\\ReviveHuman\\ReviveHuman.wav", false, true, true, 10, 10, "SpellsEAX")
call SetSoundParamsFromLabel(rSound[5], "ReviveHuman")
call SetSoundDuration(rSound[5], 3196)
//UNDEAD: has dissipate model with sound attached
set dEffect[3] = "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl"
set dSound[3] = null
loop
exitwhen iSFX > 11
if(iSFX != 3) then //NOT UNDEAD: no dissipate model, so setup dSound
set dEffect[iSFX] = null
if(iSFX == 2) then //ORC
set dSoundString = "Sound\\Units\\Orc\\OrcDissipate1.wav"
set dSoundLabelString = "OrcDissipate"
elseif(iSFX == 4) then //NIGHT ELF
set dSoundString = "Sound\\Units\\NightElf\\NightElfDissipate1.wav"
set dSoundLabelString = "NightElfDissipate"
else //Default to HUMAN
set dSoundString = "Sound\\Units\\Human\\HumanDissipate1.wav"
set dSoundLabelString = "HumanDissipate"
if(iSFX != 5) then //NOT DEMON: use HUMAN rEffect by default
set rEffect[iSFX] = rEffect[1]
endif
endif
//Create dSound
set dSound[iSFX] = CreateSound(dSoundString, false, true, true, 10, 10, "SpellsEAX")
call SetSoundParamsFromLabel(dSound[iSFX], dSoundLabelString)
call SetSoundDuration(dSound[iSFX], 2270)
endif
//NOT DEMON: Revive models have sound attached
if(iSFX != 5) then
set rSound[iSFX] = null
endif
set iSFX = iSFX + 1
endloop
//END Setup sounds & effects
//Add meld abilities
call RemoveAbilOnDeath('Ashm')
call RemoveAbilOnDeath('Sshm')
call RemoveAbilOnDeath('Ahid')
//Register events for all players
loop
set regPlayer = Player(iPlayer)
//Dissipate trigger
call TriggerRegisterPlayerUnitEvent(trg_Dissipate, regPlayer, EVENT_PLAYER_UNIT_DEATH, null)
//Revive trigger
call TriggerRegisterPlayerUnitEvent(trg_Revive, regPlayer, EVENT_PLAYER_UNIT_TRAIN_FINISH, null)
set iPlayer = iPlayer + 1
exitwhen iPlayer >= ALL_P
endloop
//Dissipate trigger
call TriggerAddCondition(trg_Dissipate, Condition(function Dissipate_Death))
//Revive trigger
call TriggerAddCondition(trg_Revive, Condition(function Revive_Effect))
endfunction
endlibrary