I tried with some experiments will JASS triggers, I'm curious with AI and wanted to see how it can turns my ideas into reality.
Gave it an idea to create 2 dummy missles that travel to target point in parabolic, but seems it FAILED TO MOVE and doesn't know how where is wrong.
Here are the trigger & map:
Gave it an idea to create 2 dummy missles that travel to target point in parabolic, but seems it FAILED TO MOVE and doesn't know how where is wrong.
Here are the trigger & map:
JASS:
//===========================================================================
// Arcane Bolts Spell
// Date: 2024-05-20 (Version 19 - No Debug Messages)
// Author: Gemini AI (with user input)
// Movement: Straight Line X/Y, Parabolic Z
//===========================================================================
//===========================================================================
// Configuration
//===========================================================================
globals
// --- Spell and Dummy Unit Raw Codes ---
constant integer SPELL_ID = 'A000'
constant integer DUMMY_GOLD_BOLT_ID = 'e001'
constant integer DUMMY_ENERGY_BOLT_ID = 'e000'
constant integer DUMMY_ABILITY_LOCUST = 'Aloc'
constant integer ABILITY_CROW_FORM = 'Amrf' // For ensuring flying movement type
// --- Damage and Mana Burn Scaling ---
constant real DAMAGE_BASE = 25.0
constant real DAMAGE_PER_LEVEL = 25.0
constant real MANA_BURN_BASE = 25.0
constant real MANA_BURN_PER_LEVEL = 25.0
constant real AREA_OF_EFFECT = 200.00
// --- Timings & Movement Parameters ---
constant real INITIAL_DELAY = 0.50
constant real TRAVEL_TIME = 1.5 // Increased for visual clarity
constant real ENERGY_BOLT_SPAWN_DELAY = 0.50
constant real MOVEMENT_UPDATE_INTERVAL = 0.03
// --- Z-Arc Calculation ---
constant real Z_ARC_PEAK_FACTOR = 0.50 // Peak Z height as a factor of total distance
hashtable BoltData = null
// --- Hashtable Keys ---
constant integer HT_KEY_TIMER_CASTER_UNIT = 0
constant integer HT_KEY_TIMER_TARGET_X = 1
constant integer HT_KEY_TIMER_TARGET_Y = 2
constant integer HT_KEY_TIMER_OWNER_PLAYER = 3
constant integer HT_KEY_MOVE_TIMER_DUMMY_UNIT = 100 // Key for timer -> dummy link
constant integer HT_KEY_DUMMY_BOLT_TYPE = 200
constant integer HT_KEY_DUMMY_ORIG_CASTER_UNIT = 201
constant integer HT_KEY_DUMMY_TARGET_X = 202
constant integer HT_KEY_DUMMY_TARGET_Y = 203
constant integer HT_KEY_DUMMY_START_X = 204
constant integer HT_KEY_DUMMY_START_Y = 205
constant integer HT_KEY_DUMMY_TIME_ELAPSED = 208
constant integer HT_KEY_DUMMY_MOVEMENT_TIMER = 209 // Key for dummy -> timer link
constant integer HT_KEY_DUMMY_TOTAL_DISTANCE = 210
constant integer BOLT_TYPE_GOLDEN = 1
constant integer BOLT_TYPE_ENERGY = 2
endglobals
//===========================================================================
// Bolt Logic - Functions ordered for correct JASS parsing
//===========================================================================
function CleanupBoltData takes unit dummyBolt returns nothing
local integer dummyUnitId
local timer moveTimer
local integer moveTimerHandle
if dummyBolt == null then
return
endif
set dummyUnitId = GetHandleId(dummyBolt)
if HaveSavedHandle(BoltData, dummyUnitId, HT_KEY_DUMMY_MOVEMENT_TIMER) then
set moveTimer = LoadTimerHandle(BoltData, dummyUnitId, HT_KEY_DUMMY_MOVEMENT_TIMER)
if moveTimer != null then
set moveTimerHandle = GetHandleId(moveTimer)
call PauseTimer(moveTimer)
call DestroyTimer(moveTimer)
call FlushChildHashtable(BoltData, moveTimerHandle)
endif
endif
call FlushChildHashtable(BoltData, dummyUnitId)
if GetUnitTypeId(dummyBolt) != 0 then
call RemoveUnit(dummyBolt)
endif
set dummyBolt = null
set moveTimer = null
endfunction
function BoltImpact takes unit dummyBolt returns nothing
local integer dummyUnitId
local integer boltType
local unit originalCaster
local player originalCasterOwner
local real targetX
local real targetY
local location impactLoc
local group g
local unit FoGUnit
local integer spellLevel
local real calculatedDamage
local real calculatedManaBurn
if dummyBolt == null then
return
endif
set dummyUnitId = GetHandleId(dummyBolt)
if not HaveSavedInteger(BoltData, dummyUnitId, HT_KEY_DUMMY_BOLT_TYPE) then
if GetUnitTypeId(dummyBolt) != 0 then
call RemoveUnit(dummyBolt)
endif
return
endif
set boltType = LoadInteger(BoltData, dummyUnitId, HT_KEY_DUMMY_BOLT_TYPE)
set originalCaster = LoadUnitHandle(BoltData, dummyUnitId, HT_KEY_DUMMY_ORIG_CASTER_UNIT)
set targetX = LoadReal(BoltData, dummyUnitId, HT_KEY_DUMMY_TARGET_X)
set targetY = LoadReal(BoltData, dummyUnitId, HT_KEY_DUMMY_TARGET_Y)
set impactLoc = Location(targetX, targetY)
set g = CreateGroup()
set spellLevel = 0
if originalCaster != null and GetUnitTypeId(originalCaster) != 0 then
set originalCasterOwner = GetOwningPlayer(originalCaster)
set spellLevel = GetUnitAbilityLevel(originalCaster, SPELL_ID)
else
set originalCasterOwner = GetOwningPlayer(dummyBolt)
endif
set calculatedDamage = DAMAGE_BASE + (DAMAGE_PER_LEVEL * spellLevel)
set calculatedManaBurn = MANA_BURN_BASE + (MANA_BURN_PER_LEVEL * spellLevel)
call GroupEnumUnitsInRangeOfLoc(g, impactLoc, AREA_OF_EFFECT, null)
loop
set FoGUnit = FirstOfGroup(g)
exitwhen FoGUnit == null
call GroupRemoveUnit(g, FoGUnit)
if originalCaster != null and GetUnitTypeId(originalCaster) != 0 and IsUnitEnemy(FoGUnit, originalCasterOwner) and GetWidgetLife(FoGUnit) > 0.405 and not IsUnitType(FoGUnit, UNIT_TYPE_STRUCTURE) and not IsUnitType(FoGUnit, UNIT_TYPE_DEAD) then
if boltType == BOLT_TYPE_GOLDEN then
call UnitDamageTarget(originalCaster, FoGUnit, calculatedDamage, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
elseif boltType == BOLT_TYPE_ENERGY then
call UnitDamageTarget(originalCaster, FoGUnit, calculatedDamage, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
call SetUnitState(FoGUnit, UNIT_STATE_MANA, GetUnitState(FoGUnit, UNIT_STATE_MANA) - calculatedManaBurn)
endif
endif
endloop
call RemoveLocation(impactLoc)
call DestroyGroup(g)
call CleanupBoltData(dummyBolt)
set impactLoc = null
set g = null
set FoGUnit = null
set originalCaster = null
set originalCasterOwner = null
endfunction
function MoveBolt takes nothing returns nothing
local timer t
local integer timerHandle
local unit dummyBolt
local integer dummyUnitId
local real timeElapsed
local real startX
local real startY
local real targetX
local real targetY
local real currentPROGRESS
local real newX
local real newY
local real newZ
local real totalDistance
local real zArcPeakHeight
set t = GetExpiredTimer()
set timerHandle = GetHandleId(t)
set dummyBolt = LoadUnitHandle(BoltData, timerHandle, HT_KEY_MOVE_TIMER_DUMMY_UNIT)
if dummyBolt == null or GetUnitTypeId(dummyBolt) == 0 then
call PauseTimer(t)
call FlushChildHashtable(BoltData, timerHandle)
call DestroyTimer(t)
set t = null
return
endif
set dummyUnitId = GetHandleId(dummyBolt)
if not HaveSavedReal(BoltData, dummyUnitId, HT_KEY_DUMMY_TIME_ELAPSED) then
call PauseTimer(t)
call FlushChildHashtable(BoltData, timerHandle)
call DestroyTimer(t)
set t = null
set dummyBolt = null
return
endif
set timeElapsed = LoadReal(BoltData, dummyUnitId, HT_KEY_DUMMY_TIME_ELAPSED) + MOVEMENT_UPDATE_INTERVAL
set startX = LoadReal(BoltData, dummyUnitId, HT_KEY_DUMMY_START_X)
set startY = LoadReal(BoltData, dummyUnitId, HT_KEY_DUMMY_START_Y)
set targetX = LoadReal(BoltData, dummyUnitId, HT_KEY_DUMMY_TARGET_X)
set targetY = LoadReal(BoltData, dummyUnitId, HT_KEY_DUMMY_TARGET_Y)
set totalDistance = LoadReal(BoltData, dummyUnitId, HT_KEY_DUMMY_TOTAL_DISTANCE)
if TRAVEL_TIME <= 0 then
set currentPROGRESS = 1.0
else
set currentPROGRESS = timeElapsed / TRAVEL_TIME
endif
if currentPROGRESS > 1.0 then
set currentPROGRESS = 1.0
endif
if currentPROGRESS >= 1.0 then
set newX = targetX
set newY = targetY
else
set newX = startX + (targetX - startX) * currentPROGRESS
set newY = startY + (targetY - startY) * currentPROGRESS
endif
call SetUnitX(dummyBolt, newX)
call SetUnitY(dummyBolt, newY)
if totalDistance > 0 then
set zArcPeakHeight = totalDistance * Z_ARC_PEAK_FACTOR
set newZ = zArcPeakHeight * 4.0 * currentPROGRESS * (1.0 - currentPROGRESS)
call SetUnitFlyHeight(dummyBolt, newZ, 0)
else
call SetUnitFlyHeight(dummyBolt, 0.0, 0)
endif
if currentPROGRESS >= 1.0 then
call BoltImpact(dummyBolt)
else
call SaveReal(BoltData, dummyUnitId, HT_KEY_DUMMY_TIME_ELAPSED, timeElapsed)
endif
set t = null
set dummyBolt = null
endfunction
function LaunchBolt takes integer boltType, unit casterUnit, player ownerPlayer, real targX, real targY returns nothing
local real startX
local real startY
local real casterFacing
local unit dummyBolt
local integer dummyBoltUnitIdToCreate
local real dx
local real dy
local real dist
local integer createdDummyUnitId
local timer moveTimer
local integer moveTimerHandle
if casterUnit == null or GetUnitTypeId(casterUnit) == 0 then
return
endif
if ownerPlayer == null then
set ownerPlayer = GetOwningPlayer(casterUnit)
if ownerPlayer == null then
return
endif
endif
set startX = GetUnitX(casterUnit)
set startY = GetUnitY(casterUnit)
set casterFacing = GetUnitFacing(casterUnit)
if boltType == BOLT_TYPE_GOLDEN then
set dummyBoltUnitIdToCreate = DUMMY_GOLD_BOLT_ID
else
set dummyBoltUnitIdToCreate = DUMMY_ENERGY_BOLT_ID
endif
set dummyBolt = CreateUnit(ownerPlayer, dummyBoltUnitIdToCreate, startX, startY, casterFacing)
if dummyBolt == null then
return
endif
call UnitAddAbility(dummyBolt, DUMMY_ABILITY_LOCUST)
call UnitAddAbility(dummyBolt, ABILITY_CROW_FORM)
call UnitRemoveAbility(dummyBolt, ABILITY_CROW_FORM)
call UnitApplyTimedLife(dummyBolt, 'BTLF', TRAVEL_TIME + 2.0)
set dx = targX - startX
set dy = targY - startY
set dist = SquareRoot(dx*dx + dy*dy)
set createdDummyUnitId = GetHandleId(dummyBolt)
set moveTimer = CreateTimer()
set moveTimerHandle = GetHandleId(moveTimer)
call SaveInteger(BoltData, createdDummyUnitId, HT_KEY_DUMMY_BOLT_TYPE, boltType)
call SaveUnitHandle(BoltData, createdDummyUnitId, HT_KEY_DUMMY_ORIG_CASTER_UNIT, casterUnit)
call SaveReal(BoltData, createdDummyUnitId, HT_KEY_DUMMY_TARGET_X, targX)
call SaveReal(BoltData, createdDummyUnitId, HT_KEY_DUMMY_TARGET_Y, targY)
call SaveReal(BoltData, createdDummyUnitId, HT_KEY_DUMMY_START_X, startX)
call SaveReal(BoltData, createdDummyUnitId, HT_KEY_DUMMY_START_Y, startY)
call SaveReal(BoltData, createdDummyUnitId, HT_KEY_DUMMY_TOTAL_DISTANCE, dist)
call SaveReal(BoltData, createdDummyUnitId, HT_KEY_DUMMY_TIME_ELAPSED, 0.0)
call SaveTimerHandle(BoltData, createdDummyUnitId, HT_KEY_DUMMY_MOVEMENT_TIMER, moveTimer)
call SaveUnitHandle(BoltData, moveTimerHandle, HT_KEY_MOVE_TIMER_DUMMY_UNIT, dummyBolt)
call TimerStart(moveTimer, MOVEMENT_UPDATE_INTERVAL, true, function MoveBolt)
set dummyBolt = null
set moveTimer = null
endfunction
function HandleDelayed_LaunchEnergyBolt takes nothing returns nothing
local timer t
local integer timerHandle
local unit casterUnit
local real targetX
local real targetY
local player ownerPlayer
set t = GetExpiredTimer()
set timerHandle = GetHandleId(t)
set casterUnit = LoadUnitHandle(BoltData, timerHandle, HT_KEY_TIMER_CASTER_UNIT)
set targetX = LoadReal(BoltData, timerHandle, HT_KEY_TIMER_TARGET_X)
set targetY = LoadReal(BoltData, timerHandle, HT_KEY_TIMER_TARGET_Y)
set ownerPlayer = LoadPlayerHandle(BoltData, timerHandle, HT_KEY_TIMER_OWNER_PLAYER)
if casterUnit != null and GetUnitTypeId(casterUnit) != 0 and ownerPlayer != null then
call LaunchBolt(BOLT_TYPE_ENERGY, casterUnit, ownerPlayer, targetX, targetY)
endif
call FlushChildHashtable(BoltData, timerHandle)
call DestroyTimer(t)
set t = null
set casterUnit = null
set ownerPlayer = null
endfunction
function HandleInitialDelay_LaunchGoldenAndScheduleEnergy takes nothing returns nothing
local timer t
local integer timerHandle
local unit casterUnit
local real targetX
local real targetY
local player ownerPlayer
local timer energyBoltDelayTimer
local integer energyBoltDelayTimerHandle
set t = GetExpiredTimer()
set timerHandle = GetHandleId(t)
set casterUnit = LoadUnitHandle(BoltData, timerHandle, HT_KEY_TIMER_CASTER_UNIT)
set targetX = LoadReal(BoltData, timerHandle, HT_KEY_TIMER_TARGET_X)
set targetY = LoadReal(BoltData, timerHandle, HT_KEY_TIMER_TARGET_Y)
set ownerPlayer = LoadPlayerHandle(BoltData, timerHandle, HT_KEY_TIMER_OWNER_PLAYER)
if casterUnit != null and GetUnitTypeId(casterUnit) != 0 and ownerPlayer != null then
call LaunchBolt(BOLT_TYPE_GOLDEN, casterUnit, ownerPlayer, targetX, targetY)
set energyBoltDelayTimer = CreateTimer()
set energyBoltDelayTimerHandle = GetHandleId(energyBoltDelayTimer)
call SaveUnitHandle(BoltData, energyBoltDelayTimerHandle, HT_KEY_TIMER_CASTER_UNIT, casterUnit)
call SaveReal(BoltData, energyBoltDelayTimerHandle, HT_KEY_TIMER_TARGET_X, targetX)
call SaveReal(BoltData, energyBoltDelayTimerHandle, HT_KEY_TIMER_TARGET_Y, targetY)
call SavePlayerHandle(BoltData, energyBoltDelayTimerHandle, HT_KEY_TIMER_OWNER_PLAYER, ownerPlayer)
call TimerStart(energyBoltDelayTimer, ENERGY_BOLT_SPAWN_DELAY, false, function HandleDelayed_LaunchEnergyBolt)
set energyBoltDelayTimer = null
endif
call FlushChildHashtable(BoltData, timerHandle)
call DestroyTimer(t)
set t = null
set casterUnit = null
set ownerPlayer = null
endfunction
//===========================================================================
// Main Trigger Setup
//===========================================================================
function ArcaneBolts_Conditions takes nothing returns boolean
return GetSpellAbilityId() == SPELL_ID
endfunction
function ArcaneBolts_Actions takes nothing returns nothing
local unit caster
local player casterOwner
local location targetLoc
local real targetX
local real targetY
local timer initialDelayTimer
local integer initialDelayTimerHandle
if BoltData == null then
// If you ever need to debug hashtable initialization, uncomment the next line
// call BJDebugMsg("ArcaneBolts_Actions ERROR: BoltData hashtable is NULL!")
return
endif
set caster = GetTriggerUnit()
if caster == null then
return
endif
set casterOwner = GetOwningPlayer(caster)
set targetLoc = GetSpellTargetLoc()
if targetLoc == null then
return
endif
set targetX = GetLocationX(targetLoc)
set targetY = GetLocationY(targetLoc)
call RemoveLocation(targetLoc)
set targetLoc = null
set initialDelayTimer = CreateTimer()
set initialDelayTimerHandle = GetHandleId(initialDelayTimer)
call SaveUnitHandle(BoltData, initialDelayTimerHandle, HT_KEY_TIMER_CASTER_UNIT, caster)
call SaveReal(BoltData, initialDelayTimerHandle, HT_KEY_TIMER_TARGET_X, targetX)
call SaveReal(BoltData, initialDelayTimerHandle, HT_KEY_TIMER_TARGET_Y, targetY)
call SavePlayerHandle(BoltData, initialDelayTimerHandle, HT_KEY_TIMER_OWNER_PLAYER, casterOwner)
call TimerStart(initialDelayTimer, INITIAL_DELAY, false, function HandleInitialDelay_LaunchGoldenAndScheduleEnergy)
set caster = null
set casterOwner = null
set initialDelayTimer = null
endfunction
//===========================================================================
// Initialization
//===========================================================================
function InitTrig_ArcaneBolts takes nothing returns nothing
local trigger t = CreateTrigger()
if BoltData == null then
set BoltData = InitHashtable()
endif
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function ArcaneBolts_Conditions))
call TriggerAddAction(t, function ArcaneBolts_Actions)
set t = null
endfunction
Attachments
Last edited: