ППЦ, НЕРВЫ УЖЕ ВООБЩЕ НИКУДА...
Рядом бушует герой с такими спеллами.
Это какой-то один из этих трех спеллов, ульта еще у героя есть, но она не была изучена на момент фатала.
Скорее всего проблемы со StartAbilityCooldown, но что может быть не так? Она запускает только спелл на основе инфернала дредлорда.
function AestheticsRefresh takes nothing returns nothing
//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t, u5, u6, p, Aestheticsunits1
local timer t = GetExpiredTimer()
local unit u5=LoadUnitHandle(udg_Hash,GetHandleId(t),2)
local unit u6
local location p  = GetUnitLoc(u5)
local group Aestheticsunits1 = CreateGroup()
//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО

local integer numberofbuildings = 0
local real addmanalife = 3 * GetUnitAbilityLevelSwapped('A320',u5)


//Если героя нет, то разрушить таймер, чтобы не крутился и не жрал ресурсы.
if (u5 == null) then
call PauseTimer(t)
call DestroyTimer(t)
call FlushChildHashtable(udg_Hash,GetHandleId(t))

else



set Aestheticsunits1 = GetUnitsInRangeOfLocAll(900.00, p)
  
loop 
set u6=FirstOfGroup(Aestheticsunits1)
exitwhen u6==null
call GroupRemoveUnit(Aestheticsunits1,u6)
if ( ( IsUnitType((u6), UNIT_TYPE_ANCIENT) == true ) and (IsDead(u6) == false)) then
set numberofbuildings = numberofbuildings + 1
else
endif
endloop

//ЕСЛИ НАШЛИ ХОТЬ ОДНО ЗДАНИЕ - ДОБАВЛЯЕМ МАНУ И ХП

if (numberofbuildings > 0) then

call DestroyEffect(AddSpecialEffectTarget("Sculptor3.mdx",u5,"chest")) 

call SetUnitState(u5, UNIT_STATE_LIFE, GetUnitState(u5, UNIT_STATE_LIFE) + addmanalife)
call SetUnitState(u5, UNIT_STATE_MANA, GetUnitState(u5, UNIT_STATE_MANA) + addmanalife)

endif

//ЕСЛИ НАШЛИ ХОТЬ ОДНО ЗДАНИЕ - ДОБАВЛЯЕМ МАНУ И ХП - ЗАКРЫТО.


endif


call DestroyGroup (Aestheticsunits1)

    
call RemoveLocation (p)    

//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t, u5, u6, p, Aestheticsunits1
set t = null
set u5 = null
set u6 = null
set p = null
set Aestheticsunits1 = null
//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО
endfunction



function Ornament2 takes nothing returns nothing
//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t, u5, u6
local timer t= GetExpiredTimer()
local unit u5= LoadUnitHandle(udg_Hash,GetHandleId(t),2)
local unit u6= LoadUnitHandle(udg_Hash,GetHandleId(t),1)
//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО
local integer level = GetUnitAbilityLevelSwapped('A31Z',u5)
local real x = GetUnitX(u6)
local real y = GetUnitY(u6)

call SaveReal(udg_Hash,GetHandleId(u5),StringHash("ornamenthash"),0)

//ВОТ ТУТ ВСЕ ДЕЙСТВИЯ ПО СПЕЛЛУ - СПЕЛЛБУКИ БУДУТ ПРЕЛОАД Д. МАМКЕ И УДАЛЕНЫ С НЕЁ!!! !!! !!! !!! !!! !!!


//ЕСЛИ ПЕРВОГО НЕТ

if (GetUnitAbilityLevelSwapped ('A322', u6) == 0) and (GetUnitAbilityLevelSwapped ('A326', u6) == 0) then

if (IsUnitEnemy(u6,GetOwningPlayer(u5)) == true) then


