war.js 62.78 KiB
var orig_exec_dir = js.exec_dir;
var game_dir = orig_exec_dir;
/*
Solomoriah's WAR!
war.js -- main procedure file for war user interface
Copyright 2013 Stephen Hurd
All rights reserved.
3 Clause BSD License
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright
notice, self list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, self list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the author nor the names of any contributors
may be used to endorse or promote products derived from self software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Based on:
/*
Solomoriah's WAR!
war.c -- main procedure file for war user interface
Copyright 1993, 1994, 2001, 2013 Chris Gonnerman
All rights reserved.
3 Clause BSD License
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright
notice, self list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, self list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the author nor the names of any contributors
may be used to endorse or promote products derived from self software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
load("sbbsdefs.js");
load(game_dir+'/warcommon.js');
// From display.c
const gran = 2;
const terminate = "\033q ";
var avcnt = 0;
var avpnt = 0;
var enemies = [
{nation:0, armies:0, heros:0},
{nation:0, armies:0, heros:0}
];
var armyview = [
{id:0, mark:' '},
{id:0, mark:' '},
{id:0, mark:' '},
{id:0, mark:' '},
{id:0, mark:' '},
{id:0, mark:' '},
{id:0, mark:' '},
{id:0, mark:' '},
{id:0, mark:' '},
{id:0, mark:' '},
{id:0, mark:' '},
{id:0, mark:' '}
];
var mvcnt = 0;
var movestack = [
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 }
];
// These were extern...
var trans;
var pfile;
// From data.c
const terrain = "~.%#^"; /* standard types only. */
const terr_attr = {
' ':'HB4',
'~':'HB4',
'.':'HY3',
'%':'HG2',
'#':'HW1',
'^':'HW7',
'*':'HW',
'!':'HR'
};
const genattrs = {
'help':'N',
'prompt':'N',
'border':'NW',
'header':'N',
'title':'N'
};
const attrs = {
'main_screen':'NW',
'status_area':'N',
'world_bar':'NK7',
'reader_pointer':'N',
'reader_topbottom':'N',
'reader_deleted':'N',
'viewer_deleted':'N',
'viewer_text':'N',
'army_area':'NW',
'army_list':'NW',
'army_total':'NW',
'city_name':'NW',
'nation_status':'N',
'army_types':'N',
'title_text':'N',
'title_copyright':'N',
'title_world':'N',
'title_newcity':'N',
'title_retry':'N',
'title_symbollist':'N',
'title_symboltitle':'N',
'title_welcome':'N',
'title_anykey':'N',
'title_nameprompt':'N',
'mailer':'N'
};
const terr_names = [
"Water",
"Plains",
"Forest",
"Hills",
"Mountains",
];
const special_desc = [
"None",
"Transport Hero",
"Transport One",
"Transport Stack"
];
// From reader.c
var r_index = [];
var killed=[];
var index_cnt = 0;
var pages = [];
var page_cnt = 0;
function nationname(n)
{
return nations[n].name;
}
function turn()
{
return gen;
}
function mainscreen()
{
var i;
console.clear(attrs.main_screen);
console.attributes = genattrs.border;
console.gotoxy(2, 1);
console.print(ascii(201));
console.gotoxy(2, 18);
console.print(ascii(200));
console.gotoxy(35, 1);
console.print(ascii(205)+ascii(187));
console.gotoxy(35, 18);
console.print(ascii(205)+ascii(188));
for(i = 0; i < 16; i++) {
console.gotoxy(2, i+2);
console.print(ascii(186));
console.gotoxy(35, i+2);
console.print(' '+ascii(186));
console.gotoxy(i*2+3, 1);
console.print(ascii(205)+ascii(205));
console.gotoxy(i*2+3, 18);
console.print(ascii(205)+ascii(205));
}
console.gotoxy(1, 20);
console.attributes = genattrs.border;
console.print((new Array(81)).join(ascii(196)));
console.attributes = attrs.status_area;
for(i=21; i<=console.screen_rows; i++) {
console.gotoxy(1, i);
console.cleartoeol();
}
console.gotoxy(41, 1);
console.attributes = attrs.world_bar;
console.print(format(" %-20s Turn %d ", world, gen));
}
function saystat(msg)
{
console.gotoxy(2, 21);
console.attributes = attrs.status_area;
console.print(msg);
console.cleartoeol();
}
function mailer(from, to)
{
var heading;
var logf;
var fp;
var f;
var fname;
var inbuf;
if(to > 0) {
heading = format("From %s of %s to %s of %s (Turn %d)",
nationname(from), nationcity(from),
nationname(to), nationcity(to), turn());
} else {
heading = format("From %s of %s (Turn %d)",
nationname(from), nationcity(from), turn());
}
console.clear(attrs.mailer);
console.gotoxy(1,1);
f = new File(format("%s/%04d.tmpmsg", game_dir, nations[from].uid));
if(!f.open("wb")) {
mainscreen();
saystat("Message Creation Failed (System Error)");
return;
}
f.close();
if(console.editfile(f.name)) {
if(file_size(f.name) > 0) {
if(to > 0)
fname = game_dir+'/'+format(MAILFL, to);
else
fname = game_dir + '/' + NEWSFL;
if(!f.open('rb')) {
file_remove(f.name);
mainscreen();
saystat("Mail Could Not Be Read... Cancelled.");
return;
}
fp = new File(fname);
if(!fp.open('ab')) {
file_remove(f.name);
mainscreen();
saystat("Mail Could Not Be Sent... Cancelled.");
return;
}
fp.write(HEADERMARK);
fp.write(heading);
fp.write("\n\n");
while((inbuf = f.readln()) != null)
fp.writeln(inbuf);
f.close();
fp.close();
logf = new File(game_dir+'/'+MAILLOG);
if(logf.open('ab')) {
logf.write(HEADERMARK);
logf.write(heading);
logf.write("\n\n");
}
}
}
file_remove(f.name);
mainscreen();
saystat("Mail Sent.");
}
/*
showpage() shows a page of text from the current file, starting at
the file position in pages[pg], for 19 lines.
*/
function showpage(fp, pg)
{
var i, len;
var inbuf;
fp.position = pages[pg];
len = HEADERMARK.length;
console.attributes = attrs.viewer_text;
for(i = 0; i < 19; i++) {
if((inbuf = fp.readln(1024)) == null)
break;
inbuf = inbuf.substr(0, 77);
if(inbuf.substr(0, 2) == HEADERMARK)
break;
console.gotoxy(3, i+1);
console.print(inbuf);
console.cleartoeol();
}
for(; i < 19; i++) {
console.gotoxy(3, i+1);
console.cleartoeol();
}
console.gotoxy(56, 22);
console.attributes = genattrs.help;
console.print(format("Page %d of %d", pg + 1, page_cnt));
console.cleartoeol();
console.gotoxy(71, 22);
console.attribtes = attrs.reader_prompt;
console.print("< >");
}
function show_killed(pos)
{
console.gotoxy(41, 22);
if(killed[pos]) {
console.attributes = attrs.viewer_deleted;
console.print("[Deleted]");
}
else {
console.attributes = genattrs.help;
console.print(" ");
}
}
function viewerscr(mode)
{
console.clear(genattrs.header);
console.gotoxy(3, 21);
console.attributes = genattrs.help;
if(mode)
console.print("Mail: (d)elete ");
else
console.print("News: ");
console.print("(z)down (a)up");
console.gotoxy(3, 22);
console.print("Press SPACE to Exit.");
console.gotoxy(71, 22);
console.attributes = attrs.viewer_prompt;
console.print("< >");
}
/*
delete_msgs() deletes messages from the named file, using the
information in the r_index[] and killed[] arrays.
*/
function delete_msgs(fn)
{
var i, done, len;
var tmp, inbuf;
var inf, outf;
len = HEADERMARK.length;
tmp = 'tmp'+fn;
inf = new File(game_dir+'/'+fn);
outf = new File(game_dir+'/'+tmp);
if(!inf.open("rb")) {
log("Message Deletion Failed (System Error)");
saystat("Message Deletion Failed (System Error)");
return;
}
if(!outf.open("wb")) {
inf.close();
log("Message Deletion Failed (System Error)");
saystat("Message Deletion Failed (System Error)");
return;
}
for(i = 0; i < index_cnt; i++) {
if(!killed[i]) {
outf.write(HEADERMARK);
inf.position = r_index[i];
done = 0;
while((inbuf = inf.readln(1024)) != null && !done) {
inbuf = inbuf.substr(0, 77);
if(inbuf.substr(0, len) == HEADERMARK)
done = 1;
else
outf.write(inbuf+'\n');
}
}
}
inf.close();
outf.close();
file_remove(inf.name);
file_rename(outf.name, inf.name);
}
function readerscr(mode)
{
console.clear(genattrs.header);
console.gotoxy(3, 21);
console.attributes = genattrs.help;
if(mode)
console.print("Mail: (v)iew current (d)elete ");
else
console.print("News: (v)iew current ");
console.print("(z)down (a)up (]) next ([) previous");
console.gotoxy(3, 22);
console.print("Press SPACE to Exit.");
}
/*
viewer() displays text from the given file, from the given
index offset to the end of file or until a line beginning with
a HEADERMARK is encountered.
*/
function viewer(fp, pos, mode)
{
var ch, i, len, done, pg, opg;
var dummy;
len = HEADERMARK.length;
fp.position = r_index[pos];
viewerscr(mode);
show_killed(pos);
done = 0;
page_cnt = 0;
do {
pages[page_cnt++] = fp.position;
for(i = 0; i < 15; i++) {
if((dummy = fp.readln(1024)) == null) {
done = 1;
break;
}
dummy = dummy.substr(0, 77);
if(dummy.substr(0, len) == HEADERMARK) {
done = 1;
break;
}
}
} while(!done);
pg = 0;
showpage(fp, pg);
do {
console.gotoxy(72, 22);
ch = console.getkey();
opg = pg;
switch(ch) {
case 'z' :
case KEY_DOWN:
pg++;
break;
case 'a' :
case KEY_UP:
pg--;
break;
case 'd' :
killed[pos] = killed[pos] ? 0 : 1;
show_killed(pos);
break;
case ' ' :
case 'q' :
ch = '\033';
break;
}
if(pg < 0) pg = 0;
if(pg >= page_cnt) pg = page_cnt - 1;
if(pg != opg)
showpage(fp, pg);
} while(ch != '\033');
readerscr(mode);
}
/*
show_screen() shows headers from the given file, centering at the
header number given.
*/
function show_screen(pos, fp)
{
var i;
var inbuf;
pos -= 8;
for(i = 0; i < 18; i++) {
console.gotoxy(3, i+1);
if(pos + i < index_cnt && pos + i >= 0) {
if(killed[pos+i]) {
console.attributes = attrs.reader_deleted;
console.print('*');
}
else {
console.attributes = genattrs.header;
console.print(' ');
}
fp.position = r_index[pos+i];
inbuf = fp.readln(1024);
if(inbuf != null) {
inbuf = inbuf.substr(0, 77);
console.print(inbuf);
}
} else {
console.attributes = attrs.reader_topbottom;
if(pos + i == -1)
console.print("*** Top of List ***");
else if(pos + i == index_cnt)
console.print("*** End of List ***");
}
console.cleartoeol();
}
}
/*
indexer() reads the given file, scanning for headings. headings
are flagged with a space-backspace combination (" \b") which is
not shown on most printouts but is easily recognized.
the array r_index[] contains the seek positions of each heading.
it is limited by index_cnt.
*/
function indexer(fp)
{
var inbuf, len, pos;
index_cnt = 0;
len = HEADERMARK.length;
fp.rewind();
for(pos = fp.position; (inbuf = fp.readln(1024)) != null; pos = fp.position) {
inbuf=inbuf.substr(0, 77);
if(inbuf.substr(0, len)==HEADERMARK) {
killed[index_cnt] = 0;
r_index[index_cnt++] = pos + len;
}
}
}
/*
reader() displays a list of headings from the given file. the
user may page or "arrow" up or down to view long listings. when
a heading is selected with the (v)iew command, viewer() is called.
mode is 0 for news, 1 for mail. mode 1 enables deletion of headings.
*/
function reader(fname, mode)
{
var top, pos, ch, killcnt;
var fp = new File(game_dir+'/'+fname);
if(!fp.open("rb")) {
if(mode)
saystat("No Mail in your box.");
else
saystat("No News to Read.");
return;
}
readerscr(mode);
indexer(fp);
top = pos = index_cnt - 1;
show_screen(top, fp);
do {
console.gotoxy(2, pos - top + 9);
console.attributes = attrs.reader_pointer;
console.print('>');
ch = console.getkey();
console.gotoxy(2, pos - top + 9);
console.attributes = genattrs.header;
console.print(' ');
switch(ch) {
case 'z' :
case KEY_DOWN:
pos++;
break;
case 'a' :
case KEY_UP:
pos--;
break;
case ']' :
//case KEY_PAGEDOWN:
pos += 11;
break;
case '[' :
//case KEY_PAGEUP:
pos -= 10;
break;
case 'd' :
if(!mode)
break;
killed[pos]++;
killed[pos] %= 2;
show_screen(top, fp);
break;
case 'v' :
case '\r' :
viewer(fp, pos, mode);
show_screen(top, fp);
break;
case ' ' :
case 'q' :
ch = '\033';
break;
}
if(pos < 0) pos = 0;
if(pos >= index_cnt) pos = index_cnt - 1;
if(pos > top + 8 || pos < top - 7) {
top = pos;
show_screen(top, fp);
}
} while(ch != '\033');
fp.close();
mainscreen();
/* handle deletion. */
if(mode) {
for(killcnt = 0, pos = 0; pos < index_cnt; pos++)
killcnt += killed[pos];
if(killcnt != 0) { /* deleted some... */
if(killcnt == index_cnt) /* deleted ALL */
file_remove(game_dir+'/'+fname);
else
delete_msgs(fname);
}
}
}
function analyze_stack(ms, mc)
{
var i, j, a, b, mmode;
mmode = 0;
for(i = 0; i < mc; i++)
if(armies[ms[i].id].special_mv > mmode)
mmode = armies[ms[i].id].special_mv;
switch(mmode) {
case TRANS_ALL :
/* locate fastest transporter */
j = -1;
for(i = 0; i < mc; i++) {
a = ms[i].id;
if(armies[a].special_mv == TRANS_ALL) {
if(j == -1 || armies[a].move_left > armies[j].move_left)
j = a;
ms[i].dep = a;
}
}
/* set (almost) all */
for(i = 0; i < mc; i++) {
a = ms[i].id;
if(armies[a].special_mv == TRANS_ALL)
ms[i].dep = a;
else if(ms[i].dep == -1)
ms[i].dep = j;
}
break;
case TRANS_SELF :
/* easy... all move by themselves */
for(i = 0; i < mc; i++)
ms[i].dep = ms[i].id;
break;
default :
/* transport hero and/or one army */
/* set all heros for transportation */
for(i = 0; i < mc; i++) {
a = ms[i].id;
if(armies[a].hero > 0)
for(j = 0; j < mc; j++) {
b = ms[j].id;
if(armies[b].special_mv == TRANS_HERO
&& ms[j].dep == -1) {
ms[j].dep = b;
ms[i].dep = b;
break;
}
}
}
/* set remaining for transportation */
for(i = 0; i < mc; i++) {
a = ms[i].id;
if(ms[i].dep == -1
&& armies[a].special_mv == TRANS_SELF) {
for(j = 0; j < mc; j++) {
b = ms[j].id;
if(armies[b].special_mv == TRANS_ONE
&& ms[j].dep == -1) {
ms[j].dep = b;
ms[i].dep = b;
break;
}
}
}
}
/* leftovers transport self. */
for(i = 0; i < mc; i++)
if(ms[i].dep == -1)
ms[i].dep = ms[i].id;
break;
}
}
function movecost(a, r, c)
{
var t, tbl, mv, city, cost, a2;
city = city_at(r, c);
if(city == -1) {
/* not a city */
/* search for a TRANS_ALL unit there */
if(!(armies[a].r == r && armies[a].c == c)
&& armies[a].special_mv != TRANS_ALL)
for(a2 = 0; a2 < armycnt; a2++)
if(a != a2
&& armies[a2].r == r
&& armies[a2].c == c
&& armies[a2].nation == armies[a].nation
&& armies[a2].special_mv == TRANS_ALL)
return 1;
/* else do this: */
t = terrain.indexOf(map[r][c]);
if(t != -1) {
tbl = armies[a].move_tbl;
mv = move_table[tbl].cost[t];
if(mv == 0
/* otherwise can't move, and */
&& map[r][c] != '~'
/* target space is not water, and */
&& map[armies[a].r][armies[a].c] == '~'
/* current space is water, and */
&& (cost = move_table[tbl].cost[0]) > 0)
/* this unit moves on water, then */
mv = cost * 2; /* beaching is allowed */
} else {
if(map[r][c] == '+')
mv = 1;
else
mv = 3;
}
} else {
/* city */
if(armies[a].nation == cities[city].nation)
mv = 1;
else
mv = 2;
}
return mv;
}
function clearstat(mode)
{
console.attributes = attrs.status_area;
if(mode == -1) {
console.gotoxy(2, 21); console.cleartoeol();
console.gotoxy(2, 22); console.cleartoeol();
console.gotoxy(2, 23); console.cleartoeol();
console.gotoxy(2, 24); console.cleartoeol();
} else {
console.gotoxy(2, 21+mode); console.cleartoeol();
}
}
function mark_of(a)
{
var i;
if(mvcnt < 1)
return 0;
for(i = 0; i < mvcnt; i++)
if(movestack[i].id == a)
return 1;
return 0;
}
function sortview()
{
var gap, i, j, temp;
avpnt = 0;
for(gap = parseInt(avcnt / 2); gap > 0; gap = parseInt(gap / 2)) {
for(i = gap; i < avcnt; i++) {
for(j = i - gap; j >= 0 && isgreater(armyview[j+gap].id, armyview[j].id); j -= gap) {
temp = armyview[j].id;
armyview[j].id = armyview[j+gap].id;
armyview[j+gap].id = temp;
temp = armyview[j].mark;
armyview[j].mark = armyview[j+gap].mark;
armyview[j+gap].mark = temp;
}
}
}
}
function setfocus(ntn, r, c)
{
var i, j, e;
avcnt = 0;
/* clear the enemies list */
for(e = 0; e < 2; e++) {
enemies[e].nation = -1;
enemies[e].armies = 0;
enemies[e].heros = 0;
}
/* clear the map overlay */
for(i = 0; i < map_height; i++)
for(j = 0; j < map_height; j++)
mapovl[i][j] = ' ';
/* find the player's armies */
for(i = 0; i < armycnt; i++) {
if(r == armies[i].r
&& c == armies[i].c)
if(armies[i].nation == ntn || ntn == -1) {
armyview[avcnt].id = i;
armyview[avcnt++].mark = mark_of(i);
} else {
e = 0;
if(enemies[0].nation != armies[i].nation
&& enemies[0].nation != -1)
e = 1;
enemies[e].nation = armies[i].nation;
if(armies[i].hero > 0)
enemies[e].heros++;
else
enemies[e].armies++;
}
if(armies[i].nation >= 0) {
if(mapovl[armies[i].r][armies[i].c] == ' '
|| mapovl[armies[i].r][armies[i].c] ==
marks[1].substr(marks[0].indexOf([nations[armies[i].nation].mark]), 1)) {
mapovl[armies[i].r][armies[i].c] =
marks[1].substr(marks[0].indexOf([nations[armies[i].nation].mark]), 1);
}
else {
mapovl[armies[i].r][armies[i].c] = '!';
}
}
}
sortview();
/* update map overlay with cities */
for(i = 0; i < citycnt; i++) {
if(mapovl[cities[i].r][cities[i].c] == ' '
|| marks[1].indexOf(mapovl[cities[i].r][cities[i].c]) != -1) {
mapovl[cities[i].r][cities[i].c] =
nations[cities[i].nation].mark;
}
else {
mapovl[cities[i].r][cities[i].c] = '!';
}
}
}
function showarmies()
{
var i, a;
var buff;
console.attributes = attrs.army_area;
for(i = 0; i < 12; i++) {
console.gotoxy(38, i + 3);
if(i < avcnt) {
a = armyview[i].id;
console.print(format("%s %c ",
i == avpnt ? "=>" : " ",
armyview[i].mark ? '*' : ' '));
buff = armyname(a);
if(armies[a].name.length == 0 && armies[a].hero > 0)
buff = "(Nameless Hero)";
else if(armies[a].hero > 0)
buff += " (Hero)";
console.print(format("%-31.31s %d:%d", buff,
armies[a].move_rate, armies[a].move_left));
}
console.cleartoeol();
}
console.gotoxy(38, 14);
console.attributes = attrs.army_total;
if(avcnt > 0)
console.print(format(" Total %d Armies", avcnt));
console.cleartoeol();
console.attributes = attrs.army_area;
for(i = 0; i < 2; i++) {
console.gotoxy(43, i + 16);
if(enemies[i].nation != -1)
console.print(format("%-15.15s %3d Armies, %3d Heros",
i == 1
? "(Other Nations)"
: nationcity(enemies[i].nation),
enemies[i].armies, enemies[i].heros));
console.cleartoeol();
}
}
function gmapspot(r, c, terr, mark, focus, extra_attr)
{
var attr;
if(mark == ' ')
mark = terr;
if(terr == '~')
terr = ' ';
if(mark == '~')
mark = ' ';
console.gotoxy(c * 2 + 4, r + 2);
if(terr_attr[mark] == undefined)
attr = 'N';
else {
attr = terr_attr[terr];
if(terr_attr[mark] != undefined)
attr += terr_attr[mark];
}
console.attributes = attr+extra_attr;
console.print(mark);
console.attributes = terr_attr[terr]+extra_attr;
console.print(terr);
console.gotoxy(c * 2 + 4, r + 2);
return 0;
}
function cityowner(c)
{
var n;
n = cities[c].nation;
if(n == 0) return "Neutral";
if(n == 27) return "Rogue";
return cities[nations[n].city].name;
}
function showcity(r, c)
{
var i;
var buff = '';
console.gotoxy(4, 19);
i = city_at(r, c);
if(i != -1) {
buff = format("City: %s (%s)",
cities[i].name, cityowner(i));
}
console.attributes = attrs.city_name;
console.print(format("%-40.40s", buff));
}
var old_ul_r = -1;
var old_ul_c = -1;
function showmap(r, c, force)
{
var ul_r, ul_c, f_r, f_c;
var i, j, zr, zc;
var rem;
showarmies();
rem = parseInt((16 - gran) / 2);
f_r = r % gran;
f_c = c % gran;
ul_r = ((parseInt(r / gran) * gran - rem) + map_height) % map_height;
ul_c = ((parseInt(c / gran) * gran - rem) + map_width) % map_width;
if(old_ul_r != ul_r || old_ul_c != ul_c || force) {
for(i = 0; i < 16; i++) {
for(j = 0; j < 16; j++) {
zr = (ul_r+i) % map_height;
zc = (ul_c+j) % map_width;
gmapspot(i, j, map[zr][zc], mapovl[zr][zc], 0, '');
}
}
}
console.attributes='N';
old_ul_r = ul_r;
old_ul_c = ul_c;
if(mapovl[r][c] != ' ')
showcity(r, c);
console.gotoxy(f_c * 2 + 16, f_r + 8);
}
function showfocus(r, c)
{
var f_r, f_c, rem;
rem = parseInt((16 - gran) / 2);
f_r = (r % gran) + rem;
f_c = (c % gran) + rem;
gmapspot(f_r, f_c, map[r][c], mapovl[r][c], 1, '');
console.attributes='N';
}
function fixrow(r)
{
r += map_height;
r %= map_height;
return r;
}
function fixcol(c)
{
c += map_width;
c %= map_width;
return c;
}
function move_mode(ntn, rp, cp)
{
var i, j, mv, ch, city, army, t_r, t_c, a, b, ok, cnt, max, flag;
var ac, hc, mmode;
var mvc = new Array(TRANS_ALL+1);
var gap, temp;
var unmarked = [
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 },
{ id:0, moved:0, dep:0 }
];
var uncnt;
var buff;
if(avcnt < 1) {
saystat("No Army to Move.");
return {r:rp,c:cp};
}
mvcnt = 0;
flag = 0;
for(i = 0; i < avcnt; i++) {
if(armyview[i].mark)
flag = 1;
if(armyview[i].mark
&& (armies[armyview[i].id].move_left > 0 || ntn == -1)) {
if(mvcnt >= 10 && ntn >= 0) {
saystat("Too Many Armies for Group.");
return {r:rp,c:cp};
}
movestack[mvcnt].moved = 0;
movestack[mvcnt].dep = -1;
movestack[mvcnt++].id = armyview[i].id;
}
}
if(mvcnt < 1 && !flag
&& (armies[armyview[avpnt].id].move_left > 0 || ntn == -1)) {
movestack[mvcnt].moved = 0;
movestack[mvcnt].dep = -1;
movestack[mvcnt++].id = armyview[avpnt].id;
armyview[avpnt].mark = 1;
}
if(mvcnt < 1 && ntn > -1) {
saystat("Armies Have No Movement Left.");
return {r:rp,c:cp};
}
/*
prevent stranding:
analyze the unmarked armies
create a "movestack" of the unmarked.
if they can move, stranding can't occur.
*/
if(ntn > -1) {
uncnt = 0;
for(i = 0; i < avcnt; i++) {
if(!armyview[i].mark) {
unmarked[uncnt].moved = 0;
unmarked[uncnt].dep = -1;
unmarked[uncnt++].id = armyview[i].id;
}
}
analyze_stack(unmarked, uncnt);
for(i = 0; i < uncnt; i++) {
a = unmarked[i].id;
if(unmarked[i].dep == a
&& armies[a].special_mv != TRANS_ALL
&& movecost(a, armies[a].r, armies[a].c) == 0) {
saystat("Armies Would Be Stranded... Movement Cancelled.");
return {r:rp,c:cp};
}
}
}
/* analyze move stack */
if(ntn > -1)
analyze_stack(movestack, mvcnt);
/* perform movement */
clearstat(-1);
console.gotoxy(2, 22);
console.attributes = attrs.status_area;
console.print(format("Move %s", mvcnt > 1 ? "Armies" : "Army"));
console.gotoxy(21, 23);
console.print("4 6 or d g");
console.gotoxy(21, 24);
console.print("1 2 3 c v b");
console.gotoxy(21, 22);
console.print("7 8 9 e r t SPACE to Stop. ");
setfocus(ntn, rp, cp);
showmap(rp, cp, false);
showfocus(rp, cp);
while(mvcnt > 0 && (ch = console.getkey()) != ' '
&& terminate.indexOf(ch) == -1) {
showfocus(rp, cp);
clearstat(0);
t_r = rp;
t_c = cp;
switch(ch) { /* directions */
case '7' :
case 'e' :
t_r--;
t_c--;
break;
case '8' :
case 'r' :
t_r--;
break;
case '9' :
case 't' :
t_r--;
t_c++;
break;
case '4' :
case 'd' :
t_c--;
break;
case '6' :
case 'g' :
t_c++;
break;
case '1' :
case 'c' :
t_r++;
t_c--;
break;
case '2' :
case 'v' :
t_r++;
break;
case '3' :
case 'b' :
t_r++;
t_c++;
break;
case '\f' :
// TODO: Refresh
break;
}
t_r = fixrow(t_r);
t_c = fixcol(t_c);
if(t_r != rp || t_c != cp) {
/* actual move code... */
ok = 1;
/* verify that all units can make the move. */
if(ntn > -1) {
for(i = 0; i < mvcnt; i++) {
a = movestack[i].id;
if(movestack[i].dep == a) {
mv = movecost(a, t_r, t_c);
if(mv > armies[a].move_left
|| mv == 0) {
ok = 0;
buff = format("%s Can't Move There.",
armyname(a));
saystat(buff);
break;
}
}
}
}
/* prevent overstacking. */
if(ok && ntn > -1) {
cnt = 0;
for(i = 0; i < armycnt; i++)
if(armies[i].nation == ntn
&& armies[i].r == t_r
&& armies[i].c == t_c)
cnt++;
city = city_at(t_r, t_c);
max = 10;
if(city != -1
&& cities[city].nation == ntn)
max = 12;
if(mvcnt + cnt > max) {
ok = 0;
saystat("Too Many Armies There.");
}
}
/* can't leave combat. */
if(ok && ntn > -1)
for(i = 0; i < armycnt; i++)
if(armies[i].nation != ntn
&& armies[i].r == rp
&& armies[i].c == cp) {
ok = 0;
saystat("Can't Leave Combat.");
break;
}
/* do it! */
if(ok) {
for(i = 0; i < mvcnt; i++) {
a = movestack[i].id;
if(ntn > -1) {
if(movestack[i].dep == a)
mv = movecost(a, t_r, t_c);
else
mv = armies[a].move_left ? 1 : 0;
} else
mv = 0;
movestack[i].moved += mv;
armies[a].move_left -= mv;
armies[a].r = t_r;
armies[a].c = t_c;
}
rp = t_r;
cp = t_c;
}
/* redo screen. */
setfocus(ntn, rp, cp);
showmap(rp, cp, ok);
}
showfocus(rp, cp);
}
if(ntn > -1)
for(i = 0; i < mvcnt; i++)
if(movestack[i].moved > 0 || ntn == -1)
pfile.write(format("move-army %d %d %d %d %d\n",
movestack[i].id, movestack[i].moved,
rp, cp, movestack[i].dep));
clearstat(-1);
mvcnt = 0;
return {r:rp,c:cp};
}
function my_army_at(r, c, n)
{
var i;
for(i = 0; i < armycnt; i++)
if(armies[i].nation == n
&& armies[i].r == r
&& armies[i].c == c)
return i;
return -1;
}
function show_info(r, c)
{
var buff, buf2;
var i, ch, city, ntn, ac, hc;
buff = '';
city = city_at(r, c);
if(city != -1) {
buff = format("City: %s (%s)", cities[city].name,
nationcity(cities[city].nation));
} else {
ch = map[r][c];
i=terrain.indexOf(ch);
if(i==-1 && ch==' ')
buff = "Ocean";
else
buff = terr_names[i];
}
ac = 0;
hc = 0;
for(i = 0; i < armycnt; i++)
if(armies[i].r == r && armies[i].c == c)
if(armies[i].hero > 0)
hc++;
else
ac++;
buf2 = format(" %d Armies, %d Heros", ac, hc);
buff += buf2;
saystat(buff);
}
function info_mode(rp, cp, n, ch)
{
var done, r, c, ul_r, ul_c, f_r, f_c, t_r, t_c, a_r, a_c;
var city, army, i, focus;
var rem;
rem = parseInt((16 - gran) / 2);
showfocus(rp, cp);
clearstat(-1);
console.gotoxy(21,23);
console.attributes = attrs.status_area;
console.print("4 6 or d g");
console.gotoxy(21,24);
console.print("1 2 3 c v b");
console.gotoxy(2,22);
console.print("Info Mode: 7 8 9 e r t ESC to Stop. ");
r = a_r = rp;
c = a_c = cp;
ul_r = ((parseInt(r / gran) * gran - rem) + map_height) % map_height;
ul_c = ((parseInt(c / gran) * gran - rem) + map_width) % map_width;
f_r = (r % gran) + rem;
f_c = (c % gran) + rem;
t_r = (ul_r + f_r + map_height) % map_height;
t_c = (ul_c + f_c + map_width) % map_width;
done = 0;
do {
gmapspot(f_r, f_c, map[t_r][t_c], mapovl[t_r][t_c], 0, '');
console.attributes='N';
switch(ch) {
case '7' :
case 'e' :
f_r--;
f_c--;
break;
case '8' :
case 'r' :
f_r--;
break;
case '9' :
case 't' :
f_r--;
f_c++;
break;
case '4' :
case 'd' :
f_c--;
break;
case '6' :
case 'g' :
f_c++;
break;
case '3' :
case 'b' :
f_r++;
f_c++;
break;
case '2' :
case 'v' :
f_r++;
break;
case '1' :
case 'c' :
f_r++;
f_c--;
break;
case 'i' :
break;
case '\f' :
// TODO: refresh
break;
default :
done = 1;
}
if(f_r < gran-1) {
f_r += gran;
ul_r -= gran;
rp = (rp - gran + map_height) % map_height;
focus = true;
}
if(f_c < gran-1) {
f_c += gran;
ul_c -= gran;
cp = (cp - gran + map_width) % map_width;
focus = true;
}
if(f_r > 15 - gran + 1) {
f_r -= gran;
ul_r += gran;
rp = (rp + gran + map_height) % map_height;
focus = true;
}
if(f_c > 15 - gran + 1) {
f_c -= gran;
ul_c += gran
cp = (cp + gran + map_width) % map_width;
focus = true;
}
t_r = (ul_r + f_r + map_height) % map_height;
t_c = (ul_c + f_c + map_width) % map_width;
city = my_city_at(t_r, t_c, n);
army = my_army_at(t_r, t_c, n);
if(focus || army >= 0 || city >= 0) {
if(army >= 0|| city >= 0) {
a_r = t_r;
a_c = t_c;
setfocus(n, t_r, t_c);
}
showmap(rp, cp, false);
focus = false;
}
show_info(t_r, t_c);
gmapspot(f_r, f_c, map[t_r][t_c], mapovl[t_r][t_c], 1, 'I');
console.attributes='N';
console.gotoxy(f_c * 2 + 4, f_r + 2);
if(!done)
ch = console.getkey();
} while(!done);
gmapspot(f_r, f_c, map[t_r][t_c], mapovl[t_r][t_c], 0, '');
console.attributes='N';
return {r:a_r,c:a_c,ch:ch};
}
function groupcmp(r1, c1, r2, c2)
{
/* quadrant check */
if(parseInt(r1 / 16) > parseInt(r2 / 16))
return 1;
if(parseInt(r1 / 16) < parseInt(r2 / 16))
return -1;
if(parseInt(c1 / 16) > parseInt(c2 / 16))
return 1;
if(parseInt(c1 / 16) < parseInt(c2 / 16))
return -1;
/* exact check */
if(r1 > r2)
return 1;
if(r1 < r2)
return -1;
if(c1 > c2)
return 1;
if(c1 < c2)
return -1;
return 0;
}
function prevgroup(ntn, rp, cp)
{
var i, t_r, t_c;
t_r = -1;
t_c = -1;
for(i = 0; i < citycnt; i++) {
if(cities[i].nation == ntn) {
if(groupcmp(cities[i].r, cities[i].c, rp, cp) < 0
&& (t_r == -1
|| groupcmp(cities[i].r, cities[i].c, t_r, t_c) > 0)) {
t_r = cities[i].r;
t_c = cities[i].c;
}
}
}
for(i = 0; i < armycnt; i++) {
if(armies[i].nation == ntn) {
if(groupcmp(armies[i].r, armies[i].c, rp, cp) < 0
&& (t_r == -1
|| groupcmp(armies[i].r, armies[i].c, t_r, t_c) > 0)) {
t_r = armies[i].r;
t_c = armies[i].c;
}
}
}
if(t_r != -1)
return {r:t_r,c:t_c,ret:true};
else
return {r:rp,c:cp,ret:false};
}
function prevarmy(ntn, rp, cp)
{
var i, t_r, t_c;
t_r = -1;
t_c = -1;
for(i = 0; i < armycnt; i++) {
if(armies[i].nation == ntn && armies[i].move_left > 0) {
if(groupcmp(armies[i].r, armies[i].c, rp, cp) < 0
&& (t_r == -1
|| groupcmp(armies[i].r, armies[i].c, t_r, t_c) > 0)) {
t_r = armies[i].r;
t_c = armies[i].c;
}
}
}
if(t_r != -1)
return {r:t_r,c:t_c,ret:true};
else
return {r:rp,c:cp,ret:false};
}
function nextgroup(ntn, rp, cp)
{
var i, t_r, t_c;
t_r = -1;
t_c = -1;
for(i = 0; i < citycnt; i++) {
if(cities[i].nation == ntn) {
if(groupcmp(cities[i].r, cities[i].c, rp, cp) > 0
&& (t_r == -1
|| groupcmp(cities[i].r, cities[i].c, t_r, t_c) < 0)) {
t_r = cities[i].r;
t_c = cities[i].c;
}
}
}
for(i = 0; i < armycnt; i++) {
if(armies[i].nation == ntn) {
if(groupcmp(armies[i].r, armies[i].c, rp, cp) > 0
&& (t_r == -1
|| groupcmp(armies[i].r, armies[i].c, t_r, t_c) < 0)) {
t_r = armies[i].r;
t_c = armies[i].c;
}
}
}
if(t_r != -1)
return {r:t_r,c:t_c,ret:true};
else
return {r:rp,c:cp,ret:false};
}
function nextarmy(ntn, rp, cp)
{
var i, t_r, t_c;
t_r = -1;
t_c = -1;
for(i = 0; i < armycnt; i++) {
if(armies[i].nation == ntn && armies[i].move_left > 0) {
if(groupcmp(armies[i].r, armies[i].c, rp, cp) > 0
&& (t_r == -1
|| groupcmp(armies[i].r, armies[i].c, t_r, t_c) < 0)) {
t_r = armies[i].r;
t_c = armies[i].c;
}
}
}
if(t_r != -1)
return {r:t_r,c:t_c,ret:true};
else
return {r:rp,c:cp,ret:false};
}
var help_mode = 0;
function help()
{
console.gotoxy(2, 21);
console.attributes = attrs.status_area;
console.print(format("Commands, Page %d (Press ? for More Help)", help_mode + 1));
console.cleartoeol();
switch(help_mode) {
case 0 :
console.gotoxy(2, 22);
console.print(" (])next group ([)previous group (})last group ({)first group (q)uit game");
console.cleartoeol();
console.gotoxy(2, 23);
console.print(" (CTRL-]) Next movable army (CTRL-[) Previous movable army (s)tatus display");
console.cleartoeol();
console.gotoxy(2, 24);
console.print(" (A)rmy capabilities army (p)roduction (?) toggle quick help (h)elp");
console.cleartoeol();
break;
case 1 :
console.gotoxy(2, 22);
console.print(" (z/Down)pointer down (a/Up)pointer up (SPACE)mark/unmark (*)mark all");
console.cleartoeol();
console.gotoxy(2, 23);
console.print(" (/)unmark all (m)ove marked armies (f/5)mark all and move (n)ame hero");
console.cleartoeol();
console.gotoxy(2, 24);
console.print(" (I)nformation about current army Read (N)ews (S)end message read (M)ail");
console.cleartoeol();
break;
}
help_mode++;
help_mode %= 2;
}
function status()
{
var ch, pos, i, j, hc, ac, cc;
/* set up screen */
console.clear(attrs.nation_status);
console.gotoxy(21, 2);
console.attributes = genattrs.title;
console.print("Nation Status Display");
console.attributes = genattrs.header;
console.gotoxy(2, 4); console.print("Nation:");
console.gotoxy(2, 5); console.print("Ruler:");
console.gotoxy(2, 6); console.print("Mark:");
console.gotoxy(2, 8); console.print("Cities:");
console.gotoxy(2, 9); console.print("Heros:");
console.gotoxy(2, 10); console.print("Armies:");
console.attributes = genattrs.help;
console.gotoxy(2, 12); console.print("<]> next page <[> previous page (SPACE) to exit");
/* display loop */
pos = 0;
do {
/* show the nations. */
for(i = 4; i <= 11; i++) {
console.gotoxy(17, i);
console.cleartoeol();
}
console.attributes = attrs.nation_status;
for(i = 0; i < 4; i++)
if(pos + 1 + i < nationcnt) {
console.gotoxy(i * 16 + 17, 4);
console.print(nationcity(pos+1+i));
console.gotoxy(i * 16 + 17, 5);
console.print(nations[pos+1+i].name);
console.gotoxy(i * 16 + 20, 6);
console.print(nations[pos+1+i].mark);
cc = 0;
for(j = 0; j < citycnt; j++)
if(cities[j].nation == pos + 1 + i)
cc++;
hc = 0;
ac = 0;
for(j = 0; j < armycnt; j++)
if(armies[j].nation == pos + 1 + i)
if(armies[j].hero > 0)
hc++;
else
ac++;
console.gotoxy(i * 16 + 17, 8);
console.print(format("%5d", cc));
console.gotoxy(i * 16 + 17, 9);
console.print(format("%5d", hc));
console.gotoxy(i * 16 + 17, 10);
console.print(format("%5d", ac));
}
/* handle input. */
console.gotoxy(71, 12);
console.attributes = attrs.nstatus_prompt;
console.print("< >\b\b");
switch((ch = console.getkey())) {
case ' ' :
case 'q' :
ch = '\033';
break;
case ']' :
if(pos + 5 < nationcnt)
pos += 4;
break;
case '[' :
if(pos - 3 > 0)
pos -= 4;
break;
}
} while(ch != '\033');
/* clean up */
mainscreen();
}
function armytypes()
{
var ch, pos, i, j, hc, ac, cc;
/* set up screen */
console.clear(attrs.army_types);
console.gotoxy(21, 2);
console.attributes = genattrs.title;
console.print("Army Type Display");
console.attributes = genattrs.header;
console.gotoxy(2, 4); console.print("Name:");
console.gotoxy(2, 5); console.print("Combat:");
console.gotoxy(2, 6); console.print("Move Rate:");
for(i = 0; terr_names[i] != undefined; i++) {
console.gotoxy(2, 7+i); console.print(terr_names[i]+":");
}
console.gotoxy(2, 7+i); console.print("Special:");
console.attributes = genattrs.help;
console.gotoxy(2, 9+i); console.print("<]> next page <[> previous page (SPACE) to exit");
/* display loop */
pos = 0;
do {
/* show the types. */
console.attributes = attrs.army_types;
for(i = 4; i <= 8+terr_names.length; i++) {
console.gotoxy(17, i);
console.cleartoeol();
}
for(i = 0; i < 4; i++)
if(pos + i < ttypecnt) {
console.gotoxy(i * 16 + 17, 4);
console.print(ttypes[pos+i].name);
console.gotoxy(i * 16 + 17, 5);
console.print(format("%5d",ttypes[pos+i].combat));
console.gotoxy(i * 16 + 17, 6);
console.print(format("%5d",ttypes[pos+i].move_rate));
cc = 0;
for(j = 0; j < terr_names.length; j++) {
console.gotoxy(i * 16 + 17, 7+j);
if(move_table[ttypes[pos+i].move_tbl].cost[j] == 0)
console.print("Impassable");
else
console.print(format("%5d", move_table[ttypes[pos+i].move_tbl].cost[j]));
}
console.gotoxy(i * 16 + 17, 7+j);
console.print(special_desc[ttypes[pos+i].special_mv]);
}
/* handle input. */
console.gotoxy(71, 9+j);
console.attributes = attrs.atype_prompt;
console.print("< >\b\b");
switch((ch = console.getkey())) {
case ' ' :
case 'q' :
ch = '\033';
break;
case ']' :
if(pos + 5 < ttypecnt)
pos += 4;
break;
case '[' :
if(pos - 3 > 0)
pos -= 4;
break;
}
} while(ch != '\033');
/* clean up */
mainscreen();
}
function produce(city)
{
var i, t, ch;
var buff='';
var okstring='';
console.attributes = attrs.status_area;
console.gotoxy(2, 22); console.cleartoeol();
console.gotoxy(2, 23); console.cleartoeol();
for(i = 0; i < cities[city].ntypes; i++) {
if(cities[city].types[i] != -1) {
console.gotoxy(parseInt(i / 2) * 30 + 2, (i % 2) + 22);
console.print(format("%d) %-14.14s (%d)", i + 1,
ttypes[cities[city].types[i]].name,
cities[city].times[i]));
okstring += (i+1);
}
}
console.gotoxy(2, 24);
t = cities[city].types[cities[city].prod_type];
console.print(format("Current: %s (%d turns left) ESC to Cancel",
ttypes[t].name,
cities[city].turns_left));
console.cleartoeol();
saystat(format("Set Army Production for %s: ", cities[city].name));
if(okstring.indexOf(ch = console.getkeys(okstring+'\x1b', 0)) != -1) {
buff = format("set-produce %d %d\n", city, ch - 1);
pfile.write(buff);
execpriv(buff);
}
clearstat(-1);
}
function mainloop(ntn)
{
var ch, r, c, i, n, city, army, force, obj;
var inbuf, buff;
var keep_ch = false;
r = -1;
c = -1;
army = -1;
/* find the player's capitol city */
if(cities[nations[ntn].city].nation == ntn) {
i = nations[ntn].city;
r = cities[i].r;
c = cities[i].c;
setfocus(ntn, r, c);
}
/* find the player's first city */
city = city_at(r, c);
if(city != -1 && cities[city].nation != ntn)
city = -1;
if(city == -1)
for(i = 0; i < citycnt; i++)
if(cities[i].nation == ntn) {
r = cities[i].r;
c = cities[i].c;
setfocus(ntn, r, c);
break;
}
/* find the player's first army */
if(city == -1)
for(i = 0; i < armycnt; i++)
if(armies[i].nation == ntn) {
r = armies[i].r;
c = armies[i].c;
army = i;
setfocus(ntn, r, c);
break;
}
if(army == -1 && city == -1) {
saystat("Nation is Destroyed. Press Any Key: ");
console.getkey();
return;
}
/* Check for messages */
inbuf = format(MAILFL, ntn);
reader(inbuf, 1);
/* enter the loop */
saystat("Welcome to Solomoriah's WAR! Press <h> for Help.");
force = false;
for(;;) {
showmap(r, c, force);
force = false;
showfocus(r, c);
if(!keep_ch)
ch = console.getkey();
keep_ch = false;
showfocus(r, c);
clearstat(-1);
switch(ch) {
case 'p' : /* production */
city = city_at(r, c);
if(city != -1 && cities[city].nation == ntn)
produce(city);
else
saystat("Must View Your Own City First.");
break;
case 'A' : /* army types */
armytypes();
force = true;
break;
case ctrl(']') : /* next army */
obj = nextarmy(ntn, r, c);
r = obj.r;
c = obj.c;
if(!obj.ret)
saystat("No Next Army with Movement Found");
else
setfocus(ntn, r, c);
break;
case ']' : /* next group */
obj = nextgroup(ntn, r, c);
r = obj.r;
c = obj.c;
if(!obj.ret) {
saystat("No More Groups Remain.");
} else
setfocus(ntn, r, c);
break;
case '}' : /* last group */
while((obj = nextgroup(ntn, r, c)).ret) {
r = obj.r;
c = obj.c;
}
r = obj.r;
c = obj.c;
setfocus(ntn, r, c);
break;
case ctrl('[') : /* prev army */
obj = prevarmy(ntn, r, c);
r = obj.r;
c = obj.c;
if(!obj.ret)
saystat("No Previous Army with Movement Found");
else
setfocus(ntn, r, c);
break;
case '[' : /* previous group */
obj = prevgroup(ntn, r, c);
r = obj.r;
c = obj.c;
if(!obj.ret) {
saystat("No Previous Groups Remain.");
} else
setfocus(ntn, r, c);
break;
case '{' : /* first group */
while((obj = prevgroup(ntn, r, c)).ret) {
r = obj.r;
c = obj.c;
}
r = obj.r;
c = obj.c;
setfocus(ntn, r, c);
break;
case 'n' : /* name hero */
if(avcnt > 0 && armies[armyview[avpnt].id].name.length == 0) {
saystat("Enter Hero's Name: ");
inbuf = console.getstr(16);
buff = format("name-army %d '%s'\n", armyview[avpnt].id, inbuf);
pfile.write(buff);
execpriv(buff);
setfocus(ntn, r, c);
clearstat(-1);
} else
saystat("Can Only Rename Heros.");
break;
case 'z' : /* next army */
case KEY_DOWN:
if(avcnt > 0) {
avpnt++;
avpnt += avcnt;
avpnt %= avcnt;
} else
saystat("No Armies!");
break;
case 'a' : /* previous army */
case KEY_UP:
if(avcnt > 0) {
avpnt--;
avpnt += avcnt;
avpnt %= avcnt;
} else
saystat("No Armies!");
break;
case ' ' : /* mark */
if(avcnt > 0)
armyview[avpnt].mark =
armyview[avpnt].mark ? 0 : 1;
break;
case 'I' : /* army information */
if(avcnt > 0) {
var id = armyview[avpnt].id;
buff = format("%s: Combat %d / Hero %d %s",
armyname(id), armies[id].combat, armies[id].hero,
((armies[id].hero > 0 && armies[id].eparm1) ? "(Loyal)" : ""));
saystat(buff);
} else
saystat("No Armies!");
break;
case '*' : /* mark all */
case 'f' : /* mark all and move */
case '5' : /* mark all and move */
for(i = 0; i < avcnt; i++)
if(armies[armyview[i].id].move_left > 0)
armyview[i].mark = 1;
if(ch == '*')
break;
/* fall through */
case 'm' : /* move */
obj = move_mode(ntn, r, c);
r = obj.r;
c = obj.c;
setfocus(ntn, r, c);
break;
case '/' : /* unmark all */
for(i = 0; i < avcnt; i++)
armyview[i].mark = 0;
break;
case 'i' : /* information */
case '1' :
case '2' :
case '3' :
case '4' :
case '6' :
case '7' :
case '8' :
case '9' :
case 'e' :
case 'r' :
case 't' :
case 'd' :
case 'g' :
case 'c' :
case 'v' :
case 'b' :
obj = info_mode(r, c, ntn, ch);
r = obj.r;
c = obj.c;
ch = obj.ch;
if(ch != '')
keep_ch = true;
break;
case 'N' : /* read news... */
reader(NEWSFL, 0);
force = true;
break;
case 'M' : /* mail... */
inbuf = format(MAILFL, ntn);
reader(inbuf, 1);
force = true;
break;
case 'S' : /* send mail */
saystat("Send Mail -- Enter Ruler's Name, or All to Post News: ");
inbuf = console.getstr(16);
n = -1;
for(i = 0; i < nationcnt; i++)
if(nations[i].name.toUpperCase() == inbuf.toUpperCase())
n = i;
if(n == -1 && "all" == inbuf.toLowerCase())
n = 0;
if(n == -1) {
saystat("No Such Ruler.");
break;
}
mailer(ntn, n);
force = true;
clearstat(-1);
break;
case 's' : /* status */
status();
force = true;
break;
case '?' : /* help */
help();
break;
case 'h' : /* help */
case 'H' : /* help */
reader('help.news', 0);
force = true;
break;
case '\f' :
force = true;
break;
case 'q' : /* quit */
saystat("Quit? (Y/N) ");
if(console.getkey().toLowerCase() == 'y')
return;
clearstat(-1);
break;
}
}
}
function titlescreen()
{
var i;
var title = [
" S O L O M O R I A H ' S",
"",
" # # ## ##### ###",
" # # # # # # ###",
" # # # # # # ###",
" # # ###### ##### # ",
" # ## # # # # # ",
" ## ## # # # # ###",
" # # # # # # ###",
];
console.clear(attrs.title_text);
for(i=0; i<title.length; i++) {
console.gotoxy(11, i+3);
console.print(title[i]);
}
console.attributes = attrs.title_copyright;
console.gotoxy(11, i+3);
console.print('JSWAR Version '+(major_ver)+'.'+(minor_ver)+' "Code by Solomoriah"');
console.gotoxy(11, i+5);
console.print("Original Copyright 1993, 1994, 2001, 2013, Chris Gonnerman");
console.gotoxy(11, i+6);
console.print("JavaScript Version Copyright 2013, Stephen Hurd");
console.gotoxy(11, i+7);
console.print("All Rights Reserved.");
}
function newcity()
{
var i;
var clusters = [];
var cc;
var c;
var cl;
for(i = 0; i<MAXCITIES; i++)
clusters[i] = -1;
for(i=0; i<citycnt; i++) {
if(cities[i].nation > 0)
clusters[cities[i].cluster % MAXCITIES] = 1;
else if(clusters[cities[i].cluster % MAXCITIES] == -1)
clusters[cities[i].cluster % MAXCITIES] = 0;
}
cc = 0;
for(i = 0; i < MAXCITIES; i++)
if(clusters[i] == 0)
cc++;
if(cc == 0) { /* any city will do... */
cc = 0;
for(i = 0; i < citycnt; i++)
if(cities[i].nation == 0)
cc++;
if(cc == 0)
return -1;
c = random(cc);
for(i = 0; i < citycnt; i++)
if(cities[i].nation == 0)
if(c == 0)
return i;
else
c--;
/* should not get here... */
return -1;
} else { /* find an empty cluster... */
c = random(cc);
for(i = 0; i < MAXCITIES; i++)
if(clusters[i] == 0)
if(c == 0)
break;
else
c--;
if(i < MAXCITIES) {
cl = i;
cc = 0;
for(i = 0; i < citycnt; i++)
if(cities[i].cluster == cl)
cc++;
c = random(cc);
for(i = 0; i < citycnt; i++)
if(cities[i].cluster == cl)
if(c == 0)
return i;
else
c--;
}
/* should not get here. */
return -1;
}
}
function main(argc, argv)
{
var rc;
var uid;
var n;
var c;
var ch;
var t;
var trys;
var mtbl;
var line;
var offset;
var confirmed;
var fp=new File();
var pp=new File();
var filename='';
var inbuf='';
var name='';
var p;
var u;
var pass='';
var cmd='';
var gamedir='';
var st_buf;
var i;
if(argc)
set_game(argv[0]);
load("loadfont.js", '-H', game_dir+'/terrain.fnt');
if(bbs.mods.CTerm_Version != null) {
console.write("\x1b[?31h\x1b[1;255 D");
js.on_exit('console.write("\x1b[?31l")');
}
titlescreen();
/* load map file */
if((rc = loadmap()) != 0) {
console.gotoxy(11,21);
console.print("Error Loading Map ("+rc+")\r\n");
console.getkey();
exit(1);
}
if((rc = loadsave()) != 0) {
console.gotoxy(11,21);
console.print("Error Loading Game Save ("+rc+")\r\n");
console.getkey();
exit(2);
}
console.gotoxy(11,17);
console.attributes = attrs.title_world;
if(world.length > 0)
console.print("World: "+world+" ");
console.print("Turn: "+gen);
uid = user.number;
if(uid == undefined || uid == 0) {
console.gotoxy(11, 21);
console.print("UID Not Available; Aborting.");
console.getkey();
exit(3);
}
console.gotoxy(11, 21);
console.attributes = attrs.status_area;
console.print("Reading Master Commands... ");
fp = new File(game_dir+'/master.cmd');
if(fp.open('rb')) {
for(i=0; (inbuf = fp.readln())!=null; i++) {
if((rc=execpriv(inbuf)==0)) {
console.gotoxy(49,21);
console.print(format("%3d lines"));
}
else {
console.gotoxy(11,21);
console.print("Master Cmd Failed, Line "+(i+1)+", Code "+rc+" ");
console.getkey();
exit(4);
}
}
fp.close();
}
/* check if nation is entered. */
n = -1;
for(i = 0; i < nationcnt; i++) {
if(nations[i].uid == uid) {
/* TODO: Check for the canary file! */
n = i;
break;
}
}
if(n == -1) { /* nation not entered yet... */
c = newcity();
if(c >= 0) {
fp = new File(game_dir+'/master.cmd');
if(!fp.open('ab')) {
console.gotoxy(11,21);
console.print("Can't Write Master Cmd's ");
console.cleartoeol();
console.getkey();
exit(5);
}
n = nationcnt;
confirmed = 0;
while(!confirmed) {
console.gotoxy(11,19);
console.attributes = attrs.title_newcity;
console.print("Your City is "+ cities[c].name);
console.cleartoeol();
inbuf='';
while(inbuf.length==0) {
console.gotoxy(11, 21);
console.attributes = attrs.title_retry;
console.print("(Enter ! to Retry)");
console.cleartoeol();
console.gotoxy(11, 20);
console.attributes = attrs.title_nameprompt;
console.print("Enter your name: ");
console.cleartoeol();
inbuf = console.getstr(NATIONNMLEN);
}
if(inbuf == '!')
c = newcity();
else
confirmed = 1;
}
name=inbuf;
console.gotoxy(11,22);
console.attributes = attrs.title_symboltitle;
console.print("Available: ");
inbuf = marks[0];
for(i=0; i<nationcnt; i++)
inbuf = inbuf.replace(nations[i].mark, '');
inbuf = inbuf.replace(' ','');
console.attributes = attrs.title_symbollist;
console.print(inbuf);
ch='';
while(ch == '' || ch==null || inbuf.indexOf(ch)==-1) {
console.gotoxy(11, 21);
console.attributes = attrs.title_symbolprompt;
console.print("Select Nation Mark: ");
console.cleartoeol();
ch = console.getkeys(inbuf);
}
console.gotoxy(11,22);
console.cleartoeol();
inbuf = "new-nation '"+name+"' "+uid+' '+c+' '+ch;
fp.writeln(inbuf);
execpriv(inbuf);
/* ... and the hero... */
t = cities[c].types[0];
mtbl = ttypes[t].move_tbl;
inbuf = "make-army '"+name+"' "+n+' '+cities[c].r+' '+cities[c].c+' '+(random(3)+4)+' '+(random(2)+1)+' 8 '+(mtbl)+' 0 1';
fp.writeln(inbuf);
execpriv(inbuf);
/* ...and take the city. */
inbuf = 'control-city '+(n)+' '+(c);
fp.writeln(inbuf);
execpriv(inbuf);
fp.close();
}
else {
console.gotoxy(11, 21);
console.print("No Cities Available. ");
console.cleartoeol();
console.getkey();
exit(6);
}
}
/* execute player commands */
console.gotoxy(11, 21);
console.attributes = attrs.status_area;
console.print("Reading Player Commands... ");
console.cleartoeol();
fp = new File(format("%s/%04d.cmd", game_dir, uid));
if(fp.open('rb')) {
for(i=0; (inbuf = fp.readln()) != null; i++) {
if((rc = execpriv(inbuf))==0) {
if((i+1) % 10 == 0) {
console.gotoxy(49, 21);
console.print(format("%3u lines", i));
}
}
else {
console.gotoxy(11, 21);
console.print("Player Cmd Failed, Line "+(i+1)+", Code "+(rc)+" ");
console.getkey();
exit(7);
}
}
fp.close();
}
/* append to player file... */
pfile = new File(format("%s/%04d.cmd", game_dir, uid));
if(!pfile.open('ab', 0 /* Not buffered */)) {
console.gotoxy(11, 21);
console.print("Can't Create or Append Player Cmd's ");
console.cleartoeol();
console.getkey();
exit(8);
}
/* main loop */
for(i=0; i<nationcnt; i++)
if(nations[i].uid == uid)
n = i;
console.gotoxy(11, 20);
console.attributes = attrs.title_welcome;
console.print("Welcome, "+nations[n].name+" of "+nationcity(n)+"!");
console.cleartoeol();
console.gotoxy(11, 21);
console.attributes = attrs.title_anykey;
console.print("Press Any Key to Begin... ");
console.cleartoeol();
console.getkey();
mainscreen();
mainloop(n);
/* clean up */
pfile.close();
exit(0);
}
main(argc, argv);
/* end of file. */