Собственно небольшая библиотека, требующая мемхак_второй_версии и больше ничего.
Позволяет передавать аргументы функциям, которые используются в качестве аргумента code в StartTimer(), ForGroup(),ForFroce(),Condition() и так далее.
Позволяет передавать аргументы функциям, которые используются в качестве аргумента code в StartTimer(), ForGroup(),ForFroce(),Condition() и так далее.
код
library Lambda initializer Init uses Memory
globals
constant integer BOOL = 0x1 // boolean
constant integer CODE = 0x2 // code
constant integer HAND = 0x3 // handle
constant integer INTG = 0x4 // ineteger
constant integer REAL = 0x5 // real
constant integer STRI = 0x6 // string
integer array PowArr
integer bj_lastConvertedCode = 0
endglobals
function ShiftLeft takes integer bits, integer shift returns integer
return bits * PowArr[shift]
endfunction
// a - highest, d - lowest
function DwordFromBytes takes integer a, integer b, integer c, integer d returns integer
local integer result = 0
set result = result + ShiftLeft( a, 24 )
set result = result + ShiftLeft( b, 16 )
set result = result + ShiftLeft( c, 8 )
set result = result + ShiftLeft( d, 0 )
return result
endfunction
function WriteInstruction takes integer address, integer id, integer a1, integer a2, integer a3, integer param returns nothing
call WMem( address + 0x00, DwordFromBytes( id, a1, a2, a3 ) )
call WMem( address + 0x04, param )
endfunction
function CreateLambdaByIdOneArg takes integer func_id, integer param , integer argtype returns code
local integer buffer = malloc( 4 * 0x08 )
// size - 0x08
// litheral - 0x0C
// push - 0x13
// call - 0x16
// return - 0x27
call WriteInstruction( buffer + 0 * 0x08, 0x0C, 1, argtype, 0, param ) //0x01 - boolean, 0x2 - code, 0x3 - handle, 0x4 - integer, 0x5 - real, 0x6 - string
call WriteInstruction( buffer + 1 * 0x08, 0x13, 1, 0, 0, 0 )
call WriteInstruction( buffer + 2 * 0x08, 0x16, 0, 0, 0, func_id )
call WriteInstruction( buffer + 3 * 0x08, 0x27, 0, 0, 0, 0 )
return I2C( buffer )
endfunction
function CreateLambdaByIdTwoArg takes integer func_id, integer param1, integer param2, integer argtype1, integer argtype2 returns code
local integer buffer = malloc( 6 * 0x08 )
// size - 0x08
// litheral - 0x0C
// push - 0x13
// call - 0x16
// return - 0x27
call WriteInstruction( buffer + 0 * 0x08, 0x0C, 1, argtype1, 0, param1 ) //0x01 - boolean, 0x2 - code, 0x3 - handle, 0x4 - integer, 0x5 - real, 0x6 - string
call WriteInstruction( buffer + 1 * 0x08, 0x13, 1, 0, 0, 0 )
call WriteInstruction( buffer + 2 * 0x08, 0x0C, 2, argtype2, 0, param2 ) //0x01 - boolean, 0x2 - code, 0x3 - handle, 0x4 - integer, 0x5 - real, 0x6 - string
call WriteInstruction( buffer + 3 * 0x08, 0x13, 2, 0, 0, 0 )
call WriteInstruction( buffer + 4 * 0x08, 0x16, 0, 0, 0, func_id )
call WriteInstruction( buffer + 5 * 0x08, 0x27, 0, 0, 0, 0 )
return I2C( buffer )
endfunction
function SetCodeOneArg takes code func, integer argtype, integer arg returns code
if ( func == null ) then
return null
endif
set bj_lastConvertedCode = RMem( C2I( func ) - 0x04 )
if ( bj_lastConvertedCode < 1 ) or ( argtype < 0 ) or ( argtype > 6 ) then
return null
endif
return CreateLambdaByIdOneArg( bj_lastConvertedCode, arg, argtype )
endfunction
function SetCodeTwoArg takes code func, integer argtype1, integer arg1, integer argtype2, integer arg2 returns code
if ( func == null ) then
return null
endif
set bj_lastConvertedCode = RMem( C2I( func ) - 0x04 )
if ( bj_lastConvertedCode < 1 ) or ( argtype1 < 0 ) or ( argtype1 > 6 ) or ( argtype2 < 0 ) or ( argtype2 > 6 ) then
return null
endif
return CreateLambdaByIdTwoArg( bj_lastConvertedCode, arg1, arg2, argtype1, argtype2 )
endfunction
private function Init takes nothing returns nothing
local integer i = 1
set PowArr[0] = 1
loop
set PowArr[i] = PowArr[i - 1] * 2
set i = i + 1
exitwhen i == 32
endloop
set PowArr[32] = -2147483648
endfunction
endlibrary
Собственно пример применения:
function TestFunc takes boolean breturns nothing
if b then
call BJDebugMsg("b - true")
else
call BJDebugMsg("b - false")
endif
endfunction
//# +nosemanticerror
function somename takes nothing returns nothing
...
call TimerStart( CreateTimer( ), 0.50, true, SetCodeOneArg( function TestFunc,BOOL, 1 ) )
...
endfunction
При срабатывании таймера наша функция TestFunc получит b, равное true или false в зависимости от того чему мы установили последний аргумент функции SetCodeOneArg.
ОБРАТИТЕ ВНИМАНИЕ //# +nosemanticerror обазателен, т.к пасер не знают что каллбеки или code может иметь аргументы и выдаст нам вот такую ошибку:
ОБРАТИТЕ ВНИМАНИЕ //# +nosemanticerror обазателен, т.к пасер не знают что каллбеки или code может иметь аргументы и выдаст нам вот такую ошибку:
Как видим: Функция Имя не должна иметь аргументов когда используется в качестве code!
Поэтому нам необходимо использовать данную инструкцию, чтобы пасер игнорировал это.
Самому же Warcraft3 совершенно пофиг на это, карта без JNGP в которой функцию с аргументам используют в качестве code спокойно сохранится и даже запустится, правда при вызове этой функции вар крашнется с фаталом.
Но так как у нас давным давно есть мемхак, нам нечего не стоит исправить эту проблему, на самом деле функция внутри виртуальной JASM машине может иметь аргументы всегда, не важно используется она как call func( args) или как ForGroup( grp, function func), но во втором случаи нечему засунуть список аргументов в функцию, тут нам помогает мемхак
Поэтому нам необходимо использовать данную инструкцию, чтобы пасер игнорировал это.
Самому же Warcraft3 совершенно пофиг на это, карта без JNGP в которой функцию с аргументам используют в качестве code спокойно сохранится и даже запустится, правда при вызове этой функции вар крашнется с фаталом.
Но так как у нас давным давно есть мемхак, нам нечего не стоит исправить эту проблему, на самом деле функция внутри виртуальной JASM машине может иметь аргументы всегда, не важно используется она как call func( args) или как ForGroup( grp, function func), но во втором случаи нечему засунуть список аргументов в функцию, тут нам помогает мемхак
Собственно описание функционала:
- SetCodeOneArg - берет code (т.е function имя вашей функции, которой вы хотите задать аргументы) , целое - тип переменной, целое значение.
constant integer BOOL = 0x1 // boolean
constant integer CODE = 0x2 // code
constant integer HAND = 0x3 // handle
constant integer INTG = 0x4 // ineteger
constant integer REAL = 0x5 // real
constant integer STRI = 0x6 // string
Вроде бы все просто - 0x1 буль, 0х2 код, 0х3 хендл, 0x4 целое...
Но как нам передавать handle или real, или string,и ? Функция берет только целочисленные значения.
А очень просто, нужно их преобразовать в целые числа с помощью функционала мемхака или встроенных jass функций (в одном случаи)
Но как нам передавать handle или real, или string,и ? Функция берет только целочисленные значения.
А очень просто, нужно их преобразовать в целые числа с помощью функционала мемхака или встроенных jass функций (в одном случаи)
local code c = SetCodeOneArg( function TestFunc, CODE, C2I( function otherfunc )) // если вы хотите передать аргумент типа code
При этом не забудьте что функция TestFunc (та в которую вы хотите передать аргумент), должна иметь только 1 принимаемый аргумент типа code
Вот так это выглядит на jass:
Вот так это выглядит на jass:
function TestFunc takes code c returns nothing
call ForGroup( udg_TempGroup, c )
endfunction
При этом обратных преобразований из целого в код не потребуется, просто работаем с аргументом как с локалкой.
Примерно так же делаем с handle:
Примерно так же делаем с handle:
local code c = SetCodeOneArg( function TestFunc, HAND, GetHandleId(некий хендл)) // если вы хотите передать аргумент типа handle*
Но тут есть одна загвоздка, если в первом случаи мы строго указывали тип - буль, или код, то как же быть с хендалми, хендл наследуют 100500 типов, от agent до widgetevent, а точнее около 90. Но вспоминаем, что хендлы то особо не отличаются, все это ссылки на внутреннюю хештаблицу объектов, следовательно для всех типов наследуемых от handle, нужно использовать тип аргумента HAND (0x3).
Но не забывайте, в функции которой вы хотите передать аргумент, тип аргумента должен соотсветсвовать тому, которому вы хотите преедать, т.е если вы передаете юнита, то функция должна иметь тип аргумента unitа не hanlde или item...
Пример на jass:
Но не забывайте, в функции которой вы хотите передать аргумент, тип аргумента должен соотсветсвовать тому, которому вы хотите преедать, т.е если вы передаете юнита, то функция должна иметь тип аргумента unitа не hanlde или item...
Пример на jass:
function TestFunc takes unit u returns nothing // хоть мы указали тип handle в SetCodeOneArg, но все равно указываем юнита.
call BJDebugMsg( GetUnitName(u))
endfunction
...
local code c = SetCodeOneArg( function TestFunc, HAND, GetHandleId(handle)) // указываем ссылку на юнита, если хотим предать юнита.
...
Точно так же для любого другого хендла, в SetCodeOneArg указываем тип HAND (0x3) и GetHandleId() от нужного нам обьекта, но в функцию которой мы хотим передать аргументы пишем takes тот тип, которому соответствует наш обьект, юнт-юниту, предмет-предмету, а не наоборот, передаем предмета а пишем юнит, наблюдаем фатальную ошибку....
Для целочисленных переменных никаких преобразований ненужно, указываем тип INTG (0x4), и праямо указываем число в качестве аргумента, тут никаких преобразований не потребуется:
Для целочисленных переменных никаких преобразований ненужно, указываем тип INTG (0x4), и праямо указываем число в качестве аргумента, тут никаких преобразований не потребуется:
function TestFunc takes integer a returns nothing
call BJDebugMsg(I2S(a))
endfunction
call TimerStart( CreateTimer( ), 0.50, true, SetCodeOneArg( function TestFunc, INTG, 100500 ) )
С типом real все несколько сложнее, но тут нам поможет мемхак, а точнее пару функций из библиотеки Typecast:
//# +nosemanticerror
function realToIndex takes real r returns integer
loop
return r
endloop
return 0
endfunction
function cleanInt takes integer i returns integer
return i
endfunction
function mR2I takes real r returns integer
return cleanInt( realToIndex( r ) )
endfunction
Далле, делаем вот так:
function TestFunc takes real r returns nothing
call BJDebugMsg(R2S(r))
endfunction
call TimerStart( CreateTimer( ), 0.50, true, SetCodeOneArg( function TestFunc, REAL, mR2I( 3,142857142857143 ) ) )
И спокойно передаем вещественные значения в аргумент.
Ну и последний тип, string - строки не такие уж простые обьекты как кажутся, это своего рода хендлы, ссылки в таблице строк, чтобы передать в аргумент строку нам потребуется функция из мемхака SH2I, которая находится в библиотеке Typecast, собственно пример кода ниже:
Ну и последний тип, string - строки не такие уж простые обьекты как кажутся, это своего рода хендлы, ссылки в таблице строк, чтобы передать в аргумент строку нам потребуется функция из мемхака SH2I, которая находится в библиотеке Typecast, собственно пример кода ниже:
function TestFunc takes string msg returns nothing
call BJDebugMsg(msg)
endfunction
...
call TimerStart( CreateTimer( ), 0.50, true, SetCodeOneArg( function TestFunc, STRI, SH2I("Ура, в попе дыра!") ) )
...
Так же в библиотеке имеется возможность передачи в функцию сразу двух аргументов, SetCodeTwoArg - code функция в которую хотим передать аргументы, тип аргумента1, аргумент1, тип аргумента 2, аргумент2.
Так же не забываем что у функции в которую мы хотим передать эти 2 аргумента, должно быть объявлено 2 аргумента соответствующего типа, ни больше ни меньше.
Так же не забываем что у функции в которую мы хотим передать эти 2 аргумента, должно быть объявлено 2 аргумента соответствующего типа, ни больше ни меньше.
function TestFunc takes integer a, trigger trig returns nothing
if a > 5 then
call TriggerEvaluate( trig )
endif
endfunction
...
call TimerStart( CreateTimer( ), 0.50, true, SetCodeTwoArg( function TestFunc, INTG, 10, HAND, GetHandleId( GetTriggeringTrigger( ) ) ) )
...
Всем спасибо за внимание!
Отдельное спасибо IseFog за посильную помощь.
Отдельное спасибо IseFog за посильную помощь.
Также, в принципе, очень юзабельно в узких местах (вроде передачи аргумента при использовании одного глобального таймера)
Еще не осознал есть ли в этом смыл, но геморно ли менять аргумент не перезапуская таймер?
Этот код позволяет сравнительно просто передать аргумент в функцию таймера, чтобы не трахать себе мозг хештаблицами и структурами там, где это особо не надо. Так же это снижает кол-во писанины в разы.
Аргумент функции по сути локалка, так что пробуйте, мб пригодится.
Пока я писал ИИ меня осинило, как же надоедать вызывать всякие отсроченные дейсвтия, вроде приказал кастануть абилку и ждешь, докастует или нет, аттачить к таймеру каждый раз юнита.... гемор, при этом подобных задач куча, а просто как в гуях - указал юнита и запустил таймер, все...
Структуры не всегда годятся, плодить овер 100500 тоже не торт, хт - много лишней писанины.