//ДОБАВКА И ДИЗЕЙБЛ СПЕЛЛБУКОВ
call UnitAddAbility(u6,'A327')
call SetPlayerAbilityAvailableBJ( false, 'A327', GetOwningPlayer(u6) )
call UnitMakeAbilityPermanent(u6,true,'A327')
call UnitMakeAbilityPermanent(u6,true,'A326')
//ДОБАВКА И ДИЗЕЙБЛ СПЕЛЛБУКОВ - ЗАКРЫТО.

else

//ДОБАВКА И ДИЗЕЙБЛ СПЕЛЛБУКОВ
call UnitAddAbility(u6,'A323')
call SetPlayerAbilityAvailableBJ( false, 'A323', GetOwningPlayer(u6) )
call UnitMakeAbilityPermanent(u6,true,'A323')
call UnitMakeAbilityPermanent(u6,true,'A322')
//ДОБАВКА И ДИЗЕЙБЛ СПЕЛЛБУКОВ - ЗАКРЫТО.


call UnitRemoveAbility(u6,'A327')
call UnitRemoveAbility(u6,'A326')


endif


//ЕСЛИ ПЕРВОГО НЕТ - ЗАКРЫТО.

else

//ЕСЛИ ВТОРОГО НЕТ

if (GetUnitAbilityLevelSwapped ('A324', u6) == 0) and (GetUnitAbilityLevelSwapped ('A328', u6) == 0) then

if (IsUnitEnemy(u6,GetOwningPlayer(u5)) == true) then

//ДОБАВКА И ДИЗЕЙБЛ СПЕЛЛБУКОВ
call UnitAddAbility(u6,'A329')
call SetPlayerAbilityAvailableBJ( false, 'A329', GetOwningPlayer(u6) )
call UnitMakeAbilityPermanent(u6,true,'A329')
call UnitMakeAbilityPermanent(u6,true,'A328')
//ДОБАВКА И ДИЗЕЙБЛ СПЕЛЛБУКОВ - ЗАКРЫТО.


else

//ДОБАВКА И ДИЗЕЙБЛ СПЕЛЛБУКОВ
call UnitAddAbility(u6,'A325')
call SetPlayerAbilityAvailableBJ( false, 'A325', GetOwningPlayer(u6) )
call UnitMakeAbilityPermanent(u6,true,'A325')
call UnitMakeAbilityPermanent(u6,true,'A324')
//ДОБАВКА И ДИЗЕЙБЛ СПЕЛЛБУКОВ - ЗАКРЫТО.

call UnitRemoveAbility(u6,'A329')
call UnitRemoveAbility(u6,'A328')

endif


//ЕСЛИ ВТОРОГО НЕТ - ЗАКРЫТО.

else


//ТЕЛЕПОРТАЦИЯ
call DestroyEffect(AddSpecialEffectTarget("Sculptor23.mdx",u5,"origin"))
call DestroyEffect(AddSpecialEffectTarget("Sculptor23.mdx",u6,"origin"))  
//Задание координат с проверкой на края карты.
if RectContainsCoords(bj_mapInitialPlayableArea,x,y) then
call SetUnitX (u5,x)
call SetUnitY (u6,y)
endif
//Задание координат c проверкой на края карты - закрыто.
//ТЕЛЕПОРТАЦИЯ - ЗАКРЫТО.

endif
endif


call SetUnitAbilityLevelSwapped( 'A322', u6, level )
call SetUnitAbilityLevelSwapped( 'A324', u6, level )
call SetUnitAbilityLevelSwapped( 'A326', u6, level )
call SetUnitAbilityLevelSwapped( 'A328', u6, level )



//ВОТ ТУТ ВСЕ ДЕЙСТВИЯ ПО СПЕЛЛУ - ЗАКРЫТО.


call PauseTimer(t)
call DestroyTimer(t)
call FlushChildHashtable(udg_Hash, GetHandleId(t))

//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t, u5, u6
set t=null
set u5=null
set u6=null
//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО
endfunction


