
Lunatic -- Lua for EDuke32 [preview introduction] "The moon has come around!"
#31 Posted 23 August 2013 - 09:49 AM
The Lunatic User Manual has been extended with an appendix containing hints on how to read first edition of Programming in Lua (targeting Lua 5.0). It's recommended to read the relevant parts as a first step to get acquainted with the base language.
#32 Posted 23 August 2013 - 09:50 AM
M210, on 22 August 2013 - 08:40 PM, said:
gameevent{ "DISPLAYWEAPON", function() gv.g_RETURN = -1 end }
This code works fine for this
It's not about whether it works or not, but that you are only supposed to use features considered finalized, and accordingly, documented in the Manual. Undocumented features are in no way guaranteed to stay. In fact, the new build changes the name of the variable to "gv.RETURN". But I also could have eliminated it completely and replaced it with a different mechanism -- like really returning a value from the event callback. I decided to keep it though, because the alternative approach has its problems too (e.g. how would you return a value from chained callbacks?).
Quote
There's no way to access it in this pseudo-member form in Lunatic. (I see it the positive way -- it removes the possibility of confusion if you name your variable "cstat", for example.) You have to create a per-actor variable and index that one.
Note that whether a variable gets stored with savegames is a separate issue in Lunatic. To make them do that (becoming game variables), you put them between the opening "module(...)" and the closing "require('end_gamevars')", as described in the modules subsection. It may be lacking examples, but the demo Lua files like helixspawner.lua demonstrate this. For instance, you definitely do not want to store variables whose values are never changed after initialization in savegames.
#33 Posted 23 August 2013 - 01:18 PM
Helixhorned, on 23 August 2013 - 09:50 AM, said:
Like this?
module(...) local se_xvel = con.actorvar(0) local se_yvel = con.actorvar(0) local se_zvel = con.actorvar(0) local se_lotag = con.actorvar(0) local se_hitag = con.actorvar(0) local se_pal = con.actorvar(0) local spr_sync = con.actorvar(0) local sectordist = con.actorvar(0) local sectorspeed = con.actorvar(0) require("end_gamevars")
It seems to be working, But I don't understand what is"defaultval" and why it's always equal 0. Maybe I can unterstand it when I read user manual
And at future - I want to see a "numsprites" variable in lunacon too :P
#34 Posted 24 August 2013 - 04:49 AM
I tried set cstat to sprite from 257 to 1 and this command was igrored.
if(bit.band(sprite[i].cstat, 256) ~= 0) then sprite[i].cstat = bit.bxor(sprite[i].cstat, 256) end
if sprite has cstat = 256, then it's all right - returns 0,
but if it has cstat = 257 then it returns 257.
And if I trying set cstat to 1 ( sprite[i].cstat = 1), when sprite has cstat 257, command was ignored
This post has been edited by M210: 24 August 2013 - 04:54 AM
#35 Posted 24 August 2013 - 04:56 AM
This post has been edited by M210: 24 August 2013 - 06:58 AM
#36 Posted 24 August 2013 - 05:10 AM
M210, on 23 August 2013 - 01:18 PM, said:
module(...) local se_xvel = con.actorvar(0) [...] require("end_gamevars")
Yes, that's precisely how you register variables to be stored with savegames. But the important question is, do they need to? This is only the case if they in a sense represent persistent state and need to be restored across savegame reloads. For example:
- Variables used only temporarily absolutely do not need to be gamevars. Usually they shouldn't even be per-actor variables, but simply scalars retrieved and used in a limited scope. So if there's a variable named "temp" being registered as gamevar in Lunatic (or even is introduced at file scope), that's clearly a sign of inappropriate CON mentality one needs to unlearn.
- Variables whose values are initialized once to a value independent of the game state, as noted earlier, don't need to be gamevars either.
I'm not suggesting that you're doing something wrong, after all only you know their usage. But just giving general advice -- you want to have only as many gamevars as necessary, not more.
Quote
The <defaultval> is just the same what you'd provide as the first argument after the name in CON gamevar. It denotes the value that is retrieved from the per-actor variable when indexing a position that has not been previously assigned to.
Quote
That can be done, but out of curiosity -- do you folks need it? In all the CON code I'm batch-testing, I didn't encouter it once (else it would already have been included). I guess the canonical use case is controlling the "level of detail" of some effect conditional on how many sprites are in the world, but this seems rather brittle.
#37 Posted 24 August 2013 - 06:21 AM
M210, on 24 August 2013 - 04:49 AM, said:
I tried set cstat to sprite from 257 to 1 and this command was igrored.
if(bit.band(sprite[i].cstat, 256) ~= 0) then sprite[i].cstat = bit.bxor(sprite[i].cstat, 256) end
if sprite has cstat = 256, then it's all right - returns 0,
but if it has cstat = 257 then it returns 257.
And if I trying set cstat to 1 ( sprite[i].cstat = 1), when sprite has cstat 257, command was ignored
If you're clearing bit 256 and examining it later, it could have been set again by some "hard-wired" C code or from CON. Can you post the example if it's not too long? If you're clearing the bit and a read following immediately afterwards produces a value that has it set, it's definitely a bug.
By the way: you don't need to write the clumsy "if ... then" for such bit twiddling in Lunatic. For members that are bitfields, some sugar is provided: your line reduces to
sprite[i].cstatbits:clear(256)
So, "cstat" and "cstatbits" mean the same location, but "cstat" is the raw number while the latter is a name providing convenience bitwise methods.
#38 Posted 24 August 2013 - 07:30 AM
Quote
There are not a temporarily variables. For example, I saving xvel variable separately because I had a lot of bugs with changing xvel during the game.
more specifically I use xvel as a home address in the city and it's doesn't must changing throughout game. Sometimes I had a bug when xvel addresses was changing after being hit sprites by the radiusexplosion and "I can't to sent a letter to such houses with changing addresses" I hope you understood what I mean:)
Also I have to expand the number of variables defined for the interactive elements in the editor...for example my slidedoors have such variables as
sector speed, sector distance, sound of moving to one side, sound of movign to other side, sound of closing door on one side, sound of closing door on other side, autoclose variable....at my new system of moving with acceleration, my slide doors also must have accleration, full-speed variable, deceleration, and times of acceleration, speed, decelerations. This variables must be at such of my interactive elements (my SECTOREFFECTOR with lotag 64) and all these variables differ. A lot of these variables I set in editor.
// this is the part of my sectoreffector code in EVENT_LOADACTOR ifvare lotag 64 // slider/rotator SE { setvar spriteid 0 setvar rot_varmax 0 whilevarvarn spriteid NUMACTORS // THIS IS A ANSWER TO OTHER YOUR QUESTION - NUMSPRITES NEED TO ME FOR REDUCTION OF THE CYCLES { getactor[spriteid].picnum picnum ifvare picnum GPSPEED { getactor[THISACTOR].xvel rx_can // if xvel of GPSPEED equal xvel of SE (address) then get lotag and hitag of //GPSPEED and set these variables as sector speed and sector distance getactor[spriteid].xvel tx_can ifvarvare rx_can tx_can { getactor[spriteid].lotag lotag getactor[spriteid].hitag hitag ifvare lotag 0 setvar sectordist 256 else setvarvar sectordist lotag ifvare hitag 0 setvar sectorspeed 16 else setvarvar sectorspeed hitag } //BTW GPSPEED is killing after load actors, therefore I cant using "headspritesect spriteid sectorid" -- "nextspritesect spriteid spriteid" cycles } else ifvare picnum MUSICANDSFX // if addresses is found, get lotag's and hitag's of MUSICANDSFX and set these to sounds for sector moving { getactor[spriteid].xvel rx_can getactor[THISACTOR].xvel tx_can getactor[spriteid].zvel zvel ifvarvare tx_can rx_can { ifvare zvel 0 { getactor[spriteid].lotag lotag setvarvar doorsound_open lotag getactor[spriteid].hitag hitag setvarvar doorsound_close hitag } ifvare zvel 1 { setvar se64_msf 1 getactor[spriteid].lotag lotag setvarvar doorsound_open2 lotag getactor[spriteid].hitag hitag setvarvar doorsound_close2 hitag } } } [ ... ], etc..
Quote
Thanks for this :P
This post has been edited by M210: 24 August 2013 - 08:11 AM
#39 Posted 24 August 2013 - 07:47 AM
sprite[i].cstatbits:clear(256)works as my
if(bit.band(sprite[i].cstat, 256) ~= 0) then sprite[i].cstat = bit.bxor(sprite[i].cstat, 256) end
if sprite have cstat 258 or else, both of function returns cstat 2 and if cstat of sprite is 257, both of function returns cstat 257.
I don't know, but cstat 1 automaticly changing to 257 always
#40 Posted 24 August 2013 - 09:40 AM
M210, on 24 August 2013 - 07:30 AM, said:
more specifically I use xvel as a home address in the city and it's doesn't must changing throughout game.
So you back up .xvel for your own use, right? Then why not call the variable "homeaddr" or something? People reading your code will thank you for not having to infer its meaning by digging into it (well, at least they would have a hint).
Quote
sector speed, sector distance, sound of moving to one side, sound of movign to other side, sound of closing door on one side, sound of closing door on other side, autoclose variable....at my new system of moving with acceleration, my slide doors also must have accleration, full-speed variable, deceleration, and times of acceleration, speed, decelerations. This variables must be at such of my interactive elements (my SECTOREFFECTOR with lotag 64) and all these variables differ. A lot of these variables I set in editor.
This sounds like those variables are effectively constant after their first assignment. It may not be necessary for them to be gamevars, but maybe the logic is easier that way, in which case it's OK. What I said was just a general guideline... if you make a variable a gamevar that doesn't need it, nothing bad will happen, it's just redundant. (Btw, note that it's invalid to access game structures from file scope, so any initialization of such "effector members" has to happen from some event.)
I only took a short glance at the code, but
Quote
whilevarvarn spriteid NUMACTORS // THIS IS A ANSWER TO OTHER YOUR QUESTION - NUMSPRITES NEED TO ME FOR REDUCTION OF THE CYCLES
What's the point? That event is run once at some kind of initialization. Any attempt at "speeding it up" is wasted effort, unless you are really feeling a slowdown. Besides -- in CON, just break when encountering the first sprite with a .statnum of MAXSTATUS (i.e. it's not in the game world).
Quote
//BTW GPSPEED is killing after load actors, therefore I cant using "headspritesect spriteid sectorid" -- "nextspritesect spriteid spriteid" cycles
I don't think that the fact that GPSPEED sprites are killed after level initialization prevents you from looping the "headspritesect" way, but see above.
#41 Posted 24 August 2013 - 09:42 AM
M210, on 24 August 2013 - 07:47 AM, said:
sprite[i].cstatbits:clear(256)works as my
if(bit.band(sprite[i].cstat, 256) ~= 0) then sprite[i].cstat = bit.bxor(sprite[i].cstat, 256) end
if sprite have cstat 258 or else, both of function returns cstat 2 and if cstat of sprite is 257, both of function returns cstat 257.
I don't know, but cstat 1 automaticly changing to 257 always
Reread what I said. Yes, these are equivalent, but the resetting is not Lunatic's fault (unless proven otherwise by reading the value immediately after the assignment and discovering this behavior). It's just that hard-wired code, either C or CON, interferes with your assignment.
edit: of course, CON code isn't "hard-wired". What I meant was rather code you didn't write yourself, like the original GAME.CON you're likely including in one form or another.
#42 Posted 24 August 2013 - 11:36 AM
Quote
After level initialization GPSPEED changing statnum to 1024, variables like xvel yvel zvel can be read at this case, but headspritesect doesn't works for it, because GPSPEED haven't been a valid sector after changed statnum.
Quote
I don't know other ways to found addressed sprites for my SE. When SE is loaded, I start a cycle to all other sprites for finding links xvel - xvel (btw, when I said home address for easy to understand. In fact it's an analogue of Blood RX-TX channels for communicate information about events RX is xvel, TX - yvel)
So, if your question about NUMACTORS, as I said, such cycles works faster than whilevarvarn spriteid 16384 about once every 5-10 times.
NUMACTORS is my variable, which I made by formula:
//from EVENT_LOADACTOR ifvare NUMACTORS 0 setvarvar NUMACTORS Numsprites //set NUMACTOR == Numsprites, because Numsprite may decrease after deleting sprites like GPSPEED, // and whilevarn can't find such GPSPEED's ifvarvarg Numsprites NUMACTORS setvarvar NUMACTORS Numsprites //NUMACTOR correction //from EVENT_SPAWN ifvarvarg THISACTOR NUMACTORS setvarvar NUMACTORS THISACTOR //during level loading there are added and removed sprites, therefore numactors need correct too.
Quote
I don't know other ways, therefore I gave an example of my code. Maybe you can give me some ideas
#43 Posted 25 August 2013 - 04:54 AM
M210, on 24 August 2013 - 11:36 AM, said:
OK, so because the GPSPEED are deleted after level init, you need to back them up. But it doesn't make sense to access sprites not in the game world afterwards, because they may have been overwritten by other inserted sprites. That's why the Lunatic manual reads: "The sprite composite is accessible with indices from 0 to gv.MAXSPRITES-1, but accesses to sprites that do not exist in the game world have no meaning." What you should aim for instead is a mapping of sector numbers to their corresponding former GPSPRITE data; these accesses are then in constant time for a given sector.
Quote
I think it's reasonable to store a chain of links using a linked list constructed once. In CON, they could be implemented in an analogous fashion to the {head,next}sprite{sect,stat} ones.
Quote
I admit that I misunderstood you on the first read. When there's talk of "cycles", I usually imagine CPU cycles, where you meant loop iterations. I think it's sensible to expose Numsprites because if I keep the restriction of disallowing access to void sprites completely, it would be the only way to loop over all sprites present at map load time [edit: without resorting to a double-loop like "for i=0,gv.numsectors-1 for j in spritesofsect(i)"]. (Maybe I should relax the restriction and write something like "for sprites not in the game world, the only allowed operation is reading its .statnum".)
Quote
Yeah, I've been pretty vague. Hopefully I'll come up with concrete examples someday.
#44 Posted 25 August 2013 - 09:00 AM
Quote
Yep, I will wait you :P) Big thanks!
And what's about getangle operate?
I tried this but it doesn't works correctly.
local dx = wall[se_wallptr].x - spr.x local dy = wall[se_wallptr].y - spr.y local se_ang = math.atan2 (dx, dy) --getangle se_ang = se_ang * (180/math.pi) - 90 se_ang = 2048 * se_ang / 360 if(se_ang < 0) then se_ang = se_ang + 1024 end print(se_ang)
I found lua function like getangle at control.lua in eduke32 source code, but this function requires FFI.lua (or maybe FFI.c?) and I can't find this library, and btw I don't want using oher external files for my mod.
May be I can recalculate atan operate for working with duke angles?
#45 Posted 29 August 2013 - 03:57 AM
M210, on 25 August 2013 - 09:00 AM, said:
Build's getangle() is there, but undocumented and as the following timing results show, slower than a similar function implemented using Lua's math.atan2(). Use this function:
local A2B = 1024/math.pi local function ourgetang(x, y) local ang = A2B*math.atan2(y, x) -- note the swapped args return bit.band(math.floor(ang), 2047) end
Results:
---------- getangle test 10, 100: getangle: 480, math.atan2: 479 10,-100: getangle:1569, math.atan2:1568 -10,-100: getangle:1504, math.atan2:1503 -10, 100: getangle: 545, math.atan2: 544 0, 0: getangle: 0, math.atan2: 0 Time for 100000 runs: getangle: 2.289 ms, math.atan2: 0.156 ms ----------
#46 Posted 30 August 2013 - 10:50 AM
So, here's a modified function:
local function ourgetang(x, y) local ang = (1024/math.pi)*math.atan2(y, x) -- note the swapped args return bit.band(math.ceil(ang), 2047) end
Note how I put the calculation of the constant factor into the function -- the run time stays the same, because supposedly LuaJIT constant-folds that expression. (To be exact, it seems that it either infers or assumes that the rounding mode stays the same in the test loop.) I have to say that I really love LuaJIT's ability to optimize code for which you might be tempted to do premature manual optimization by e.g. pulling subexpressions out, etc.
Here's are results:
---------- getangle test x, y: getangle(x, y) | math.atan2/ceil | math.atan2/floor 10, 100: 480, 480, 479 10,-100: 1569, 1569, 1568 -10,-100: 1504, 1504, 1503 -10, 100: 545, 545, 544 0, 0: 0, 0, 0 1, 0: 0, 0, 0 0, 1: 512, 512, 512 -1, 0: 1024, 1024, 1024 0, -1: 1536, 1536, 1536 Time for 100000 runs: getangle: 2.219 ms, math.atan2: 4.924 ms
So, getangle() is the winner as long as you only have integer inputs. That function is available by the name "gv.getangle" (no coincidence, I'll explain the gv part one day). One thing to note though is that because the inputs are integers, the rules and restrictions of converting Lua numbers to them apply.
Edit: then again, what I did (running only the functions in a loop) is pretty synthetic and may not indicate how well they perform in realistic code. So, this not-so-large factor shouldn't be reason to strongly prefer one version over the other. Specifically, the Lua version may be faster in case the containing loop is not JIT-compiled. There was some discussion of the LuaJIT list about how the FFI is relatively slow when this happens. This is also likely the reason for some cases of what you might call performance regressions against the C-CON interpreter. (Yes, there are some, but so far they were not of practical relevance and besides only did a service by exposing cases of qudratic run time.)
#47 Posted 30 August 2013 - 02:14 PM
Sometimes I kinda miss having to develop things on really low spec hardware--I was still developing EDuke32 on an Athlon XP up until 2010, and that thing was slow enough that a bunch of optimizations to the CON stuff could be measured by just looking at the fps counter versus actually timing things. I still put high value into such hand optimization because I do not believe machine inflection will ever truly outpace human intention... see the performance of AMD's modern processors designed by a machine versus what Intel is doing or even what AMD did during the Athlon and Athlon64 era for a particularly glaring example. You will always get better performance from doing things by hand, but I do acknowledge that such performance might not be enough better to make it worth the investment of time to actually do it.
#48 Posted 30 August 2013 - 02:59 PM
TerminX, on 30 August 2013 - 02:14 PM, said:
That's been mathematically proven, I believe.
#49 Posted 31 August 2013 - 11:30 AM
For activate moving sector you must "jump" :lol: Buttons not coded yet (and btw, I need hitscan by player for it, I have a problems with player.horiz) And I don't know, how play sounds at moving via lua. also I need function like sectorinterpolation
and maybe necessary to make autostart lua code, if it named as eduke.lua?
And moreelse question: Can I create a file with define variables?
I want to have files like game.con, defs.con, etc. BloodTC have a lot of files with different functions. I tried request("name"), loadfile("name.lua") but it have a errors
Attached File(s)
-
interact.rar (2.44K)
Number of downloads: 124
This post has been edited by M210: 31 August 2013 - 11:52 AM
#50 Posted 02 September 2013 - 06:42 AM
TerminX, on 30 August 2013 - 02:14 PM, said:
The major one I encountered with the starting map of WGR2, where you're in that spaceship and a bunch of stars get spawned and deleted continuously. With optimized builds, C-CON settles at about 3 ms when there's dynamic equilibrium, whereas LunaCON runs at about 4 ms for each actor movement loop, as read off the DNCOORDS timer.
However -- there's a hidden quadratic run time there, noticeable when the time is plotted/eyeballed against the actor count. The stars are moved with both wall and sprite collision, and the latter takes its toll.
(When I introduced SFLAG_NOCLIP, I made the affected sprites not clip against anything, but here we need the wall collision, so it's out of the question. Also, now I think that it would be better off as actor[].movflags bit -- that is, the one set with AIs, not actor[].movflag, the result of A_MoveSprite()).
My current understanding of what is happening here has two parts: first and likely more substantially, I think that the bytecode of these actors doesn't get compiled, at least not fully. This can be examined by looking at the IR/mcode dumps when enabling the relevant _DEBUG_LUNATIC flag in defs_common.lua. Second, Mike has been saying that calling into Lua from C repeatedly (lua_pcall()) is much slower than mass-calling a C function from Lua via the FFI. So, this might be mitigated by pulling the actor movement loop to Lua too, but in this case, I think that's not the reason for the slowdown -- I did just that in uncommitted code and noticed no improvement.
Quote
On one hand, I think there's a place for this approach to optimization because it relates directly to percieved, "total" performance. But on the other hand, timing seems more objective and less prone to be influenced by unrelated variables (in the statistical sense).
Quote
I wouldn't call anything at hand to be "designed by machines" though. Much the opposite, in fact -- the core of the LuaJIT interpreter is written in assembly (or rather, assemblies) and surely you're not objecting to the concept of compilation of higher-level languages into machine code, be it offline or dynamically. I definitely agree that there's probably performance to be gained by manual inspection -- but in this case, this simply means looking at the JIT dumps and figuring out how to coerce LuaJIT into compiling stuff.
#51 Posted 02 September 2013 - 06:58 AM
Helixhorned, on 02 September 2013 - 06:42 AM, said:
I was actually just referring to manual optimization vs the widespread misconception that whatever optimizations a compiler provides are "good enough," not anything relating to Lunatic or LuaJIT in particular.
#52 Posted 09 September 2013 - 07:13 AM
M210, on 31 August 2013 - 11:30 AM, said:
I took a look at your code, there are various points to complain about. Would you consider maintaining your project under revision control? On one hand, it would be easier to communicate improvements directly, but OTOH that might make me less inclined to discuss them here. Anyway, here goes...
Starting with how you coded the "jump" trigger: player[]._input and player._INPUT_BITS are internal, undocumented API and thus must not be used by scripting code. I know you took it over from test.lua, but earlier I warned that everyone looking into that file has to make sure that only documented interfaces are used. Internal interfaces are subject to change and cannot be relied on staying in future versions. The fix is easy: just use EVENT_JUMP, like this:
gameevent{ "JUMP", function() for i in spritesofstat(actor.STAT.EFFECTOR) do -- NOTE: don't code these loops by hand if (sprite[i].xvel == 10) then active[i] = 1 -- should be a boolean instead, see below end end end }
The original piece of code further had these issues:
- Accesses to sprites not in the game world are not allowed. I understood what you meant by your earlier complaints about GPSPEED deletion -- they're now deleted after all EVENT_LOADACTOR events have run (r4045), not after each one. Btw, a "strict" mode has been added [1], enabled by -Lopts=strict at the command line, which is recommended for development. After all, the wording in the Lunatic Manual allows the implementation to e.g. error on such accesses, but it's not done for reasons of performance and minimalism.
- Lua comes with a boolean type, so you don't need the C-like encoding of "true" as 1 and "false" as 0. Simply use the true and false literals directly. This makes your intention more explicit and allows you to write more obvious code. For example, the unsightly
if (moving_var[i] == 0) then moving_var[i] = 1 else moving_var[i] = 0 end
reduces to
moving_var[i] = not moving_var[i]
- While indentation is mostly properly done, sometimes you don't indent an "if" or "while" block. I think indentation should always carried out consistently.
- Note the convenient iterators (currently, spritesofstat, spritesofsect, wallsofsect, player.all, sprite.all [1]) provided by Lunatic. Manually coding these loops is a waste of at least two lines and may be incorrect (such as "for i=0,gv.MAXSPRITES-1 ...").
[1] No synthesis build has been produced since I added these.
Here's another improved snippet of code.
-- сохранение геометрии движущихся секторов -- (Saving of moving sector geometry) for s=0,gv.numsectors-1 do local sec = sector[s] if (spr.hitag == sec.hitag) then for w in wallsofsect(s) do -- NOTE: There's no arithmetic defined between wall[] -- and sprite[] objects defined, but we can make a -- 'xmath.ivec3' out of the latter. That one will -- operate with the left-hand side. local dvec = wall[w] - spr^0 -- The above could also e.g. be --local dvec = wall[w] - xmath.vec3(spr) -- (or ivec3, doesn't matter that much here.) -- NOTE: These two should be gamevars, too. LOADACTOR -- events are NOT run on savegame restoration! SE_PNDIST[w] = dvec:len2() -- distance SE_PNANG[w] = gv.getangle(dvec.x, dvec.y) -- angle end end end
To note here:
- Lunatic allows writing concise code by using vectorial values instead of writing out calculations with the individual components. Big plus for readability, IMO. (And in loops like these, I'm pretty sure LuaJIT's allocation sinking optimization makes a pretty good job. OK, in this loop, being run at initialization, it doesn't really matter.)
- The two SE_ variables must be gamevars, for the reason noted.
- In another place, you're "require"ing 'xmath' in a loop. Why? Just cache it into a local in the import section, before the "module(...)".
- Certain often-used operations such as taking the Euclidean distance of a vector are provided by Lunatic.
Miscellaneous:
- There are some instances of redundant checks and repeated code:
[REMOVED, I misread the code]
se_moving[i] = 1 active[i] = 0 -- I'd find it prettier... else active[i] = 0 -- ...to move these... end -- ... here.
- In this snippet,
local se_spr = sprite[j] if(se_spr.picnum ~= NAME.SECTOREFFECTOR) then if(spr.owner > 0 and spr.owner == se_spr.owner and spr ~= se_spr) then
there's a comparison of sprite[] objects. The equality and not-equal operations are not defined for for these objects in the Manual, although the expected effect ensues (effectively comparison of the sprite indices, which it should be rewritten into).
By the way: a generic, nestable movement system (e.g. ship containing swinging doors) has been on my list for a while too, and was partially also the driving force to develop something more adequate than CON (a not-so-readable prototype CON version exists). This bias is somewhat reflected in the fact that engine-side stuff is maybe a bit more documented than the game side.
Quote
and maybe necessary to make autostart lua code, if it named as eduke.lua?
The "hitscan" engine function is there, but currently not documented. Sometimes I'm getting stuck on details, like whether it would be better to accept the "shooting ray" as one vector, or the individual components. What problem with player[].horiz?
As far as sounds, interpolation, and pretty much everything else is concerned: in principle, everything provided by CON is there in the LunaCON internal layer. Of course, you're not allowed to use it in Lua code: its goal is to improve these interfaces. An example: CON commands often suffer from combination bloat: shoot, zshoot, eshoot, ..., plus the *var versions: ugh! A hypothetical Lua interface (not code) simply looks like this:
-- <spritenum>: the sprite number of the newly spawned object, or nil spritenum = con.shoot(parentspritenum, tilenum [, addtoqueue])
So, you have all combinations wrapped up in a single function. Isn't that a lot prettier?
What I'm getting at -- you (that is, everyone) can be involved in the development process by suggesting interfaces or certain aspects of the design. Say, you need sounds, so you'd just throw me a line like
"Helix, what do you think of the following sound interface: sound and music-related functions live in the 'sound' module, containing these functions: sound.play(aci, sndnum [, once]), sound.stop(aci, sndnum), sound.gplay(sndnum), ...". (Hm, in fact this doesn't look that bad.) Without some external pushing, I tend spend way too much time on details. It helps if you can read the EDuke32 source code and have a feeling for where inconsistencies are lurking.
Quote
I want to have files like game.con, defs.con, etc. BloodTC have a lot of files with different functions. I tried request("name"), loadfile("name.lua") but it have a errors
You're free to origanize modules in any way you wish, and in fact sensible decomposition is encouraged. In Lua, a module usually returns a table -- either implicitly because of a "module(...)" at file scope (the table will be the global environment of the module), or directly using return from the module. I added some examples to the Manual.
--
Some point to take home, because they can't be repeated too often:
- Scripting coders must make sure they're using only interfaces documented in the Manual
- If anything is not documented (be it a function or something already "well-known" as a particular structure member), ask me whether I consider it final first. If yes, either feel free to write the documentation, or wait until I do it.
- Anything not documented is subject to change without notice.
- If you need a particular feature (right now, meaning one in CON but not in Lunatic), you're encouraged to suggest an interface and discuss pros/cons -- always aiming at how it can be improved from CON.
#53 Posted 09 September 2013 - 09:26 AM
Quote
It will not be need for me at future. When I'll solve problems with hitscan, I'll can make a button, which will be activate other sprites by adress (xvel - yvel)
Quote
I worried about performance, and I think, if I will call "xmath" when it's necessary, it will take up less memory than if this lib will be enabled at game always?
Quote
I deleted code with hitscan and forgot this problem, but I remember, that I tried repeat example of con hitscan
getplayer[THISACTOR].horiz MY_ZDIST subvar MY_ZDIST 100 mulvar MY_ZDIST -2048
and this MY_ZDIST variable at lua returns a very big value and of cource, coordinates of crosshair and hitscan's not equal.
I can get you code for my hitscan later, after I write it again.
Quote
Yes, and I alot of time want to have change volume of sound :lol: Maybe sound.play(aci, sndnum, volume?)
use volume or maybe is better use radius of sound like ambient sounds (hitag for MSFX)
This post has been edited by M210: 09 September 2013 - 09:29 AM
#54 Posted 12 September 2013 - 08:39 PM
if(spr.picnum == 4362) then local pid = player[pi].i local pspr = sprite[pid] local aimv = 256*xmath.bangvec(pspr.ang) local zvel = ( player[pi].horiz - 100 ) * (-2048) local hit = hitscan(pspr, pspr.sectnum, aimv.x, aimv.y, zvel, gv.CLIPMASK1) print(hit.pos.x) print(hit.pos.y) sprite.changestat(i, 1) spr.x = hit.pos.x spr.y = hit.pos.y spr.z = hit.pos.z end
What's wrong with zvel variable?
This code I got from wiki http://wiki.eduke32.com/wiki/Hitscan
getplayer[THISACTOR].horiz MY_ZDIST subvar MY_ZDIST 100 mulvar MY_ZDIST -2048
mulvar to -2048 is too big for lua hitscan I think.
#55 Posted 22 September 2013 - 04:44 AM
M210, on 09 September 2013 - 09:26 AM, said:
The Lunatic libraries are always pre-loaded when a Lua state is created anyway. As a general recommendation, it's best not to second-guess particular non-functional behavior (such as performance or memory usage) but instead focus on witing clear and readable code. If you afterwards notice that the code is, say, too slow for your needs, then it's time to do some profiling and finding out why. Because... this is really the only way you can obtain actual empirical data about this behavior, and with time, you'll get a feeling for what really matters.
Now about hitscan(). There's nothing wrong with it at all, but the description on the wiki is misleading. You don't need to pass the cos and sin (times 16384) of the angle of the ray. Rather, these are simply directly the x and y components of the ray vector. This also applies to the z component, but with the caveat that it's in BUILD scaling (16 times the precision).
As an example, why is the factor 2048 used when passing the cos and sin? The horiz wiki entry mentions that a horiz difference of 128=27 corresponds to a tangent difference of unity. Thus we need a factor of 27 to reach 16384=214, but in addition, scale this by 16=24. So, the ultimate factor is 27*24 = 211 = 2048.
The Lunatic manual now documents hitscan(), as well as the convenience function kangvec(). Your example of a "crosshair" that exists as an actual sprite in the world can be written like this (from test.lua):
local ps = player[pli] local ray = xmath.kangvec(ps.ang, -(ps.horiz-100)*2048) local hit = hitscan(ps.pos, ps.cursectnum, ray, 0) if (hit.sector >= 0) then sprite[chair]:setpos(hit.pos) -- "chair" is the index of the crosshair sprite sprite.changesect(chair, hit.sector) end
(Note that I just renamed the 'sect' member of the hitscan return structure to 'sector', but the latest Lunatic synthesis build doesn't have this change yet...)
#56 Posted 26 December 2013 - 12:14 PM
As someone who's been working with Lunatic for over a year now, I must say that I'm really glad to see that it's progressing so well and I give serious props to Helix for finally giving some of us who would rather suffocate in a barrel of acid than use CON an adequate alternative.
#57 Posted 26 December 2013 - 12:50 PM
#58 Posted 26 December 2013 - 01:39 PM
Helixhorned, on 22 September 2013 - 04:44 AM, said:
To be more specific, for the hitscan command you put the difference beetween the x y z of your initial position to your final position (similar to getangle). Since the example in the wiki is using the player view (i.e. crosshair location) as a reference, its angle and horiz must be converted in x y z equivalents.
#59 Posted 26 December 2013 - 01:43 PM
James, on 26 December 2013 - 12:50 PM, said:
Lua is a very simple to learn language I think, if you're familiar with any other programming languages you should be able to pick it up fairly quickly.
#60 Posted 26 December 2013 - 01:56 PM
James, on 26 December 2013 - 12:50 PM, said:
I'm not past adding new CON commands, but they need a good purpose.