1--[[ 2 Usage: in Mapster32, 3 > lua "shadexfog=reload'shadexfog'" 4 -- for example 5 > lua "shadexfog.create(100, 255,255,255)" 6 > lua "shadexfog.translate(100, 2)" 7 In EDuke32, simply pass this module at the command line. 8--]] 9 10local assert = assert 11local error = error 12local print = print 13local printf = printf 14local tonumber = tonumber 15local type = type 16local unpack = unpack 17 18local bit = require("bit") 19local math = require("math") 20local string = require("string") 21local min, max = math.min, math.max 22local floor = math.floor 23 24local sector, wall, sprite = sector, wall, sprite 25 26local engine = require("engine") 27local gv = gv 28 29---------- 30 31local shadexfog = {} 32 33-- Example: 34-- lua "shadexfog.createremap(30, {[2]=0, [3]=1, [12]=0, [13]=1})" 35-- creates a pal 30 which maps the blue and orange ramps to the gray ones. 36-- (Compare with the rows of http://wiki.eduke32.com/wiki/File:Pala.png) 37-- 38-- Sexdecatuple remappings of Duke3D pals loaded from LOOKUP.DAT: 39-- Remappings that are not expressible as such and identity maps (pal 3 and 9) 40-- omitted. 41-- 42-- 2: { [0]=8, [1]=13, [2]=8, [3]=13, [4]=13, [5]=8, [6]=8, [7]=13, [9]=8, [10]=8, [11]=13, [12]=8, [14]=8, } 43-- 5: { [8]=2, [13]=3, } 44-- 7: { [0]=10, [1]=9, [2]=10, [3]=9, [4]=9, [5]=10, [6]=10, [7]=9, [8]=10, [11]=9, [12]=9, [13]=9, [14]=9, } 45-- 8: { [0]=6, [1]=7, [2]=6, [3]=7, [4]=7, [5]=6, [8]=6, [9]=7, [10]=6, [11]=7, [12]=7, [13]=7, [14]=6, } 46-- 11: { [4]=7, [5]=6, } 47-- 12: { [4]=1, [5]=0, } 48-- 15: { [4]=3, [5]=2, } 49-- 17: { [2]=5, [3]=4, [4]=7, [5]=6, [6]=5, [7]=4, [12]=5, [14]=4, } 50-- 18: { [4]=1, [5]=0, } 51-- 19: { [2]=8, [3]=13, [4]=1, [5]=0, [6]=8, [7]=13, [12]=8, [14]=13, } 52-- 20: { [2]=5, [3]=4, [4]=1, [5]=0, [6]=5, [7]=4, [12]=5, [14]=4, } 53-- 21: { [4]=13, [5]=8, } 54-- 22: { [4]=7, [5]=6, } 55-- 25: { [6]=8, [7]=13, } 56function shadexfog.createremap(palnum, remaptab) 57 local sht = engine.getshadetab(0) 58 engine.setshadetab(palnum, sht:remap16(remaptab)) 59end 60 61-- Create 32 palookups corrensponding to different *shade levels* of a fog 62-- palookup, called a "shade-x-fog" palookup set in the following. 63-- 64-- Pals <startpalnum> .. <startpalnum>+31 will be taken. 65-- <fogr>, <fogg>, <fogb>: intensities of the fog color, [0 .. 255] 66function shadexfog.create(startpalnum, fogr, fogg, fogb) 67 local MAXPALNUM = 255-31-engine.RESERVEDPALS 68 if (not (startpalnum >= 1 and startpalnum <= MAXPALNUM)) then 69 error("invalid startpalnum, max="..MAXPALNUM, 2) 70 end 71 72 local basesht = engine.getshadetab(0) 73 74 -- Encode the shade in different pal numbers! The shade tables are 75 -- constructed with a fog in their place. 76 for dummyshade=0,31 do 77 local sht = engine.shadetab() 78 79 for f=0,31 do 80 for i=0,255 do 81 local r, g, b = engine.getrgb(basesht[dummyshade][i]) 82 83 local nr, ng, nb = 84 (r*(32-f) + fogr*f) / 32, 85 (g*(32-f) + fogg*f) / 32, 86 (b*(32-f) + fogb*f) / 32 87 88 sht[f][i] = engine.nearcolor(nr, ng, nb) 89 end 90 end 91 92 engine.setshadetab(startpalnum + dummyshade, sht) 93 end 94end 95 96local function trans(what, startpalnum, fogintensity) 97 if (what.pal >= startpalnum and what.pal <= startpalnum+31) then 98 -- Auto-detect earlier translation with the same <startpalnum>. 99 what.shade = what.pal - startpalnum 100 end 101 102 local shade = min(max(what.shade, 0), 31) 103 what.pal = startpalnum + shade 104 what.shade = fogintensity 105end 106 107-- shadexfog.translate(startpalnum, fogintensity [, vis]) 108-- 109-- Translate the whole map for use with a shade-x-fog palookup set. 110-- .pal becomes the <startpalnum> + former .shade 111-- .shade becomes the <fogintensity> [0 .. 31] 112-- If <vis> is passed and >= 0, set all sector's visibility to that value. 113-- 114-- Notes: 115-- - auto-detects when the translation has been applied with the *same* 116-- <startpalnum> (if a different one is desired, must reload map). 117-- - if shades < 0 or > 31 present, loss of information 118function shadexfog.translate(startpalnum, fogintensity, vis) 119 for i=0,gv.numsectors-1 do 120 trans(sector[i].ceiling, startpalnum, fogintensity) 121 trans(sector[i].floor, startpalnum, fogintensity) 122 if (vis and vis >= 0) then 123 sector[i].visibility = vis 124 end 125 end 126 127 for i=0,gv.numwalls-1 do 128 trans(wall[i], startpalnum, fogintensity) 129 end 130end 131 132if (gv.LUNATIC_CLIENT == gv.LUNATIC_CLIENT_EDUKE32 and LUNATIC_FIRST_TIME) then 133 shadexfog.create(100, 255,255,255) 134 print("created shadexfog palookups") 135end 136 137---------- BASE SHADE TABLE TESTS ---------- 138 139-- sht = shadexfog.create_depth_shtab([palnum]) 140function shadexfog.create_depth_shtab(palnum) 141 local sht = engine.shadetab() 142 143 for s=0,31 do 144 for i=0,255 do 145 sht[s][i] = s 146 end 147 end 148 149 if (palnum) then 150 engine.setshadetab(palnum, sht) 151 end 152 return sht 153end 154 155function shadexfog.create_vismarker_shtab(palnum) 156 local sht = engine.getshadetab(0) 157 158 for i=0,255 do 159 sht[1][i] = 242 160 sht[30][i] = 245 161 end 162 163 if (palnum) then 164 engine.setshadetab(palnum, sht) 165 end 166 return sht 167end 168 169-- Basic test of whether for a color index i corresponding to a color (r,g,b), 170-- getclosestcol() returns a color index ii corresponding to the same color. 171-- (In the Duke3D palette, there are duplicates, so the requirement i==ii is 172-- too strict.) 173function shadexfog.test_nearcolor() 174 for i=0,255 do 175 local r, g, b = engine.getrgb(i) 176 local ii = engine.nearcolor(r, g, b) 177 local rr, gg, bb = engine.getrgb(ii) 178 179 if (r~=rr or g~=gg or b~=bb) then 180 printf("diff %d: %d,%d,%d %d,%d,%d", i, r,g,b, rr,gg,bb) 181 end 182 end 183end 184 185-- Change the .pal member of all sector ceilings/floors, walls and sprites to 186-- <palnum>. 187function shadexfog.challpal(palnum) 188 for i=0,gv.numsectors-1 do 189 sector[i].ceilingpal = palnum 190 sector[i].floorpal = palnum 191 end 192 for i=0,gv.numwalls-1 do 193 wall[i].pal = palnum 194 end 195 for i in sprite.all() do 196 sprite[i].pal = palnum 197 end 198end 199 200-- Create our version of the base shade table (palookup 0) 201-- 202-- NOTE: Nope, the base shade table is NOT created by applying a linear ramp to 203-- the base palette colors!!! 204local function create_base_shtab(basesht) 205 local basesht = basesht or engine.getshadetab(0) 206 207 local sht = engine.shadetab() 208 sht[0] = basesht[0] 209 for sh=1,31 do 210 for i=0,255-16 do 211 -- NOTE that this fails, see BASESHT_0_NOT_IDENTITY: 212-- assert(basesht[0][i] == i) 213 local r, g, b = engine.getrgb(i) 214 local f = 1 215 r = ((32-f*sh+0.5)*r)/32 216 g = ((32-f*sh+0.5)*g)/32 217 b = ((32-f*sh+0.5)*b)/32 218 r, g, b = max(0,r), max(0,g), max(0,b) -- if f is > 1 219 sht[sh][i] = engine.nearcolor(r, g, b) 220 end 221 222 for i=255-16+1,255 do 223 -- fullbrights 224 sht[sh][i] = basesht[0][i] 225 end 226 end 227 228 return sht 229end 230 231local function create_base_shtab_2(basesht) 232 local basesht = basesht or engine.getshadetab(0) 233 234 local perm16 = { [0]=0,1, 2,3, 5,4, 6,7, 8,13, 10,11, 12,9, 14,15 } 235 basesht = basesht:remap16(perm16) 236 237 local iperm16 = {} 238 for i=0,15 do 239 iperm16[perm16[i]] = i 240 end 241 242 local iperm = {} 243 for i=0,255 do 244 iperm[i] = 16*(iperm16[floor(i/16)]) + i%16 245 end 246 247 local baseidx = {} 248 for i=0,255-16 do 249 baseidx[i] = i < 192 and 32*floor(i/32) or 16*floor(i/16) 250 end 251 252 local sht = engine.shadetab() 253 254 for sh=0,31 do 255 for i=0,255-16 do 256 local bi = baseidx[i] 257 local cidx = bi + floor(((31-sh)*(i - bi))/31) 258 sht[sh][i] = iperm[cidx] 259 end 260 261 for i=255-16+1,255 do 262 -- fullbrights 263 sht[sh][i] = basesht[0][i] 264 end 265 end 266 267 return sht:remap16(iperm16) 268end 269 270local ismapster32 = (gv.LUNATIC_CLIENT == gv.LUNATIC_CLIENT_MAPSTER32) 271 272if (ismapster32) then 273 -- Wrapper around engine.savePaletteDat() that errors on unexpected events. 274 function shadexfog.save(filename, palnum, blendnum, moreblends, lognumalphatabs) 275 local ok, errmsg, nummoreblends = engine.savePaletteDat( 276 filename, palnum, blendnum, moreblends, lognumalphatabs) 277 if (not ok) then 278 error(errmsg) 279 end 280 281 printf('Wrote base palette, shade and translucency tables to "%s".', filename) 282 if (nummoreblends > 0) then 283 printf(" Also wrote %d additional translucency tables.", nummoreblends) 284 end 285 end 286 287 function shadexfog.saveLookupDat(filename, lookups) 288 local ok, errmsg, numlookups = engine.saveLookupDat(filename, lookups) 289 if (not ok) then 290 error(errmsg) 291 end 292 293 printf('Wrote %d lookup tables and 5 base palettes to "%s".', 294 numlookups, filename) 295 end 296end 297 298-- Create our (failed) version of the base shade table at set it to palookup 299-- number <palnum>. 300-- <secver>: use second attempt? 301function shadexfog.create0(palnum, secver) 302 local sht0 = secver and create_base_shtab_2() or create_base_shtab() 303 engine.setshadetab(palnum, sht0) 304end 305 306function shadexfog.test_create0() 307 local basesht = engine.getshadetab(0) 308 309 for i=0,255 do 310 if (basesht[0][i] ~= i) then 311 -- BASESHT_0_NOT_IDENTITY 312 printf("Base shade table at index %d: %d", i, basesht[0][i]) 313 end 314 end 315 316 local sht = create_base_shtab(basesht) 317 318 local ok = true 319 for sh=1,31 do 320 for i=0,255 do 321 local ouri, origi = sht[sh][i], basesht[sh][i] 322-- if (sht[sh][i] ~= basesht[sh][i]) then 323 if (math.abs(ouri - origi) > 1) then 324 printf("Constructed shade table DIFFERS AT shade %d index %d: orig %d ours %d", 325 sh, i, basesht[sh][i], sht[sh][i]) 326 ok = false 327 goto out 328 end 329 end 330 end 331 332 ::out:: 333 if (ok) then 334 printf("Constructed shade table IDENTICAL WITH original one") 335 end 336end 337 338---------- Blending table tests ---------- 339 340-- shadexfog.create_trans(startblendidx, func [, numtables [, fullbrightsOK]]) 341-- 342-- <func>: must be 343-- rr, gg, bb = f(r,g,b, R,G,B, level, numtables) 344-- If reverse translucency bit clear, (r,g,b) is background and (R,G,B) is 345-- foreground (incoming). 346-- ('level' is the table index, from 1 to <numtables>) 347-- <numtables>: number of tables to create, from <startblendidx> on. Default: 1 348function shadexfog.create_trans(startblendidx, func, numtables, fullbrightsOK) 349 numtables = numtables or 1 350 local lastokcol = fullbrightsOK and 255 or 255-16 351 352 local tab = engine.blendtab() 353 354 for level=1,numtables do 355 for i=0,255 do 356 local r,g,b = engine.getrgb(i) 357 for j=0,255 do 358 local R,G,B = engine.getrgb(j) 359 360 local rr, gg, bb = func(r,g,b, R,G,B, level, numtables) 361 tab[i][j] = engine.nearcolor(rr, gg, bb, lastokcol) 362 end 363 end 364 365 engine.setblendtab(startblendidx + level-1, tab) 366 end 367end 368 369local function check_numtables(numtables) 370 if (numtables ~= nil) then 371 if (type(numtables) ~= "number" or not (numtables >= 1 and numtables <= 128)) then 372 error("invalid argument #2: must be a number in [1 .. 128]", 2) 373 end 374 375 if (bit.band(numtables, numtables-1) ~= 0) then 376 error("invalid argument #2: must be a power of two", 2) 377 end 378 end 379end 380 381-- shadexfog.create_alpha_trans(startblendidx [, numtables [, fullbrightsOK]]) 382-- 383-- Creates <numtables> blending tables of smooth alpha translucency, with 384-- fractions 1/(2*numtables), 2/(2*numtables) ... numtables/(2*numtables). 385-- <numtables> must be a power of two in [1 .. 128]. 386function shadexfog.create_alpha_trans(startblendidx, numtables, fullbrightsOK) 387 check_numtables(numtables) 388 389 shadexfog.create_trans( 390 startblendidx, 391 392 function(r,g,b, R,G,B, alpha, numtabs) 393 local f = alpha/(2*numtabs) 394 local F = 1-f 395 return f*r+F*R, f*g+F*G, f*b+F*B 396 end, 397 398 numtables, fullbrightsOK 399 ) 400end 401 402-- shadexfog.create_additive_trans(startblendidx [, numtables [, fullbrightsOK]]) 403function shadexfog.create_additive_trans(startblendidx, numtables, fullbrightsOK) 404 shadexfog.create_trans( 405 startblendidx, 406 407 function(r,g,b, R,G,B, level, numtabs) 408 local f = level/numtabs 409 return min(f*r+R, 255), min(f*g+G, 255), min(f*b+B, 255) 410 end, 411 412 numtables, fullbrightsOK 413 ) 414end 415 416-- shadexfog.create_brightpass_trans(startblendidx [, numtables [, fullbrightsOK]]) 417function shadexfog.create_brightpass_trans(startblendidx, numtables, fullbrightsOK) 418 shadexfog.create_trans( 419 startblendidx, 420 421 function(r,g,b, R,G,B, alpha, numtabs) 422 local a = alpha/numtabs 423 local F = 1 - min(a, (R+G+B) / (3*255)) 424 local f = 1 - F 425 return f*r+F*R, f*g+F*G, f*b+F*B 426 end, 427 428 numtables, fullbrightsOK 429 ) 430end 431 432if (not ismapster32) then 433 return shadexfog 434end 435 436--========== Mapster32 Lua menu hooks ==========-- 437 438local getnumber16 = engine.getnumber16 439local GNF = engine.GETNUMFLAG 440local GNF_BOOL = GNF.NEXTFREE 441 442local df = GNF.RET_M1_ON_CANCEL -- default getnumber16() flags 443 444local MAXUSERPALOOKUP = 256-1-8 -- KEEPINSYNC engine.lua:check_palidx() 445 446-- wrapped_func = CreateMenuFunction(argdesc) 447-- 448-- <argdesc>: table with [0]=<func> and then, entries { name, init, max [, noret] } 449local function CreateMenuFunction(argdesc) 450 return function() 451 local func = argdesc[0] 452 assert(type(func) == "function") 453 local args = {} 454 455 for i=1,#argdesc do 456 local ad = argdesc[i] 457 assert(type(ad) == "table" and #ad == 3 or #ad == 4) 458 459 local moreflags = ad[4] or 0 460 args[i] = getnumber16(ad[1]..": ", ad[2], ad[3], bit.bor(df, moreflags)) 461 if (bit.band(moreflags, GNF.NEG_ALLOWED)==0 and args[i] < 0) then 462 return 463 end 464 if (bit.band(moreflags, GNF_BOOL)~=0) then 465 args[i] = (args[i] > 0) 466 end 467 end 468 469 func(unpack(args)) 470 end 471end 472 473-- Replace chevrons (angle brackets!) with printext16 markup. 474local function replchev(matchstr) return "^15"..matchstr:sub(2,-2).."^O" end 475-- Replace ASCII code escapes like \XXX. We can't use these escapes in Lua [[ ... ]] strings. 476local function replascii(matchstr) return string.char(tonumber(matchstr)) end 477-- Format a whole string for the menu system: 478local function formatHelp(str) 479 return str:gsub( 480 "(%b<>)", replchev):gsub( 481 "%*(.-)%*", "^014%1^O"):gsub( 482 "%\\(%d+)", replascii) 483end 484 485---------- 486 487engine.clearMenu() 488 489engine.registerMenuFunc( 490 "Create shadexfog palset", 491 CreateMenuFunction{ 492 [0] = shadexfog.create, 493 { "Starting palnum", 100, MAXUSERPALOOKUP-31 }, 494 { "Red fog color [0-255]", 0, 255 }, 495 { "Green fog color [0-255]", 0, 255 }, 496 { "Blue fog color [0-255]", 0, 255 }, 497 }, 498 499formatHelp 500[[ 501<shadexfog.create(startpalnum, fogr, fogg, fogb)> 502<_______________________________________________> 503 504Creates 32 shade tables corresponding to different *shade levels* 505of a fog palookup, together called a *shade-x-fog* palookup set. 506 507 Pals <startpalnum> to <startpalnum>+31 will be taken. 508 <fogr>, <fogg>, <fogb>: intensities of the fog color, [0 .. 255] 509]] 510) 511 512engine.registerMenuFunc( 513 "Translate map for shxfog", 514 CreateMenuFunction{ 515 [0] = shadexfog.translate, 516 { "Starting palnum", 100, MAXUSERPALOOKUP-31 }, 517 { "Fog intensity [0-31]", 0, 31 }, 518 { "Change all sectors' visibility to (Esc: don't)", 0, 255, GNF.NEG_ALLOWED }, 519 }, 520 521formatHelp 522[[ 523<shadexfog.translate(startpalnum, fogintensity [, vis])> 524<______________________________________________________> 525 526Translates the whole map for use with a shade-x-fog palookup set. 527 528 .pal becomes the <startpalnum> + former .shade 529 .shade becomes the <fogintensity> [0 .. 31] 530 531 If <vis> is passed and >= 0, set all sector's visibility to that 532 value. 533 534 *Notes:* 535 - auto-detects when the translation has been applied with the *same* 536 <startpalnum> (if a different one is desired, must reload map). 537 - if shades > 31 or < 0 present, there is loss of information 538]] 539) 540 541engine.registerMenuFunc( 542 "Change pal of everything", 543 CreateMenuFunction{ 544 [0] = shadexfog.challpal, 545 { "Pal to change to", 0, MAXUSERPALOOKUP }, 546 }, 547 548formatHelp 549[[ 550<shadexfog.challpal(palnum)> 551<__________________________> 552 553Changes the .pal member of all sector ceilings/floors, walls and 554sprites to <palnum>. 555]] 556) 557 558engine.registerMenuFunc( 559 "Create alpha trans. tabs", 560 CreateMenuFunction{ 561 [0] = shadexfog.create_alpha_trans, 562 { "Starting blendnum", 1, 255 }, 563 { "Number of blending tables", 32, 255 }, 564 { "Fullbright result colors OK?", 0, 1, GNF_BOOL }, 565 }, 566 567formatHelp 568[=[ 569<shadexfog.create_alpha_trans(startblend [, numtables [, fullbriOK]])> 570<____________________________________________________________________> 571 572Creates <numtables> blending tables of smooth alpha translucency, 573starting with the blending number <startblend>, with values of alpha 574 575 1/(2\255<numtables>), 2/(2\255<numtables>) ... <numtables>/(2\255<numtables>). 576 577 <numtables> must be a power of two in [1 .. 128]. 578 579 <fullbriOK>: should fullbright color indices (>= 240) be permitted as 580 the blending result of two color indices? 581]=] 582) 583 584engine.registerMenuFunc( 585 "Create addtv. trans. tabs", 586 CreateMenuFunction{ 587 [0] = shadexfog.create_additive_trans, 588 { "Starting blendnum", 1, 255 }, 589 { "Number of blending tables", 32, 255 }, 590 { "Fullbright result colors OK?", 0, 1, GNF_BOOL }, 591 }, 592 593formatHelp 594[=[ 595<shadexfog.create_additive_trans(startbl [, numtables [, fullbriOK]])> 596<____________________________________________________________________> 597 598Creates <numtables> blending tables of smooth additive translucency, 599starting with the blending number <startbl>, with factors for the 600background color 601 602 1/<numtables>, 2/<numtables> ... <numtables>/<numtables>. 603 604 <numtables> must be a power of two in [1 .. 128]. 605 606 <fullbriOK>: should fullbright color indices (>= 240) be permitted as 607 the blending result of two color indices? 608]=] 609) 610 611engine.registerMenuFunc( 612 "Create bri.pass tr. tabs", 613 CreateMenuFunction{ 614 [0] = shadexfog.create_brightpass_trans, 615 { "Starting blendnum", 1, 255 }, 616 { "Number of blending tables", 32, 255 }, 617 { "Fullbright result colors OK?", 0, 1, GNF_BOOL }, 618 }, 619 620formatHelp 621[=[ 622<shadexfog.create_brightpass_trans(startbl [, numtabs [, fullbriOK]])> 623<____________________________________________________________________> 624 625Creates <numtabs> blending tables of "brightpass" translucency, 626starting with the blending number <startbl>, with fractions 627 628 1/<numtables>, 2/<numtables> ... <numtables>/<numtables>. 629 630 <fullbriOK>: should fullbright color indices (>= 240) be permitted as 631 the blending result of two color indices? 632]=] 633) 634 635engine.registerMenuFunc( 636 "Create base shade table", 637 CreateMenuFunction{ 638 [0] = shadexfog.create0, 639 { "Pal number", 100, MAXUSERPALOOKUP }, 640 { "Second attempt?", 1, 1, GNF_BOOL }, 641 }, 642 643formatHelp 644[[ 645<shadexfog.create0(palnum, secver)> 646<_________________________________> 647 648Creates our version of the base shade table at set it to palookup 649number <palnum>. 650 651 <secver>: use second attempt instead of the first? This one is more 652 similar to the base shade table shipped with Duke3D, but still 653 shows significant differences. 654]] 655) 656 657engine.registerMenuFunc( 658 "Create c.index remapping", 659 function() 660 local palnum = getnumber16("Pal number: ", 100, MAXUSERPALOOKUP) 661 if (palnum < 0) then return end 662 663 local remaptab = {} 664 while (true) do 665 local srchex = getnumber16("Source hexadecatuple (0: finish): ", 0, 14) 666 if (srchex < 0) then return end 667 local dsthex = getnumber16("Destn. hexadecatuple (0: finish): ", 0, 14) 668 if (dsthex < 0) then return end 669 670 if (srchex == 0 and dsthex == 0) then 671 break 672 end 673 674 remaptab[srchex] = dsthex 675 end 676 677 shadexfog.createremap(palnum, remaptab) 678 end, 679 680formatHelp 681[[ 682<shadexfog.createremap(palnum, remaptab)> 683<_______________________________________> 684 685Creates a color index remapping expressed as mappings of sexdeca- 686tuples (16-tuples) of the base palette at pal <palnum>. 687 688 Duke3D's default base palette can be considered to consist of six 689 ramps of 32 colors each, three ramps of 16 colors each and a 690 remaining fullbright color set. The sexdecatuples are as follows: 691 692< 0, 1>: gray ramp 693< 2, 3>: skin color ramp 694< 5, 4>: blue ramp (note that the 16-tuples are in reverse order) 695< 6, 7>: nightvision yellow/green 696< 8, 13>: red ramp 697< 10, 11>: almost gray ramp, but with a slight red hue 698 699 < 9>: yellow (slightly more red than green) 700 < 12>: "dirty" orange 701 < 14>: blue-purple-red 702]] 703) 704 705local function getNumberRange(what, whating) 706 local str = engine.getstring("Additional "..what.." numbers (e.g. '64,100-131,255'): ") 707 if (str == nil) then return end 708 if (str == "") then return {} end 709 710 if (not str:find("^[%d,%-]+$")) then 711 error("Additional "..whating.." numbers string must contain only digits or ',' or '-'", 2) 712 end 713 714 local moreblends = {} 715 local didnumstr = {} 716 717 for n1, n2 in str:gmatch("(%d+)%-(%d+)") do -- parse number ranges 718 moreblends[#moreblends+1] = { tonumber(n1), tonumber(n2) } 719 didnumstr[n1] = true 720 didnumstr[n2] = true 721 end 722 723 for n in str:gmatch("%d+") do -- parse single numbers 724 if (not didnumstr[n]) then 725 moreblends[#moreblends+1] = tonumber(n) 726 end 727 end 728 729 return moreblends 730end 731 732engine.registerMenuFunc( 733 "Save pal+sh+trans DAT f.", 734 function() 735 local filename = engine.getstring("File name: ") 736 if (filename == nil) then return end 737 738 local palnum = getnumber16("Pal number of base shade table: ", 0, MAXUSERPALOOKUP) 739 if (palnum < 0) then return end 740 local blendnum = getnumber16("Blendnum of base transluc. table: ", 0, 255) 741 if (blendnum < 0) then return end 742 743 local moreblends = getNumberRange("blend", "blending") 744 if (moreblends == nil) then return end 745 746 local lognumalphatabs 747 if (#moreblends > 0) then 748 lognumalphatabs = getnumber16("log2 of last alpha blending table index (1-7, 0: none): ", 0, 7) 749 if (lognumalphatabs < 0) then return end 750 if (lognumalphatabs == 0) then lognumalphatabs = nil end 751 end 752 753 shadexfog.save(filename, palnum, blendnum, moreblends, lognumalphatabs) 754 end, 755 756formatHelp 757[[ 758<shadexfog.save(filename, palnum, blendnum, moreblends, lognumalpha)> 759<___________________________________________________________________> 760 761Writes out a full PALETTE.DAT-formatted file named <filename> with the 762base shade table numbered <palnum> and the base translucency table 763numbered <blendnum>. 764 765Finally, you are asked to specify additional blending tables that can 766be stored in EDuke32's extended PALETTE.DAT format. If one or more 767additional blending table is specified, you are also queried for the 768log2 of the last alpha blending table index, <lognumalpha>. Since alpha 769blending tables are assumed to be set up at indices 1 to 770exp(2, <lognumalpha>), it is also the log2 of their total count. 771]] 772) 773 774engine.registerMenuFunc( 775 "Save lookups DAT file", 776 function() 777 local filename = engine.getstring("File name: ") 778 if (filename ~= nil and filename ~= "") then 779 local lookups = { 780 -- Duke3D 1.5 LOOKUP.DAT order 781 1,2,6,7,8, 3,4,5,9,10, 782 12,13,15,16,18, 19,11,14,17,20, 783 21,22,23,24,25 784 } 785 786 local morelookups = getNumberRange("lookup", "lookup") 787 if (morelookups == nil) then return end 788 789 if (#morelookups > 0) then 790 for i=1,#morelookups do 791 lookups[#lookups+1] = morelookups[i] 792 end 793 end 794 795 shadexfog.saveLookupDat(filename, lookups) 796 end 797 end, 798 799formatHelp 800[[ 801<shadexfog.saveLookupDat(filename, lookups)> 802<__________________________________________> 803 804Saves the color index lookups (i.e. first 256 values of each shade 805table) of the pal numbers which have lookups in Duke3D's unaltered 806LOOKUP.DAT, plus optional ones provided by the user. 807 808The default ones are, in this order: 8091,2,6,7,8, 3,4,5,9,10, 12,13,15,16,18, 19,11,14,17,20, 21,22,23,24,25. 810(All pal numbers from 1 to 25 are present.) 811 812 <filename>: the name of the LOOKUP.DAT-formatted file to create 813]] 814) 815 816engine.registerMenuFunc("_________DEBUG_________", function() end 817--[[ 818, 819" \01\02\03\04\05\06\07\08\09\10\11\12\13\14\15\ 820\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31\ 821\128\129\130\131\132\133\134\135\136\137\138\139\140\141\142\143\ 822\144\145\146\147\148\149\150\151\152\153\154\155\156\157\158\159\ 823\160\161\162\163\164\165\166\167\168\169\170\171\172\173\174\175\ 824\176\177\178\179\180\181\182\183\184\185\186\187\188\189\190\191\ 825\192\193\194\195\196\197\198\199\200\201\202\203\204\205\206\207\ 826\208\209\210\211\212\213\214\215\216\217\218\219\220\221\222\223\ 827\224\225\226\227\228\229\230\231\232\233\234\235\236\237\238\239\ 828\240\241\242\243\244\245\246\247\248\249\250\251\252\253\254\255" 829--]] 830) 831 832engine.registerMenuFunc("Setup dbg. water basepal", engine.setupDebugBasePal, 833formatHelp 834[[ 835<engine.setupDebugBasePal()> 836<__________________________> 837 838Overwrites the water base palette with one where each 16-tuple 839(except the fullbrights) consists of a single representative 840color. 841 842This can be used to get a quick glimpse about what ramps are present 843in particular tiles. With this information, custom lookups can be 844created more directedly with the <Create c.index remapping> menu 845entry. 846]] 847) 848 849engine.registerMenuFunc( 850 "Create depth shade tab", 851 CreateMenuFunction{ 852 [0] = shadexfog.create_depth_shtab, 853 { "Pal number", 100, MAXUSERPALOOKUP }, 854 }, 855 856formatHelp 857[[ 858<shadexfog.create_depth_shtab(palnum)> 859<____________________________________> 860 861Creates a shade table for debugging purposes at pal <palnum>. 862 863 For every color index, the shade table maps shade index <i> to 864 color index <i> (of the first ramp of gray colors, assuming Build 865 has loaded a base shade table with 32 shade levels). 866]] 867) 868 869engine.registerMenuFunc( 870 "Create vismarker sh. tab", 871 CreateMenuFunction{ 872 [0] = shadexfog.create_vismarker_shtab, 873 { "Pal number", 100, MAXUSERPALOOKUP }, 874 }, 875 876formatHelp 877[[ 878<shadexfog.create_vismarker_shtab(palnum)> 879<________________________________________> 880 881Creates a shade table for debugging purposes at pal <palnum>. 882 883 For every color index, the shade table maps shade index 1 to 884 a ^14bright yellow^O color and shade index 30 to a ^13purple^O color. 885 Thus, it can be useful in visualizing the limits of the 886 fog/visibility attenuation. 887]] 888) 889 890engine.registerMenuFunc("Linearize default basep.", engine.linearizeBasePal, 891formatHelp 892[[ 893<engine.linearizeBasePal()> 894<_________________________> 895 896Overwrites the default base palette with one where certain ramps have 897their attenuation linearized. This is mainly useful for debugging 898purposes as it excludes the effect of this nonlinearity for 899comparison of fog/visibility between classic and OpenGL modes. 900]] 901) 902 903 904do 905 return shadexfog 906end 907