function Ornament takes nothing returns nothing
//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t, u5, u6
local timer t= CreateTimer()
local unit u5= GetTriggerUnit()
local unit u6= GetSpellTargetUnit()
//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО
local integer i= 0
local real ornamenthash = 0
set ornamenthash = LoadReal(udg_Hash,GetHandleId(u5),StringHash("ornamenthash"))

if ( GetSpellAbilityId() == 'A31Z' ) and ornamenthash == 0 then

call PlaySoundOnUnitBJ( gg_snd_SculptorOrnament, 100, u6 )

call SaveReal(udg_Hash,GetHandleId(u5),StringHash("ornamenthash"),1)

call SaveUnitHandle(udg_Hash, GetHandleId(t), 2, u5)
call SaveUnitHandle(udg_Hash, GetHandleId(t), 1, u6)
call SaveInteger(udg_Hash, GetHandleId(t), 3, i)
call TimerStart(t, 0.01, true, function Ornament2)
else
call PauseTimer(t)
call DestroyTimer(t)
call FlushChildHashtable(udg_Hash, GetHandleId(t))
endif

//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t, u5, u6
set t=null
set u5=null
set u6=null
//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО
endfunction



function GreatHammer takes nothing returns nothing
//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t, u5, u6, p, GreatHammerunits1
local timer t = GetExpiredTimer()
local unit u5=LoadUnitHandle(udg_Hash,GetHandleId(t),2)
local unit u6
local location p  = GetUnitLoc(u5)
local group GreatHammerunits1 = CreateGroup()
//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО

//Если героя нет, то разрушить таймер, чтобы не крутился и не жрал ресурсы.
if (u5 == null) then
call PauseTimer(t)
call DestroyTimer(t)
call FlushChildHashtable(udg_Hash,GetHandleId(t))

else


//ЕСЛИ ЕСТЬ МОЛОТ, ТО "ПОДОБРАТЬ" И НАЧАТЬ КД 1, КСТАТИ, НОРМ ПРОВЕРКА НА НАЛИЧИЕ ОБЪЕКТА ПО ID РЯДОМ 1-СЕКУНДНЫМ ТАЙМЕРОМ
set GreatHammerunits1  = GetUnitsInRangeOfLocAll(200.00, p)
  
loop 
set u6=FirstOfGroup(GreatHammerunits1)
exitwhen u6==null
call GroupRemoveUnit(GreatHammerunits1,u6)
if ( ( GetUnitTypeId(u6) == 'n402' ) and ( IsDead(u6) == false ) ) then
call RemoveUnit (u6)
call StartAbilityCooldown(u5,'A31Y',1)
else
endif
endloop
//ЕСЛИ ЕСТЬ МОЛОТ, ТО "ПОДОБРАТЬ" И НАЧАТЬ КД 1 - ЗАКРЫТО.


endif


call DestroyGroup (GreatHammerunits1)

    
call RemoveLocation (p)    

//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t, u5, u6, p, GreatHammerunits1
set t = null
set u5 = null
set u6 = null
set p = null
set GreatHammerunits1 = null
//ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО
endfunction

Принятый ответ

function IsAbilityOnCooldown takes integer a returns boolean
	return IsFlagBitSet(RMem(a+0x20),512)
endfunction

function StartAbilityCooldownFxByAddress takes integer pAbility, real cd returns boolean
	if pAbility < 1 then
		return false
	endif
	set Memory[pReserverdIntArg1 / 4] = mR2I(cd)
	call CallThisCallWith2Args( RMem(RMem(pAbility)+0x3A4) , pAbility, pReserverdIntArg1 )//pStartAbilityCD
	return IsAbilityOnCooldown( pAbility )
endfunction

function StartAbilityCooldownFx takes unit whichUnit, integer abilityId, real cd returns boolean
	local integer pAbility = 0
	if GetUnitAbilityLevel( whichUnit, abilityId  ) == 0 or cd == 0.00  then
		return false
	endif
	return StartAbilityCooldownFxByAddress(GetUnitAbility(whichUnit, abilityId),cd)
