diff --git a/xtrn/lord2/lord2.js b/xtrn/lord2/lord2.js index 51abb5ee80a02ff1847408cf215580363c0af56f..40d8076adb00bf71b23902f8ebe1e2285e3b06b7 100644 --- a/xtrn/lord2/lord2.js +++ b/xtrn/lord2/lord2.js @@ -38,7 +38,7 @@ var enemy = undefined; var saved_cursor = {x:0, y:0}; var progname = ''; var time_warnings = []; -var file_pos = {}; +var file_pos = {con:0}; var items = []; var other_players = {}; @@ -520,6 +520,10 @@ function getfname(str) if (UCASE) { return js.exec_dir + str.toUpperCase(); } + if (str.indexOf(maildir) === 0) { + str = str.toLowerCase().replace(/([\\\/])([^\/\\]+)$/, function(m, p1, p2) { return p1 + p2.toUpperCase(); }); + return js.exec_dir + str; + } return js.exec_dir + str.toLowerCase(); } @@ -1103,7 +1107,10 @@ function sclrscr() function clearrows(start, end) { var row; + if (end === undefined) + end = start; + dk.console.attr.value = 2; for (row = start; row <= end; row++) { dk.console.gotoxy(0, row); dk.console.cleareol(); @@ -1574,6 +1581,40 @@ function chooseplayer() return undefined; } +function yes_no(y, title, question) { + var ch; + var ret; + var box = draw_box(y, title, ['', question, '', '`r5`$Yes','`$No']); + + dk.console.gotoxy(box.x + 3, box.y + 4); + ret = true; + do { + ch = getkey().toUpperCase(); + switch (ch) { + case '8': + case 'KEY_UP': + case '4': + case 'KEY_LEFT': + case '2': + case 'KEY_DOWN': + case '6': + case 'KEY_RIGHT': + ret = !ret; + dk.console.gotoxy(box.x + 3, box.y + 4); + if (ret) + lw('`r5'); + lw('`$Yes`r1'); + dk.console.gotoxy(box.x + 3, box.y + 5); + if (!ret) + lw('`r5'); + lw('`$No`r1'); + dk.console.gotoxy(box.x + 3, box.y + 5 - (ret ? 1 : 0)); + break; + } + } while (ch !== '\r'); + return ret; +} + function run_ref(sec, fname) { var line; @@ -1635,6 +1676,7 @@ function run_ref(sec, fname) if (player.deleted === 1) // TODO: Do we tell the player or something? return; + update_players(); if (players[to-1].onnow) { f = new File(getfname(maildir+'talk'+to+'.tmp')); if (f.open('ab')) { @@ -2004,10 +2046,12 @@ function run_ref(sec, fname) break; case 'exist': case 'exists': - if (file_exists(getfname(args[0])) === (args[2].toLowerCase() === 'true')) + if (file_exists(getfname(args[0])) === (args[2].toLowerCase() === 'true')) { handlers.do(args.slice(4)); - else if (args[4].toLowerCase() === 'begin') + } + else if (args[4].toLowerCase() === 'begin') { handlers.begin(args.slice(5)); + } break; case 'inside': if (getvar(args[2]).toLowerCase().indexOf(getvar(args[0]).toLowerCase()) !== -1) @@ -2327,7 +2371,6 @@ function run_ref(sec, fname) var i; var sattr = dk.console.attr.value; - dk.console.attr.value = 2; clearrows(start, end - 1); dk.console.attr.value = sattr; }, @@ -2509,7 +2552,7 @@ rescan: continue rescan; } if (player.i[itm.Record] > 1) { - box = draw_box(14, items[itm.Record].name, ['', ' `$Sell how many? ','']) + box = draw_box(14, items[itm.Record].name, ['', '`$Sell how many? ','']) dk.console.gotoxy(box.x + 21, box.y + 2); // TODO: This isn't exactly right... cursor is in wrong position, and selected colour is used. ch = dk.console.getstr({edit:player.i[itm.Record].toString(), integer:true, input_box:true, attr:new Attribute(47), len:11}); @@ -2519,33 +2562,7 @@ rescan: continue rescan; } } - box = draw_box(16, itm.name, ['', '`$Sell '+amt+' of \'em for '+(amt * price)+' gold?','','`r5`$Yes','`$No']); - dk.console.gotoxy(box.x + 3, box.y + 4); - yn = true; - do { - ch = getkey().toUpperCase(); - switch (ch) { - case '8': - case 'KEY_UP': - case '4': - case 'KEY_LEFT': - case '2': - case 'KEY_DOWN': - case '6': - case 'KEY_RIGHT': - yn = !yn; - dk.console.gotoxy(box.x + 3, box.y + 4); - if (yn) - lw('`r5'); - lw('`$Yes`r1'); - dk.console.gotoxy(box.x + 3, box.y + 5); - if (!yn) - lw('`r5'); - lw('`$No`r1'); - dk.console.gotoxy(box.x + 3, box.y + 5 - yn); - break; - } - } while (ch !== '\r'); + yn = yes_no(16, itm.name, '`$Sell '+amt+' of \'em for '+(amt * price)+' gold?'); if (yn) { player.i[itm.Record] -= amt; if (player.i[itm.Record] <= 0) { @@ -3031,8 +3048,10 @@ function mail_check(messenger) var ch; var reply = []; - if (!file_exists(fn)) + if (!file_exists(fn)) { + con_check(); return; + } if (messenger) { update_bar('`2A messenger stops you with the following news. (press `0R`2 to read it)', true); @@ -3099,9 +3118,8 @@ function mail_check(messenger) } } con_check(); + more(); if (messenger) { - sln(''); - more(); draw_map(); update(); } @@ -3214,8 +3232,6 @@ function con_check() { var al; - if (file_pos.con === undefined) - file_pos.con = 0; if (!cfile.open('r')) throw new Error('Unable to open '+cfile.name); cfile.position = file_pos.con; @@ -3252,6 +3268,37 @@ function con_check() }); } +function update_players() +{ + var i; + var op; + var u; + + for (i = 0; i < ufile.length; i++) { + if (i === player.Record) + continue; + if (i >= players.length) { + op = pfile.get(i); + if (op === null) + break; + players.push({x:op.x, y:op.y, map:op.map, onnow:op.onnow, busy:op.busy, battle:op.battle, deleted:op.deleted}); + } + u = ufile.get(i); + if (u === null) { + u = ufile.new(); + u.x = players[i].x; + u.y = players[i].y; + u.map = players[i].map; + u.onnow = players[i].onnow; + u.busy = players[i].busy; + u.battle = players[i].battle; + u.deleted = players[i].deleted; + u.put(); + } + players[i] = u; + } +} + var next_update = -1; function update(skip) { var i; @@ -3264,21 +3311,7 @@ function update(skip) { if ((!skip) || now > next_update) { next_update = now + game.delay; // First, update player data - for (i = 0; i < ufile.length; i++) { - if (i === player.Record) - continue; - if (i >= players.length) { - op = pfile.get(i); - if (op === null) - break; - players.push({x:op.x, y:op.y, map:op.map, onnow:op.onnow, busy:op.busy, battle:op.battle, deleted:op.deleted}); - } - if (players[i].deleted) - continue; - u = ufile.get(i); - if (u.map !== 0 && u.x !== 0 && u.y !== 0) - players[i] = u; - } + update_players(); // First, erase any moved players and update other_players done = new Array(80*20); @@ -3607,7 +3640,7 @@ newpage: cur = choice.cur; switch(choice.ch) { case 'D': - draw_box(12, items[inv[cur] - 1].name, ['',' `$Drop how many? ','']); + draw_box(12, items[inv[cur] - 1].name, ['','`$Drop how many? ','']); dk.console.gotoxy(38, 14); // TODO: This isn't exactly right... cursor is in wrong position, and selected colour is used. ch = dk.console.getstr({edit:player.i[inv[cur] - 1].toString(), integer:true, input_box:true, attr:new Attribute(47), len:11}); @@ -3673,7 +3706,6 @@ newpage: player.armournumber = 0; break; case 'S': - dk.console.attr.value = 2; clearrows(12, 22); ret = run_ref(items[inv[cur] - 1].refsection, 'items.ref'); if (items[inv[cur] - 1].useonce) { @@ -3729,6 +3761,7 @@ function hbar(x, y, opts, cur) if (cur === undefined) cur = 0; + lw('`r0`2'); while (1) { dk.console.gotoxy(x, y); opts.forEach(function(o, i) { @@ -3784,7 +3817,7 @@ function enemy_hp(enm) lw(space_pad('`r1`0`e\'s `2hitpoints: '+disp_hp(enm.hp, enm.maxhp), 44)); } -function offline_battle() +function offline_battle(no_super, skip_see) { var ch; var dmg; @@ -3798,6 +3831,11 @@ function offline_battle() var atk; var enm = enemy; var supr; + if (no_super === undefined) + no_super = false; + if (skip_see === undefined) + skip_see = false; + var ret; enemy = undefined; switch(enm.sex) { @@ -3820,9 +3858,18 @@ function offline_battle() your_hp(); enemy_hp(enm); dk.console.gotoxy(2,21); - lw('`r0`2'+enm.see); + if (skip_see) + lw('`r0`2'); + else + lw('`r0`2'+enm.see); while(1) { - ch = hbar(2, 23, ['Attack', 'Run For it']); + if (skip_see) { + ch = 0; + skip_see = 0; + } + else { + ch = hbar(2, 23, ['Attack', 'Run For it']); + } dk.console.gotoxy(2,21); dk.console.cleareol(); if (ch === 1) { @@ -3834,11 +3881,15 @@ function offline_battle() else { if (enm.run_reffile !== '' && enm.run_refname !== '') run_ref(enm.run_refname, enm.run_reffile); + ret = 'RAN'; break; } } else { - supr = (random(10) === 0); + if (no_super) + supr = false; + else + supr = (random(10) === 0); dmg = hstr + random(hstr) + 1 - enm.defence; if (supr) dmg *= 2; @@ -3892,6 +3943,7 @@ function offline_battle() getkey(); if (enm.win_reffile !== '' && enm.win_refname !== '') run_ref(enm.win_refname, enm.win_reffile); + ret = 'WON'; break; } else { @@ -3924,6 +3976,7 @@ function offline_battle() if (player.p[1] < 1) { if (enm.lose_reffile !== '' && enm.lose_refname !== '') run_ref(enm.lose_refname, enm.lose_reffile); + ret = 'LOST'; break; } } @@ -3937,6 +3990,7 @@ function offline_battle() dk.console.gotoxy(0, 23); dk.console.cleareol(); redraw_bar(true); + return ret; } function mail_to(pl, quotes) @@ -4256,11 +4310,89 @@ function hail() var ch; var f; var fn; - var inv; var choice; var done = true; // Defaulting to true helps find bugs! var cur; + function give_item(online) { + var inv; + var ch; + var f; + var cur = 0; + + lln('`c`r0`2 `r1`% GIVING. `r0'); + sln(''); + sln(''); + sln(''); + lw('`r5Item To Give Amount Owned'); + dk.console.cleareol(); + dk.console.gotoxy(0, 23); + lw(' `$Q `2to quit, `$ENTER `2to give item. You have `$'+player.money+' gold.'); + dk.console.cleareol(); + dk.console.gotoxy(0, 7); + inv = get_inventory(); + if (inv.length === 0) { + dk.console.gotoxy(0, 7); + lw('`r0 `2You have nothing to give, loser. (press `%Q `2to continue)'); + do { + ch = getkey().toUpperCase(); + } while (ch !== 'Q'); + } + else { + while (1) { + choice = items_menu(inv, cur, false, false, '', 7, 22); + cur = choice.cur; + if (choice.ch === 'Q') + break; + if (items[inv[choice.cur] - 1].questitem) { + // This is presumably in a popup box too. + draw_box(12, '', ['','`$You don\'t think it would be wise to give that away.`2','']); + getkey(); + } + else { + if (player.i[inv[choice.cur] - 1] === 1) + ch = 1; + else if (player.i[inv[choice.cur] - 1] < 1) { + draw_box(12, items[inv[choice.cur] - 1].name, ['', "You don't have any more of those!", '']); + getkey(); + ch = 0; + } + else { + draw_box(12, items[inv[choice.cur] - 1].name, ['','`$Give how many?`2 ','']); + dk.console.gotoxy(44, 14); + ch = parseInt(dk.console.getstr({len:7, crlf:false, integer:true})); + } + if ((!isNaN(ch)) && ch > 0 && ch <= player.i[inv[choice.cur] - 1]) { + if (yes_no(14, items[inv[choice.cur] - 1].name, '`$Give '+pretty_int(ch)+' of \'em to '+op.name+'`$?')) { + player.i[inv[choice.cur] - 1] -= ch; + f = new File(getfname(maildir+'con'+(op.Record + 1)+'.tmp')); + if (!f.open('ab')) + throw new Error('Unable to open '+f.name); + f.write('ADDITEM|'+inv[choice.cur]+'|'+ch+'\r\n'); + f.close(); + if (!online) { + f = new File(getfname(maildir+'mail'+(op.Record + 1)+'.dat')); + if (!f.open('ab')) + throw new Error('Unable to open '+f.name); + f.write(' `%`r1GOOD NEWS!`r0\r\n`0 '+repeat_chars(ascii(0xc4), 77)+'\r\n `0'+player.name+' `2gives you '); + if (ch === 1) + f.write('a '); + else + f.write(pretty_int(ch) + ' '); + f.write('really nice `$' + items[inv[choice.cur] - 1].name + '`2!\r\n'); + f.write('@DONE\r\n'); + f.close(); + } + } + } + } + } + draw_map(); + update(); + dk.console.gotoxy(0, 21); + } + } + function draw_menu() { var x, y; @@ -4294,12 +4426,7 @@ function hail() } function erase_menu() { - dk.console.gotoxy(0, 21); - dk.console.cleareol(); - dk.console.gotoxy(0, 22); - dk.console.cleareol(); - dk.console.gotoxy(0, 23); - dk.console.cleareol(); + clearrows(21, 23); } update(); @@ -4389,10 +4516,12 @@ function hail() player.battle = 1; update_update(); player.put(); - // TODO: Handle the nofighting map flag if (op.onnow === 0) { op.battle = 1; op.put(false); + update_players(); + players[op.Record].battle = 1; + players[op.Record].put(); update_bar('You find `0'+op.name+'`2 sleeping like a baby. (hit a key)', true); getkey(); done = false; @@ -4404,23 +4533,124 @@ function hail() done = true; break; case 1: // Attack -log(LOG_ERROR, "`v9: "+world.v[8]+" OP: "+op.p[8]); - if (world.v[8] === 0 || op.p[8] < world.v[8]) { - update_bar("No fighting is allowed in this area.", true); - break; + if (map.nofighting) { + if (world.v[8] === 0 || op.p[8] < world.v[8]) { + update_bar("No fighting is allowed in this area.", true); + break; + } + } + enemy = { + name:op.name, + see:'', + killstr:'', + sex:op.sex, + defence:op.p[4], + gold:op.money === 0 ? 0 : Math.floor(op.money / 2), + experience:op.experience === 0 ? 0 : Math.floor(op.experience / 10), + hp:op.p[1], + maxhp:op.p[2], + attacks:[], + win_reffile:'gametxt.ref', + lose_reffile:'gametxt.ref', + run_reffile:'gametxt.ref', + win_refname:'live', + lose_refname:'die', + run_refname:'run', + }; + if (op.weaponnumber == 0) { + enemy.attacks.push({ + strength:(op.p[3]), + hitaction:'`0`e`2 hits you with '+(op.sexmale === 1 ? 'his' : 'her')+' fists', + }); } + else { + enemy.attacks.push({ + strength:(op.p[3] + items[op.weaponnumber - 1].strength), + hitaction:'`0`e`2 hits you with '+(op.sexmale === 1 ? 'his' : 'her')+' `0'+items[op.weaponnumber-1].name+'`2', + }); + } + if (op.armournumber !== 0) { + enemy.defence += otems[op.armournumber - 1].defence; + } + setvar('`v39', op.Record + 1); + switch (offline_battle(true, true)) { + case 'RAN': + // TODO: Do the opponents HP drop in an offline battle they win? + break; + case 'WON': + op.dead = 1; + op.x = 0; + op.y = 0; + op.map = 1; + players[op.Record].x = 0; + players[op.Record].y = 0; + players[op.Record].map = 0; + players[op.Record].battle = 0; + players[op.Record].put(); + if (op.money > 0) + op.money -= Math.floor(op.money / 2); + if (op.experience > 0) + op.experience -= Math.floor(op.exterience / 10); + break; + case 'LOST': + // TODO: Do the opponents HP drop in an offline battle they win? + break; + } + done = true; break; case 2: // Give Item + give_item(false); break; case 3: // Transfer Gold + clearrows(21,23); + dk.console.gotoxy(1, 22); + lw('`r0`2Give `0'+op.name+'`2 how much of your `$$'+player.money+'`2? : '); + ch = dk.console.getstr({edit:player.money.toString(), integer:true, input_box:true, attr:new Attribute(31), len:11}); + ch = parseInt(ch, 10); + clearrows(22); + if (ch > 0) { + if (ch > player.money) { + dk.console.gotoxy(1, 22); + lw("`r0 `2You sort of don't have that much, friend. `2(`0hit a key`2)"); + getkey(); + clearrows(22); + } + else { + dk.console.gotoxy(1, 22); + lw("`r0 `$$"+pretty_int(ch)+" `2 gold transfered! `2(`0hit a key`2)"); + player.money -= ch; + player.put(); + f = new File(getfname(maildir+'con'+(op.Record + 1)+'.tmp')); + if (!f.open('ab')) + throw new Error('Unable to open '+f.name); + f.write('ADDGOLD|'+ch+'\r\n'); + f.close(); + f = new File(getfname(maildir+'mail'+(op.Record + 1)+'.dat')); + if (!f.open('ab')) + throw new Error('Unable to open '+f.name); + f.write(' `%`r1GOOD NEWS!`r0\r\n`0 '+repeat_chars(ascii(0xc4), 77)+'\r\n `0'+player.name+' `2gives you `$' + pretty_int(ch) + ' `2 gold!'); + f.write('@DONE\r\n'); + f.close(); + getkey(); + clearrows(22); + } + } break; case 4: // Write Mail + sclrscr(); + sln(''); + sln(''); + mail_to(op.Record); + draw_map(); + update(); break; } } + players[op.Record].battle = 0; + players[op.Record].put(); op.battle = 0; op.put(false); - // TODO: Offline battles, giving things, etc... + // TODO: giving things, etc... } else { op.unLock(); @@ -4457,63 +4687,15 @@ log(LOG_ERROR, "`v9: "+world.v[8]+" OP: "+op.p[8]); done = true; break; case 1: // TODO: Attack... - if (world.v[8] === 0 || op.p[8] < world.v[8]) { - update_bar("No fighting is allowed in this area.", true); - break; + if (map.nofighting) { + if (world.v[8] === 0 || op.p[8] < world.v[8]) { + update_bar("No fighting is allowed in this area.", true); + break; + } } break; case 2: // TODO: Give Item - lln('`c`r0`2 `r1`% GIVING. `r0'); - sln(''); - sln(''); - sln(''); - lw('`r5Item To Give Amount Owned'); - dk.console.cleareol(); - dk.console.gotoxy(0, 23); - lw(' `$Q `2to quit, `$ENTER `2to give item. You have `$'+player.money+' gold.'); - dk.console.cleareol(); - dk.console.gotoxy(0, 7); - inv = get_inventory(); - if (inv.length === 0) { - dk.console.gotoxy(0, 7); - lw('`r0 `2You have nothing to give, loser. (press `%Q `2to continue)'); - do { - ch = getkey().toUpperCase(); - } while (ch !== 'Q'); - } - else { - // TODO: I'm not sure this is how it actually looks... - choice = items_menu(inv, 0, false, false, '', 7, 22); - if (items[inv[choice.cur] - 1].questitem) { - // This is presumably in a popup box too. - draw_box(12, '', ['',' `$You don\'t think it would be wise to give that away.`2 ','']); - getkey(); - } - else { - draw_box(12, items[inv[choice.cur] - 1].name, ['',' `$Give how many?`2 ','']); - // Confirmation box line 14, same title... - // ' `$Give 1 of `em to <name>`$? - // - // Yes - // No - // Then stays in hail menu. - dk.console.gotoxy(46, 14); - ch = parseInt(dk.console.getstr({len:7, crlf:false, integer:true})); - if ((!isNaN(ch)) && ch > 0 && ch <= player.i[inv[choice.cur] - 1]) { - player.i[inv[choice.cur] - 1] -= ch; - f = new File(getfname(maildir+'con'+(op.Record + 1)+'.tmp')) - if (!f.open('ab')) - throw new Error('Unable to open '+f.name); - f.write('ADDITEM|'+inv[choice.cur]+'|'+ch+'\r\n'); - f.close(); - - // TODO: And a mail message or something? - } - } - draw_map(); - update(); - dk.console.gotoxy(0, 21); - } + give_item(true); break; case 3: fn = file_getcase(maildir+'chat'+(player.Record + 1)+'.tmp'); @@ -4873,11 +5055,37 @@ function setup_time_warnings() time_warnings.push(0); } +function build_index() +{ + var i; + var p; + var u; + + for (i = 0; i < pfile.length; i++) { + u = ufile.get(i); + if (u === null) { + u = ufile.new(); + p = pfile.Get(i); + u.x = p.x; + u.y = p.y; + u.map = p.map; + u.onnow = p.onnow; + u.busy = p.busy; + u.battle = p.battle; + u.deleted = p.deleted; + u.put(); + } + } +} + var pfile = new RecordFile(getfname('trader.dat'), Player_Def); var mfile = new RecordFile(getfname('map.dat'), Map_Def); var wfile = new RecordFile(getfname('world.dat'), World_Def); var ifile = new RecordFile(getfname('items.dat'), Item_Def); var ufile = new RecordFile(getfname('update.tmp'), Update_Def); +if (ufile.length < pfile.length) { + build_index(); +} var gfile = new RecordFile(getfname('game.dat'), Game_Def); var maildir = getfname('mail'); @@ -4944,7 +5152,7 @@ killfiles.push(lfile); lfile.close(); var cfile = new File(getfname(maildir + 'con'+(player.Record + 1)+'.tmp')); -if (!cfile.open('w+b')) +if (!cfile.open('ab')) throw new Error('Unable to open '+cfile.name); killfiles.push(cfile); cfile.close(); @@ -4964,4 +5172,6 @@ player.lastdayon = state.time; player.lastdayplayed = state.time; player.lastsaved = savetime(); player.put(); + +mail_check(false); do_map();