- Joined
- Nov 3, 2018
- Messages
- 79
as a casual user with knowledge only to gui this looks like basic chinese to me
but there is so much potentail here! hope it gets approved
but there is so much potentail here! hope it gets approved
Do not get intimidated by the complexity of this resource! While there are a multitude of API functions, you can build your first systems with just a few lines of code, like it is done in the examples of the Test Map. ALICE is also packaged with ready-to use libraries that provide a solid starting point for physics and missile systems. |
--The callback function on collision with the barrier:
local function ReflectProjectile(barrier, shell, x, y, z, normalSpeed, __, __)
local phi, theta = ALICE_PairGetAngle3D()
local shieldEffect = AddSpecialEffect("Abilities\\Spells\\Undead\\ReplenishMana\\ReplenishManaCasterOverhead.mdl", x, y)
BlzSetSpecialEffectZ(shieldEffect, z)
BlzSetSpecialEffectYaw(shieldEffect, phi)
BlzSetSpecialEffectPitch(shieldEffect, -theta + bj_PI/2)
BlzSetSpecialEffectAlpha(shieldEffect, math.min(128, normalSpeed // 2))
BlzSetSpecialEffectScale(shieldEffect, 1.2)
DestroyEffect(shieldEffect)
end
--This is the definition of the reflective barrier object:
ReflectiveBarrier = {
--These fields are used by ALICE itself.
identifier = "reflectiveBarrier",
interactions = {
projectile = CAT_GizmoCollisionCheck3D --This is a collision detection function included in the collisions library.
},
radius = 200,
bindToOrder = "starfall",
zOffset = 50,
--These are additional fields used by the collisions library.
onGizmoCollision = {
shell = CAT_GizmoBounce3D,
other = CAT_GizmoDevour3D
},
onGizmoCallback = ReflectProjectile,
mass = 500,
collisionRadius = 160,
elasticity = 0.2,
}
ReflectiveBarrier.__index = ReflectiveBarrier
local function CreateReflectiveBarrier()
if GetSpellAbilityId() ~= FourCC("Ashi") then
return
end
local barrier = {}
setmetatable(barrier, ReflectiveBarrier)
local u = GetSpellAbilityUnit()
barrier.anchor = u --The barrier becomes attached to Jaina.
--This creates the actor for the ReflectiveBarrier table.
ALICE_Create(barrier)
end
Feature | Description |
---|---|
Automatic Type Detection | ALICE supports any type of object passed into it, although most of its features specifically support units, destructables, items, and gizmos. Gizmos are defined as tables with coordinate fields and a special effect (missiles and such). ALICE automatically detects the types passed into it and uses the correct functions to retrieve their positions and other properties. |
Debug API | ALICE features an extensive Debug API. By typing "downtherabbithole", you can enable debug mode and select objects to get a tooltip that displays information about the actors attached to it. While selected, all interactions are shown by a green lightning effect whenever they are evaluated. You can also use hotkeys to pause the cycle or go step-by-step. You can combine the ALICE debug mode with Eikonium's Debug Utils. |
High performance | Unlike most other systems, ALICE's core avoids using enum functions entirely. Most missile systems, for example, would search for collisions with units by calling GroupEnumUnitsInRange repeatedly. But calling natives is slow and the process of calling a function repeatedly, even if there is no unit close to the missile, is inefficient itself. Think of it this way: Calling GroupEnumUnitsInRange on every iteration is like having the most advanced GPS system there is installed in your car, but then determining the time until arrival by having a kid on the back-seat ask "Are we there yet?" over and over again. ALICE instead divides the map into a grid of cells (spatial hashing), checks interaction ranges with other objects individually, and uses variable interaction intervals to drastically improve performance. |
Enum functions | While the native functions only allow enumeration of units, destructables, and items, ALICE's Enum API allows for efficient enumeration of any type of object. Although the enum functions are significantly slower than their native counterparts, the fact that they compile the objects into a Lua table instead of a group almost cancels out the speed difference, and even makes them faster in some situations. It also makes those functions much easier to work with. |
Self-interactions and Callbacks | All restaurants serve water. Sometimes, we just want to execute code on a single object. ALICE allows for this with its self-interaction and callback APIs. A self-interaction is a periodic function that only affects an object itself. |
Async mode | Since ALICE is nothing but a giant table rearrangement machine, it can be run completely asynchronously (as long as the code executed with it is async-safe). |
Extensive documentation | All API functions are fully documented and properly explained. In addition, there are manuals explaining both the basics as well as more advanced concepts. |
Support from me | If you can't figure out how something works or you encounter a bug, leave me a note and I will try my best to fix it. |
Requirement | Description |
---|---|
Total Initialization | Needs no explanation. |
Hook | Required for the default actor creation. |
HandleType | This handy little library allows ALICE to determine the correct types of the objects passed into it. Since it caches the result, it is fast and efficient. |
Precomputed Height Map (optional) | Highly recommended if you want to do any kind of 3D interactions with ALICE as it improves performance and ensures synchronicity in multiplayer. Otherwise not needed. |
CustomTooltip.toc CustomTooltip.fdf | These are the frame definition files for the actor tooltips in debug mode. Included in the test maps. |
CAT | Description |
---|---|
Objects | Requirement for all physics-related CATs. Includes the config and a collection of helper functions. |
Gizmos, Missiles, Collisions, Ballistics, Forces | These libraries contain various functions that you can use in the definitions of your objects. |
Mice | Creates an actor for each player's mouse cursor. |
Cameras | Creates an actor for each player's camera. |
Rects | Contains helper function for the creation of actors from rects. |
ALICE_CallDelayed(print, 3.5, "Hello World!")
local function PrintNumbers(data)
data.count = data.count + 1
print("The current number is " .. data.count ". The next number will be printed in " .. data.count .. " seconds.")
return data.count
end
ALICE_CallPeriodic(PrintNumbers, 1, {count = 0})
No return value is not the same as a return value of ALICE_Config.MIN_INTERVAL. The former case optimizes the function directly as one that is executed every step, avoiding the overhead of managing callback queues. However, once a function has been put into the every-step loop, it will be stuck there permanently, so you must not mix return-value and no-return-value in different branches of your function. |
local function CreateUnits()
CreateUnit(Player(0), FourCC "hfoo", 0, 0, 0)
if ALICE_TimeElapsed > 600 then
ALICE_DisableCallback()
end
return 5
end
createCaller = ALICE_CallPeriodic(CreateUnits)
function DisableCreateUnits()
ALICE_DisableCallback(createCaller)
end
ALICE_DisableCallback(caller)
caller = ALICE_CallPeriodic(MyCallback)
local function FadeOutFrame(counter, whichFrame)
BlzFrameSetAlpha(whichFrame, 255 - counter)
if counter == 255 then
BlzFrameSetVisible(whichFrame, false)
end
end
local function ExpireFrame(whichFrame)
if GetLocalPlayer() == Player(0) then
fadeCaller = ALICE_CallRepeated(FadeOutFrame, nil, 255, whichFrame)
end
end
local function ShowFrame(whichPlayer, whichFrame)
if GetLocalPlayer() == whichPlayer then
BlzFrameSetVisible(whichFrame, true)
BlzFrameSetAlpha(whichFrame, 255)
ALICE_DisableCallback(fadeCaller) --Make sure existing fade-out callbacks are stopped so they don't immediately hide the frame again.
end
end
ALICE_EnumObjects(identifier, condition, ...)
local function IsOwnedByPlayer(whichUnit, whichPlayer)
return GetOwningPlayer(whichUnit) == whichPlayer
end
ALICE_EnumObjects({"hfoo", "unit", MATCHING_TYPE_ALL}, IsOwnedByPlayer, Player(0))
local obelisk = {
x = GetSpellTargetX(), --read
y = GetSpellTargetY(), --read
owner = GetOwningPlayer(GetSpellAbilityUnit()), --read
visual = AddSpecialEffect(modelPath, GetSpellTargetX(), GetSpellTargetY()), --read
identifier = "obelisk", --copied
interactions = {unit = HealingAura}, --copied
healingPower = 5 --ignored
}
ALICE_Create(obelisk)
All copied fields must be set before ALICE_Create is called. Changing fields afterwards has no effect. Instead, you should use the appropriate API function to change an actor's properties after creation. |
local MoveMissile(missile)
missile.x = missile.x + missile.vx
missile.y = missile.y + missile.vy
missile.z = missile.z + missile.vz
BlzSetSpecialEffectPosition(missile.visual, missile.x, missile.y, missile.z)
end
local missile = {
x = 0,
y = 0,
z = 0,
vx = 10,
vy = 10,
vz = 0,
visual = AddSpecialEffect("Missile.mdx", 0, 0),
selfInteractions = MoveMissile
}
selfInteractions = {
MoveMissile,
OrientMissile
}
local function Expire(obelisk)
obelisk.time = obelisk.time - ALICE_Config.MIN_INTERVAL
if obelisk.time <= 0 then
ALICE_Kill(obelisk)
end
end
local function HealingAura(obelisk, unit)
local dist = ALICE_PairGetDistance2D()
if dist < 500 then
SetUnitState(unit, UNIT_STATE_LIFE, GetUnitState(unit, UNIT_STATE_LIFE) + 5)
end
return 0.5
end
local obelisk = {
x = GetSpellTargetX(),
y = GetSpellTargetY(),
owner = GetOwningPlayer(GetSpellAbilityUnit()),
visual = AddSpecialEffect(modelPath, GetSpellTargetX(), GetSpellTargetY()),
identifier = "obelisk",
interactions = {unit = HealingAura},
selfInteractions = Expire
radius = 500,
time = 10
}
ALICE_Create(obelisk)
interactions = {milk = Drink, apple = Eat}
interactions = {
milk = Drink,
apple = Eat,
[{"steak", "grilled"}] = Eat,
[{"steak", "raw"}] = Grill
}
{steak = Eat, [{"steak", "raw"}] = Grill}
. However, you have to make sure that the interaction function is always unambiguous or ALICE will throw an error.selfInteractions = {
MoveMissile,
OrientMissile
}
--equivalent to
interactions = {
self = {
MoveMissile,
OrientMissile
}
}
ALICE_Create(host, identifier, interactions, {isStationary = true, radius = 200})
function FireAuraDamage(source, target)
local dist = ALICE_PairGetDistance2D()
if dist < 500 then
UnitDamageTarget(source, target, DAMAGE_AMOUNT, DAMAGE_TYPE_MAGIC, ATTACK_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
end
return 0.5
end
function OnFireAuraBuff()
local identifier = "fireAura"
local interactions = {unit = FireAuraDamage}
local flags = {radius = 500, bindToBuff = "Bfir"}
ALICE_Create(GetSpellAbilityUnit(), identifier, interactions, flags)
end
local interactions = {unit = FireAuraDamage, self = FireAuraDamage}
function OnFireAuraBuff()
local fireAura = {
identifier = "fireAura",
interactions = {unit = FireAuraDamage, self = FireAuraDamage},
anchor = GetSpellAbilityUnit(),
radius = 500,
bindToBuff = "Bfir"
}
ALICE_Create(fireAura)
end
function FireAuraDamage(fireAura, target)
local dist = ALICE_PairGetDistance2D()
if dist < 500 then
UnitDamageTarget(ALICE_GetAnchor(fireAura), ALICE_GetAnchor(target), DAMAGE_AMOUNT, DAMAGE_TYPE_MAGIC, ATTACK_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
end
return 0.5
end
function LightningChain(unitA, unitB)
local data = ALICE_PairLoadData()
if ALICE_PairGetDistance2D < 500 then
local x1, y1, x2, y2 = ALICE_PairGetCoordinates2D()
if ALICE_PairIsFirstContact() then
data.lightning = AddLightning("CLPB", false, x1, y1, x2, y2)
else
MoveLightning(data.lightning, x1, y1, x2, y2)
end
elseif data.lightning then
DestroyLightning(data.lightning)
data.lightning = nil
end
end
function LightningChainOnBreak(__, __, data)
if data.lightning then
DestroyLightning(data.lightning)
data.lightning = nil
end
end
function LightningChain(unitA, unitB)
local data = ALICE_PairLoadData()
if ALICE_PairGetDistance2D < 500 then
local x1, y1, x2, y2 = ALICE_PairGetCoordinates2D()
if ALICE_PairIsFirstContact() then
data.lightning = AddLightning("CLPB", false, x1, y1, x2, y2)
else
MoveLightning(data.lightning, x1, y1, x2, y2)
end
elseif data.lightning then
DestroyLightning(data.lightning)
data.lightning = nil
end
end
ALICE_FuncSetOnBreak(LightningChain, LightningChainOnBreak)
function LightningChainOnReset(__, __, data)
DestroyLightning(data.lightning)
end
function LightningChain(unitA, unitB)
local data = ALICE_PairLoadData()
if ALICE_PairGetDistance2D < 500 then
local x1, y1, x2, y2 = ALICE_PairGetCoordinates2D()
if ALICE_PairIsFirstContact() then
data.lightning = AddLightning("CLPB", false, x1, y1, x2, y2)
else
MoveLightning(data.lightning, x1, y1, x2, y2)
end
else
ALICE_PairReset()
end
end
ALICE_FuncSetOnReset(LightningChain, LightningChainOnReset)
function FireDamage(fire, unit)
if not ALICE_PairIsEnemy() then
return 0.5
end
if ALICE_PairGetDistance2D() < 250 then
UnitDamageTarget(fire.source, unit, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
end
return 0.5
end
function FireDamage(fire, unit)
if not ALICE_PairIsEnemy() then
ALICE_PairDisable()
return 0.5
end
if ALICE_PairGetDistance2D() < 250 then
UnitDamageTarget(fire.source, unit, 25, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
end
return 0.5
end
ALICE_FuncSetInit(FireDamage, function()
if not ALICE_PairIsEnemy() then
ALICE_PairDisable()
end
end)
interactions = {
[{"unit", "flammable"}] = FireDamage,
[{"destructable", "flammable"}] = FireDamage
}
ALICE_AddIdentifier(object, "flammable")
ALICE_RemoveIdentifier(object, "flammable")
function FireDamage(fire, unit)
if fire.strength < 10 then
ALICE_PairPause()
return 0.5
end
if not ALICE_PairIsEnemy() then
ALICE_PairDisable()
return 0.5
end
if ALICE_PairGetDistance2D() < 250 then
UnitDamageTarget(fire.source, unit, 5*(fire.strength - 10), false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
end
return 0.5
end
if oldStrength < 10 and newStrength >= 10 then
ALICE_Unpause(fire, FireDamage)
end
local myAura = {
identifier = "aura",
anchor = myUnit
}
ALICE_Create(myAura)
ALICE_HasActor(myUnit, "unit") --true
ALICE_HasActor(myUnit, "aura") --true
ALICE_HasActor(myAura, "aura") --true
ALICE_HasActor(myAura, "unit") --false
if GetLocalPlayer() == whichPlayer then
ALICE_CallDelayed(function() BlzFrameSetVisible(whichFrame, false) end, 2.5)
end
ALICE_CallDelayed(function()
if GetLocalPlayer() == whichPlayer then
BlzFrameSetVisible(whichFrame, false)
end
end, 2.5)
Key | Description |
---|---|
CTRL + Q | Cycle through the different actors anchored to the selected host. |
CTRL + W | Lock selection so that clicking somewhere does not deselect the currently selected actor. |
CTRL + T | Halt the cycle. Hold SHIFT to pause all units. |
CTRL + R | Execute the next step of the cycle. |
CTRL + G | Toggle printing the function names of all invoked interaction functions for the selected actor. |
function InteractionFunc(objectA, objectB)
if not condition1 then
return
end
if not condition2 then
return
end
if not condition3 then
return
end
DoStuff(objectA, objectB)
end
function InteractionFunc(objectA, objectB)
objectA.failedCondition1 = nil
objectA.failedCondition2 = nil
objectA.failedCondition3 = nil
if not condition1 then
objectA.failedCondition1 = true
return
end
if not condition2 then
objectA.failedCondition2 = true
return
end
if not condition3 then
objectA.failedCondition3 = true
return
end
DoStuff(objectA, objectB)
end
ALICE_TrackVariables("failedCondition1", "failedCondition2", "failedCondition3")
FailedCondition1 = {}
FailedCondition2 = {}
FailedCondition3 = {}
function InteractionFunc(objectA, objectB)
--[...]
FailedCondition1[objectA] = true
--[...]
end
ALICE_TrackVariables("FailedCondition1", "FailedCondition2", "FailedCondition3")
{"unit" = DamageUnit, [{"unit", "hero"}] = DamageHero}
) is now supported.