/* lrdisp.java */ /* ロードランナー4の攻略を図示するための Java アプレット */ /* Mar. 20 2000 アイコン一括 getImage() 版 */ /* hanhan4@jeton.ne.jp */ /* Copyright (C) 1998, 2000 Hanhan4 */ /* 使用・配布は GNU GENERAL PUBLIC LICENCE に従います。 */ /* JDK 1.1.6 でコンパイルしています。 */ /* Netscape Navigator 4.5 未満で動作させることを計画していたので */ /* とくに awt 周辺で「推奨されない API」警告が山のように出ます。 */ import java.applet.Applet; import java.net.*; import java.io.*; import java.awt.*; import java.awt.image.*; import java.util.*; import java.text.*; public class lrdisp extends Applet implements Runnable { static final int BRICK = 0, IRON = 1, SPACE = 2, GOLD = 3, GOLD_S = 4, LADDER = 5, ROPE = 6, HIDDEN = 7, TRAP = 8, H_CHAR = 9, M_BLOCK = 10, MB_LADDER = 11, MB_ROPE = 12, HOLE = 13, COMMANDER = 14, GUARD = 15, HILIT_CELL = 16, NUM_BRICK = 17, NUM_BRICK_RST = 18; /* マップファイル・データファイルで使う記号の表 */ public static final String cell_type = "#O.+*H~AT=Mh^%@&FdD"; /* 定数と記号の対照表 */ /* GUARD(15) まではそれぞれを示すアイコンで描画する */ /* # 0 BRICK れんが O 1 IRON コンクリ . 2 SPACE 空白 + 3 GOLD 金塊 * 4 GOLD_S 光る金塊 H 5 LADDER はしご ~ 6 ROPE ロープ A 7 HIDDEN 隠しれんが T 8 TRAP トラップ = 9 H_CHAR 隠れキャラ M 10 M_BLOCK ブロック h 11 MB_LADDER ブロック+はしご ^ 12 MB_ROPE ブロック+ロープ % 13 HOLE 掘った穴 @ 14 COMMANDER 兵士 & 15 GUARD 番兵 F 16 HILIT_CELL 強調 d 17 NUM_BRICK 番号付きれんが D 18 NUM_BRICK_RST 番号付きれんが(番号リセット) */ /* アイコンイメージ */ Image [] p = new Image[GUARD + 1]; /* イメージ(X軸,Y軸,番号付きれんが) */ Image x_ax, y_ax; Image [] num_bl = new Image[17]; /* 1 - 16 */ /* 実行型のオブジェクト */ lrmap lm; lrtext lt; /* 描画ちらつき防止 */ Image off_img; Graphics off_gra; /* GUIオブジェクト */ Panel mp, tp; TextArea t; Font f; Button b_tp, b_bk, b_fw, b_ls, b_re; /* 点滅表示のためのスレッドなど */ Thread blink = null; boolean blink_state = false; /* 説明の番号 */ int step_no = 0; /* マップ,データのファイル名 */ /* これは DocumentBase(HTML の位置)から */ String mapname; String textname; boolean TEST; public String enc; public void init() { /* ローカル環境は Hanhan4 のテスト環境と見なす */ if ("file".equals(getCodeBase().getProtocol())) { TEST = true; enc = "JISAutoDetect"; } else { TEST = false; /* Microsoft は EUC や Autodetect を知らないようだ */ enc = "JIS"; } /* アイコンをロードする */ /* 1 つのアイコンは 24 x 24 ドットでそれを 16 個(4 x 4)まとめて */ /* lricons.gif としてあるので使いやすいように分割しておく */ Image icons; icons = getImage(getCodeBase(), "img/lricons.gif"); for (int i = 0; i < GUARD + 1; i++) { CropImageFilter cif = new CropImageFilter( (i % 4) * 24, (i / 4) * 24, 24, 24); p[i] = createImage(new FilteredImageSource(icons.getSource(), cif)); } /* 座標軸をロードする */ x_ax = getImage(getCodeBase(), "img/x_axis.gif"); y_ax = getImage(getCodeBase(), "img/y_axis.gif"); /* 番号付きれんがをロードする */ /* これも 24 x 24 を 4 x 4 個まとめてある */ Image nbricks; nbricks = getImage(getCodeBase(), "img/nbricks.gif"); for (int i = 1; i < num_bl.length; i++) { CropImageFilter cif = new CropImageFilter( ((i -1) % 4) * 24, ((i - 1) / 4) * 24, 24, 24); num_bl[i] = createImage( new FilteredImageSource(nbricks.getSource(), cif) ); } /* データはどこに? HTML が知っている */ mapname = getParameter("mapname"); textname = getParameter("textname"); /* マップをロードする */ lm = new lrmap(this); /* テキストをロードする */ lt = new lrtext(this); /* レイアウトマネージャを使いこなせないので無しにする ^^; */ setLayout(null); /* マップ表示部分のパネル作成など */ mp = new Panel(); mp.resize(600, 408); mp.move(0, 0); mp.setForeground(Color.white); mp.setBackground(Color.black); add(mp); /* テキスト表示部分のパネル作成など */ tp = new Panel(); tp.resize(600, 104); tp.move(0, 409); tp.setBackground(new Color(0xff, 0xff, 0xd0)); add(tp); /* あとで調整するので少し大きめに */ t = new TextArea("", 1, 70, TextArea.SCROLLBARS_HORIZONTAL_ONLY); t.setBackground(Color.white); f = new Font("Dialog", Font.PLAIN, 18); t.setFont(f); t.setEditable(false); /* ボタンの作成と設置 */ b_tp = new Button("最初へ"); b_bk = new Button("1つ戻る"); b_fw = new Button("1つ進む"); if (TEST) { b_ls = new Button("最後へ"); b_re = new Button("再読み込み"); } tp.add(t); tp.add(b_tp); tp.add(b_bk); tp.add(b_fw); if (TEST) { tp.add(b_ls); tp.add(b_re); } set_text(false); /* 説明文をアプレットの中に収まるサイズにする */ /* setSize() ではうまくいかないのかなあ */ Dimension dm = this.getSize(); dm.width -= 20; /* ちょっとだけ余裕を持たせてみよう */ while (t.getPreferredSize().width > dm.width) { t.setColumns(t.getColumns() - 1); } } /* init() */ /* マップ,説明文を step_no に合わせて書き換える */ void set_text(boolean need_reset) { step s; int size_of_s; /* step_no の範囲を検査してボタンの有効無効も設定する */ size_of_s = lt.contents.size(); if (step_no + 1 >= size_of_s) { step_no = size_of_s - 1; b_fw.disable(); } else { b_fw.enable(); } if (step_no <= 0) { step_no = 0; b_bk.disable(); } else { b_bk.enable(); } if (need_reset) { /* ステップを最初から追い直した絵を設定する */ lm.reset_map(); for (int i = 0; i < step_no; i++) { step s2 = (step)lt.contents.elementAt(i); lm.change_map(s2, false); } } else { /* 前のステップの後始末 */ lm.cleanup_block(); } /* 現ステップの設定を行う */ s = (step)lt.contents.elementAt(step_no); t.setText(s.text); lm.change_map(s, true); drawmap(); } /* 描画 */ public void paint(Graphics g) { /* オフライン・イメージを「表」にコピーする */ if (off_gra == null) { off_img = createImage(mp.size().width, mp.size().height); off_gra = off_img.getGraphics(); } Graphics g2 = mp.getGraphics(); g2.drawImage(off_img, 0, 0, null); /* ハイライト処理 */ if (blink_state) { for (int j = 0; j < 16; j++) { for (int i = 0; i < 24; i++) { if (lm.m[i][j].hilit == true) { g2.drawRect((i + 1) * 24, (j + 1) * 24, 23, 23); g2.drawRect((i + 1) * 24 + 1, (j + 1) * 24 + 1, 21, 21); } } } } blink_state = !blink_state; } /* マップをオフライン・イメージに描画する */ void drawmap() { if (off_gra == null) { off_img = createImage(mp.size().width, mp.size().height); off_gra = off_img.getGraphics(); } /* 座標軸の書き込み */ off_gra.drawImage(y_ax, 0, 0, this); off_gra.drawImage(x_ax, 24, 0, this); /* マップの書き込み */ for (int j = 0; j < 16; j++) { for (int i = 0; i < 24; i++) { cell tmp = lm.m[i][j]; if (tmp.now >= 0) { /* 通常のアイコン */ off_gra.drawImage(p[tmp.now], (i + 1) * 24, (j + 1) * 24, this); } else { /* 番号付きれんが */ off_gra.drawImage(num_bl[-tmp.now], (i + 1) * 24, (j + 1) * 24, this); } } } /* 兵士の書き込み */ off_gra.drawImage(p[COMMANDER], (lm.soldier.x + 1) * 24, (lm.soldier.y + 1) * 24, this); /* 番兵の書き込み */ for (int i = 0; i < lm.guard.length; i++) { if (lm.guard[i].x == -1) { break; } off_gra.drawImage(p[GUARD], (lm.guard[i].x + 1) * 24, (lm.guard[i].y + 1) * 24, this); } } public void update(Graphics g) { drawmap(); paint(g); } /* ボタン処理 */ /* Netscape Navigator 4.05 未満をサポートするつもりで */ /* 古いイベントモデルを使っています */ public boolean action(Event e, Object arg) { if (e.target == b_bk) { step_no--; set_text(true); return true; } else if (e.target == b_fw) { step_no++; set_text(false); return true; } else if (e.target == b_tp) { step_no = 0; set_text(true); return true; } if (TEST && e.target == b_ls) { step_no = lt.contents.size(); set_text(true); return true; } if (TEST && e.target == b_re) { step_no = 0; lt.init(this); set_text(true); return true; } return false; } /* 点滅用のスレッド周辺 */ public void start() { if (blink == null) { blink = new Thread(this); blink.start(); } } public void stop() { blink = null; } public void run() { while (blink != null) { try { Thread.sleep(500); } catch (InterruptedException e) {} repaint(); } blink = null; } } /* 説明に伴うマップの変更点 */ class diff { int x; int y; int now; } /* 説明の1区切り */ /* =説明文と不定数のマップ変更点 */ class step { String text; Vector df; /* 要素は diff */ step() { df = new Vector(); } } /* 攻略テキスト */ /* =説明の集合体(数は不定) */ class lrtext { Vector contents; /* 要素は step */ /* テキストのコンストラクタ */ lrtext(lrdisp lr) { init(lr); } void init(lrdisp lr) { URL url; DataInputStream dis; contents = new Vector(); /* テキストファイルをダウンロードしてみる */ try { url = new URL(lr.getDocumentBase(), lr.textname + ".dat"); dis = new DataInputStream(url.openStream()); BufferedReader br = new BufferedReader( new InputStreamReader(dis, lr.enc), 64 * 1024 ); /* テキストの各行を読み込む */ String str; while ((str = br.readLine()) != null) { /* 1行目は説明文 */ step s = new step(); s.text = str; /* 2行目はマップ変更の指示 */ str = br.readLine(); if (str == null) { break; } /* マップ変更指示は4バイトで1命令 */ while (str.length() >= 4) { String d = str.substring(0, 4); s.df.addElement(decode(d)); str = str.substring(4); } contents.addElement(s); } } catch(MalformedURLException e) {} catch(IOException e) {} } /* マップ変更の指示を「座標(x,y)に何を書くか(now)」に翻訳する */ diff decode(String s) { String xstr = "ABCDEFGHJKLMNOPQRSTUVWXY"; diff d = new diff(); d.x = xstr.indexOf(s.substring(0, 1)); d.y = Integer.valueOf(s.substring(1, 3)).intValue() - 1; d.now = lrdisp.cell_type.indexOf(s.substring(3, 4)); return d; } } class cell { int ini; int now; boolean hilit; } /* Point は NN4.05 ではダメなのか? */ class mypoint { public int x; public int y; mypoint() { x = -1; y = -1; } mypoint(int i, int j) { x = i; y = j; } void move(int i, int j) { x = i; y = j; } } class lrmap { /* マップの各マス */ cell [][] m = new cell[24][16]; /* 番号付きれんがの番号 */ int dig = 1; /* 兵士,番兵 */ mypoint soldier; mypoint [] guard = new mypoint[6]; /* マップのコンストラクタ */ lrmap(lrdisp lr) { URL url; DataInputStream dis; int num_guard = 0; /* 兵士・番兵のインスタンスを作っておく */ soldier = new mypoint(); for (int i = 0; i < guard.length; i++) { guard[i] = new mypoint(); } /* マップファイルを開いてみる */ try { url = new URL(lr.getDocumentBase(), lr.mapname + ".map"); dis = new DataInputStream(url.openStream()); BufferedReader br = new BufferedReader( new InputStreamReader(dis, lr.enc) ); /* マップの各行を読み込む */ for (int j = 0; j < 16; j++) { String str = br.readLine(); /* 各行の文字を解析する */ for (int i = 0; i < 24; i++) { /* 各セルを設定する */ m[i][j] = new cell(); m[i][j].ini = lr.cell_type.indexOf(str.substring(i, i + 1)); } } } catch(MalformedURLException e) {} catch(IOException e) {} reset_map(); } /* 初期状態の絵を再設定する */ void reset_map() { dig = 1; int num_guard = 0; clear_guard(); for (int j = 0; j < 16; j++) { for (int i = 0; i < 24; i++) { m[i][j].now = m[i][j].ini; m[i][j].hilit = false; if (m[i][j].now == lrdisp.GUARD) { guard[num_guard++].move(i, j); m[i][j].now = lrdisp.SPACE; } else if (m[i][j].now == lrdisp.COMMANDER) { soldier.move(i, j); m[i][j].now = lrdisp.SPACE; } } } } /* マップを変更する */ void change_map(step s, boolean do_full) { int n = s.df.size(); diff d; int num_guard = 0; for (int i = 0; i < n; i++) { d = (diff)s.df.elementAt(i); if (d.now == lrdisp.GUARD) { if (num_guard == 0) { clear_guard(); } guard[num_guard++].move(d.x, d.y); } else if (d.now == lrdisp.COMMANDER) { soldier.move(d.x, d.y); } else if (d.now == lrdisp.HILIT_CELL) { if (do_full) { m[d.x][d.y].hilit = true; } } else if (d.now == lrdisp.NUM_BRICK) { if (do_full) { m[d.x][d.y].now = -dig; } dig++; } else if (d.now == lrdisp.NUM_BRICK_RST) { dig = 1; if (do_full) { m[d.x][d.y].now = -dig; } dig++; } else if (d.now == lrdisp.HOLE) { if (do_full) { m[d.x][d.y].now = d.now; } } else { m[d.x][d.y].now = d.now; } } } /* 番号付きれんが,穴,ハイライトのリセット */ void cleanup_block() { for (int j = 0; j < 16; j++) { for (int i = 0; i < 24; i++) { /* ハイライトの後始末 */ m[i][j].hilit = false; /* 番号付きれんがと穴の後始末 */ if (m[i][j].now < 0 || m[i][j].now == lrdisp.HOLE) { m[i][j].now = lrdisp.BRICK; } } } } void clear_guard() { for (int i = 0; i < guard.length; i++) { guard[i].move(-1, -1); } } }