1-- test script for ELua/Lunatic Interpreter 2 3-- error=nil -- must not affect "require" 4local require = require 5local string = require("string") 6local bit = require("bit") 7local math = require("math") 8 9local pcall = pcall 10local DBG_ = require("_LUNATIC_DBG") 11 12local gv, sector, wall, sprite, spriteext = gv, sector, wall, sprite, spriteext 13local actor, player, projectile = actor, player, projectile 14local gameevent, gameactor = gameevent, gameactor 15 16local spritesofsect = spritesofsect 17local hitscan = hitscan 18 19local assert, error, print, tostring = assert, error, print, tostring 20 21local D = require("CON.DEFS") 22 23print('---=== ELua Test script ===---') 24 25local function printf(fmt, ...) 26 print(string.format(fmt, ...)) 27end 28 29local function checkfail(funcstr, expectedmsg) 30 if (DBG_ == nil) then 31 return 32 end 33 34 local status, errmsg = pcall(DBG_.loadstring(funcstr)) 35 if (status) then 36 print('^21ERROR:^O '..funcstr.." DIDN'T fail") 37 else 38 if (expectedmsg==nil or string.find(errmsg, expectedmsg, 1, true)) then 39 print("^11SUCCESS:^O "..funcstr.." failed: "..errmsg) 40 else 41 -- XXX: beginning with "^10" is counted as error in OSD_Printf() 42 print("^10ERROR*:^O "..funcstr.." failed: "..errmsg.. 43 ", but expected error message was: "..expectedmsg) 44 end 45 end 46end 47 48gameevent 49{ 50 "ENTERLEVEL", 51 52 function() 53 local vol, lev = gv.currentEpisode(), gv.currentLevel() 54 printf('volume=%d, level=%d', vol, lev) 55 56 if (vol ~= 4) then 57 -- Tweak some sector pals. 58 print('tweaking sector pals') 59 print('numsectors: ' .. gv.numsectors .. ' of ' .. gv.MAXSECTORS) 60 61 local SF = sector.STAT 62 for i = 0, gv.numsectors/2 do 63 local sec = sector[i] 64 sec.floorpal = 1; 65 sector[i].floor.shade = sec.floor.shade + 4 66 sector[i].ceilingpal = 2; 67 local ceil = sec.ceiling 68 ceil.shade = sector[i].ceiling.shade + 8 69 ceil.statbits:flip(SF.SMOOSH) 70 sec.floorstatbits:flip(SF.SWAPXY) 71 end 72 end 73 74 if (vol==1 and lev==1) then -- E1L1 75 print('tweaking some sprites 2') 76 local i = 562 77 spriteext[i].alpha = 0.5; 78 sprite[i].cstat = bit.bor(sprite[i].cstat, 2+512); 79 spriteext[i].pitch = 128; 80 spriteext[i].roll = 256; 81 82 i = 107 -- pistol ammo at rooftop 83 spriteext[i].pitch = 128; 84 spriteext[i].roll = 256; 85 86 for spr in spritesofsect(307) do -- some fence sprites in E1L1 87 printf('spr %d', spr) 88 sprite[spr].pal = 6 89 end 90 91 actor[562].flags = bit.bor(actor[562].flags, 2); -- pal 6 with goggles on front SEENINE 92 end 93---[[ 94 if (vol==1 and lev==8) then 95 local havebunch = false 96 for i=0,gv.numsectors-1 do 97 havebunch = havebunch or (sector[i].ceilingbunch >= 0) 98 end 99 if (havebunch) then 100 print('tweaking bunch 1'); 101 -- trueror1.map 102 for i, what in sectorsofbunch(1, gv.BOTH_CF) do 103 sector[i][what].z = sector[i][what].z - 3*1024; 104 end 105 end 106 end 107--]] 108 end 109} 110 111gameevent 112{ 113 "JUMP", 114 115 function() 116 print("tweaking forcefield with lotag 34 (E2L1)") 117 118 for w=0,gv.numwalls-1 do 119 local wal = wall[w] 120 if (wal.overpicnum == D.W_FORCEFIELD or wal.overpicnum == D.W_FORCEFIELD+1) then 121 if (wal.lotag==34) then 122 wal.cstat = wal.cstatbits:test(85) and 0 or 85 123 end 124 end 125 end 126 end 127} 128 129local unsafe = pcall(function() string.UNSAFE=true; end) 130 131checkfail("tostring = nil", "attempt to write into the global environment") 132--DBG_.printkv('_G in test.elua', _G) 133 134-- direct gv array access forbidden 135checkfail('gv.sprite[0].yrepeat = 100', "access forbidden") 136 137checkfail('print(sprite[100000].ceilingpal)', "out-of-bounds sprite[] read access") 138 139checkfail('print(gv.sprite[0])', "access forbidden") 140 141-- set metatable forbidden 142checkfail('setmetatable(sprite, {})', "attempt to read undeclared variable 'setmetatable'") 143 144gameevent 145{ 146 "ENTERLEVEL", 147 148 function() 149 -- OOB write access. 150 -- Note that indexing ("reading") sector fails, even if the user wants to 151 -- assign to a sector member. Potentially confusing error message. 152 checkfail('sector[-1].ceilingpal = 4', "out-of-bounds sector[] read access") 153 154 -- wallnum member is read-only 155 checkfail('sector[0].wallnum = 0', "attempt to write to constant location") -- this comes from LJ/FFI 156 157 -- direct sector write access forbidden 158 checkfail('sector[4] = sector[6]', "cannot write directly to sector[]") 159 160 -- creating new keys forbidden... handled by LuaJIT 161 checkfail('wall[4].QWE = 123', "has no member named 'QWE'") 162 163 -- no pointer arithmetic! 164 checkfail('local spr = sprite[0]; local x=spr+1', "attempt to perform arithmetic on") 165 166 -- actor[].t_data[] is not accessible for now 167 checkfail('local i = actor[0].t_data[15]', "has no member named 't_data'") 168 169 -- sprite.picnum may happen as a thinko/typo kind of error (spr.picnum was meant) 170 checkfail("local pic = sprite.picnum", "invalid access to static data") 171 172 checkfail("require('engine').setshadetab(200, nil)", 173 "setshadetab() may be run only while LUNATIC_FIRST_TIME is true") 174 175 checkfail("sprite[0]:set_picnum(-10)", "invalid tile number") 176 end 177} 178 179-- gv.numsectors is read-only 180checkfail('gv.numsectors = 4', "attempt to write to constant location") 181 182-- cannot create new fields in 'gv' 183checkfail('gv.QWE = 4', "write access forbidden") 184 185-- that would be horrible... 186checkfail('sprite._nextspritesect[4] = -666', "cannot write directly to nextspritesect[]") 187 188-- we're indexing a plain array! 189checkfail('print(sprite._nextspritesect[4].whatfield)', "attempt to index a number value") 190 191-- our 'require' has only safe stuff 192--checkfail("require('os')") 193 194-- gamevars are created using a special different mechanism 195checkfail("new_global = 345", "attempt to write into the global environment") 196-- Can't reassign to existing vars either 197assert(actor ~= nil) 198checkfail("actor = 345", "attempt to write into the global environment") 199 200-- can't redefine constants in 'gv' 201checkfail('gv.CEILING = 3', "attempt to write to constant location") 202 203-- string.dump is unavailable 204checkfail('local s=require[[string]]; local tmp=s.dump(gameevent)', 205 "attempt to call field 'dump' (a nil value)") 206 207if (not unsafe) then 208 -- changing base module tables is disallowed 209 checkfail('local s=require[[string]]; s.format=nil', "modifying base module table forbidden") 210else 211 print('WARNING: RUNNING WITH UNPROTECTED BASE MODULES') 212end 213 214print('') 215-- This is problematic, even though pretty much every access will yield a 216-- "missing declaration" error. 217-- See http://luajit.org/ext_ffi_api.html#ffi_C about what stuff ffi.C contains. 218checkfail('gv.luaJIT_setmode(nil, 0, 0)', "missing declaration for symbol 'luaJIT_setmode'") 219 220checkfail('gv.luaJIT_BC_con_lang', "attempt to call a nil value") 221checkfail('gv.gethiticks = nil', "attempt to write to constant location") 222 223checkfail('gameactor{1680, 0}', "must provide a function with last numeric arg or .func") 224 225checkfail("do local bt=require'test.test_bitar'; bt.QWE=1; end", "modifying module table forbidden") 226-- the cdata returned by player[] can't be made into a pointer! 227checkfail("do local pl=player[0]; i=pl[1]; end") 228checkfail("do local ud=gv.ud.camerasprite; end", "access forbidden") -- test for proper decl() 229checkfail("gv.g_sizes_of=nil; print(gv.g_sizes_of[0])", "write access forbidden") 230checkfail("gv.cam.sect=-1", "invalid sector number") 231checkfail("local flag=gv.SFLAG_NULL", "missing declaration") 232 233-- NOTE: player[0] is forbidden at file scope, this is just for testing purposes. 234player[0].wackedbyactor = -1 -- should succeed 235checkfail("player[0].curr_weapon = -1", "Invalid weapon ID") 236player[0].curr_weapon = 1 237checkfail("local w = player[0].weapon[-1]", "out-of-bounds weapon read access") 238 239-- XXX: This gives a very strange error message: "attempt to write to constant location". Why? 240-- (note how I forgot to index weapon with a weapon index or name) 241--player[0].weapon.firesound = 1e5 242checkfail("player[0].weapon.SHOTGUN.firesound = 1e5", "invalid sound number") 243checkfail("player[0].weapon.SHOTGUN.firesound = 0/0", "must be a non-NaN number") 244checkfail("player[0].weapon.SHOTGUN.firesound = 1/0", "invalid sound number") 245checkfail("gameactor{1680, action=require('con').action{numframes=-10}, function() end}", 246 "action has negative number of frames") 247-- NOTE: It should only be relied on that setting e.g. .firesound to -1 sets it 248-- to 0, not other negative values. 249player[0].weapon.SHOTGUN.firesound = -1/0 250assert(player[0].weapon.SHOTGUN.firesound == 0) 251 252gameevent{gv.EVENT_JUMP, 253 function(actori, playeri, dist) 254 printf("jump i=%d p=%d d=%d", actori, playeri, dist) 255 error("greetings from EVENT_JUMP") 256 end 257 } 258 259--[[ 260gameevent 261{ 262 "PROCESSINPUT", 263 264 -- Input test. 265 -- NOTE: I don't think that exposing g_player[].sync (aka "input") is a good idea... 266 func = function(actori, playeri, dist) 267 local IB = player._INPUT_BITS 268 local input = player[playeri]._input 269 if (bit.band(input.bits, IB.JUMP) ~= 0) then 270 print("JUMPED") 271 -- ... because for example this doesn't work 272 -- (P_HandleSharedKeys, where the JETPACK bit is tested, runs 273 -- before P_ProcessInput): 274 input.bits = bit.bor(input.bits, IB.JETPACK) 275 end 276 end 277} 278--]] 279 280local WEAPON = gv.WEAPON 281 282-- Bad hack to test out pitch/roll: EVENT_GAME is discouraged from Lunatic. 283local PITCH_PICNUM = { [D.SEENINE]=true, } 284gameevent{ "GAME", 285 function(aci) 286 local spr = sprite[aci] 287 if (PITCH_PICNUM[spr.picnum]) then 288 local height = spr:getheightofs() 289 290 local sexy = spriteext[aci] 291 sexy.pitch = gv.totalclock 292 sexy.mdoff.x = -height/16 -- test xoff + pitch 293 sexy.mdoff.z = -height -- test zoff + pitch 294 295 -- Test roll + yoff 296 sexy.roll = gv.totalclock 297 sexy.mdoff.y = -height/16 298 end 299 end 300} 301 302-- test event chaining 303gameevent 304{ 305 "JUMP", 306 307 flags = actor.FLAGS.chain_beg, 308 309 function(actori, playeri, dist) 310 local ps = player[playeri] 311 print("\n--- I'm first!") 312-- DBG_.oom() 313 local pistol = ps.weapon.PISTOL 314 if (pistol.shoots ~= D.RPG) then 315 pistol.shoots = D.RPG 316 else 317 pistol.shoots = D.SHOTSPARK1 318 end 319 ps.weapon[WEAPON.PISTOL].firesound = D.LIGHTNING_SLAP 320 321 -- This succeeds, because sound2time is a time, not a sound. 322 ps.weapon.SHOTGUN.sound2time = 5000 323 printf("set shotgun's sound2time to %d", ps.weapon.SHOTGUN.sound2time) 324 325 -- Set pipebomb and tripbomb to timed mode. 326 -- XXX: Provide either named constants or methods? 327 -- XXX: These are probably reset to default on new game. 328 ps.pipebombControl = 2 329 ps.tripbombControl = 2 330 331 -- Test of INTERNAL member _pals. 332 -- NOTE: setting colors partially is bad! E.g. after an item is 333 -- picked up, col[0] and col[1] remain and tint everything greenish. 334 if (DBG_ ~= nil) then 335 ps._pals[2] = 20 336 ps._pals.f = 30 337 end 338 end 339} 340 341local xmath = require "xmath" 342 343gameevent 344{ 345 gv.EVENT_ENTERLEVEL, 346 347 function() 348 if (DBG_ ~= nil) then 349 DBG_.testmembread() 350 end 351 352 -- NOTE: times are for helixhorned (Core2Duo 3GHz) 353 local i 354 local N = 1e6 355 local t = gv.gethiticks() 356 357 for i=3,N do 358 gv.gethiticks() 359 end 360 361 t = gv.gethiticks()-t 362 363 -- x86_64: 35ns/call, x86: 280 ns/call 364 -- Windows 32-bit: about 1 us/call? 365 printf("%d gethiticks() calls took %.03f ms (%.03f us/call)", 366 N, t, (t*1000)/N) 367 368 local sum=0 369 t = gv.gethiticks() 370 for i=1,N do sum = sum+gv.ksqrt(i) end 371 t = gv.gethiticks()-t 372 -- x86_64: 14ns/call 373 printf("%d ksqrt() calls took %.03f ms (%.03f us/call) [sum=%f]", 374 N, t, (t*1000)/N, sum) 375 376 sum=0 377 t = gv.gethiticks() 378 for i=1,N do sum = sum+math.sqrt(i) end 379 t = gv.gethiticks()-t 380 -- x86_64: 7ns/call 381 printf("%d math.sqrt() calls took %.03f ms (%.03f us/call) [sum=%f]", 382 N, t, (t*1000)/N, sum) 383 384 printf("sqrt(0xffffffff) = %f(ksqrt) %f(math.sqrt)", 385 gv.ksqrt(0xffffffff), math.sqrt(0xffffffff)) 386 387 local pl = player[0] 388 -- MAX < current is "allowed" 389 pl.max_ammo_amount[WEAPON.RPG] = 17 390 pl:give_weapon(WEAPON.RPG) 391 pl.ammo_amount[WEAPON.RPG] = 54 392 393 pl:give_weapon(WEAPON.SHRINKER) 394 -- This looks much prettier: 395 pl.ammo_amount.SHRINKER = 2 396 397 -- MORTER2 from test/weaponvars.con 398 local PNUM = 1653 399 local proj = projectile[PNUM] 400 if (proj ~= nil) then 401 printf('Have projectile %d', PNUM) 402 player[0].weapon.SHOTGUN.shoots = PNUM 403 proj.drop = 0 404 proj:set_trail(D.SMALLSMOKE) 405 else 406 printf('^10Do NOT have projectile %d, test/weaponvars.con not loaded?', PNUM) 407 end 408 409 if (gv._LUNATIC_STRICT == 0) then 410 t = gv.gethiticks() 411 local N=1 412 for n=1,N do 413 for i=0,gv.MAXSPRITES-1 do 414 sprite[i].blend = 1 415 end 416 for i=gv.MAXSPRITES-1,0,-1 do 417 sprite[i].shade = 1 418 end 419 for i=0,gv.MAXSPRITES-1 do 420 sprite[i].xoffset = 0 421 end 422 for i=gv.MAXSPRITES-1,0,-1 do 423 sprite[i].yoffset = 1 424 end 425 end 426 t = gv.gethiticks()-t 427 printf("%d x four 0..MAXSPRITES-1 iterations took %.03f us per outer iteration", N, (1000*t)/N) 428 -- Results on x86: 429 -- N=1: 480-1000 us (too large variance) 430 -- N=10: 190-210 us * 10 = 1.9-2.1 ms 431 -- N=100: about 160 us * 100 = about 16 ms 432 end 433 434 -- Make the DUKECAR in E1L1 into a zombie actor (temporarily) 435 -- NOTE: Use static value (not the one from 'D'). 436 if (sprite[24].picnum==2491) then 437 sprite.changestat(24, actor.STAT.ZOMBIEACTOR) 438 end 439 440 checkfail("gameevent('GAME', function() print('qwe') end)", 441 "must be called from top level") 442 443 -- Test vec3 + wall. Pseudo wall member 'z' will be accessed. 444 local mpos = xmath.vec3() 445 for i=0,gv.numwalls-1 do 446 mpos = mpos + wall[i] 447 end 448 mpos = mpos/gv.numwalls 449 local impos = xmath.ivec3(mpos)^20 -- test ivec3 with dvec3 arg, test '^' op 450 assert(impos.z == -20) 451 printf("Map center point: (%d,%f)", mpos.x, impos.y) 452 end 453} 454 455gameevent{"LOADACTOR", function(i) 456 local spr = sprite[i] 457 if (i==614 and spr.picnum==930) then 458 -- "police line" ribbon in E1L1 459 -- clear the hitag so that it doesn't spawn as FALLER from premap 460 -- Rather a HACK: relies on an implementation detail (A_Spawn() 461 -- "hard-wired" code). 462 spr.hitag = 0 463 end 464end} 465 466gameactor 467{ 468 -- "police line" ribbon 469 930, nil, 1, 470 471 func = function(i) 472 local spr = sprite[i] 473 local r = math.random 474 local d = 20 475 -- NOTE: __add metamethod is called because of the RHS: 476 local v = spr + xmath.vec3(r(-d,d), r(-d,d)) 477 spr:setpos(v):updatesect() 478 479 -- Test vec3 constructor with cdata. 480 local tempvec = xmath.vec3(player[0].pos) 481 end 482} 483 484local stat = require("stat") 485local hs = stat.new() 486 487local con = require("con") 488local AC, MV = con.AC, con.MV 489 490local CAC, CMV, CAI = require("CON.ACTION"), require("CON.MOVE"), require("CON.AI") 491assert(CAC); assert(CMV); assert(CAI) 492 493local AC, MV = {}, {} 494 495AC.TROOPSTAND = assert(CAC.ATROOPSTAND) -- or con.action{0,1,5,1,1} 496AC.TROOPFLINTCH = con.action{50, 1, 1, 1, 6} 497MV.SHRUNKVELS = con.move{hvel=32} 498con.ai(AC.TROOPFLINTCH, MV.SHRUNKVELS, 0) -- unused; TODO: test 499 500local TROOPSTRENGTH = 30 501 502local AF = actor.FLAGS 503local CS = sprite.CSTAT 504 505-- Crosshair sprite. 506-- NOTE: This ought to be a gamevar -- if a savegame is restored, a new one 507-- will be spawned. 508local chair 509 510gameactor{ D.APLAYER, AF.chain_end, 511 function(aci, pli) 512 if (chair == nil) then 513 chair = con.spawn(555, aci) 514 printf("Spawned our crosshair: sprite %d", chair) 515 local spr = sprite[chair] 516 -- Set to STAT_MISC because otherwise interpolation goes crazy (old 517 -- value never updated; dunno why...) 518 sprite.changestat(chair, actor.STAT.MISC) 519 spr.xrepeat, spr.yrepeat = 96, 96 520 spr.cstatbits:set(CS.CENTER) 521 end 522 523 local ps = player[pli] 524 local ray = xmath.kangvec(ps.ang, -(ps.horiz-100)*2048) 525 526 local hit = hitscan(ps.pos, ps.cursectnum, ray, 0) 527 if (hit.sector >= 0) then 528 sprite[chair]:setpos(hit.pos) 529 sprite.changesect(chair, hit.sector) 530 end 531 end 532} 533 534-- Add NODAMAGEPUSH flag to NEWBEAST. 535gameactor { D.NEWBEAST, AF.chain_end + AF.NODAMAGEPUSH, function() end } 536 537-- Also test actor code chaining: strength is doubled. 538gameactor 539{ 540 D.LIZTROOP, AF.chain_end+AF.enemy, 2*TROOPSTRENGTH, 541 542 action = AC.TROOPSTAND, 543 544 func = function(i, playeri, dist) 545 sprite[i].pal = math.random(32) 546-- sprite[i].ang = bit.band(sprite[i].ang-20, 2047) 547 548 local spr = sprite[i] 549 550 local t = gv.gethiticks() 551 local hit = hitscan(spr, spr.sectnum, {x=10, y=10, z=0}, gv.CLIPMASK0) 552 553 hs:add(1000*(gv.gethiticks()-t)) 554 555 if (hs.n == 300) then 556 printf("hitscan: %s", hs:getstatstr()) 557 hs:reset() 558 error("greetings from LIZTROOP actor") 559 end 560 561 local actr = actor[i] 562 if (actr:get_count() % 30 == 0) then 563 spr.cstatbits:flip(CS.YFLIP) 564 end 565 566 -- Test of bitint's ":test()" for actor[].flags. 567 actr.flagsbits:test(AF.NVG) 568 569 if (dist < 4096) then 570 -- Duke Vader / Anakin Nukewalker? 571 actor[i]:set_action(AC.TROOPFLINTCH) 572 actor[i]:set_move(MV.SHRUNKVELS) 573 574 if (dist < 1024) then 575 con.killit() 576 end 577 end 578 579 if (actr:has_action(CAC.ATROOPWALKING)) then 580 if (actr:get_count() % 50 == 0) then 581 actr.movflagsbits:flip(actor.MOVFLAGS.spin) 582 end 583 end 584 end, 585 586 -- NOTE: the animate callback is not yet documented and thus not official API! 587 animate = function(tspr) 588 local tspr2 = tspr:dup() 589 if (tspr2) then 590 tspr2.x = tspr2.x + 512*math.cos(gv.totalclock/60) 591 tspr2.y = tspr2.y + 512*math.sin(gv.totalclock/60) 592 tspr2.cstatbits:set(CS.TRANS_BITMASK) 593 end 594 595 -- XXX: inserted tsprites have floor shadow in classic! (r_shadow) 596 -- G_DoSpriteAnimations() is passed as callback to the engine on occasion, 597 -- in other words, created tsprites may be fed back to G_DoSpriteAnimations()! 598 -- classic: shows shadow for both "ghost" liztroop and aim "reticle" 599 -- Polymost: only for "ghost" 600 -- Polymer: none 601 local aimv = 256*xmath.bangvec(tspr.ang) 602 local hit = hitscan(tspr^(16*256), tspr.sectnum, aimv, gv.CLIPMASK1) 603 604 if (hit.wall >= 0) then 605 local aimtspr = tspr:dup() 606 if (aimtspr) then 607 aimtspr.pal = 2 608 aimtspr:set_picnum(555) 609 aimtspr:setpos(hit.pos, hit.sector) 610 end 611 end 612 end, 613} 614 615gameactor 616{ 617 1275, -- newspaper, E4L6 sprite #513 618 619 action = con.action{0, 4, delay=20}, -- Same as {0, 4, 1, 1, 20} 620 move = 1, -- so that the ID is nonzero and it will move 621 622 func = function(aci) 623 local a = actor[aci] 624 local delay = math.sin(0.1 * 2*math.pi*gv.totalclock/120) 625 a:set_action_delay(20 + 10*delay) 626 if (sprite[aci].pal ~= 0) then 627 a:set_hvel(1024/30) 628 a:set_vvel(-1024/30) 629 else 630 a:set_hvel(50*delay) 631 end 632 a.movflags = actor.MOVFLAGS.geth + actor.MOVFLAGS.getv 633 end, 634} 635 636gameevent 637{ 638 "DISPLAYROOMS", 639 640 function(aci, pli) 641 local ps = player[pli] 642 local cam = gv.cam 643 644 if (ps.newowner < 0) then 645 cam.pos.z = cam.pos.z + 64*16*math.sin(gv.totalclock/30) 646 end 647 648 if (ps.cursectnum >= 0) then 649 local hit = sector[ps.cursectnum]:zrangeat(cam.pos) 650 if (gv.totalclock%200==0) then 651 printf("hit %s %d at z=%d, %s %d at z=%d", 652 hit.c.spritep and "sprite" or "sector", hit.c.num, hit.c.z, 653 hit.f.spritep and "sprite" or "sector", hit.f.num, hit.f.z) 654 end 655 end 656 end 657} 658 659gameevent 660{ 661 "DISPLAYREST", 662 663 function() 664 for i=0,10 do 665 for j=1,100 do 666 -- XXX: This is slower in the Polymodes than with classic! 667-- con._gametext(2822, j, i, 12, 0, 0, 16, 0,0,gv.xdim,gv.ydim,8192) 668 end 669 end 670 671 -- Clear showing every sector with the pavement floor tile. (Unless we're 672 -- in such a sector or an adjoining one.) 673 -- XXX: We need a better place to do this, maybe an event in 674 -- G_DisplayRest() where show2dsector[] is tweaked? 675 -- NOT YET OFFICIAL API. 676 local show2dsector = sector.showbitmap 677 for i=0,gv.numsectors-1 do 678 if (sector[i].floorpicnum==815) then 679 show2dsector:set0(i) 680 end 681 end 682 end 683} 684 685gameactor 686{ 687 D.APLAYER, actor.FLAGS.chain_beg, 688 689 function(aci, pli) 690 if (con._squished(aci, pli)) then 691 printf("Squished LunaCON test") 692 end 693 end 694} 695 696gameactor 697{ 698 -- Innocent sign, similar to test/weaponvars.con actor 909 (tree trunk) 699 1211, actor.FLAGS.replace, 700 701 function(aci, pli) 702 local a = actor[aci] 703 704 if (a:get_count() >= 120) then 705 local i = con.spawn(D.TRANSPORTERSTAR, aci) 706 sprite[i].z = sprite[i].z - 1024*16 707 con.shoot(D.MORTER, aci, -4096) 708 a:set_count(0) 709 end 710 end 711} 712 713local function testbit(num, b) 714 return bit.band(num,b)~=0 and 1 or 0 715end 716 717local FADE_SPEED = { 718 [WEAPON.KNEE] = 1/2.5, 719 720 1/128, 721 1/5, 722 1/3, 723 1/2, 724 1, -- pipebomb: ~1 sec 725 2, 726 3, 727 5, 728 127, -- freezer; such a fast fade is not visible, but it clears any 729 -- existing one (if of higher priority) 730 [WEAPON.GROW] = 9.9, -- test banker's rounding -- should be like 10 731} 732 733-- Test player[]:fadecol(), a better palfrom. 734gameevent 735{ 736 "CHANGEWEAPON", 737 738 function (aci, pli) 739 local ps = player[pli] 740 local neww, curw = gv.RETURN, ps.curr_weapon 741 742 local r, g, b = testbit(neww, 1), testbit(neww, 2), testbit(neww, 4) 743 local speed = FADE_SPEED[neww] or 1 744 player[pli]:fadecol(0.5, r, g, b, speed, neww-5) 745 end 746} 747 748-- Time the above p:fadecol() test for verification of the <speed> argument. 749local last_f, last_t = 0, 0 750local last_secs = nil 751gameevent 752{ 753 "DISPLAYREST", 754 755 function(aci, pli) 756 local ps = player[pli] 757 -- WARNING: _pals in INTERNAL and off-limits to users! 758 local curf = ps._pals.f 759 if (curf > last_f) then 760 -- Starting a tint 761 last_secs = nil 762 last_f = curf 763 last_t = gv.getticks() 764 elseif (last_t > 0 and curf==0) then 765 -- Fade has ended 766 last_secs = (gv.getticks()-last_t)/1000 767 last_f, last_t = 0, 0 768 end 769 770 if (last_secs ~= nil) then 771 con.minitext(10, 10, string.format("Last fade time: %.03f s (%.03f gametics)", 772 last_secs, last_secs*30)) 773 end 774 end, 775} 776 777printf("EVENT_INIT = %d", gv.EVENT_INIT) -- tests default defines 778 779local bittest = require "test.test_bitar" 780bittest.sieve() 781 782require("test.test_geom", 123123) 783require("test.test_rotspr") 784 785do 786 -- Test ksin vs. sinb 787 local xmath = require "xmath" 788 local sum = 0 789 790 local N = 1000 791 local t = gv.gethiticks() 792 for i=0,N*2048-1 do 793 sum = sum+xmath.ksin(i) 794 end 795 t = gv.gethiticks()-t 796 sum = sum*1e12 797 printf("ksin: sum*1e12=%.03f, %.03fus per 0-2047 cycle", sum, t) 798 799 sum = 0 800 local t = gv.gethiticks() 801 for i=0,N*2048-1 do 802 sum = sum+xmath.sinb(i) 803 end 804 t = gv.gethiticks()-t 805 sum = sum*1e12 806 printf("sinb: sum*1e12=%.03f, %.03fus per 0-2047 cycle", sum, t) 807 808 -- Results (helixhorned x86): 809 -- ksin: sum*1e12=0.000, 3.801us per 0-2047 cycle 810 -- sinb: sum*1e12=0.052, 2.886us per 0-2047 cycle 811end 812 813do 814 -- Test getflorzofslopeptr()/sector[]:floorzat() 815 local N = 100 816 local t = gv.gethiticks() 817 818 for n=1,N do 819 for i=0,gv.numsectors-1 do 820 local sec = sector[i] 821 local w1 = sec.wallptr 822 sec:floorzat(wall[w1]) 823 end 824 end 825 826 printf("%d x %d floorzat: %.03f us", N, gv.numsectors, (gv.gethiticks()-t)*1000) 827 828 -- Results for 100 x 325 floorzat (helixhorned x86): 829 -- cdecl getflorzofslope(): 572.165 us 830 -- __fastcall getflorzofslope(): 830.147 us (sic, but tested only once!) 831end 832 833print('---=== END TEST SCRIPT ===---') 834 835--[[ 836function check_sector_idx() 837 error("bla") 838end 839spritesofsect(0) 840--]] 841 842--DBG_.oom() 843 844-- This will complain about wrong usage of 'error'. In particular, 845-- the nil must not propagate to C! 846checkfail('error(nil)', "error using 'error': error message must be a string") 847 848local D = require("CON.DEFS") 849checkfail('require("CON.DEFS").APLAYER=123', "modifying base module table forbidden") 850-- Test with lunatic/test/rotfixed_actor.con. 851print("DUKECAR="..tostring(D.DUKECAR)) 852 853do 854 print('---------- getangle test') 855 856 local function CreateGetAngFunc(roundfunc) 857 return function(x, y) 858 local ang = (1024/math.pi)*math.atan2(y, x) -- note the swapped args 859 return bit.band(roundfunc(ang), 2047) 860 end 861 end 862 863 local ourgetang = CreateGetAngFunc(math.ceil) 864 local ourgetangf = CreateGetAngFunc(math.floor) 865 866 local function printang(x, y) 867 printf('%4d,%4d: %13d, %16d, %16d', x, y, 868 gv.getangle(x, y), ourgetang(x, y), ourgetangf(x, y)) 869 end 870 871 print " x, y: getangle(x, y) | math.atan2/ceil | math.atan2/floor" 872 printang(10, 100) 873 printang(10, -100) 874 printang(-10, -100) 875 printang(-10, 100) 876 877 printang(0, 0) 878 879 printang(1, 0) 880 printang(0, 1) 881 printang(-1, 0) 882 printang(0, -1) 883 884 local N=1e5 885 local r = 0 886 887 local t = gv.gethiticks() 888 for i=1,N do 889 r = r + gv.getangle(10, i) 890 end 891 local t1 = gv.gethiticks()-t 892 893 r = 0 894 t = gv.gethiticks() 895 for i=1,N do 896 r = r + ourgetang(10, i) 897 end 898 local t2 = gv.gethiticks()-t 899 900 printf('Time for %d runs: getangle: %.03f ms, math.atan2: %.03f ms', N, t1, t2) 901 print('----------') 902end 903 904if (LUNATIC_FIRST_TIME) then 905 require("test.shadexfog").test_create0() 906end 907