1stead = { 2 version = "3.4.1", 3 api_version = "1.1.6", -- last version before 1.2.0 4 table = table, 5 delim = ',', 6 scene_delim = "^^", 7 space_delim = ' ', 8 string = string, 9 tostr = tostring, 10 tonum = tonumber, 11 collectgarbage = collectgarbage, 12 type = type, 13 ipairs = ipairs, 14 pairs = pairs, 15 getinfo = debug.getinfo; 16 rawget = rawget; 17 rawset = rawset; 18 math = math, 19 random = instead_random, 20 randomseed = instead_srandom, 21 next = next, 22 math = math, 23 io = io, 24 os = os, 25 readdir = instead_readdir, 26 cache = {}, 27 call_top = 0, 28 call_ctx = { txt = nil, self = nil }, 29-- functions = {}, -- code blocks 30 31 modules_ini = {}, 32 modules_done = {}, 33 modules_start = {}, 34 modules_cmd = {}, 35 busy = function() end; 36 module_init = function(f, ...) 37 if stead.type(f) ~= 'function' then 38 error ("Wrong parameter to module_init.", 2); 39 end 40 stead.table.insert(stead.modules_ini, f); 41 f(); 42 end; 43 44 module_done = function(f, ...) 45 if stead.type(f) ~= 'function' then 46 error ("Wrong parameter to module_done.", 2); 47 end 48 stead.table.insert(stead.modules_done, f); 49 end; 50 51 module_start = function(f, ...) 52 if stead.type(f) ~= 'function' then 53 error ("Wrong parameter to module_start.", 2); 54 end 55 stead.table.insert(stead.modules_start, f); 56 end; 57 58 module_cmd = function(f, ...) 59 if stead.type(f) ~= 'function' then 60 error ("Wrong parameter to module_cmd.", 2); 61 end 62 stead.table.insert(stead.modules_cmd, f); 63 end; 64} 65 66function instead_menu_toggle(n) 67 if n == nil then 68 n = 'main' 69 elseif type(n) ~= 'string' then 70 n = 'toggle' 71 end 72 stead.need_menu = n 73end 74 75stead.menu_toggle = instead_menu_toggle 76 77stead.api_atleast = function(...) 78 for k, v in stead.ipairs {...} do 79 if stead.type(v) ~= 'number' then 80 return false 81 end 82 if v > (stead.api_version_table[k] or 0) then 83 return false 84 end 85 if v < (stead.api_version_table[k] or 0) then 86 return true 87 end 88 end 89 return true 90end; 91 92stead.atleast = function(...) 93 for k, v in stead.ipairs {...} do 94 if stead.type(k) ~= 'number' then 95 return false 96 end 97 if v > (stead.version_table[k] or 0) then 98 return false 99 end 100 if v < (stead.version_table[k] or 0) then 101 return true 102 end 103 end 104 return true 105end; 106 107stead.last_act = function(s) 108 local r = game.__last_act 109 110 if s ~= nil and s ~= true then 111 game.__last_act = s 112 end 113 114 return r 115end 116 117stead.last_disp = function(s) 118 local r = game._lastdisp 119 120 if s == nil then 121 return r 122 end 123 if not s then s = nil end 124 game._lastdisp = s 125 return r 126end 127 128stead.act_text = function(s) 129 local o = stead.rawget(_G, 'ACTION_TEXT') 130 if s == nil then 131 return o 132 end 133 if not s then s = nil end 134 stead.rawset(_G, 'ACTION_TEXT', s) 135 return o 136end 137 138stead.nop = function(s) -- to refresh entry scene w/o time tick 139 stead.act_text(true) 140 return stead.last_disp() or '' 141end 142 143stead.need_scene = function(s) 144 if s == nil then 145 stead.rawset(_G, 'NEED_SCENE', true) 146 else 147 stead.rawset(_G, 'NEED_SCENE', s) 148 end 149end 150 151if _VERSION == "Lua 5.1" then 152 stead.eval = loadstring 153 stead.unpack = unpack 154else 155 stead.eval = load 156 stead.unpack = table.unpack 157 unpack = table.unpack -- buggy games 158 stead.table.maxn = table_get_maxn 159 string.gfind = string.gmatch 160 math.mod = math.fmod 161 math.log10 = function(a) 162 return stead.math.log(a, 10) 163 end 164end 165 166instead = stead; 167 168function stead.getcmd(str) 169 local a = {} 170 local n = 1 171 local cmd; 172 local i,k = stead.string.find(str, '[a-zA-Z0-9_]+'); 173 if not i or not k then 174 cmd = str; 175 else 176 cmd = stead.string.sub(str, i, k); 177 end 178 179 stead.cmd = cmd 180 if cmd == 'load' or cmd == 'save' then 181 a[1] = stead.strip(stead.string.sub(str, k + 1)); 182 stead.args = a; 183 return cmd, a 184 end 185 while i do 186 k = k + 1; 187 i,k = stead.string.find(str,'[^,]+', k); 188 if not i then 189 break 190 end 191 a[n] = stead.strip(stead.string.sub(str, i, k)); 192 n = n + 1; 193 end 194 stead.args = a; 195 return cmd, a 196end 197 198stead.tostring = function(v) 199 if isCode(v) then 200 v = stead.string.format("code %q", stead.functions[v].code); 201 elseif stead.type(v) == 'string' then 202 v = stead.string.format("%q", v); 203 elseif v == nil or stead.type(v) == 'boolean' or stead.type(v) == 'number' then 204 v = stead.tostr(v); 205 elseif stead.type(v) == 'table' and stead.type(stead.deref(v)) == 'string' then 206 v = stead.deref(v); 207 else 208 v = nil 209 end 210 return v 211end 212 213stead.cctx = function() 214 return stead.call_ctx[stead.call_top]; 215end 216 217stead.callpush = function(v, ...) 218 stead.call_top = stead.call_top + 1; 219 stead.call_ctx[stead.call_top] = { txt = nil, self = v, action = false }; 220 stead.rawset(_G, 'args', {...}); 221 stead.rawset(_G, 'arg1', args[1]) 222 stead.rawset(_G, 'arg2', args[2]) 223 stead.rawset(_G, 'arg3', args[3]) 224 stead.rawset(_G, 'arg4', args[4]) 225 stead.rawset(_G, 'arg5', args[5]) 226 stead.rawset(_G, 'arg6', args[6]) 227 stead.rawset(_G, 'arg7', args[7]) 228 stead.rawset(_G, 'arg8', args[8]) 229 stead.rawset(_G, 'arg9', args[9]) 230 -- dirty but clean and fast :) 231 stead.rawset(_G, 'self', v) 232end 233 234stead.clearargs = function() 235 stead.rawset(_G, 'arg1', nil) 236 stead.rawset(_G, 'arg2', nil) 237 stead.rawset(_G, 'arg3', nil) 238 stead.rawset(_G, 'arg4', nil) 239 stead.rawset(_G, 'arg5', nil) 240 stead.rawset(_G, 'arg6', nil) 241 stead.rawset(_G, 'arg7', nil) 242 stead.rawset(_G, 'arg8', nil) 243 stead.rawset(_G, 'arg9', nil) 244 stead.rawset(_G, 'self', nil) 245end 246 247stead.callpop = function() 248 stead.call_ctx[stead.call_top] = nil; 249 stead.call_top = stead.call_top - 1; 250 if stead.call_top < 0 then 251 error ("callpush/callpop mismatch") 252 end 253 stead.clearargs() 254end 255 256stead.pclr = function() 257 stead.cctx().txt = nil 258end 259 260stead.pget = function() 261 return stead.cctx().txt; 262end 263 264stead.p = function(...) 265 local a = {...} 266 if stead.cctx() == nil then 267 error ("Call from global context.", 2); 268 end 269 for i = 1, stead.table.maxn(a) do 270 stead.cctx().txt = stead.par('', stead.cctx().txt, stead.tostr(a[i])); 271 end 272 stead.cctx().txt = stead.cat(stead.cctx().txt, stead.space_delim); 273end 274 275stead.pr = function(...) 276 local a = {...} 277 if stead.cctx() == nil then 278 error ("Call from global context.", 2); 279 end 280 for i = 1, stead.table.maxn(a) do 281 stead.cctx().txt = stead.par('', stead.cctx().txt, stead.tostr(a[i])); 282 end 283end 284 285stead.pn = function(...) 286 if stead.cctx() == nil then 287 error ("Call from global context.", 2); 288 end 289 p(...); 290 stead.cctx().txt = stead.par('', stead.cctx().txt,'^'); 291end 292 293-- merge strings with "space" as separator 294stead.par = function(space,...) 295 local res 296 local a = {...}; 297 for i = 1, stead.table.maxn(a) do 298 if stead.type(a[i]) == 'string' then 299 if res == nil then 300 res = "" 301 else 302 res = res..space; 303 end 304 res = res..a[i]; 305 end 306 end 307 return res; 308end 309-- add to not nill string any string 310stead.cat = function(v,...) 311 local res 312 if not v then 313 return nil 314 end 315 res = v; 316 local a = {...} 317 for i = 1, stead.table.maxn(a) do 318 if stead.type(a[i]) == 'string' then 319 res = res..a[i]; 320 end 321 end 322 return res; 323end 324 325function txtnb(v) 326 if stead.type(v) ~= 'string' then return nil; end 327 return iface:nb(v); 328end 329 330function img(v) 331 if stead.type(v) ~= 'string' then return nil; end; 332 return iface:img(v); 333end 334 335function imgl(v) 336 if stead.type(v) ~= 'string' then return nil; end; 337 return iface:imgl(v); 338end 339 340function imgr(v) 341 if stead.type(v) ~= 'string' then return nil; end; 342 return iface:imgr(v); 343end 344 345function txtem(v) 346 if stead.type(v) ~= 'string' then return nil; end; 347 return iface:em(v) 348end 349 350function txtst(v) 351 if stead.type(v) ~= 'string' then return nil; end; 352 return iface:st(v) 353end 354 355function txtr(v) 356 if stead.type(v) ~= 'string' then return nil; end; 357 return iface:right(v) 358end 359 360function txtl(v) 361 if stead.type(v) ~= 'string' then return nil; end; 362 return iface:left(v) 363end 364 365function txtc(v) 366 if stead.type(v) ~= 'string' then return nil; end; 367 return iface:center(v) 368end 369 370function txttab(v,a) 371 return iface:tab(v, a) 372end 373 374function txty(v, a) 375 return iface:y(v, a) 376end 377 378function txtj(v) 379 if stead.type(v) ~= 'string' then return nil; end; 380 return iface:just(v) 381end 382 383function txtb(v) 384 if stead.type(v) ~= 'string' then return nil; end; 385 return iface:bold(v) 386end 387 388function txtu(v) 389 if stead.type(v) ~= 'string' then return nil; end; 390 return iface:under(v) 391end 392 393function txtnm(n, v) 394 if stead.type(v) ~= 'string' or not stead.tonum(n) then return nil; end 395 return iface:enum(n, v); 396end 397 398function txttop(v) 399 if stead.type(v) ~= 'string' then return nil; end; 400 return iface:top(v) 401end 402 403function txtbottom(v) 404 if stead.type(v) ~= 'string' then return nil; end; 405 return iface:bottom(v) 406end 407 408function txtmiddle(v) 409 if stead.type(v) ~= 'string' then return nil; end; 410 return iface:middle(v) 411end 412 413stead.fmt = function(...) 414 local res 415 local a = {...}; 416 417 for i = 1, stead.table.maxn(a) do 418 if stead.type(a[i]) == 'string' then 419 local s = stead.string.gsub(a[i],'[\t ]+', stead.space_delim); 420 s = stead.string.gsub(s, '[\n]+', stead.space_delim); 421 s = stead.string.gsub(s, '\\?[\\^]', { ['^'] = '\n', ['\\^'] = '^', ['\\\\'] = '\\'} ); 422 res = stead.par('', res, s); 423 end 424 end 425 return res 426end 427 428-- integer lists 429local inext = function(t, k) 430 local v 431 k, v = stead.next(t, k); 432 while k and not stead.tonum(k) do 433 k, v = stead.next(t, k); 434 end 435 if not stead.tonum(k) then 436 return nil 437 end 438 return k, v 439end 440 441local ilist = function(s, var) 442 return inext, s, nil; 443end 444 445local ordered_i = function(t) 446 local ordered = {}; 447 local max = 0 448 for i, v in ilist(t) do 449 stead.table.insert(ordered, i); 450 max = max + 1; 451 end 452 stead.table.sort(ordered); 453 ordered.i = 1; 454 ordered.max = max; 455 return ordered; 456end 457 458local onext = function(t, k) 459 local v 460 if not k then 461 k = ordered_i(t); 462 end 463 if k.i > k.max then 464 return nil 465 end 466 v = k[k.i] 467 k.i = k.i + 1 468 return k, t[v], v; 469end 470 471function opairs(s) 472 return onext, s, nil; 473end 474stead.opairs = opairs 475 476function isPlayer(v) 477 return (stead.type(v) == 'table') and (v.player_type) 478end 479 480function isRoom(v) 481 return (stead.type(v) == 'table') and (v.location_type) 482end 483 484function isPhrase(v) 485 return (stead.type(v) == 'table') and (v.phrase_type) 486end 487 488function isDialog(v) 489 return (stead.type(v) == 'table') and (v.dialog_type) 490end 491 492function isDisabled(v) 493 return (stead.type(v) == 'table') and (v._disabled) 494end 495 496function isRemoved(v) 497 return (stead.type(v) == 'table') and (v._disabled == -1) 498end 499 500function isObject(v) 501 return (stead.type(v) == 'table') and (v.object_type) 502end 503 504function isXaction(v) 505 return (stead.type(v) == 'table') and (v.xaction_type) 506end 507 508 509stead.obj_xref = function(self,str) 510 function xrefrep(str) 511 local s = stead.string.gsub(str,'[\001\002]',''); 512 return stead.xref(s, self); 513 end 514 if not str then 515 return 516 end 517 if not isObject(self) then 518 return str; 519 end 520 local s = stead.string.gsub(str, '\\?[\\{}]', 521 { ['{'] = '\001', ['}'] = '\002', [ '\\{' ] = '{', [ '\\}' ] = '}' }):gsub('\001([^\002]+)\002', xrefrep):gsub('[\001\002]', { ['\001'] = '{', ['\002'] = '}' }); 522 return s; 523end 524 525stead.obj_look = function(self) 526 local vv 527 if isDisabled(self) then 528 return 529 end 530 local v = stead.call(self,'dsc'); 531 if game.hinting then 532 v = self:xref(v); 533 elseif v then 534 v = stead.string.gsub(v, '[{}]',''); 535 end 536 for i, o in stead.opairs(self.obj) do 537 o = stead.ref(o); 538 if isObject(o) then 539 vv = stead.obj_look(o); 540 v = stead.par(stead.space_delim, v, vv); 541 end 542 end 543 return v; 544end 545 546 547stead.obj_remove = function(self) 548 self._disabled = -1; 549 return self 550end 551 552stead.obj_disable = function(self) 553 self._disabled = true; 554 return self 555end 556 557stead.obj_enable = function(self) 558 self._disabled = false; 559 return self 560end 561 562stead.obj_disabled = function(self) 563 return (self._disabled == true); 564end 565 566stead.obj_enable_all = function(s) 567 if not isObject(s) then 568 return 569 end 570 objs(s):enable_all(); 571end 572 573stead.obj_disable_all = function(s) 574 if not isObject(s) then 575 return 576 end 577 objs(s):disable_all(); 578end 579 580stead.obj_save = function(self, name, h, need) 581 local dsc; 582 if need then 583 print ("Warning: object "..name.." can not be saved!"); 584 return 585 end 586 stead.savemembers(h, self, name, need); 587end 588 589stead.obj_str = function(self) 590 local v, vv; 591 if not isObject(self) then 592 return 593 end 594 if isDisabled(self) then 595 return 596 end 597 for i, o in stead.opairs(self.obj) do 598 o = stead.ref(o); 599 if o~= nil and not isDisabled(o) then -- isObject is better, but compat layer must be ok 600 vv = stead.call(o, 'nam'); 601 vv = stead.xref(vv, o); 602 v = stead.par(',', v, vv, stead.obj_str(o)); 603 end 604 end 605 return v; 606end 607 608function obj(v) 609 if v.nam == nil then 610 if isRoom(v) then 611 if isDialog(v) then 612 error ("No dialog name in constructor.", 3); 613 end 614 error ("No room name in constructor.", 3); 615 end 616 error ("No object name in constructor.", 2); 617 end 618 v.object_type = true; 619 620 if v.xref == nil then 621 v.xref = stead.obj_xref; 622 end 623 624 if v.look == nil then 625 v.look = stead.obj_look; 626 end 627 if v.enable == nil then 628 v.enable = stead.obj_enable; 629 end 630 if v.disable == nil then 631 v.disable = stead.obj_disable; 632 end 633 if v.disabled == nil then 634 v.disabled = stead.obj_disabled; 635 end 636 if v.enable_all == nil then 637 v.enable_all = stead.obj_enable_all; 638 end 639 if v.disable_all == nil then 640 v.disable_all = stead.obj_disable_all; 641 end 642 if v.remove == nil then 643 v.remove = stead.obj_remove; 644 end 645 if v.obj == nil then 646 v.obj = {}; 647 end 648 if v.srch == nil then 649 v.srch = stead.obj_search; 650 end 651 if v.str == nil then 652 v.str = stead.obj_str; 653 end 654 v.obj = list(v.obj); 655 if v.save == nil then 656 v.save = stead.obj_save; 657 end 658 return v 659end 660 661 662function stead.ref(n, nofunc) -- ref object by name 663 if stead.type(n) == 'string' then 664 if n:find("^[_a-zA-Z][_a-zA-Z0-9]*$") then -- fastest path 665 return stead.rawget(_G, n); 666 end 667 local f = stead.eval('return '..n); 668 if f then 669 return stead.ref(f(), nofunc); 670 end 671 return nil; 672 end 673 if stead.type(n) == 'table' then 674 return n; 675 end 676 if stead.type(n) == 'function' and not nofunc then 677 local r,v = pcall(n); 678 if not r then 679 return nil 680 end 681 return stead.ref(v); 682 end 683 return nil 684end 685 686function stead.deref(n) 687 if stead.type(n) == 'string' then 688 return n 689 end 690 691 if stead.type(n) == 'table' and stead.type(n.key_name) == 'string' then 692 return n.key_name 693 end 694 return n 695end 696 697stead.list_check = function(self, name) 698 for i, v, ii in stead.opairs(self) do 699 local o = stead.ref(v); 700 if not o then -- isObject(o) then -- compat 701 error ("No object: "..name.."["..stead.tostr(ii).."]".." ("..stead.tostr(stead.type(v))..")") 702 return false 703 end 704 if stead.deref(v) then 705 self[ii] = stead.deref(v); 706 end 707 end 708 return true; 709end 710 711stead.list_str = function(self) 712 local v, vv; 713 for i, o in stead.opairs(self) do 714 o = stead.ref(o); 715 if o~= nil and not isDisabled(o) then 716 vv = stead.call(o, 'nam'); 717 vv = stead.xref(vv, o); 718 v = stead.par(',', v, vv); 719 end 720 end 721 return v; 722end 723 724 725stead.list_add = function(self, name, pos) 726 local nam 727 nam = stead.deref(name); 728 if self:look(nam) then 729 return nil 730 end 731 self.__modified__ = true; 732 if stead.tonum(pos) then 733 pos = stead.tonum(pos) 734 if pos <= #self then 735 stead.table.insert(self, pos, nam); 736 else 737 self[pos] = nam; -- for spare lists 738 end 739 else 740 stead.table.insert(self, nam); 741 end 742 return true 743end 744 745stead.list_set = function(self, name, pos) 746 local nam 747 local i = stead.tonum(pos); 748 if not i then 749 return nil 750 end 751 nam = stead.deref(name); 752 self.__modified__ = true; 753 self[i] = nam; -- for spare lists 754 return true 755end 756 757stead.list_find = function(self, name) 758 local o = stead.ref(name, true) 759 for n,v,ii in stead.opairs(self) do 760 if stead.ref(v) == o then -- do not call func while search 761 return ii; 762 end 763 end 764 return nil 765end 766 767stead.list_disable_all = function(s) 768 for k, v in stead.opairs(s) do 769 local o = stead.ref(v); 770 if isObject(o) then 771 o:disable() 772 end 773 end 774end 775 776stead.list_enable_all = function(s) 777 for k, v in stead.opairs(s) do 778 local o = stead.ref(v); 779 if isObject(o) then 780 o:enable() 781 end 782 end 783end 784 785stead.list_enable = function(s, w) 786 local o, i = stead.list_search(s, w, true); 787 o = stead.ref(o); 788 if isObject(o) then 789 o:enable() 790 return o 791 end 792end 793 794stead.list_disable = function(s, w) 795 local o, i = stead.list_search(s, w, true); 796 o = stead.ref(o); 797 if isObject(o) then 798 o:disable() 799 return o 800 end 801end 802 803stead.list_save = function(self, name, h, need) 804 if self.__modifyed__ or self.__modified__ then -- compat 805 h:write(name.." = list({});\n"); 806 need = true; 807 end 808 stead.savemembers(h, self, name, need); 809end 810 811stead.list_name = function(self, name, dis) 812 for n, o, ii in stead.opairs(self) do 813 o = stead.ref(o); 814 if isObject(o) then 815 local nam = stead.call(o,'nam') ; 816 if ( not isDisabled(o) or dis ) and name == stead.tostr(nam) then 817 return ii; 818 end 819 end 820 end 821 return nil 822end 823stead.list_id = function(self, id, dis) 824 for n, o, ii in stead.opairs(self) do 825 o = stead.ref(o); 826 if dis or not isDisabled(o) then 827 if isObject(o) and id == o.id then 828 return ii; 829 end 830 end 831 end 832end 833 834stead.list_search = function(self, n, dis) 835 local i; 836 i = self:look(n); 837 if not i then 838 i = self:name(n, dis); 839 end 840 if not i and stead.tonum(n) then 841 i = self:byid(stead.tonum(n), dis); 842 if not i then 843 return nil 844 end 845 end 846 if not dis and isDisabled(stead.ref(self[i])) then 847 return nil; 848 end 849 return self[i], i; 850end 851 852stead.list_zap = function(self) 853 for n, o, ii in stead.opairs(self) do 854 self[ii] = nil; 855 end 856 self.__modified__ = true 857 return self 858end 859 860stead.list_concat = function(self, other, pos) 861 for n, o, ii in stead.opairs(other) do 862 o = stead.ref(o); 863 if pos == nil then 864 self:add(stead.deref(o)); 865 else 866 self:add(stead.deref(o), pos); 867 pos = pos + 1; 868 end 869 end 870end 871 872stead.list_del = function(self, name) 873 local v,n 874 v, n = self:srch(name); 875 if n == nil then 876 return nil; 877 end 878 self.__modified__ = true 879 if n <= #self then 880 v = stead.table.remove(self, n); 881 else 882 v = self[n]; 883 self[n] = nil -- for spare lists 884 end 885 return v 886end 887 888stead.list_purge = function(self, name) 889 local v,n 890 v, n = self:srch(name, true); 891 if n == nil then 892 return nil; 893 end 894 self.__modified__ = true 895 v = stead.table.remove(self, n); 896 if not v then 897 v = self[n]; 898 self[n] = nil -- for spare lists 899 end 900 return v 901end 902 903stead.list_replace = function(self, name, name2) 904 local o, ii 905 o, ii = self:srch(name); 906 if ii then 907 self:set(name2, ii); 908 else 909 self:add(name2); 910 end 911 return ii; 912end 913 914function list(v) 915 v.list_type = true; 916 v.add = stead.list_add; 917 v.set = stead.list_set; 918 v.cat = stead.list_concat; 919 v.zap = stead.list_zap; 920 v.del = stead.list_del; 921 v.purge = stead.list_purge; 922 v.replace = stead.list_replace; 923 v.look = stead.list_find; 924 v.name = stead.list_name; 925 v.byid = stead.list_id; 926 v.srch = stead.list_search; 927 v.str = stead.list_str; 928 v.check = stead.list_check; 929 v.save = stead.list_save; 930 v.enable = stead.list_enable; 931 v.disable = stead.list_disable; 932 v.enable_all = stead.list_enable_all; 933 v.disable_all = stead.list_disable_all; 934 return v; 935end 936 937function isList(v) 938 return (stead.type(v) == 'table') and (v.list_type == true) 939end 940 941stead.call = function(v, n, ...) 942 if stead.type(v) ~= 'table' then 943 error ("Call on non table object:"..stead.tostr(n), 2); 944 end 945 if v[n] == nil then 946 return nil,nil; 947 end 948 if stead.type(v[n]) == 'string' then 949 return v[n]; 950 end 951 if stead.type(v[n]) == 'function' then 952 stead.callpush(v, ...) 953 local a,b = v[n](v, ...); 954 -- boolean, nil 955 if stead.type(a) == 'boolean' and b == nil then 956 b, a = a, stead.pget() 957 if a == nil then 958 if stead.cctx().action then 959 a = true 960 else 961 a = b 962 b = nil 963 end 964 end 965 elseif a == nil and b == nil then 966 a = stead.pget() 967 b = nil 968 end 969 if a == nil and b == nil and stead.cctx().action then 970 a = true 971 end 972 stead.callpop() 973 return a,b 974 end 975 if stead.type(v[n]) == 'boolean' then 976 return v[n] 977 end 978 error ("Method not string nor function:"..stead.tostr(n), 2); 979end 980 981stead.call_bool = function(v, n, ...) 982 if stead.type(v) ~= 'table' then 983 error ("Call bool on non table object:"..n, 2); 984 end 985 986 if v[n] == nil then 987 return nil 988 end 989 990 if v[n] == false then 991 return false; 992 end 993 994 if stead.type(v[n]) == 'function' then 995 stead.callpush(v, ...) 996 local r,v = v[n](v, ...); 997 stead.callpop(); 998 return r,v; 999 end 1000 return true; -- not nil 1001end 1002 1003stead.call_value = function(v, n, ...) 1004 if stead.type(v) ~= 'table' then 1005 error ("Call value on non table object:"..n, 2); 1006 end 1007 1008 if v[n] == nil then 1009 return nil 1010 end 1011 1012 if stead.type(v[n]) ~= 'function' then 1013 return v[n]; 1014 end 1015 stead.callpush(v, ...) 1016 local r,v = v[n](v, ...); 1017 stead.callpop(); 1018 return r,v; 1019end 1020 1021stead.room_scene = function(self) 1022 local v; 1023 v = iface:title(stead.call(self,'nam')); 1024 v = stead.par(stead.scene_delim, v, stead.call(self,'dsc')); --obj_look(self)); 1025 return stead.cat(v, stead.space_delim); 1026end 1027 1028stead.room_look = function(self) 1029 local vv; 1030 for i, o in stead.opairs(self.obj) do 1031 o = stead.ref(o); 1032 if isObject(o) then 1033 vv = stead.par(stead.space_delim, vv, o:look()); 1034 end 1035 end 1036 return stead.cat(vv, stead.space_delim); 1037end 1038 1039stead.obj_search = function(v, n, dis) 1040 local i; 1041 local o; 1042 if not dis and isDisabled(v) then 1043 return 1044 end 1045 o = v.obj:srch(n, dis); 1046 if o then 1047 return o, v; 1048 end 1049 for i, o in stead.opairs(v.obj) do 1050 o = stead.ref(o); 1051 if isObject(o) then 1052 local r,rr = stead.obj_search(o, n, dis); 1053 if r then 1054 return r, rr; 1055 end 1056 end 1057 end 1058 return; 1059end 1060 1061stead.room_save = function(self, name, h, need) 1062 local dsc; 1063 if need then 1064 print ("Warning: room "..name.." can not be saved!"); 1065 return 1066 end 1067 stead.savemembers(h, self, name, need); 1068end 1069 1070function room(v) --constructor 1071-- if v.nam == nil then 1072-- error ("No room name in constructor.", 2); 1073-- end 1074 if v.scene == nil then 1075 v.scene = stead.room_scene; 1076 end 1077 if v.look == nil then 1078 v.look = stead.room_look; 1079 end 1080 if v.save == nil then 1081 v.save = stead.room_save; 1082 end 1083 v.location_type = true; 1084 if v.way == nil then 1085 v.way = { }; 1086 end 1087 v.way = list(v.way); 1088 v = obj(v); 1089 return v; 1090end 1091 1092stead.dialog_enter = function(self) 1093 if not stead.dialog_rescan(self) then 1094 return nil, false 1095 end 1096 return nil, true 1097end 1098 1099stead.dialog_scene = function(self) 1100 local v 1101 v = iface:title(stead.call(self,'nam')); 1102 v = stead.par(stead.scene_delim, v, stead.call(self, 'dsc')); --obj_look(self)); 1103 return v; 1104end 1105 1106stead.dialog_look = function(self) 1107 local n, v 1108 n = 1 1109 for i, ph in stead.opairs(self.obj) do 1110 ph = stead.ref(ph); 1111 if isPhrase(ph) and not isDisabled(ph) then 1112 v = stead.par('^', v, txtnm(n, ph:look())); 1113 n = n + 1 1114 end 1115 end 1116 return v; 1117end 1118 1119stead.dialog_rescan = function(self) 1120 local k 1121 k = 1 1122 for i, ph in stead.opairs(self.obj) do 1123 ph = stead.ref(ph); 1124 if isPhrase(ph) and not isDisabled(ph) then 1125 ph.nam = stead.tostr(k); 1126 k = k + 1; 1127 end 1128 end 1129 if k == 1 then 1130 return false 1131 end 1132 return true 1133end 1134 1135stead.dialog_empty = function(self) 1136 return not stead.dialog_rescan(self); 1137end 1138 1139stead.dialog_phrase = function(self, num) 1140 if not stead.tonum(num) then 1141 if isPhrase(stead.ref(num)) then 1142 return stead.ref(num); 1143 end 1144 return nil 1145 end 1146 return stead.ref(self.obj[stead.tonum(num)]); 1147end 1148 1149stead.phrase_seen = function(s, enb, ...) 1150 local ph 1151 local a = {...} 1152 if stead.table.maxn(a) == 0 then 1153 stead.table.insert(a, stead.cctx().self); 1154 end 1155 for i = 1,stead.table.maxn(a) do 1156 ph = stead.dialog_phrase(s, a[i]); 1157 local r = not isPhrase(ph) or isRemoved(ph) or ph:disabled(); 1158 if not enb then r = not r end 1159 if r then return false end 1160 end 1161 return true 1162end 1163 1164stead.dialog_pseen = function(s, ...) 1165 return stead.phrase_seen(s, true, ...); 1166end 1167 1168stead.dialog_punseen = function(s, ...) 1169 return stead.phrase_seen(s, false, ...); 1170end 1171 1172local function ponoff(s, on, ...) 1173 local ph 1174 local a = {...} 1175 if stead.table.maxn(a) == 0 then 1176 stead.table.insert(a, stead.cctx().self) 1177 end 1178 for i = 1,stead.table.maxn(a) do 1179 ph = stead.dialog_phrase(s, a[i]); 1180 if isPhrase(ph) and not isRemoved(ph) then 1181 if on then 1182 ph:enable(); 1183 else 1184 ph:disable(); 1185 end 1186 end 1187 end 1188end 1189 1190stead.dialog_prem = function(s, ...) 1191 local ph 1192 local a = {...} 1193 if stead.table.maxn(a) == 0 then 1194 stead.table.insert(a, stead.cctx().self); 1195 end 1196 for i = 1,stead.table.maxn(a) do 1197 ph = stead.dialog_phrase(s, a[i]); 1198 if isPhrase(ph) then 1199 ph:remove(); 1200 end 1201 end 1202end 1203 1204stead.dialog_pon = function(self,...) 1205 return ponoff(self, true, ...); 1206end 1207 1208stead.dialog_poff = function(self,...) 1209 return ponoff(self, false, ...); 1210end 1211 1212function dlg(v) --constructor 1213 v.dialog_type = true; 1214 if v.ini == nil then 1215 v.ini = stead.dialog_enter; 1216 end 1217 if v.enter == nil then 1218 v.enter = stead.dialog_enter; 1219 end 1220 if v.look == nil then 1221 v.look = stead.dialog_look; 1222 end 1223 if v.scene == nil then 1224 v.scene = stead.dialog_scene; 1225 end 1226 if v.pon == nil then 1227 v.pon = stead.dialog_pon; 1228 end 1229 if v.poff == nil then 1230 v.poff = stead.dialog_poff; 1231 end 1232 if v.prem == nil then 1233 v.prem = stead.dialog_prem; 1234 end 1235 if v.pseen == nil then 1236 v.pseen = stead.dialog_pseen; 1237 end 1238 if v.punseen == nil then 1239 v.punseen = stead.dialog_punseen; 1240 end 1241 if v.empty == nil then 1242 v.empty = stead.dialog_empty; 1243 end 1244 v = room(v); 1245 return v; 1246end 1247 1248stead.phrase_action = function(self) 1249 local ph = self; 1250 local r, ret; 1251 1252 if isDisabled(ph) then 1253 return nil, false 1254 end 1255-- here it is 1256 ph:disable(); -- /* disable it!!! */ 1257 1258 local last = stead.call(ph, 'ans'); 1259 1260 if stead.type(ph.do_act) == 'string' then 1261 local f = stead.eval(ph.do_act); 1262 if f ~= nil then 1263 ret = f(); 1264 else 1265 error ("Error while eval phrase action."); 1266 end 1267 elseif stead.type(ph.do_act) == 'function' then 1268 ret = ph.do_act(self); 1269 end 1270 1271 if ret == nil then ret = stead.pget(); end 1272 1273 if last == true or ret == true then 1274 r = true; 1275 end 1276 1277 local wh = stead.here(); 1278 1279 while isDialog(wh) and not stead.dialog_rescan(wh) and stead.from(wh) ~= wh do 1280 wh = stead.from(wh) 1281 end 1282 1283 if wh ~= stead.here() then 1284 ret = stead.par(stead.space_delim, ret, stead.back(wh)); 1285 end 1286 1287 ret = stead.par(stead.scene_delim, last, ret); 1288 1289 if ret == nil then 1290 return r -- hack? 1291 end 1292 return ret 1293end 1294 1295stead.phrase_save = function(self, name, h, need) 1296 if need then 1297 local m = " = phr(" 1298 if isDisabled(self) then 1299 m = " = _phr(" 1300 end 1301 h:write(stead.string.format("%s%s%s,%s,%s);\n", 1302 name, m, 1303 stead.tostring(self.dsc), 1304 stead.tostring(self.ans), 1305 stead.tostring(self.do_act))); 1306 end 1307 stead.savemembers(h, self, name, false); 1308end 1309 1310stead.phrase_look = function(self, n) 1311 if isDisabled(self) then 1312 return 1313 end 1314 local v = stead.call(self, 'dsc'); 1315 if stead.type(v) ~= 'string' then return; end 1316 if game.hinting then 1317 return self:xref('{'..v..'}'); 1318 end 1319 return v; 1320end 1321 1322function phrase(o) --constructor 1323 local ret = o; 1324 ret.look = stead.phrase_look; 1325 ret.nam = ''; -- for start 1326 ret.phrase_type = true; 1327 ret.act = stead.phrase_action; 1328 ret.save = stead.phrase_save; 1329 ret = obj(ret); 1330 return ret; 1331end 1332 1333function _phr(ask, answ, act) 1334 local p = phrase ({ dsc = ask, ans = answ, do_act = act }); 1335 p:disable(); 1336 return p; 1337end 1338 1339function phr(ask, answ, act) 1340 local p = phrase ({ dsc = ask, ans = answ, do_act = act }); 1341-- p:enable(); 1342 return p; 1343end 1344 1345stead.player_inv = function(self) 1346 return iface:inv(stead.cat(self:str())); 1347end 1348 1349stead.player_ways = function(self) 1350 return iface:ways(stead.cat(stead.ref(self.where).way:str())); 1351end 1352 1353stead.player_objs = function(self) 1354 return iface:objs(stead.cat(stead.ref(self.where):str())); 1355end 1356 1357stead.player_look = function(self) 1358 return stead.ref(self.where):scene(); 1359end 1360 1361stead.obj_tag = function(self, id) 1362 if isDisabled(self) then 1363 return id 1364 end 1365 1366 for k, v in stead.opairs(self.obj) do 1367 v = stead.ref(v); 1368 if isObject(v) and not isDisabled(v) and v.id ~= false then 1369 id = id + 1; 1370 v.id = id; 1371 id = stead.obj_tag(v, id); 1372 end 1373 end 1374 return id; 1375end 1376 1377stead.player_tagall = function(self) 1378 local id = 0; 1379 id = stead.obj_tag(stead.here(), id); 1380 for k, v in stead.opairs(ways()) do 1381 v = stead.ref(v); 1382 if isRoom(v) and not isDisabled(v) then 1383 id = id + 1; 1384 v.id = id; 1385 end 1386 end 1387 stead.rawset(_G, 'MENU_TAG_ID', id) 1388 id = stead.obj_tag(stead.me(), id); 1389end 1390 1391stead.player_action = function(self, what, ...) 1392 local v,r,obj 1393 obj = stead.ref(self.where):srch(what); 1394 if not obj then 1395 return stead.call(stead.ref(game), 'action', what, ...); --player_do(self, what, ...); 1396 end 1397 v, r = stead.player_take(self, what, ...); 1398 if not v then 1399 v, r = stead.call(stead.ref(obj), 'act', ...); 1400 if not v and r ~= true then 1401 v, r = stead.call(stead.ref(game), 'act', obj, ...); 1402 end 1403 end 1404 return v, r; 1405end 1406 1407stead.player_take = function(self, what, ...) 1408 local v,r,obj,w 1409 obj,w = stead.ref(self.where):srch(what); 1410 if not obj then 1411 return nil, false; 1412 end 1413 v,r = stead.call(stead.ref(obj), 'tak', ...); 1414 if v and r ~= false then 1415 take(obj, w); 1416 end 1417 return v; 1418end 1419 1420stead.player_use = function(self, what, onwhat, ...) 1421 local obj, obj2, v, vv, r; 1422 local scene_use_mode = false 1423 1424 obj = self:srch(what); -- in inv? 1425 if not obj then -- no 1426 obj = stead.ref(self.where):srch(what); -- in scene? 1427 if not obj then -- no! 1428 return game.err, false; 1429 end 1430 scene_use_mode = true -- scene_use_mode! 1431 end 1432 if onwhat == nil then -- only one? 1433 if scene_use_mode then 1434 return self:action(what, ...); -- call act 1435 else 1436 v, r = stead.call(stead.ref(obj),'inv', ...); -- call inv 1437 end 1438 if not v and r ~= true then 1439 v, r = stead.call(game, 'inv', obj, ...); 1440 end 1441 return v, r; 1442 end 1443 obj2 = stead.ref(self.where):srch(onwhat); -- in scene? 1444 if not obj2 then 1445 obj2 = self:srch(onwhat); -- in inv? 1446 end 1447 if not obj2 or obj2 == obj then 1448 return game.err, false; 1449 end 1450 if not scene_use_mode or isSceneUse(stead.ref(obj)) then 1451 v, r = stead.call(stead.ref(obj), 'use', obj2, ...); 1452 if r ~= false then 1453 vv = stead.call(stead.ref(obj2), 'used', obj, ...); 1454 end 1455 end 1456 if not v and not vv then 1457 v, r = stead.call(game, 'use', obj, obj2, ...); 1458 end 1459 return stead.par(stead.space_delim, v, vv); 1460end 1461 1462stead.player_back = function(self) 1463 local where = stead.ref(self.where); 1464 if where == nil then 1465 return nil,false 1466 end 1467 return stead.go(self, where.__from__, true); 1468end 1469 1470stead.go = function(self, where, back) 1471 local was = self.where; 1472 local need_scene = false; 1473 local ret 1474 1475 if not stead.in_walk_call then 1476 ret = function(rc) stead.in_walk_call = false return nil end 1477 else 1478 ret = function(rc) return rc end 1479 end 1480 1481 stead.in_walk_call = true 1482 1483 if where == nil then 1484 return nil,ret(false) 1485 end 1486 if not isRoom(stead.ref(where)) then 1487 error ("Trying to go nowhere: "..where, 2); 1488 end 1489 if not isRoom(stead.ref(self.where)) then 1490 error ("Trying to go from nowhere: "..self.where, 2); 1491 end 1492 1493 if stead.in_entered_call or stead.in_onexit_call then 1494 error ("Do not use walk from onexit/entered action! Use exit/enter action instead:" .. self.where, 2); 1495 end 1496 1497 local v, r; 1498 if not isVroom(stead.ref(where)) and not stead.in_exit_call then 1499 stead.in_exit_call = true -- to break recurse 1500 v,r = stead.call(stead.ref(self.where), 'exit', where); 1501 stead.in_exit_call = nil 1502 if r == false then 1503 return v, ret(r) 1504 end 1505 end 1506 1507 local res = v; 1508 1509 v = nil; 1510 if not back or not isDialog(stead.ref(self.where)) or isDialog(stead.ref(where)) then 1511 v, r = stead.call(stead.ref(where), 'enter', self.where); 1512 if r == false then 1513 return v, ret(r) 1514 end 1515 need_scene = true; 1516 if stead.ref(was) ~= stead.ref(self.where) then -- jump !!! 1517 where = stead.deref(self.where); 1518 need_scene = false; 1519 end 1520 end 1521 res = stead.par(stead.scene_delim, res, v); 1522 1523 if not back then 1524 stead.ref(where).__from__ = stead.deref(self.where); 1525 end 1526 1527 self.where = stead.deref(where); 1528 1529 ret(); 1530 1531 stead.rawset(_G, 'PLAYER_MOVED', true) 1532 if need_scene then -- or isForcedsc(stead.ref(where)) then -- i'am not sure... 1533 return stead.par(stead.scene_delim, res, stead.ref(where):scene()); 1534 end 1535 return res; 1536end 1537 1538stead.player_walk = function(self, where, ...) 1539 local v, r = stead.go(self, where, ...); 1540 return v, r; 1541end 1542 1543stead.player_go = function(self, where) 1544 local w = stead.ref(self.where).way:srch(where); 1545 if not w then 1546 return nil,false 1547 end 1548 local v, r = stead.go(self, w, false); 1549 return v, r; 1550end 1551 1552stead.player_save = function(self, name, h) 1553 h:write(stead.string.format('%s.where = %q;\n', stead.tostr(name), stead.deref(self.where))); 1554 stead.savemembers(h, self, name, false); 1555end 1556 1557function player(v) 1558 if v.nam == nil then 1559 error ("No player name in constructor.", 2); 1560 end 1561 if v.where == nil then 1562 v.where = 'main'; 1563 end 1564 if v.tag == nil then 1565 v.tag = stead.player_tagall; 1566 end 1567 if v.walk == nil then 1568 v.walk = stead.player_walk; 1569 end 1570 if v.go == nil then 1571 v.go = stead.player_go; 1572 end 1573 if v.ways == nil then 1574 v.ways = stead.player_ways; 1575 end 1576 if v.back == nil then 1577 v.back = stead.player_back; 1578 end 1579 if v.look == nil then 1580 v.look = stead.player_look; 1581 end 1582 if v.inv == nil then 1583 v.inv = stead.player_inv; 1584 end 1585 if v.use == nil then 1586 v.use = stead.player_use; 1587 end 1588 if v.action == nil then 1589 v.action = stead.player_action; 1590 end 1591 if v.save == nil then 1592 v.save = stead.player_save; 1593 end 1594 if v.objs == nil then 1595 v.objs = stead.player_objs; 1596 end 1597 v.player_type = true; 1598 return obj(v); 1599end 1600 1601stead.game_life = function(self) 1602 local av, v 1603 stead.in_life_call = true; 1604 stead.in_life_move = false 1605 stead.lifes_op = {}; -- lifes to on/off 1606 stead.PLAYER_MOVED = stead.rawget(_G, 'PLAYER_MOVED') 1607 for i, o in stead.opairs(self.lifes) do 1608 local vv 1609 local pre 1610 o = stead.ref(o); 1611 if not isDisabled(o) then 1612 stead.rawset(_G, 'PLAYER_MOVED', false) 1613 vv,pre = stead.call(o, 'life'); 1614 if stead.rawget(_G, 'PLAYER_MOVED') then -- clear life output, but not current 1615 av = nil 1616 v = nil 1617 stead.in_life_move = true 1618 stead.rawset(_G, 'ACTION_TEXT', vv); 1619 elseif pre then 1620 av = stead.par(stead.space_delim, av, vv); 1621 else 1622 v = stead.par(stead.space_delim, v, vv); 1623 end 1624 end 1625 end 1626 stead.rawset(_G, 'PLAYER_MOVED', stead.in_life_move) 1627 if not stead.rawget(_G, 'PLAYER_MOVED') then stead.rawset(_G, 'PLAYER_MOVED', stead.PLAYER_MOVED) end 1628 stead.PLAYER_MOVED = nil 1629 stead.in_life_call = false; 1630 for i,o in stead.ipairs(stead.lifes_op) do 1631 if o[1] then 1632 stead.lifeon(o[2], o[3]); 1633 else 1634 stead.lifeoff(o[2]); 1635 end 1636 end 1637 stead.lifes_op = nil; 1638 return v, av; 1639end 1640 1641stead.player_moved = function() 1642 return stead.rawget(_G, 'PLAYER_MOVED') or stead.PLAYER_MOVED 1643end 1644 1645stead.life_moved = function() 1646 return stead.in_life_move 1647end 1648 1649stead.check_list = function(k, v, p) 1650 if v.check == nil or not v:check(stead.string.format("%s[%q]", p, k)) then 1651 error ("error in list: "..stead.object..'.'..k); 1652 end 1653end 1654 1655stead.check_room = function(k, v) 1656 if v.obj == nil then 1657 error("no obj in room:"..k); 1658 end 1659 if v.way == nil then 1660 error("no way in room:"..k); 1661 end 1662end 1663 1664stead.check_player = function(k, v) 1665 v.where = stead.deref(v.where); 1666end 1667 1668stead.check_object = function(k, v) 1669 if not v.nam then 1670 error ("No name in "..k); 1671 end 1672 if isRoom(v) then 1673 stead.check_room(k, v); 1674 end 1675 if isPlayer(v) then 1676 stead.check_player(k, v); 1677 end 1678 for_each(v, k, stead.check_list, isList, stead.deref(v)) 1679end 1680 1681function for_everything(f, ...) 1682 local is_ok = function(s) 1683 return true 1684 end 1685 for_each(_G, '_G', f, is_ok, ...) 1686end 1687 1688local compat_api = function() 1689 if stead.compat_api then 1690 return 1691 end 1692 1693 if not stead.api_atleast(1, 6, 0) then 1694 if not stead.rawget(_G, 'go') then 1695 go = stead.go 1696 end 1697 if not stead.rawget(_G, 'goin') then 1698 goin = walkin 1699 end 1700 if not stead.rawget(_G, 'goout') then 1701 goout = walkout 1702 end 1703 if not stead.rawget(_G, 'goback') then 1704 goback = walkback 1705 end 1706 if not stead.rawget(_G, "goto") then 1707 if _VERSION == "Lua 5.1" then -- 5.1 lua 1708 stead.rawset(_G, "goto", walk) 1709 end 1710 end 1711 end 1712 1713 if not stead.api_atleast(1, 7, 1) then 1714 if not stead.rawget(_G, 'goin') then 1715 goin = function() error ("Please use 'walkin' instead 'goin'.", 2) end 1716 end 1717 if not stead.rawget(_G, 'goout') then 1718 goout = function() error ("Please use 'walkout' instead 'goout'.", 2) end 1719 end 1720 if not stead.rawget(_G, 'goback') then 1721 goback = function() error ("Please use 'walkback' instead 'goback'.", 2) end 1722 end 1723 if not stead.rawget(_G, "goto") then 1724 if _VERSION == "Lua 5.1" then -- 5.1 lua 1725 stead.rawset(_G, "goto", function() error ("Please use 'walk' instead 'goto'.", 2) end) 1726 end 1727 end 1728 1729 get_savepath = instead_savepath 1730 get_gamepath = instead_gamepath 1731 get_steadpath = instead_steadpath 1732 get_gamespath = instead_gamespath 1733 1734 menu_toggle = instead_menu_toggle 1735 readdir = instead_readdir 1736 1737 call = stead.call 1738 call_bool = stead.call_bool 1739 call_value = stead.call_value 1740 1741 get_title = stead.get_title 1742 get_picture = stead.get_picture 1743 get_inv = stead.get_inv 1744 get_ways = stead.get_ways 1745 1746 get_autosave = stead.get_autosave 1747 1748 fmt = stead.fmt 1749 1750 obj_tag = stead.obj_tag 1751 1752 module_init = stead.module_init 1753 1754 player_inv = stead.player_inv 1755 dialog_enter = stead.dialog_enter 1756 end 1757 1758 if not stead.api_atleast(1, 4, 5) then 1759 stead.xref = function(...) 1760 return xref(...); 1761 end 1762 1763 -- internals of call 1764 cctx = stead.cctx 1765 callpush = stead.callpush 1766 callpop = stead.callpop 1767 clearargs = stead.clearargs 1768 -- saving 1769 savemembers = stead.savemembers; 1770 savevar = stead.savevar 1771 clearvar = stead.clearvar 1772 end 1773 1774 stead.compat_api = true 1775end 1776 1777stead.do_ini = function(self, load) 1778 local v = '' 1779 local vv 1780 local function call_key(k, o) 1781 o.key_name = k; 1782 end 1783 local function call_codekey(k, o) 1784 stead.functions[o].key_name = k; 1785 end 1786 local function call_ini(k, o, ...) 1787 v = stead.par('', v, stead.call(o, 'ini', ...)); 1788 end 1789 stead.math.randomseed(stead.os.time(stead.os.date("*t"))) 1790 stead.rnd(1); stead.rnd(2); stead.rnd(3); -- Lua bug? 1791 if stead.type(game) ~= 'table' then 1792 error ("No valid 'game' object."); 1793 end 1794 if not isPlayer(stead.me()) then 1795 error ("No valid player."); 1796 end 1797 if not isRoom(stead.here()) then 1798 error ("No valid room."); 1799 end 1800 game.pl = stead.deref(game.pl); 1801 stead.me().where = stead.deref(stead.me().where); 1802-- game.where = stead.deref(game.where); 1803 1804 for i, f in ipairs(stead.modules_start) do 1805 f(load) 1806 end 1807 1808 if not load then 1809 compat_api() 1810 for_everything(function(k, s) 1811 if isObject(s) then 1812 call_key(k, s) 1813 elseif isCode(s) then 1814 call_codekey(k, s) 1815 end 1816 end) 1817 for_each_object(stead.check_object); 1818 call_key("game", game); 1819 for_each(game, "game", stead.check_list, isList, stead.deref(game)) 1820 end 1821 1822 for_each_object(call_ini, load); 1823 stead.me():tag(); 1824 stead.initialized = true 1825 return v 1826end 1827 1828stead.game_ini = function(self) 1829 local v,vv 1830 v = stead.do_ini(self); 1831 vv = iface:title(stead.call(self,'nam')); 1832 vv = stead.par(stead.scene_delim, vv, stead.call(self,'dsc')); 1833 if stead.type(stead.rawget(_G, 'init')) == 'function' then 1834 init(); 1835 end 1836 return stead:fmt(stead.par(stead.scene_delim, vv, v)); 1837end 1838 1839stead.game_start = function(s) 1840 if stead.type(stead.rawget(_G, 'start')) == 'function' then 1841 start() -- start function 1842 end 1843 stead.started = true 1844 if not s.showlast then 1845 stead.last_disp(false) 1846 end 1847 return stead.cat('', stead.last_disp()) 1848end 1849 1850function game(v) 1851 if v.nam == nil then 1852 error ("No game name in constructor.", 2); 1853 end 1854 if v.pl == nil then 1855 v.pl = 'player'; 1856 end 1857 if v.ini == nil then 1858 v.ini = stead.game_ini; 1859 end 1860 if v.start == nil then 1861 v.start = stead.game_start 1862 end 1863 if v.save == nil then 1864 v.save = stead.game_save; 1865 end 1866 if v.load == nil then 1867 v.load = stead.game_load; 1868 end 1869 if v.life == nil then 1870 v.life = stead.game_life; 1871 end 1872 if v.step == nil then 1873 v.step = stead.game_step; 1874 end 1875 if v.lifes == nil then 1876 v.lifes = {}; 1877 end 1878 v.lifes = list(v.lifes); 1879 v._time = 0; 1880 v._running = true; 1881 v.game_type = true; 1882 return v; 1883end 1884 1885function live(v) 1886 return stead.ref(game.lifes:srch(v)); 1887end 1888 1889function isEnableSave() 1890 if game.enable_save == nil or stead.get_autosave() then 1891 return true 1892 end 1893 return stead.call_bool(game, 'enable_save'); 1894end 1895 1896function isEnableAutosave() 1897 if game.enable_autosave == nil then 1898 return true 1899 end 1900 return stead.call_bool(game, 'enable_autosave'); 1901end 1902 1903function for_each(o, n, f, fv, ...) 1904 local call_list = {} 1905 1906 if stead.type(o) ~= 'table' then 1907 return 1908 end 1909 stead.object = n; 1910 1911 for k, v in stead.pairs(o) do 1912 if v ~= _G and fv(v) then 1913 stead.table.insert(call_list, { k = k, v = v }); 1914 end 1915 end 1916 1917 for k, v in stead.ipairs(call_list) do 1918 f(v.k, v.v, ...); 1919 end 1920end 1921 1922function isCode(s) 1923 return stead.type(s) == 'function' and stead.type(stead.functions[s]) == 'table' 1924end 1925function for_each_codeblock(f,...) 1926 for_each(_G, '_G', f, isCode, ...) 1927end 1928 1929function for_each_object(f,...) 1930 for_each(_G, '_G', f, isObject, ...) 1931end 1932 1933function for_each_player(f,...) 1934 for_each(_G, '_G', f, isPlayer, ...) 1935end 1936 1937function for_each_room(f,...) 1938 for_each(_G, '_G', f, isRoom, ...) 1939end 1940 1941function for_each_list(f,...) 1942 for_each(_G, '_G', f, isList, ...) 1943end 1944 1945stead.clearvar = function(v) 1946 for k, o in stead.pairs(v) do 1947 if stead.type(o) == 'table' and o ~= _G and o.__visited__ ~= nil then 1948 o.__visited__ = nil 1949 o.auto_saved = nil 1950 stead.clearvar(o) 1951 end 1952 end 1953end 1954 1955stead.savemembers = function(h, self, name, need) 1956 for k, v in stead.pairs(self) do 1957 local need2 1958 if k ~= "__visited__" then 1959 need2 = false 1960 if isForSave(k, v, self) then 1961 need2 = true; 1962 end 1963 1964 if stead.type(k) == 'string' then 1965 stead.savevar(h, v, name..'['..stead.string.format("%q",k)..']', need or need2); 1966 elseif stead.type(k) == 'number' then 1967 stead.savevar(h, v, name.."["..k.."]", need or need2) 1968 elseif stead.type(k) == 'table' and stead.type(k.key_name) == 'string' then 1969 stead.savevar(h, v, name.."["..k.key_name.."]", need or need2) 1970 end 1971 end 1972 end 1973end 1974-- savemembers = stead.savemembers; 1975 1976stead.savevar = function(h, v, n, need) 1977 local r,f 1978 1979 if v == nil or stead.type(v) == "userdata" or 1980 stead.type(v) == "function" then 1981 if isCode(v) and need then 1982 if stead.type(stead.functions[v].key_name) == 'string' 1983 and stead.functions[v].key_name ~= n then 1984 h:write(stead.string.format("%s=%s\n", n, stead.functions[v].key_name)) 1985 else 1986 h:write(stead.string.format("%s=code %q\n", n, stead.functions[v].code)) 1987 end 1988 end 1989-- if need then 1990-- error ("Variable "..n.." can not be saved!"); 1991-- end 1992 return 1993 end 1994 1995-- if stead.string.find(n, '_') == 1 or stead.string.match(n,'^%u') then 1996-- need = true; 1997-- end 1998 1999 if stead.type(v) == "string" then 2000 if not need then 2001 return 2002 end 2003 h:write(stead.string.format("%s=%q\n",n,v)) 2004 return; 2005 end 2006 2007 if stead.type(v) == "table" then 2008 if v == _G then return end 2009 if stead.type(v.key_name) == 'string' and v.key_name ~= n then -- just xref 2010 if v.auto_allocated and not v.auto_saved then 2011 v:save(v.key_name, h, false, true); -- here todo 2012 end 2013 if need then 2014 if stead.ref(v.key_name) == nil then 2015 v.key_name = 'null' 2016 end 2017 h:write(stead.string.format("%s = %s\n", n, v.key_name)); 2018 end 2019 return 2020 end 2021 if v.__visited__ ~= nil then 2022 return 2023 end 2024 2025 v.__visited__ = n; 2026 2027 if stead.type(v.save) == 'function' then 2028 v:save(n, h, need); 2029 return; 2030 end 2031 2032 if need then 2033 h:write(n.." = {};\n"); 2034 end 2035 2036 stead.savemembers(h, v, n, need); 2037 return; 2038 end 2039 2040 if not need then 2041 return 2042 end 2043 h:write(n, " = ",tostring(v)) 2044 h:write("\n") 2045end 2046-- savevar = stead.savevar 2047 2048stead.gamereset = function(file, forget) 2049 stead.clearargs() 2050 if stead.api_atleast(2, 2, 0) then 2051 init = function() -- init called only once 2052 end 2053 end 2054 if forget then 2055 stead:done(); 2056 stead.rawset(_G, 'init', function() -- null init function 2057 end) 2058 stead.rawset(_G, 'start', function() -- null start function 2059 end) 2060 for_each_object(function(k, o) -- destroy all objects 2061 if o.system_type then 2062 return 2063 end 2064 stead.rawset(_G, k, nil) 2065 end); 2066 game._scripts = { } 2067 game.lifes:zap() 2068 game.scriptsforget = true 2069 stead.collectgarbage() 2070 -- anything else? 2071 stead:init(); 2072 end 2073 dofile(file); 2074 game:ini(); 2075 2076 if #game._scripts == 0 or file ~= game._scripts[#game._scripts] then 2077 if #game._scripts ~= 0 or file ~= 'main.lua' then 2078 stead.table.insert(game._scripts, file); 2079 end 2080 end 2081end 2082 2083stead.gamefile = function(file, forget) 2084 stead.gamereset(file, forget) 2085 if forget then 2086 game:start() 2087 return stead.walk(stead.here(), false, false, true); 2088 end 2089end 2090 2091 2092stead.do_savegame = function(s, h) 2093 stead.busy(true) 2094 local function save_object(key, value, h) 2095 stead.busy(true) 2096 stead.savevar(h, value, key, false); 2097 end 2098 local function save_var(key, value, h) 2099 stead.busy(true) 2100 stead.savevar(h, value, key, isForSave(key, value, _G)) 2101 end 2102 local forget = game.scriptsforget 2103 for i, v in stead.ipairs(s._scripts) do 2104 h:write(stead.string.format("stead.gamereset(%q,%s)\n", 2105 v, stead.tostr(forget))) 2106 forget = nil 2107 end 2108 save_object('allocator', allocator, h); -- always first! 2109 for_each_object(save_object, h); 2110 save_object('game', s, h); 2111 for_everything(save_var, h); 2112-- save_object('_G', _G, h); 2113 stead.clearvar(_G); 2114 stead.busy(false) 2115end 2116 2117stead.savename = function() 2118 return stead.call(stead.here(), 'nam'); 2119end 2120 2121stead.game_save = function(self, name, file) 2122 local h; 2123 2124 if file ~= nil then 2125 file:write(stead.string.format("%s.pl = %q\n", name, stead.deref(self.pl))); 2126 stead.savemembers(file, self, name, false); 2127 return nil, true 2128 end 2129 2130 if not isEnableSave() then 2131 return nil, false 2132 end 2133 2134 if name == nil then 2135 return nil, false 2136 end 2137 h = stead.io.open(name .. '.tmp', "wb"); 2138 if not h then 2139 return nil, false 2140 end 2141 local n 2142 if stead.type(stead.savename) == 'function' then 2143 n = stead.savename() 2144 end 2145 if stead.type(n) == 'string' and n ~= "" then 2146 h:write("-- $Name: "..n:gsub("\n","\\n").."$\n"); 2147 end 2148 stead.do_savegame(self, h); 2149 h:flush(); 2150 h:close(); 2151 stead.os.remove(name); 2152 stead.os.rename(name .. '.tmp', name); 2153 game.autosave = false; -- we have only one try for autosave 2154 stead.restart_game = false 2155 return nil; 2156end 2157 2158stead.game_load = function(self, name) 2159 if name == nil then 2160 return nil, false 2161 end 2162 2163 if stead.started then 2164 stead.gamereset('main.lua', true) 2165 end 2166 2167 local f, err = loadfile(name); 2168 if f then 2169 local i,r = f(); 2170 if r then 2171 return nil, false 2172 end 2173 i, r = stead.do_ini(self, true); 2174 if not stead.started then 2175 i, r = game:start() 2176 end 2177 return i, r 2178 end 2179 return nil, false 2180end 2181 2182 2183stead.game_step = function(self) 2184 self._time = self._time + 1; 2185 return self:life(self); 2186end 2187 2188 2189game = game { 2190 codepage = "UTF-8"; 2191 nam = [[INSTEAD -- Simple Text Adventure interpreter v]]..stead.version..[[ '2009-2020 by Peter Kosyh]]; 2192 dsc = [[ 2193Commands:^ 2194 look(or just enter), act <on what> (or just what), use <what> [on what], go <where>,^ 2195 back, inv, way, obj, quit, save <fname>, load <fname>.]]; 2196 pl ='pl'; 2197 showlast = true; 2198 _scripts = {}; 2199}; 2200 2201stead.strip = function(s) 2202 local s = stead.tostr(s); 2203 s = stead.string.gsub(s, '^[ \t]*', ''); 2204 s = stead.string.gsub(s, '[ \t]*$', ''); 2205 return s; 2206end 2207 2208function isForcedsc(v) 2209 local r,g 2210 r = stead.call_bool(v, 'forcedsc'); 2211 if r then 2212 return true 2213 end 2214 g = stead.call_bool(game, 'forcedsc', v); 2215 return g and r ~= false 2216end 2217 2218function isSceneUse(v) 2219 local o,g 2220 o = stead.call_bool(v, 'scene_use'); 2221 if o then 2222 return true 2223 end 2224 g = stead.call_bool(game, 'scene_use', v); 2225 return g and o ~= false 2226end 2227 2228iface = { 2229 anchor = function(self) 2230 return ''; 2231 end; 2232 img = function(self, str) 2233 return ''; 2234 end, 2235 nb = function(self, str) 2236 return str; 2237 end, 2238 em = function(self, str) 2239 return str; 2240 end, 2241 right = function(self, str) 2242 return str; 2243 end, 2244 left = function(self, str) 2245 return str; 2246 end, 2247 center = function(self, str) 2248 return str; 2249 end, 2250 just = function(self, str) 2251 return str; 2252 end, 2253 top = function(self, str) 2254 return str; 2255 end, 2256 bottom = function(self, str) 2257 return str; 2258 end, 2259 middle = function(self, str) 2260 return str; 2261 end, 2262 tab = function(self, str, al) 2263 return ''; 2264 end; 2265 bold = function(self, str) 2266 return str; 2267 end, 2268 under = function(self, str) 2269 return str; 2270 end, 2271 st = function(self, str) 2272 return str; 2273 end, 2274 enum = function(self, n, str) 2275 return n..' - '..str; 2276 end, 2277 xref = function(self, str, obj) 2278 local o = stead.ref(stead.here():srch(obj)); 2279 if not o then 2280 o = stead.ref(ways():srch(obj)); 2281 end 2282 if not o then 2283 o = stead.ref(stead.me():srch(obj)); 2284 end 2285 if not o or not o.id then 2286 return str; 2287 end 2288 local n = stead.tonum(stead.nameof(o)) 2289 return stead.cat(str, "("..stead.tostr(n or o.id)..")"); 2290 end, 2291 title = function(self, str) 2292 return "["..str.."]"; 2293 end, 2294 objs = function(self, str) 2295 return str; 2296 end, 2297 ways = function(self, str) 2298 return str; 2299 end, 2300 inv = function(self, str) 2301 return str; 2302 end, 2303 text = function(self, str) 2304 if str then 2305 print(str); 2306 end 2307 end, 2308 fmt = function(self, cmd, st, moved, r, av, objs, pv) -- st -- changed state (main win), move -- loc changed 2309 local l, vv 2310 if st and not moved then 2311 if cmd ~= 'look' then 2312 av = txtem(av); 2313 pv = txtem(pv); 2314 r = txtem(r); 2315 if isForcedsc(stead.here()) then 2316 l = stead.me():look(); 2317 end 2318 end 2319 end 2320 vv = stead.fmt(stead.cat(stead.par(stead.scene_delim, l, r, av, objs, pv), '^')); 2321 return vv 2322 end, 2323 cmd = function(self, inp) 2324 local r, v; 2325 v = false 2326 stead.state = false; -- changed state (main screen) 2327 local a = { }; 2328 local cmd; 2329 2330 stead.rawset(_G, 'RAW_TEXT', nil) 2331 stead.rawset(_G, 'PLAYER_MOVED', nil) 2332 stead.cache = {} 2333 cmd, a = stead.getcmd(inp); 2334 2335 for i, f in ipairs(stead.modules_cmd) do 2336 local r, v = f(cmd, stead.unpack(a)) 2337 if r ~= nil or v ~= nil then 2338 return r, v 2339 end 2340 end 2341 if cmd == '' then cmd = 'look' end 2342-- stead.me():tag(); 2343 local oldloc = stead.here(); 2344 if cmd == 'look' then 2345 stead.state = true 2346 r, v = stead.me():look(); 2347 elseif cmd == 'obj' then 2348 r, v = stead.me():objs(); 2349 elseif cmd == 'inv' then 2350 r, v = stead.me():inv(); 2351 elseif cmd == 'way' then 2352 r, v = stead.me():ways(); 2353 elseif cmd == 'ls' then 2354 r = stead.par(stead.scene_delim, stead.me():objs(), stead.me():inv(), stead.me():ways()); 2355 v = nil; 2356 elseif cmd == 'go' then 2357 stead.state = true 2358 r, v = stead.me():go(stead.unpack(a)); 2359 elseif cmd == 'back' then 2360 stead.state = true 2361 r, v = stead.me():go(stead.from()); 2362 elseif cmd == 'act' then 2363 stead.state = true 2364 r, v = stead.me():action(stead.unpack(a)); 2365 elseif cmd == 'use' then 2366 stead.state = true 2367 r, v = stead.me():use(stead.unpack(a)); 2368 elseif cmd == 'save' then 2369 r, v = game:save(stead.unpack(a)); 2370 elseif cmd == 'load' then 2371 r, v = game:load(stead.unpack(a)); 2372 if v ~= false and game.showlast then 2373 return r; 2374 end 2375 elseif cmd == 'wait' then -- nothing todo in game, skip tick 2376 v = nil; 2377 r = true; 2378 stead.state = true 2379 elseif cmd == 'nop' then -- inv only 2380 v = true; 2381 r = nil; 2382 stead.state = true 2383 else 2384 stead.state = true 2385 r, v = stead.me():action(cmd, stead.unpack(a)); 2386 end 2387 -- here r is action result, v - ret code value 2388 -- state -- game state changed 2389 if stead.state and r == nil and v == true then -- we do nothing 2390 return nil, true; -- menu 2391 end 2392 2393 if stead.state and r == nil and v == nil and stead.api_atleast(1, 3, 5) then -- new goto 2394 return nil, false -- really nothing 2395 end 2396 2397 if stead.rawget(_G, 'RAW_TEXT') and v ~= false then 2398 return stead.cat(r, '\n'), true; 2399 end 2400 2401 if v == false then 2402 return stead.cat(r, '\n'), false; 2403 end 2404 2405 stead.rawset(_G, 'ACTION_TEXT', r); -- here, life methods can redefine this 2406 2407 local av, pv -- av -- active lifes, pv -- background 2408 local vv 2409 2410 if stead.state then 2411 pv,av = game:step(); 2412 stead.me():tag(); 2413 vv = stead.here():look(); 2414 end 2415 2416 vv = self:fmt(cmd, stead.state, (oldloc ~= stead.here()) or stead.player_moved(), 2417 stead.act_text(), av, vv, pv); 2418 2419 if stead.state then 2420 stead.last_disp(vv or false) 2421 stead.last_act(stead.act_text()) 2422 end 2423 if vv == nil then -- nil is error 2424 vv = '' 2425 end 2426 return vv, true; -- action is here 2427 end, 2428 shell = function(self) 2429 local inp, i, k, cmd, a, n; 2430 stead.me():tag(); 2431 while game._running do 2432 inp = stead.io.read("*l"); 2433 if inp == 'quit' then 2434 break; 2435 end 2436 self:text(self:cmd(inp)); 2437 end 2438 end 2439}; 2440 2441 2442function me() 2443 return stead.ref(game.pl); 2444end 2445stead.me = me 2446 2447function where(s) 2448 if not isObject(stead.ref(s)) then error("Wrong parameter to where.", 2); end 2449 if isPlayer(stead.ref(s)) then 2450 return stead.ref(stead.ref(s).where); 2451 end 2452 return stead.ref(stead.ref(s).__where__); 2453end 2454 2455function here() 2456 return stead.ref(stead.me().where); 2457end 2458stead.here = here 2459 2460function from(w) 2461 if w == nil then 2462 w = stead.here(); 2463 else 2464 w = stead.ref(w); 2465 end 2466 return stead.ref(w.__from__); 2467end 2468stead.from = from 2469 2470stead.time = function(s) 2471 local n = game._time; 2472 if stead.type(s) == 'number' then 2473 game._time = s 2474 end 2475 return n 2476end 2477 2478function inv() 2479 return stead.me().obj; 2480end 2481 2482function objs(w) 2483 if not w then 2484 return stead.here().obj; 2485 else 2486 return stead.ref(w).obj; 2487 end 2488end 2489 2490function ways(w) 2491 if not w then 2492 return stead.here().way; 2493 else 2494 return stead.ref(w).way; 2495 end 2496end 2497 2498stead.xref = function(str, obj, ...) 2499 if stead.type(str) ~= 'string' then return nil; end; 2500 return iface:xref(str, obj, ...); 2501end 2502xref = stead.xref 2503 2504function pseen(...) 2505 if not isDialog(stead.here()) then 2506 return 2507 end 2508 return stead.here():pseen(...); 2509end 2510 2511function punseen(...) 2512 if not isDialog(stead.here()) then 2513 return 2514 end 2515 return stead.here():punseen(...); 2516end 2517 2518function pon(...) 2519 if not isDialog(stead.here()) then 2520 return 2521 end 2522 stead.here():pon(...); 2523end 2524 2525function poff(...) 2526 if not isDialog(stead.here()) then 2527 return 2528 end 2529 stead.here():poff(...); 2530end 2531 2532function prem(...) 2533 if not isDialog(stead.here()) then 2534 return 2535 end 2536 stead.here():prem(...); 2537end 2538 2539function lifeon(what, nr) 2540 if stead.in_life_call then 2541 stead.table.insert(stead.lifes_op, { true, what, nr }); 2542 return 2543 end 2544 game.lifes:add(what, nr); 2545end 2546stead.lifeon = lifeon 2547 2548function lifeoff(what) 2549 if stead.in_life_call then 2550 stead.table.insert(stead.lifes_op, { false, what }); 2551 return 2552 end 2553 game.lifes:del(what); 2554end 2555stead.lifeoff = lifeoff 2556 2557stead.allocator_save = function(s, name, h, need, auto) 2558 if s.auto_allocated and not auto then 2559 return 2560 end 2561 if need then 2562 if s.auto_allocated then -- in current realization always false 2563 local m = stead.string.format("allocator:new(%s, %s)\n", 2564 stead.tostring(s.constructor), 2565 stead.tostring(s.constructor)); 2566 h:write(m); 2567 else 2568 local m = stead.string.format(" = allocator:get(%s, %s)\n", 2569 stead.tostring(name), 2570 stead.tostring(s.constructor)); 2571 h:write(name..m); 2572 if stead.api_atleast(1, 3, 0) then 2573 m = stead.string.format("stead.check_object(%s, %s)\n", 2574 stead.tostring(name), 2575 name); 2576 h:write(m); 2577 end 2578 end 2579 end 2580 stead.savemembers(h, s, name, false); 2581 if s.auto_allocated then 2582 s.auto_saved = true 2583 end 2584end 2585 2586function new(str) 2587 if stead.type(str) ~= 'string' then 2588 error("Non string constructor in new.", 2); 2589 end 2590 return allocator:new(str); 2591end 2592 2593function delete(v) 2594 allocator:delete(v); 2595end 2596 2597stead.vobj_save = function(self, name, h, need) 2598 local dsc = self.dsc; 2599 local w = stead.deref(self.where); 2600 2601 if need then 2602 h:write(stead.string.format("%s = vobj(%s, %s, %s, %s);\n", 2603 name, 2604 stead.tostring(self.key), 2605 stead.tostring(self.nam), 2606 stead.tostring(dsc), 2607 stead.tostring(w))); 2608 2609 end 2610 stead.savemembers(h, self, name,false); 2611end 2612 2613stead.vobj_act = function(self, ...) 2614 local o, r = stead.here():srch(self); -- self.nam 2615 if stead.ref(o) and stead.ref(o).where then 2616 return stead.walk(stead.ref(o).where); 2617 end 2618 return stead.call(stead.ref(r),'act', self.key, ...); 2619end 2620 2621stead.vobj_used = function(self, ...) 2622 local o, r = stead.here():srch(self.nam); 2623 return stead.call(stead.ref(r),'used', self.key, ...); 2624end 2625 2626function vobj(key, name, dsc, w) 2627 if not stead.tonum(key) then 2628 error ("vobj key must be number!", 2); 2629 end 2630 return obj{ key = key, nam = name, dsc = dsc, where = stead.deref(w), act = stead.vobj_act, used = stead.vobj_used, save = stead.vobj_save, obj = list({}) }; 2631end 2632 2633function vway(name, dsc, w) 2634 return obj{ key = -1, nam = name, dsc = dsc, act = stead.vobj_act, where = stead.deref(w), used = stead.vobj_used, save = stead.vobj_save, obj = list({}), }; 2635end 2636 2637stead.vroom_save = function(self, name, h, need) 2638 if need then 2639 local t = stead.string.format("%s = vroom(%s, %q);\n", 2640 name, stead.tostring(self.nam), 2641 stead.deref(self.where)) 2642 h:write(t); 2643 end 2644 stead.savemembers(h, self, name,false); 2645end 2646 2647stead.vroom_enter = function(self, ...) 2648 return stead.walk(self.where); 2649end 2650 2651function isVroom(v) 2652 return (stead.type(v) == 'table') and (v.vroom_type) 2653end 2654 2655function vroom(name, w) 2656 if w == nil then 2657 error("Wrong parameter to vroom.", 2); 2658 end 2659 return room { vroom_type = true, nam = name, where = stead.deref(w), enter = stead.vroom_enter, save = stead.vroom_save, }; 2660end 2661 2662function walk(what) 2663 local v,r=stead.me():walk(what); 2664 stead.me():tag(); 2665 return v,r; 2666end 2667stead.walk = walk; 2668 2669function back() 2670 return stead.me():back(); 2671end 2672stead.back = back; 2673 2674stead.rnd = function(...) 2675 if stead.random then 2676 return stead.random(...) 2677 end 2678 return stead.math.random(...); 2679end 2680 2681stead.rndseed = function(...) 2682 if stead.randomseed then 2683 return stead.randomseed(...) 2684 end 2685 stead.math.randomseed(...) 2686end 2687 2688function taken(obj) 2689 if isObject(stead.ref(obj)) and stead.ref(obj)._taken then 2690 return true 2691 end 2692 return false; 2693end 2694 2695function remove(obj, from) 2696 local o,w 2697 from = stead.ref(from) 2698 if from then 2699 if isList(from) then 2700 return from:del(obj) 2701 end 2702 o,w = from:srch(obj); 2703 else 2704 o,w = stead.here():srch(obj); 2705 end 2706 if w then 2707 stead.ref(w).obj:del(obj); 2708 end 2709 o = stead.ref(o); 2710 if not isObject(o) then 2711 o = stead.ref(obj); 2712 end 2713 if isObject(o) then 2714 o.__where__ = nil; 2715 end 2716 return o 2717end 2718stead.remove = remove 2719 2720function purge(obj, from) 2721 local o,w 2722 from = stead.ref(from) 2723 if from then 2724 if isList(from) then 2725 return from:purge(obj) 2726 end 2727 o,w = from:srch(obj, true); 2728 else 2729 o,w = stead.here():srch(obj, true); 2730 end 2731 if w then 2732 stead.ref(w).obj:purge(obj); 2733 end 2734 o = stead.ref(o); 2735 if not isObject(o) then 2736 o = stead.ref(obj); 2737 end 2738 if isObject(o) then 2739 o.__where__ = nil; 2740 end 2741 return o 2742end 2743stead.purge = purge 2744 2745function taketo(obj, wh, pos) 2746 local o = remove(obj, wh); 2747 if not isObject(o) then 2748 error ("Trying to take wrong object.", 2); 2749 end 2750 inv():add(obj, pos); 2751 o._taken = true 2752 wh = stead.deref(stead.me()) 2753 if stead.type(wh) == 'string' then 2754 o.__where__ = wh; 2755 end 2756 return o 2757end 2758 2759function take(obj, wh) 2760 return taketo(obj, wh); 2761end 2762 2763function takef(obj, wh) 2764 return taketo(obj, wh, 1); 2765end 2766 2767function putto(obj, w, pos) 2768 local wh 2769 local o = stead.ref(obj); 2770 if not isObject(o) then 2771 error ("Trying to put wrong object.", 2); 2772 end 2773 if not w then 2774 wh = stead.deref(stead.here()); 2775 w = stead.here(); 2776 else 2777 wh = stead.deref(w); 2778 w = stead.ref(w); 2779 end 2780 if isList(w) then 2781 w:add(obj, pos); 2782 else 2783 w.obj:add(obj, pos); 2784 end 2785 if stead.type(wh) == 'string' then 2786 o.__where__ = wh; 2787 end 2788 return o; 2789end 2790 2791 2792function put(obj, w) 2793 return stead.placeto(obj, w); 2794end 2795 2796function putf(obj, w) 2797 return stead.placeto(obj, w, 1); 2798end 2799 2800place = put 2801placef = putf 2802placeto = putto 2803stead.placeto = placeto 2804 2805function replace(obj, obj2, from) 2806 local o,w,i 2807 if not isObject(stead.ref(obj2)) then 2808 error ("Wrong parameter to replace.", 2); 2809 end 2810 from = stead.ref(from) 2811 if from then 2812 if isList(from) then 2813 from:replace(obj, obj2); 2814 return stead.ref(obj) 2815 end 2816 o,w = from:srch(obj); 2817 else 2818 o,w = stead.here():srch(obj); 2819 end 2820 if w then 2821 stead.ref(w).obj:replace(o, obj2); 2822 stead.ref(obj2).__where__ = stead.deref(w); 2823 else 2824 stead.placeto(obj2, from); 2825 end 2826 o = stead.ref(o); 2827 if not isObject(o) then 2828 o = stead.ref(obj); 2829 end 2830 if isObject(o) then 2831 o.__where__ = nil; 2832 end 2833 return o; 2834end 2835 2836function drop(obj, w) 2837 local o = put(obj, w); 2838 if not isObject(o) then 2839 error ("Trying to drop wrong object.", 2); 2840 end 2841 inv():del(obj); 2842 o._taken = false 2843 return o; 2844end 2845 2846function dropf(obj, w) 2847 local o = putf(obj, w); 2848 if not isObject(o) then 2849 error ("Trying to dropf wrong object.", 2); 2850 end 2851 inv():del(obj); 2852 o._taken = false 2853 return o; 2854end 2855 2856function dropto(obj, w, pos) 2857 local o = putto(obj, w, pos); 2858 if not isObject(o) then 2859 error ("Trying to dropto wrong object.", 2); 2860 end 2861 inv():del(obj); 2862 o._taken = false 2863 return o; 2864end 2865 2866function seen(obj, wh) 2867 if not wh then 2868 wh = stead.here(); 2869 else 2870 wh = stead.ref(wh); 2871 end 2872 local o,w = wh:srch(obj); 2873 o = stead.ref(o); 2874 if isObject(o) then 2875 return o,w 2876 end 2877 return nil 2878end 2879 2880function exist(obj, wh) 2881 if not wh then 2882 wh = stead.here(); 2883 else 2884 wh = stead.ref(wh); 2885 end 2886 local o,w = wh:srch(obj, true); 2887 o = stead.ref(o); 2888 if isObject(o) then 2889 return o,w 2890 end 2891 return nil 2892end 2893 2894function have(obj) 2895 local o = inv():srch(obj); 2896 o = stead.ref(o); 2897 if isObject(o) then 2898 return o 2899 end 2900 return nil 2901end 2902 2903function moveto(obj, there, from, pos) 2904 stead.remove(obj, from); 2905 stead.placeto(obj, there, pos); 2906 return stead.ref(obj); 2907end 2908stead.moveto = moveto 2909 2910function move(obj, there, from) 2911 return stead.moveto(obj, there, from); 2912end 2913 2914function movef(obj, there, from) 2915 return stead.moveto(obj, there, from, 1); 2916end 2917 2918stead.cacheable = function(n, f) 2919 return function(...) 2920 local s = stead.cache[n] 2921 if s ~= nil then 2922 if s == -1 then s = nil end 2923 return s 2924 end 2925 stead.cache[n] = -1 2926 s = f(...) 2927 if s ~= nil then 2928 stead.cache[n] = s 2929 end 2930 return s 2931 end 2932end 2933 2934stead.get_picture = stead.cacheable('pic', function() 2935 local s = stead.call(stead.here(), 'pic'); 2936 if not s then 2937 s = stead.call(game, 'pic'); 2938 end 2939 return s; 2940end) 2941 2942stead.get_title = stead.cacheable('title', function() 2943 local s = stead.call(stead.here(), 'nam'); 2944 return s; 2945end) 2946 2947if instead_savepath == nil then 2948 function instead_savepath() 2949 return "./" 2950 end 2951end 2952 2953function autosave(slot) 2954 game.autosave = true; 2955 game.autosave_slot = slot; 2956end 2957stead.autosave = autosave; 2958 2959stead.get_restart = function() 2960 return stead.restart_game 2961end 2962 2963stead.get_menu = function() 2964 local n = stead.need_menu 2965 stead.need_menu = nil 2966 return n 2967end 2968 2969stead.restart = function() 2970 stead.restart_game = true 2971end 2972 2973stead.get_autosave = function() 2974 return game.autosave, game.autosave_slot 2975end 2976 2977function change_pl(p) 2978 local o = stead.ref(p); 2979 if stead.type(stead.deref(p)) ~= 'string' or not o then 2980 error ("Wrong player name in change_pl...", 2); 2981 end 2982 game.pl = stead.deref(p); 2983 return stead.walk(o.where, false, true, true); -- no call enter/exit 2984end 2985 2986function disabled(o) 2987 return isDisabled(stead.ref(o)) 2988end 2989 2990function disable(o) 2991 o = stead.ref(o) 2992 if isObject(o) then 2993 o:disable() 2994 end 2995 return o 2996end 2997 2998function enable(o) 2999 o = stead.ref(o) 3000 if isObject(o) then 3001 o:enable() 3002 end 3003 return o 3004end 3005 3006function disable_all(o) 3007 o = stead.ref(o) 3008 if isObject(o) or isList(o) then 3009 o:disable_all() 3010 end 3011 return o 3012end 3013 3014function enable_all(o) 3015 o = stead.ref(o) 3016 if isObject(o) or isList(o) then 3017 o:enable_all() 3018 end 3019 return o 3020end 3021 3022function isForSave(k, v, s) -- k - key, v - value, s -- parent table 3023 if stead.type(k) == 'function' then 3024 return false 3025 end 3026 if stead.type(v) == 'function' or stead.type(v) == 'userdata' then 3027 return false 3028 end 3029 return stead.string.find(k, '_') == 1 or stead.string.match(k,'^%u') 3030end 3031 3032stead.inherit = function(o, f) 3033 return function(...) 3034 return f(o(...)) 3035 end 3036end 3037inherit = stead.inherit 3038 3039stead.hook = function(o, f) 3040 return function(...) 3041 local ff 3042 if stead.type(o) ~= 'function' then 3043 ff = function(s) 3044 return o; 3045 end 3046 else 3047 ff = o 3048 end 3049 return f(ff, ...) 3050 end 3051end 3052hook = stead.hook 3053 3054function nameof(v) 3055 if isObject(v) then 3056 local r = stead.call(v, 'nam'); 3057 return r 3058 end 3059end 3060 3061stead.nameof = nameof 3062 3063stead.dispof = function(v) 3064 if isObject(v) then 3065 local r 3066 if game.gui then 3067 r = stead.call(v, 'disp') 3068 end 3069 if r == nil then 3070 r = stead.call(v, 'nam'); 3071 end 3072 return r 3073 end 3074end 3075 3076function stead_version(v) 3077 if not stead.tostr(v) then 3078 return 3079 end 3080 3081 stead.version_table = {} 3082 stead.api_version_table = {} 3083 3084 for n in stead.string.gmatch(stead.version, "[0-9]+") do 3085 stead.table.insert(stead.version_table, stead.tonum(n)) 3086 end 3087 3088 for n in stead.string.gmatch(v, "[0-9]+") do 3089 stead.table.insert(stead.api_version_table, stead.tonum(n)) 3090 end 3091 3092 if not stead.atleast(stead.unpack(stead.api_version_table)) then 3093 error ([[The game requires instead engine of version ]] ..v.. [[ or higher. 3094 https://instead-hub.github.io]], 2) 3095 end 3096 3097 stead.api_version = v 3098 3099 if stead.api_atleast(1, 2, 0) then 3100 require ("walk") 3101 require ("vars") 3102 require ("object") 3103 end 3104 if stead.api_atleast(1, 6, 3) then 3105 require ("dlg") 3106 end 3107end 3108instead_version = stead_version 3109 3110function code(v) 3111 local f = stead.eval(v) 3112 if not f then 3113 error ("Wrong script: "..stead.tostr(v), 2); 3114 end 3115 stead.functions[f] = { f = f, code = v }; 3116 return f; 3117end 3118stead.code = code 3119 3120--- here the game begins 3121stead.objects = { 3122 null = obj { 3123 nam = 'null'; 3124 }; 3125 3126 allocator = function() 3127 return obj { 3128 nam = 'allocator', 3129 get = function(s, n, c) 3130 if isObject(stead.ref(n)) and stead.api_atleast(1, 3, 0) then -- static? 3131 return stead.ref(n); 3132 end 3133 local v = stead.ref(c); 3134 if not v then 3135 error ("Null object in allocator: "..stead.tostr(c)); 3136 end 3137 v.key_name = n; 3138 v.save = stead.allocator_save; 3139 v.constructor = c; 3140 return v 3141 end, 3142 delete = function(s, w) 3143 w = stead.ref(w); 3144 if stead.type(w.key_name) ~= 'string' then 3145 return 3146 end 3147 local f = stead.eval(w.key_name..'= nil;'); 3148 if f then 3149 f(); 3150 end 3151 end, 3152 new = function(s, n, key) 3153 local v = stead.ref(n); 3154 if stead.type(v) ~= 'table' or stead.type(n) ~= 'string' then 3155 error ("Error in new.", 2); 3156 end 3157 v.save = stead.allocator_save; 3158 v.constructor = n; 3159 if key then 3160 s.objects[key] = v 3161 v.key_name = stead.string.format('allocator["objects"][%s]', stead.tostring(key)); 3162 else 3163 local nm = #s.objects + 1 -- here is new index 3164 stead.table.insert(s.objects, v); 3165 v.key_name = 'allocator["objects"]['..stead.tostr(nm)..']'; 3166 end 3167 if stead.api_atleast(1, 3, 0) then 3168 stead.check_object(v.key_name, v) 3169 end 3170 return v 3171 end, 3172 objects = { 3173 save = function(self, name, h, need) 3174 stead.savemembers(h, self, name, true); 3175 end, 3176 }, 3177 }; 3178 end; 3179 pl = function() 3180 return player { 3181 nam = "Incognito", 3182 where = 'main', 3183 obj = { } 3184 }; 3185 end; 3186 main = function() 3187 return room { 3188 nam = 'main', 3189 dsc = 'No main room defined.', 3190 }; 3191 end; 3192} 3193 3194 3195stead.sandbox = function() 3196 if STANDALONE then 3197 return 3198 end 3199-- sandbox -- 3200local check_path = function(realpath, type, find, gsub, savepath, gamepath, path) 3201 if not path then 3202 return false 3203 end 3204 path = realpath(path) 3205 if not path then 3206 return false 3207 end 3208 local spath = realpath(savepath) 3209 if not spath then 3210 return false 3211 end 3212 local s = find(path, spath..'/', 1, true) 3213 if s ~= 1 then 3214 spath = realpath(gamepath); 3215 if spath then 3216 s = find(path, spath..'/', 1, true) 3217 end 3218 end 3219 if s ~= 1 then 3220 return false 3221 end 3222 return true 3223end 3224 3225local build_sandbox_open = function(realpath, error, type, find, gsub, savepath, gamepath) 3226 return stead.hook(io.open, function(f, path, acc, ...) 3227 if type(acc) ~= 'string' or not find(acc, "[aw+]") then -- only write access 3228 return f(path, acc, ...) 3229 end 3230 if not check_path(realpath, type, find, gsub, savepath, gamepath, path) then 3231 error ("Access denied (write): ".. path, 3); 3232 return false 3233 end 3234 return f(path, acc, ...) 3235 end) 3236end 3237 3238local build_sandbox_remove = function(realpath, error, type, find, gsub, savepath, gamepath) 3239 return stead.hook(os.remove, function(f, path, ...) 3240 if type(path) ~= 'string' then 3241 return f(path, ...) 3242 end 3243 if not check_path(realpath, type, find, gsub, savepath, gamepath, path) then 3244 error ("Access denied (remove): ".. path, 3); 3245 return false 3246 end 3247 return f(path, ...) 3248 end) 3249end 3250 3251local build_sandbox_rename = function(realpath, error, type, find, gsub, savepath, gamepath) 3252 return stead.hook(os.rename, function(f, oldname, newname, ...) 3253 if not check_path(realpath, type, find, gsub, savepath, gamepath, oldname) or 3254 not check_path(realpath, type, find, gsub, savepath, gamepath, newname) then 3255 error ("Access denied (rename): ".. oldname .. ', '.. newname, 3); 3256 return false 3257 end 3258 return f(oldname, newname, ...) 3259 end) 3260end 3261 3262local build_sandbox_output = function(realpath, error, type, find, gsub, savepath, gamepath) 3263 return stead.hook(io.output, function(f, path, ...) 3264 if type(path) == 'string' and not check_path(realpath, type, find, gsub, savepath, gamepath, path) then 3265 error ("Access denied (output): ".. path, 3); 3266 return false 3267 end 3268 return f(path, ...) 3269 end) 3270end 3271 3272local build_sandbox_load = function(eval, error, type, find) 3273 return stead.hook(eval, function(f, str, ...) 3274 if type(str) == 'string' and find(str, "\x1b", 1, true) == 1 then 3275 error ("Loading bytecode is forbidden!", 3) 3276 return false 3277 end 3278 return f(str, ...) 3279 end) 3280end 3281 3282io.open = build_sandbox_open(instead_realpath, error, type, string.find, string.gsub, 3283 instead_savepath(), instead_gamepath()); 3284 3285os.remove = build_sandbox_remove(instead_realpath, error, type, string.find, string.gsub, 3286 instead_savepath(), instead_gamepath()); 3287 3288os.rename = build_sandbox_rename(instead_realpath, error, type, string.find, string.gsub, 3289 instead_savepath(), instead_gamepath()); 3290 3291io.output = build_sandbox_output(instead_realpath, error, type, string.find, string.gsub, 3292 instead_savepath(), instead_gamepath()); 3293 3294os.execute = function(s) 3295 print ("Warning: trying to do os.execute: "..s); 3296end 3297 3298io.popen = function(s) 3299 print ("Warning: trying to do io.popen: "..s); 3300end 3301 3302os.tmpname = function(s) 3303 print ("Warning: trying to do os.tmpname"); 3304end 3305 3306if not DEBUG then 3307 debug = nil 3308end 3309if _VERSION == "Lua 5.1" then 3310 loadstring = build_sandbox_load(loadstring, error, type, string.find) 3311 stead.eval = loadstring 3312else 3313 load = build_sandbox_load(load, error, type, string.find) 3314 stead.eval = load 3315end 3316package.cpath = "" 3317package.preload = {} 3318package = nil 3319 3320end 3321-- end of sandbox -- 3322 3323stead.init = function(s) 3324 stead.initialized = false 3325 stead.started = false 3326 for k, v in pairs(stead.objects) do 3327 if type(v) == 'function' then 3328 stead.rawset(_G, k, v()) 3329 else 3330 stead.rawset(_G, k, v) 3331 end 3332 end 3333 s.functions = {} -- code blocks 3334 3335 for k,v in stead.ipairs(s.modules_ini) do 3336 v(); 3337 end 3338 3339 if stead.type(stead.sandbox) == 'function' then 3340 stead.sandbox() 3341 stead.sandbox = nil 3342 end 3343end 3344 3345stead.done = function(s) 3346 for k, v in stead.ipairs(s.modules_done) do 3347 v(); 3348 end 3349end 3350 3351ref = stead.ref 3352deref = stead.deref 3353 3354pclr = stead.pclr 3355pget = stead.pget 3356p = stead.p 3357pr = stead.pr 3358pn = stead.pn 3359par = stead.par 3360cat = stead.cat 3361player_moved = stead.player_moved 3362rnd = stead.rnd; 3363gamefile = stead.gamefile 3364time = stead.time 3365 3366instead_version(stead.api_version) 3367-- vim:ts=4 3368