endfunction

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
0
21
6 лет назад
0
^ закачать архив выше там папки Logs и Errors после фатала
А вот функция стартабилитикулдаун, к-рой ща пользуюсь
function StartAbilityCooldown takes unit whichUnit, integer abilityId, real cd returns boolean
local integer pAbility = 0

if whichUnit == null or GetUnitAbilityLevel( whichUnit, abilityId ) < 1 or cd == 0.00 then
return false
endif

set pAbility = GetUnitAbilityForAddresss( ConvertHandle( whichUnit ), abilityId )

if pAbility == 0 then
return false
else
set Memory[pReserverdIntArg1 / 4] = cleanInt( realToIndex( cd ) )
call CallThisCallWith2Args( pStartAbilityCD, pAbility, pReserverdIntArg1 )
return IsAbilityOnCooldown( pAbility )
endif

return false
endfunction
5
16
6 лет назад
Отредактирован DracoL1ch
5
function IsAbilityOnCooldown takes integer a returns boolean
	return IsFlagBitSet(RMem(a+0x20),512)
endfunction

function StartAbilityCooldownFxByAddress takes integer pAbility, real cd returns boolean
	if pAbility < 1 then
		return false
	endif
	set Memory[pReserverdIntArg1 / 4] = mR2I(cd)
	call CallThisCallWith2Args( RMem(RMem(pAbility)+0x3A4) , pAbility, pReserverdIntArg1 )//pStartAbilityCD
	return IsAbilityOnCooldown( pAbility )
endfunction

function StartAbilityCooldownFx takes unit whichUnit, integer abilityId, real cd returns boolean
	local integer pAbility = 0
	if GetUnitAbilityLevel( whichUnit, abilityId  ) == 0 or cd == 0.00  then
		return false
	endif
	return StartAbilityCooldownFxByAddress(GetUnitAbility(whichUnit, abilityId),cd)
endfunction
Принятый ответ
0
21
6 лет назад
0
DracoL1ch, это тут сейчас зачем?
Эта функция вроде запускает только фактический кд абилки, а мне как раз нужно заданный мной
И причина-то фатала все-таки в чем?
///////////
Просто еще инфа:
-первый спелл - 90% уверенность, что причина в нем, там это фактически спелл на основе infernal дредлорда, только вместо инфернала падает молот. Сам спелл дефолт почти, но вот еще таймер периодически проверяет, не лежит ли рядом с героем хоть один такой молот и если лежит, то убирает молот из игры и запускает 1-секундную перезарядку этого спелла.
-второй - дает поцеленному зданию абилы, если все абилы есть, то телепортация к зданию. Тут хз, появилось предположение, что мб из-за того, что у разных аур одинаковые баффы, может быть фатал? Т. е. тогда вообще причина в РО? Но бред, или нет? Короче, ПЛЗ СМ ЛОГ И ПРОШУ УКАЗАТЬ ВСЕ, ЧТО МОЖНО, ПРО ФАТАЛ!!!
-третий - просто смотрит ежесекундно, есть ли здания рядом с героем и если есть, то ресает ему ману и здоровье. Тут практически полная уверенность, что все чисто, даже мх вроде нет, но для чистоты эксперимента все же пусть он будет...
4
16
6 лет назад
4
я тебе дал безопасную функцию, вставь и смотри, в кд ли проблема была
0
32
6 лет назад
0
ClotPh, тоже очень долго побеждал несовместимость абилкок с функцией кд, после того как `IceFog, не дал немного другую функцию старта кулдауна.

из инита мемхака:
set pStartAbilityCD = (GameDLL + $050B70) // раньше было GameDLL + 0x126990 и фаталило, теперь нет.

 function StartAbilityCooldown takes unit whichUnit, integer abilityId, real cd returns boolean
        local integer pAbility = 0
        local integer offset1
        local integer offset2
        local boolean bRes = false
        
        if GetUnitAbilityLevel( whichUnit, abilityId ) == 0 or cd == 0.00 then
            return bRes
        endif
        
        set pAbility = GetUnitAbility( whichUnit, abilityId )
        
        if pAbility < 1 then
            return bRes
        else
            set offset1 = RMem( pAbility + 0x134 )
            set offset2 = RMem( pAbility + 0x138 )
          
            if offset1 > 0 or offset1 < 0 or offset2 > 0 or offset2 < 0 then
                call WMem( pAbility + 0x134, mR2I( cd ) )
                call WMem( pAbility + 0x138, mR2I( 10.00 ) )
                if not InGame then // дебаг для детекта несовместимых абилок, можно удалить.
                    call BJDebugMsg( "Start Cooldown WARNING!" ) 
                    call BJDebugMsg( "ADRESS-VAL1-VAL2" )
                    call BJDebugMsg( Int2Hex(pAbility)+" - "+Int2Hex(offset1)+" - "+Int2Hex(offset2))
                    call BJDebugMsg( "abil-"+Id2String(abilityId) +" owner-"+ Int2Hex(pAbility)  )
                endif
            endif
            set Memory[pReserverdIntArg1 / 4] = mR2I( cd )
            call CallThisCallWith2Args( pStartAbilityCD, pAbility, pReserverdIntArg1 )
            set bRes =  IsAbilityOnCooldown( pAbility )
            call WMem( pAbility + 0x134, offset1 )
            call WMem( pAbility + 0x138, offset2 )
        endif
        return bRes
    endfunction
Несовместимые абилки - блинк\берсерк. Но функция выше корректно запускает их в кд.
2
16
6 лет назад
2
Да хватит херней страдать, берите ту, что я скинул, она УНИВЕРСАЛЬНАЯ
0
21
6 лет назад
0
А архив скачать и посмотреть хотя бы никак, в чем причина?! А то я сейчас начну шаманить с кд, а выяснится, что зря время трачу...
Так, ладно, функция импортнута, в тест-карте норм, но там и та функция и вообще весь герой были норм и не фаталили, и в игровой карте все спеллы юзались не раз без проблем, фатал в любом случае не всегда, надо тестить, ща в игровой потестирую, если будут выявлены дальше проблемы, напишу
только не пойму: если спелл УЖЕ в кулдауне, эта startabilitycooldownfx, я так понимаю, не убирает его текущий кулдаун визуально, а просто ждет указанное время и обновляет спелл?
Просто у меня со startabilitycooldown было так:
  • герой кастует спелл
  • он запускается в свою обычную перезарядку (10 секунд)
  • я подхожу к упавшему молоту (там триггерно периодик ищет молоты, только вокруг выучившего этот спелл героя, и если рядом есть молот, то ремувит его и запускает перезарядку в 1 сек., типа "подбирает")
  • молот исчезает и четко видно, что вместо текущей перезарядки в 10 секунд (она была там, допустим, где-то на половине и медленной, так что это видно) сразу запускается "быстрая" перезарядка 1 секунду, а "старая" перезарядка, ятп, просто исчезает.
А у этой так:
  • герой кастует спелл
  • он запускается в свою обычную перезарядку (10 секунд)
  • я подхожу к упавшему молоту (там триггерно периодик ищет молоты, только вокруг выучившего этот спелл героя, и если рядом есть молот, то ремувит его и запускает перезарядку в 1 сек., типа "подбирает")
  • молот исчезает и... вот тут не могу наспех отследить, через секунду (а у меня прописана цифра 1) или сразу, но перезарядка "старая" идущая внезапно просто "пропадает", и все. "Маленькой" перезарядки в 1 НЕ видно!
(Нет, это не плохо, в данном заклинании точно, просто интересна механика действия функции, если она запускает в кулдаун спелл, к-рый уже в кулдауне).
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.