comparison scn2k/scn2k_text.cc @ 0:223b71206888

Initial import
author thib
date Fri, 01 Aug 2008 16:32:45 +0000
parents
children a95c130b567b
comparison
equal deleted inserted replaced
-1:000000000000 0:223b71206888
1 /*
2 TODO:
3 日付のラベルが画面切り替え時に欠けるのを修正
4 画像効果 : 人間の入れ換わりなど
5 kcursor の操作を WidText クラスに任せる
6 WidText クラスには新たに以下の操作を加える
7 ・ウェイト終了後、クリアなしに新たなテキストを追加、新たにstart-waitする
8 ・文字の描画 (Start) と Wait(カーソル表示待ち)の分離。
9 Start すると文字を描画開始する。クリックで全描画。
10 Flush するとバッファ内の文字をすべて描画する
11 Wait すると全描画後、クリックされるまでカーソルを表示するまで待つ
12 TextImpl 側の状態としては Wait のみを持つ (PREPAREに戻るのを待つ)
13 ただし、Skip の権利はどっちがもつ?(現状は?)
14
15 GrpObj: NextObj と GrpObj を分離。CreateObj は現状通り、Visible=1 時に行う。
16 それぞれ num=0 (screen) の枝leaf として実装。delete時は親のdeleteのみを
17 行い、子はGrpObjの実体だけを削除する
18 Visible 後のhide は実際に hide とする
19 ExecReservedCmd() はなくせるはず。Delete() もなくなる。
20 カノギ:ReBlit() がうまくいかないせいで名前ウィンドウが消えた時の背景がなくなる
21
22 くら:回想表示
23 SEL画像効果
24 DONE:
25 ともよのテキストウィンドウ実装、ボタン実装
26 shake の画像効果
27 オブジェクト内のテキスト色の実装
28 画像効果の改善
29 */
30
31 /*
32 * Copyright (c) 2004-2006 Kazunori "jagarl" Ueno
33 * All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 * 3. The name of the author may not be used to endorse or promote products
44 * derived from this software without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
47 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
48 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
49 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
50 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
51 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
52 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
53 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
54 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
55 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 */
57
58 #include"window/event.h"
59 #include"window/picture.h"
60 #include"window/widget.h"
61 #include"system/file.h"
62 #include"system/system_config.h"
63 #include"scn2k.h"
64
65 #include<string>
66 using namespace std;
67
68 // kanji conv : デバッグ表示用
69 void kconv(const unsigned char* src, unsigned char* dest);
70 void kconv_rev(const unsigned char* src, unsigned char* dest);
71 string kconv(const string& s);
72 string kconv_rev(const string& s);
73 // render.cc
74 void DSurfaceFillA(Surface* src, const Rect& rect, int r, int g, int b, int a); // テキストウィンドウ背景の設定
75 void DSurfaceMove(Surface* src_o, const Rect& srcrect, Surface* dst_o, const Rect& dstrect); // コピー
76
77 /**************************************************************::
78 **
79 ** TextImpl(interface)
80 */
81 struct TimerAtom {
82 int from;
83 int to;
84 unsigned int start_time;
85 unsigned int total_time;
86 };
87
88 struct TextWindow {
89 /* @@@ : SetWindowColor での surface 再設定に注意 */
90 WidText* wid;
91 bool name_visible;
92 WidLabel* name;
93 PicContainer* name_container;
94 PicBase* face;
95 PicBase* face_pics[8];
96 TextWindow(PicContainer& parent, Event::Container& event, int window_no, const AyuSysConfig& config, void* callback);
97 ~TextWindow() {
98 if (name_container) delete name_container;
99 int i; for (i=0; i<8; i++) {
100 if (face_pics[i]) delete face_pics[i];
101 }
102 if (wid) delete wid;
103 }
104 Rect WakuSize(PicContainer& pic, int waku_no, const AyuSysConfig& config);
105 void MakeWaku(PicContainer& pic, Event::Container& event, int waku_no,int window_no, bool* use_btn, const AyuSysConfig& config, void* callback);
106 void show(void) {
107 wid->show();
108 if (name_container && name_visible) name_container->show();
109 if (face) face->show();
110 }
111 void hide(void) {
112 wid->hide();
113 if (name_container) name_container->hide();
114 if (face) face->hide();
115 }
116 void ShowFace(const char* path) {
117 if (!face) return;
118 face->SetSurface( path, 0,0);
119 }
120 void ResetFace(void) {
121 if (!face) return;
122 face->SetSurface( (Surface*)0, 0,0);
123 }
124 void StartText(const TextStream& _stream) {
125 wid->Clear();
126 wid->stream = _stream;
127 if (name_container) {
128 char namestr[1024];
129 namestr[0] = 0;
130 wid->stream.RemoveName(namestr, 1024);
131 if (namestr[0] == 0) {
132 name_container->hide();
133 } else {
134 if (name) {
135 name_container->show_all();
136 name->SetText(namestr);
137 }
138 }
139 }
140 wid->Start();
141 }
142 void SetName(const char* n) {
143 if (name_container && name) {
144 if (n[0]) {
145 name_container->show();
146 name->SetText(n);
147 name_visible = true;
148 } else {
149 name_container->hide();
150 name_visible = false;
151 }
152 }
153 }
154 };
155
156 class TextImpl {
157 private:
158 public:
159 TextWindow* text;
160 typedef enum {NORMAL=0, WAIT_TEXT=1, WAIT=2, WAIT_CLICK=3, WAIT_ABORT=4, WAIT_CLICK_MOUSEPOS = 5, WAIT_CLICK_MOUSEPOSEND_L = 6, WAIT_CLICK_MOUSEPOSEND_R = 7,
161 WAIT_SELECT_INBOX = 10, WAIT_SELECT_OUTBOX=11, WAIT_SELECT_VALUE = 12,
162 WAIT_EXTRN_MASK = 64, SAVEMASK = 128, LOADMASK = 256, SKIPMASK = 512,
163 CLEARSCR_MASK = 1024, STATSAVE_MASK = 2048, CLEARSCR_WAIT_MASK=(1<<12),
164 SKIPEND_MASK = (1<<13), BACKLOG_MASK=(1<<14), BACKLOG_MASK_FWD=(1<<15), BACKLOG_MASK_KOE=(1<<16), BACKLOG_WAIT_MASK=(1<<17),
165 ALLMASK = (CLEARSCR_MASK | WAIT_EXTRN_MASK | SAVEMASK | LOADMASK | SKIPMASK | BACKLOG_MASK | BACKLOG_MASK_FWD | BACKLOG_MASK_KOE | BACKLOG_WAIT_MASK | STATSAVE_MASK | CLEARSCR_WAIT_MASK | SKIPEND_MASK)
166 } Status;
167 Status status, status_saved, status_mask;
168 private:
169 std::string ruby_text;
170 bool ruby_text_flag;
171 unsigned int wait_time;
172 unsigned int old_time;
173 unsigned int base_time;
174 int text_window_number;
175 bool text_parsing;
176 TextStream text_stream;
177 SkipMode skip_mode;
178 int save_selectcount;
179
180 std::map<int, TimerAtom> timer_var;
181 std::vector<WidTextButton*> selects;
182 std::vector<int> sel_backlog_pos;
183 string replace_name[26];
184 string replace_name2[26];
185 PicContainer* sel_widget;
186 PicWidget* backlog_widget;
187
188 vector<BacklogItem>& backlog;
189 BacklogItem& backlog_item;
190 BacklogItem cur_backlog_item;
191 BacklogItem drawn_backlog_item;
192
193 public:
194 PicContainer& parent;
195 Event::Container& event;
196 AyuSysConfig& config;
197 private:
198 TextWindow* widgets[32];
199 WidTimeCursor* kcursor;
200 Surface* sel_bg1;
201 Surface* sel_bg2;
202 Rect sel_bg_rect;
203
204 void SetCursor(int num);
205 VarInfo wait_savedvar[2];
206
207 public:
208 void AddText(const char* str);
209
210 static void PressFuncSkip(void* pointer, WidButton* from);
211 static void PressFuncLoad(void* pointer, WidButton* from);
212 static void PressFuncSave(void* pointer, WidButton* from);
213 static void PressFuncBacklog(void* pointer, WidButton* from);
214 static void PressFuncBacklogFwd(void* pointer, WidButton* from);
215 private:
216 static void PressFuncButton(void* pointer, WidButton* from);
217 static bool PressFunc(int x, int y, void* pointer);
218
219 public:
220 TextImpl(Event::Container& _event, PicContainer& _parent, AyuSysConfig& config, vector<BacklogItem>& parent_backlog, BacklogItem& parent_backlog_item);
221 ~TextImpl();
222 void InitWindow(void);
223 void SetWindowColor(int r, int g, int b, int a, bool is_transparent);
224 void SetTextSpeed(int new_speed);
225 void SetTextWait(int new_wait);
226 void CreateSelect(Cmd& cmd);
227 void Exec(Cmd& cmd);
228 bool Wait(unsigned int current_time, Cmd& cmd);
229 void hide(void);
230 void show(void) { show(text_window_number); }
231 void show(int num);
232 void DrawBacklog(BacklogItem& item, Cmd& cmd);
233 void Save(std::string& str, bool select_save);
234 void Load(const char* str);
235 void SetSkipMode(SkipMode _mode);
236 void CreateSelBG(void);
237 };
238
239 /**************************************************************::
240 **
241 ** TextImpl(implementation)
242 */
243 TextImpl::TextImpl(Event::Container& _event, PicContainer& _parent, AyuSysConfig& _config, vector<BacklogItem>& parent_backlog, BacklogItem& parent_backlog_item) :
244 text(0),status(TextImpl::NORMAL), status_saved(TextImpl::NORMAL), status_mask(TextImpl::NORMAL), ruby_text_flag(false),
245 old_time(0), base_time(0), text_window_number(0), text_parsing(false), skip_mode(SKIP_NO), save_selectcount(0), sel_widget(0),
246 backlog_widget(0), backlog(parent_backlog), backlog_item(parent_backlog_item), parent(_parent), event(_event), config(_config),
247 kcursor(0), sel_bg1(0), sel_bg2(0), sel_bg_rect(0,0,0,0) {
248 int i;
249 for (i=0; i<32; i++) {
250 widgets[i] = 0;
251 }
252 text_stream.kanji_type = TextStream::sjis;
253 event.RegisterGlobalPressFunc(&PressFunc, (void*)this);
254 }
255
256 TextImpl::~TextImpl() {
257 if (sel_widget) delete sel_widget;
258 int i;
259 for (i=0; i<32; i++) {
260 if (widgets[i]) delete widgets[i];
261 }
262 if (backlog_widget) delete backlog_widget;
263 if (sel_bg1) parent.Root().DeleteSurface(sel_bg1);
264 if (sel_bg2) parent.Root().DeleteSurface(sel_bg2);
265 event.DeleteGlobalPressFunc(&PressFunc, (void*)this);
266 }
267
268 bool TextImpl::PressFunc(int x, int y, void* pointer) {
269 TextImpl* t = (TextImpl*)pointer;
270 if (t->status == WAIT_CLICK) {
271 t->status = WAIT_ABORT;
272 } else if (t->status == WAIT_CLICK_MOUSEPOS) {
273 t->status = WAIT_CLICK_MOUSEPOSEND_L;
274 } else if (t->status_mask & CLEARSCR_WAIT_MASK) {
275 t->status_mask = Status(t->status_mask & (~CLEARSCR_WAIT_MASK));
276 if (t->text) t->text->show();
277 if (t->kcursor) {
278 if (t->status == WAIT_TEXT) t->kcursor->show();
279 }
280 if (t->sel_widget) t->sel_widget->show();
281 if (t->backlog_widget) t->backlog_widget->show();
282 } else if (t->status_mask & BACKLOG_WAIT_MASK) {
283 t->status_mask = Status(t->status_mask | BACKLOG_MASK_KOE);
284 } else if ( (t->skip_mode & SKIP_TEXT) && (!(t->skip_mode & SKIP_IN_MENU)) ) {
285 if (t->status == WAIT_SELECT_INBOX) ;
286 else if (t->status == WAIT_SELECT_OUTBOX) ;
287 else if (t->status == WAIT_SELECT_VALUE) ;
288 else t->status_mask = Status(t->status_mask | SKIPEND_MASK);
289 }
290 return true; // event not deleted
291 }
292 void TextImpl::PressFuncButton(void* pointer, WidButton* from) {
293 TextImpl* t = (TextImpl*)pointer;
294 if (t->status != WAIT_SELECT_INBOX && t->status != WAIT_SELECT_OUTBOX) return;
295 vector<WidTextButton*>::iterator it;
296 int sel = 0;
297 for (it=t->selects.begin(); it != t->selects.end(); it++, sel++) {
298 if (from == *it) break;
299 }
300 if (it == t->selects.end()) {
301 fprintf(stderr,"TextImpl::PressFuncButton: Cannot find select widget\n");
302 return;
303 }
304 t->status = Status(WAIT_SELECT_VALUE + sel);
305 return;
306 }
307
308 void TextImpl::SetSkipMode(SkipMode _mode) {
309 if ( (skip_mode & SKIP_IN_MENU) && (_mode & SKIP_IN_MENU) == 0) {
310 if (status_mask & BACKLOG_WAIT_MASK) { // backlog mode から復帰
311 status_mask = Status(status_mask & (~(BACKLOG_MASK|BACKLOG_MASK_FWD|BACKLOG_MASK_KOE|BACKLOG_WAIT_MASK)));
312 text->wid->Clear();
313 if (status == WAIT_TEXT && text != 0) {
314 text->StartText(text_stream);
315 text->ShowFace(backlog_item.face.c_str());
316 text->wid->Flush();
317 if (kcursor) kcursor->show();
318 }
319 drawn_backlog_item.Clear();
320 }
321 if (text) text->wid->activate();
322 if (sel_widget) {
323 sel_widget->show();
324 if (kcursor) kcursor->hide();
325 }
326 if (backlog_widget) backlog_widget->show();
327 if (status_mask & STATSAVE_MASK) {
328 status_mask = Status(status_mask & (~STATSAVE_MASK));
329 status = status_saved;
330 }
331 } else if ( (skip_mode & SKIP_IN_MENU) == 0 && (_mode & SKIP_IN_MENU) ) {
332 if (text) text->wid->deactivate();
333 if (sel_widget) sel_widget->hide();
334 if (backlog_widget) backlog_widget->hide();
335 }
336 skip_mode = _mode;
337 }
338
339 /* hash_map が欲しい……*/
340 #include<map>
341 #include<list>
342 struct SaveFaceHash { // バックログセーブ時の顔画像管理を行う
343 map<string, int> facetonum;
344 typedef pair<string,int> Node;
345 typedef list<Node> List;
346 List container;
347 int id_max;
348 static int size_max;
349 SaveFaceHash() : id_max(0) {
350 }
351 void NewNode(string face, int face_id) {
352 facetonum[face] = face_id;
353 container.push_front(Node(face, face_id));
354 if (container.size() > size_max) {
355 Node remove = container.back();
356 container.pop_back();
357 facetonum.erase(remove.first);
358 }
359 }
360 int Add(string face) {
361 int id; int ret = -1;
362 int i; List::iterator it;
363 if (face.empty()) return -1;
364 if (facetonum.find(face) == facetonum.end()) {
365 id = ++id_max;
366 NewNode(face, id);
367 ret = -1;
368 } else {
369 id = facetonum[face];
370 for (i=0, it=container.begin(); it != container.end(); i++, it++) {
371 if (it->second == id) {
372 ret = i;
373 Node n = *it;
374 container.erase(it);
375 container.push_front(n);
376 break;
377 }
378 }
379 }
380 return ret;
381 }
382 string Get(int num) {
383 if (num < 0) return "";
384 List::iterator it = container.begin();
385 for (; it != container.end(); it++) {
386 if (num == 0) return it->first;
387 num--;
388 }
389 return "";
390 }
391 };
392 int SaveFaceHash::size_max = 20;
393
394 void TextImpl::Save(string& str, bool rollback_save) {
395 char buf[1024];
396 str = "\n";
397 str += "[TextImpl Window]\n";
398 sprintf(buf, "TextImplWindow=%d\n",text_window_number);
399 str += buf;
400 if (rollback_save) {
401 ++save_selectcount;
402 BacklogItem save_item;
403 save_item.SetSavepos(save_selectcount);
404 backlog.push_back(save_item);
405 }
406 sprintf(buf, "SaveSelectCount=%d\n",save_selectcount);
407
408 str += buf;
409 int i;
410 for (i=0; i<26; i++) {
411 if (replace_name2[i].empty()) continue;
412 sprintf(buf, "RName.%c=%s\n",i+'A',replace_name2[i].c_str());
413 str += buf;
414 }
415 int cnt = 0;
416 vector<BacklogItem>::iterator it;
417 it = backlog.begin();
418 if (!rollback_save) {
419 SaveFaceHash face_log;
420 do {
421 int cur_scn = -1; int cur_pos = -1;
422 sprintf(buf, "Backlog.%d=",++cnt);
423 str += buf;
424 for (; it != backlog.end(); it++) {
425 buf[0] = 0; int buflen = 0;
426 if (it->scn == -1) continue;
427 if (it->pos == -1 && it->scn != 0) continue;
428
429 buf[buflen++] = ';';
430 if (it->scn == 0 && it->pos == -1) {
431 buflen += snprintf(buf+buflen, 1000-buflen, "\"%s\".", it->text.Save().c_str());
432 } else {
433 if (cur_scn != -1 && cur_scn != it->scn) break; // scn change
434 if (cur_pos != -1 && cur_pos/5000 != it->pos/5000) break; // pos exceeded
435 if (!it->text.container.empty()) {
436 buflen += snprintf(buf+buflen, 1000-buflen, "\"%s\"", it->text.Save().c_str());
437 }
438 if (cur_scn == -1) { // scene change
439 buflen += snprintf(buf+buflen, 1000-buflen, ":%d:%d",it->scn,it->pos);
440 cur_scn = it->scn;
441 } else {
442 buflen += snprintf(buf+buflen, 1000-buflen, "%d",it->pos);
443 }
444 cur_pos = it->pos;
445 }
446 if (it->koe != -1)
447 buflen += snprintf(buf+buflen, 1000-buflen, ",%d",it->koe);
448 if (!it->face.empty()) {
449 if (it->koe == -1) buf[buflen++] = ',';
450 int face_num = face_log.Add(it->face);
451 if (face_num >= 0 && face_num < 20)
452 buflen += snprintf(buf+buflen, 1000-buflen, ",%c", 'A'+face_num);
453 else
454 buflen += snprintf(buf+buflen, 1000-buflen, ",\"%s\"", it->face.c_str());
455 }
456 buf[buflen++] = '\0';
457 if (buflen >= 1000) { // 万が一、バックログ1アイテムの大きさが 1000byte を越えるとき
458 fprintf(stderr,"Fatal : Cannot save backlog crrectly; Please send bug report to the author.\n");
459 } else str += buf;
460 }
461 str += "\n";
462 } while(it != backlog.end());
463 }
464 return;
465 }
466 void TextImpl::Load(const char* str) {
467 if (text) text->wid->Clear();
468 hide();
469 text_window_number = 0;
470 save_selectcount = 0;
471 if (sel_widget) {
472 selects.clear();
473 sel_backlog_pos.clear();
474 delete sel_widget;
475 sel_widget = 0;
476 }
477 if (backlog_widget) {
478 delete backlog_widget;
479 backlog_widget = 0;
480 }
481 status = NORMAL;
482 status_mask = NORMAL;
483 status_saved = NORMAL;
484 text_parsing = false;
485 text_stream.Clear();
486 // backlog.clear();
487 vector<BacklogItem> new_backlog;
488 backlog_item.Clear();
489 cur_backlog_item.Clear();
490 drawn_backlog_item.Clear();
491
492 str = strstr(str, "\n[TextImpl Window]\n");
493
494 if (str) {
495 SaveFaceHash face_log;
496 str += strlen("\n[TextImpl Window]\n");
497 const char* strend = str;
498 do {
499 str = strend;
500
501 strend = strchr(str, '\n');
502 if (strend == 0) strend = str + strlen(str);
503 else strend++;
504
505 if (str[0] == '[') break; // next section
506 if (strncmp(str, "TextImplWindow=",15) == 0) {
507 str += 15;
508 sscanf(str, "%d", &text_window_number);
509 } else if (strncmp(str, "SaveSelectCount=",16) == 0) {
510 str += 16;
511 sscanf(str, "%d", &save_selectcount);
512 } else if (strncmp(str, "RName.", 6) == 0) {
513 int n = str[6]-'A';
514 if (n >= 0 && n < 26 && str[7] == '=') {
515 const char* s = strchr(str, '\n');
516 int len = -1;
517 if (s) len = s-(str+8);
518 if (len > 0) {
519 replace_name2[n].assign(str+8, len);
520 }
521 }
522 } else if (strncmp(str, "Backlog.", 8) == 0) {
523 int cur_scn = -1;
524 int n = -1;
525 sscanf(str+8, "%d", &n); /* not used */
526 const char* next_str = strchr(str, ';');
527 while(next_str != 0 && next_str < strend) {
528 str = next_str + 1;
529 next_str = strchr(str, ';');
530 if (next_str == 0) next_str = strend;
531
532 BacklogItem item;
533 if (str[0] == '"') {
534 const char* send = strchr(str+1, '"');
535 if (send == 0 || send > next_str) continue;
536 string tmp_str; tmp_str.assign(str+1, send-str-1);
537 item.DeleteTextPos();
538 item.text.Load(tmp_str);
539 str = send + 1;
540 }
541 if (str[0] == '.') {
542 item.DeleteTextPos();
543 str++;
544 } else if (str[0] == ':') {
545 sscanf(str, ":%d:%d", &item.scn, &item.pos);
546 cur_scn = item.scn;
547
548 } else {
549 item.scn = cur_scn;
550 sscanf(str, "%d", &item.pos);
551 }
552 str = strchr(str, ',');
553 if (str == 0 || str > next_str) goto backlog_store;
554 str++;
555 if (str[0] == ';' || str[0] == ',')
556 item.koe = -1;
557 else
558 sscanf(str, "%d", &item.koe);
559 str = strchr(str, ',');
560 if (str == 0 || str > next_str) goto backlog_store;
561 str++;
562 if (*str == '"') {
563 const char* send = strchr(str+1, '"');
564 if (send) {
565 item.face.assign(str+1, send-str-1);
566 }
567 } else if (*str >= 'A' && *str <= 'Z') {
568 item.face = face_log.Get(*str - 'A');
569 }
570 face_log.Add(item.face);
571 backlog_store:
572 new_backlog.push_back(item);
573 }
574 }
575 } while (*strend != 0);
576 }
577 if (new_backlog.empty() && (!backlog.empty())) { // empty なら save_selectcount まで backlog を巻き戻す
578 vector<BacklogItem>::iterator it = backlog.end();
579 do {
580 it--;
581 if (it->scn == BacklogItem::SaveSelect && it->pos == save_selectcount) {
582 // Save 位置を見つけたらそれ以降を erase
583 backlog.erase(it, backlog.end());
584 break;
585 }
586 } while(it != backlog.begin());
587 --save_selectcount;
588 } else {
589 backlog.swap(new_backlog);
590 }
591 // backlog.clear();
592 return;
593 }
594
595 void TextImpl::hide(void) {
596 if (text) text->hide();
597 if (kcursor) kcursor->hide();
598 text = 0;
599 }
600 void TextImpl::show(int num) {
601 if (num != text_window_number) {
602 hide();
603 if (num >= 0 && num < 32 && widgets[num] != 0) {
604 text_window_number = num;
605 }
606 }
607 text = widgets[text_window_number];
608 text->show();
609 if (kcursor) {
610 int kx, ky, d;
611 char key[1024];
612 sprintf(key, "#WINDOW.%03d.KEYCUR_MOD", text_window_number);
613 config.GetParam(key, 3, &d, &kx, &ky);
614 // 正しくない気がする
615 kx += text->wid->Pic()->PosX();
616 ky += text->wid->Pic()->PosY();
617 // 微妙に下にする
618 ky += 8;
619 kcursor->Pic()->Move(kx, ky);
620 }
621 }
622 void TextImpl::DrawBacklog(BacklogItem& item, Cmd& cmd) {
623 text->show();
624 text->wid->deactivate();
625 status_mask = Status(status_mask | BACKLOG_WAIT_MASK);
626 drawn_backlog_item = item;
627 if (item.text.container.empty()) {
628 // cmd から text 内容を再構成
629 TextStream saved_text = text_stream;
630 text_stream.Clear();
631 AddText(cmd.Str(cmd.args[0]));
632 item.text = text_stream;
633 text_stream = saved_text;
634 }
635 char namestr[1024];
636 namestr[0] = 0;
637 item.text.RemoveName(namestr, 1024);
638 text->SetName(namestr);
639 item.text.InsertColor(0, item.text.container.size(), 0xff,0xff,0);
640 text->wid->Clear();
641 text->wid->stream = item.text;
642 text->wid->Start();
643 text->wid->Flush();
644 if (item.face.empty()) text->ResetFace();
645 else text->ShowFace(item.face.c_str());
646 if (kcursor) kcursor->hide();
647 }
648
649 void TextImpl::CreateSelBG(void) {
650 if (sel_bg1 != 0 || sel_bg2 != 0) return;
651
652 const char* btnfile1 = config.GetParaStr("#SELBTN.000.NAME");
653 const char* btnfile2 = config.GetParaStr("#SELBTN.000.BACK");
654 char path[1024];
655 strcpy(path, btnfile1);
656 sel_bg1 = parent.Root().NewSurface(path);
657 if (sel_bg1 == 0) {
658 sprintf(path,"%s.g00",btnfile1);
659 sel_bg1 = parent.Root().NewSurface(path);
660 }
661 strcpy(path, btnfile2);
662 sel_bg2 = parent.Root().NewSurface(path);
663 if (sel_bg2 == 0) {
664 sprintf(path,"%s.g00",btnfile2);
665 sel_bg2 = parent.Root().NewSurface(path);
666 }
667 sel_bg_rect = Rect(0,0,0,0);
668 if (sel_bg1) sel_bg_rect.join(Rect(*sel_bg1));
669 if (sel_bg2) sel_bg_rect.join(Rect(*sel_bg2));
670 return;
671 }
672
673 void TextImpl::CreateSelect(Cmd& cmd) {
674 char key[1024];
675 sprintf(key, "#WINDOW.%03d.SELCOM_USE",text_window_number);
676 int sel_type = 0;
677 config.GetParam(key, 1, &sel_type);
678
679 int sel_size = cmd.args.size() / 2;
680 int i;
681 // cur_backlog_item に次にbacklogに入るべき内容を作成
682 // CreateSelect() 後、SAVEPOINT なので現在のbacklogの内容(前のメッセージ)が
683 // backlog に代入される。その後、backlog_item に cur_backlog_item の内容がセットされる(Wait()内)
684 char backlog_sel_text[11] = {0x81,0x69,0x91,0x49,0x91,0xf0,0x8e,0x88,0x81,0x6a,0x00};
685 cur_backlog_item.Clear();
686 cur_backlog_item.AddTextPos(cmd);
687 cur_backlog_item.text.Add(backlog_sel_text);
688 cur_backlog_item.text.AddReturn();
689 sel_backlog_pos.clear();
690 for (i=0; i<sel_size; i++) {
691 sel_backlog_pos.push_back(cur_backlog_item.text.container.size());
692 cur_backlog_item.text.Add(cmd.Str(cmd.args[i*2]));
693 cur_backlog_item.text.AddReturn();
694 }
695 sel_backlog_pos.push_back(cur_backlog_item.text.container.size());
696
697 if (sel_type == 0) { // Princess Bride: 選択ウィンドウを別表示
698 External_select:
699 CreateSelBG();
700 hide(); // なので、テキストウィンドウは消去
701 int baseposx, baseposy, repposx, repposy;
702 int mojisize, col1, col2;
703 config.GetParam("#SELBTN.000.BASEPOS", 2, &baseposx, &baseposy);
704 config.GetParam("#SELBTN.000.REPPOS", 2, &repposx, &repposy);
705 config.GetParam("#SELBTN.000.MOJISIZE", 1, &mojisize);
706 config.GetParam("#SELBTN.000.MOJIDEFAULTCOL", 1, &col1);
707 config.GetParam("#SELBTN.000.MOJISELECTCOL", 1, &col2);
708 if (col1 == col2) col2 = 1; // CLANNAD でとりあえず。
709 int r, g, b;
710 sprintf(key, "#COLOR_TABLE.%03d", col1);
711 config.GetParam(key, 3, &r, &g, &b);
712 Color fore(r,g,b);
713 sprintf(key, "#COLOR_TABLE.%03d", col2);
714 config.GetParam(key, 3, &r, &g, &b);
715 Color seled(r,g,b);
716
717 /* ウィジット作成 */
718 /* ウィンドウ背景の大きさを求める */
719 if (baseposx == 0 && sel_bg_rect.width() != 0)
720 baseposx = (parent.Width()-sel_bg_rect.width()) / 2; // ボタン位置をセンタリング
721
722 sel_widget = parent.create_node( Rect(0, 0, parent.Width(), parent.Height()),0);
723
724 for (i=0; i<sel_size; i++) {
725 PicBase* p;
726 // 背景作成
727 if (sel_bg2) {
728 p = sel_widget->create_node(Rect(baseposx, baseposy, baseposx+sel_bg_rect.width(), baseposy+sel_bg_rect.height()),0);
729 p->SetSurface(sel_bg2, 0, 0);
730 }
731 if (sel_bg1) {
732 p = sel_widget->create_node(Rect(baseposx, baseposy, baseposx+sel_bg_rect.width(), baseposy+sel_bg_rect.height()),0);
733 p->SetSurface(sel_bg1, 0, 0);
734 }
735 /* ボタン作成 */
736 const char* str = cmd.Str(cmd.args[i*2]);
737 int value = cmd.args[i*2+1].value;
738 while(selects.size() <= value) selects.push_back(0); // vector の大きさを広げる
739
740 kconv( (const unsigned char*)str, (unsigned char*)key);
741 selects[value] = new WidTextButton(event, sel_widget, key, mojisize, WidTextButton::CENTER,
742 Rect(baseposx, baseposy, baseposx+sel_bg_rect.width(), baseposy+sel_bg_rect.height()), 1, fore, seled, Color(0,0,0,0));
743 selects[value]->press_func = &PressFuncButton;
744 selects[value]->press_pointer = (void*)this;
745
746 baseposx += repposx;
747 baseposy += repposy;
748 }
749 sel_widget->show_all();
750 status = WAIT_SELECT_OUTBOX;
751 } else { // CLANNAD: テキストウィンドウ内に選択肢表示
752 int mojisize;
753 config.GetParam("#SELBTN.000.MOJISIZE", 1, &mojisize);
754 Color fore(0xff,0xff,0xff);
755 Color seled(0xff,0xff,0xff);
756
757 show();
758 if (text == 0) goto External_select; // テキスト・ウィンドウを表示できなければ外部選択肢にする
759 text->wid->Clear();
760 if (kcursor) kcursor->hide();
761 /* ウィジット作成 : テキスト表示範囲と同じ*/
762 int posx = text->wid->pictext->PosX();
763 int posy = text->wid->pictext->PosY();
764 int sel_w = text->wid->pictext->Width();
765 int sel_h = text->wid->pictext->Height();
766 sel_widget = text->wid->PicNode()->create_node(Rect(posx, posy, posx+sel_w, posy+sel_h), 0);
767
768 int sel_y = 0;
769 for (i=0; i<sel_size; i++) {
770 PicBase* p;
771 /* ボタン作成 */
772 const char* str = cmd.Str(cmd.args[i*2]);
773 int value = cmd.args[i*2+1].value;
774 while(selects.size() <= value) selects.push_back(0); // vector の大きさを広げる
775
776 kconv( (const unsigned char*)str, (unsigned char*)key);
777 selects[value] = new WidTextButton(event, sel_widget, key, mojisize, WidTextButton::Attribute(WidTextButton::REVERSE | WidTextButton::NOPADDING),
778 Rect(0, sel_y, sel_w, sel_y), 1, fore, seled, Color(0,0,0,0));
779 selects[value]->press_func = &PressFuncButton;
780 selects[value]->press_pointer = (void*)this;
781
782 sel_y += selects[value]->Pic()->Height() + 1;
783 }
784 sel_widget->show_all();
785 status = WAIT_SELECT_INBOX;
786 }
787 }
788
789 void TextImpl::AddText(const char* str_o) {
790 char str[10001];
791 if (text == 0) return;
792 /* まず、replace string を変換 */
793 int i;
794 int cnt = 0;
795 /* * = 81 96 A-Z = 0x82 [0x60-0x79] */
796 /* % = 81 93 A-Z = 0x82 [0x60-0x79] */
797 for (i=0; cnt<10000 && str_o[i] != 0; i++) {
798 if (str_o[i] < 0) {
799 if ( (unsigned char)str_o[i] == 0x81 && (unsigned char)str_o[i+1] == 0x96 && (unsigned char)str_o[i+2] == 0x82) {
800 int c = str_o[i+3];
801 if (c >= 0x60 && c <= 0x79 && replace_name[c-0x60].length() != 0) { // 名前変換
802 i += 3;
803 strcpy(str+cnt, replace_name[c-0x60].c_str());
804 cnt += replace_name[c-0x60].length();
805 continue;
806 }
807 } else if ( (unsigned char)str_o[i] == 0x81 && (unsigned char)str_o[i+1] == 0x93 && (unsigned char)str_o[i+2] == 0x82) {
808 int c = str_o[i+3];
809 if (c >= 0x60 && c <= 0x79 && replace_name2[c-0x60].length() != 0) { // 名前変換2
810 i += 3;
811 strcpy(str+cnt, replace_name2[c-0x60].c_str());
812 cnt += replace_name2[c-0x60].length();
813 continue;
814 }
815 }
816 str[cnt++] = str_o[i++];
817 }
818 str[cnt++] = str_o[i];
819 }
820 str[cnt] = 0;
821 str[10000] = 0;
822 char* str_top = str;
823
824 for (char* s = str_top; *s != 0; s++) {
825 // if (*(unsigned char*)s == 0xa1 && *(unsigned char*)(s+1) == 0xda) { /* euc */
826 if (*(unsigned char*)s == 0x81 && *(unsigned char*)(s+1) == 0x79) { /* sjis */
827 // 名前
828 *s = 0;
829 if (s != str_top) text_stream.Add(str_top);
830 s += 2;
831 char* name_top = s;
832 for (; *s != 0; s++) {
833 // if (*(unsigned char*)s == 0xa1 && *(unsigned char*)(s+1) == 0xdb) { /* euc */
834 if (*(unsigned char*)s == 0x81 && *(unsigned char*)(s+1) == 0x7a) { /* sjis */
835 *s = 0;
836 s += 2;
837 text_stream.AddName(name_top);
838 break;
839 }
840 if (*s < 0 && s[1] != 0) s++; // 全角文字なら2字飛ばす
841 }
842 str_top = s;
843 }
844 if (*s == 0x0a) {
845 *s = 0;
846 text_stream.Add(str_top);
847 text_stream.AddReturn();
848 str_top = s;
849 } else if (*s < 0 && s[1] != 0) s++;
850 }
851 text_stream.Add(str_top);
852 return;
853 }
854
855 void TextImpl::Exec(Cmd& cmd) {
856 if (cmd.cmd_type == CMD_TEXT) {
857 if (text == 0) {
858 show();
859 }
860 if (cmd.args.size() != 1) return;
861 if (ruby_text_flag) {
862 ruby_text = cmd.Str(cmd.args[0]);
863 ruby_text_flag = 0;
864 cmd.clear();
865 return;
866 }
867 cur_backlog_item.AddTextPos(cmd);
868 AddText(cmd.Str(cmd.args[0]));
869 char debug[1024];
870 kconv( (unsigned char*)cmd.Str(cmd.args[0]), (unsigned char*)debug);
871 eprintf("text: %s\n",debug);
872 if (text_parsing)
873 cmd.clear();
874 else
875 cmd.cmd_type = CMD_SAVEPOINT;
876 text_parsing = true; /* テキスト待ち直後のテキスト位置=セーブ位置 */
877 return;
878 }
879 if (cmd.cmd_type != CMD_OTHER) return;
880 /* テキストウィンドウを消去するコマンド類をチェックする */
881 if (cmd.cmd1 == 1 && cmd.cmd2 == 0x21) {
882 if (cmd.cmd3 == 0x49 || cmd.cmd3 == 0x4b || cmd.cmd3 == 0x4c) {
883 if (text) text->ResetFace();
884 cur_backlog_item.face = "";
885 hide();
886 }
887 }
888 if (cmd.cmd1 == 1 && cmd.cmd2 == 0x17 && cmd.cmd3 == 0 && cmd.cmd4 == 1) {
889 // PlayKoe ; 声出力コマンドをチェックする */
890 cur_backlog_item.koe = cmd.args[0].value;
891 }
892 if (cmd.cmd1 == 0 && cmd.cmd2 == 3 && cmd.cmd3 == 0x97) { // いいのかなー
893
894 if (text) {
895 text->ResetFace();
896 text->wid->Clear();
897 }
898 cur_backlog_item.face = "";
899 text_stream.Clear();
900 hide();
901 }
902 if (cmd.cmd1 == 0 && cmd.cmd2 == 3) {
903 if (cmd.cmd3 == 0x11) { // テキスト表示、クリック待ち
904 if (text) {
905 eprintf("start\n");
906 text->StartText(text_stream);
907 if (skip_mode & SKIP_TEXT) text->wid->Flush();
908 else if (kcursor) kcursor->show();
909 status = WAIT_TEXT;
910 text_parsing = false;
911 }
912 backlog_item = cur_backlog_item;
913 if (cur_backlog_item.scn == 0 && cur_backlog_item.pos == -1) backlog_item.text = text_stream;
914 cur_backlog_item.Clear();
915
916 cmd.clear();
917 cmd.cmd_type = CMD_WAITFRAMEUPDATE; // 画像描画に戻る(skip時にテキストが描画されやすくするため)
918 // これだと1フレーム1テキストしか表示されなくなるので注意
919 } else if (cmd.cmd3 == 3 || cmd.cmd3 == 0xc9) { // リターン挿入
920 text_stream.AddReturn();
921 cur_backlog_item.DeleteTextPos();
922 cmd.clear();
923 } else if (cmd.cmd3 == 0x3e8 || cmd.cmd3 == 0x3e9) { // 顔グラフィック変更
924 if (text == 0) {
925 show();
926 }
927 if (cmd.cmd3 == 0x3e8) {
928 string s = cmd.Str(cmd.args[0]);
929 s += ".g00";
930 if (text) text->ShowFace(s.c_str());
931 cur_backlog_item.face = s;
932 cmd.cmd_type = CMD_SAVECMD_ONCE;
933 } else if (cmd.cmd3 == 0x3e9) { // 顔グラフィック消去
934 if (text) text->ResetFace();
935 cur_backlog_item.face = "";
936 cmd.cmd_type = CMD_SAVECMD_ONCE;
937 }
938 } else if (cmd.cmd3 == 0x78) { // ルビ関連
939 if (text == 0) {
940 show();
941 }
942 if (cmd.cmd4 == 1) {
943 ruby_text_flag = true;
944 eprintf("SetRubyTextImpl.");
945 cmd.clear();
946 } else if (cmd.cmd4 == 0) {
947 if (ruby_text.length() == 0) { // ルビを振るテキストがない
948 eprintf("Cannot find ruby text.\n");
949 return;
950 }
951 if (cmd.args.size() != 1) return;
952 char debug1[1024], debug2[1024];
953 kconv( (unsigned char*)ruby_text.c_str(), (unsigned char*)debug1);
954 kconv( (unsigned char*)cmd.Str(cmd.args[0]), (unsigned char*)debug2);
955 eprintf("SetRuby. %s, %s",debug1, debug2);
956 text_stream.AddRuby(ruby_text.c_str(), cmd.Str(cmd.args[0]));
957 cur_backlog_item.DeleteTextPos();
958 cmd.clear();
959 }
960 } else if (cmd.cmd3 == 0x66) { // テキストウィンドウの形
961 if (cmd.cmd4 == 0) {
962 eprintf("set text window <- %d\n",cmd.args[0].value);
963 if (text) show(cmd.args[0].value);
964 else text_window_number = cmd.args[0].value;
965 } else if (cmd.cmd4 == 1) { // default value
966 eprintf("set text window <- default\n");
967 if (text) show(0);
968 else text_window_number = 0;
969 }
970 cmd.clear();
971 } else if (cmd.cmd3 == 0x67) { // テキストウィンドウ表示?
972 show();
973 // 表示の際はテキストをクリアしない?
974 // if (text) text->wid->Clear();
975 // text_stream.Clear();
976 cmd.clear();
977 } else if (cmd.cmd3 == 0x68) { // テキスト表示?
978 // 全テキスト表示
979 if (text) {
980 text->StartText(text_stream);
981 text->wid->Flush();
982 }
983 cmd.clear();
984 } else if (cmd.cmd3 == 0x98) { // テキストウィンドウクリア?
985 show();
986 if (text) text->wid->Clear();
987 text_stream.Clear();
988 cmd.clear();
989 }
990 } else if (cmd.cmd1 == 0 && cmd.cmd2 == 2 && (cmd.cmd3 == 1 || cmd.cmd3 == 3) && cmd.cmd4 == 0) {
991 // 選択肢
992 CreateSelect(cmd);
993 cmd.cmd_type = CMD_ROLLBACKPOINT; /* 選択肢はセーブ位置 / シナリオ巻き戻し位置 */
994 // cmd.clear();
995 } else if (cmd.cmd1 == 0 && cmd.cmd2 == 4) {
996 if (cmd.cmd3 == 0x44c) { // テキストスキップ開始
997 status_mask = Status(SKIPMASK | status_mask);
998 cmd.clear();
999 } else if (cmd.cmd3 == 0x3e8) { // ウィンドウ消去
1000 status_mask = Status(CLEARSCR_MASK | status_mask);
1001 cmd.clear();
1002 }
1003 } else if (cmd.cmd1 == 1 && cmd.cmd2 == 0x04) {
1004 /* ウェイト関連命令 */
1005 if (cmd.cmd3 == 0x64 || cmd.cmd3 == 0x6f || cmd.cmd3 == 0x79) {
1006 eprintf("wait %dmsec\n",cmd.args[0].value);
1007 if (cmd.cmd3 == 0x64 && text) {
1008 /* 0x64 だと文字描画中の待ちに使うことがある */
1009 text->StartText(text_stream);
1010 text->wid->Flush();
1011 }
1012 if (cmd.cmd3 == 0x6f || cmd.cmd3 == 0x79) wait_time = base_time + cmd.args[0].value;
1013 else wait_time = old_time + cmd.args[0].value;
1014 status = WAIT;
1015 cmd.cmd_type = CMD_WAITFRAMEUPDATE; // 画像描画に戻る(skip時にテキストが描画されやすくするため)
1016 } else if (cmd.cmd3 == 0x65 || cmd.cmd3 == 0x70) {
1017 eprintf("wait %dmsec(click stop)\n",cmd.args[0].value);
1018 if (cmd.cmd3 == 0x70) wait_time = base_time + cmd.args[0].value;
1019 else wait_time = old_time + cmd.args[0].value;
1020 status = WAIT_CLICK;
1021 cmd.cmd_type = CMD_WAITFRAMEUPDATE; // 画像描画に戻る(skip時にテキストが描画されやすくするため)
1022 } else if (cmd.cmd3 == 0x83) {
1023 /* マウスがクリックされるまで待つ */
1024 eprintf("wait and get mouse pos at click\n");
1025 wait_time = old_time + 1000 * 1000;
1026 status = WAIT_CLICK_MOUSEPOS;
1027 wait_savedvar[0] = cmd.args[0];
1028 wait_savedvar[1] = cmd.args[1];
1029 cmd.clear();
1030 } else if (cmd.cmd3 == 0x1fe) {
1031 eprintf("get timer value[%d]\n",cmd.args[0].value);
1032 if (timer_var.find(cmd.args[0].value) == timer_var.end()) {
1033 cmd.SetSysvar(0);
1034 } else {
1035 TimerAtom& atom = timer_var[cmd.args[0].value];
1036 if (atom.total_time <= 0) atom.total_time = 1;
1037 int cur_tm = old_time - atom.start_time;
1038 if (cur_tm < 0) cur_tm = atom.total_time; // エラーなら最終時間に合わせる
1039 if (cur_tm > atom.total_time) cur_tm = atom.total_time;
1040 // use 'long long'(64bit) or 'double'(80bit) type, since total_time, to and from is 32 bit.
1041 int v = atom.from + (long long)(atom.to-atom.from)*cur_tm/int(atom.total_time);
1042 cmd.SetSysvar(v);
1043 }
1044 /* From rldev-1.40, reallive.kfn
1045 0x72 fun Timer (store) <1:Sys:00114, 1> ('counter') ()
1046 0x73 fun CmpTimer (store) <1:Sys:00115, 1> ('time') ('time', 'counter')
1047 0x74 fun SetTimer <1:Sys:00116, 1> ('time') ('time', 'counter')
1048
1049 0x78 fun ResetExTimer <1:Sys:00120, 1> ('counter') ()
1050 0x79 fun timeEx <1:Sys:00121, 1> ('time') ('time', 'counter')
1051 0x7a fun timeExC (store) <1:Sys:00122, 1> ('time') ('time', 'counter')
1052 0x7b fun timeExC2 (store) <1:Sys:00123, 1> ('time') ('time', 'counter') // UNDOCUMENTED
1053 0x7c fun ExTimer (store) <1:Sys:00124, 1> ('counter') ()
1054 0x7d fun CmpExTimer (store) <1:Sys:00125, 1> ('time') ('time', 'counter')
1055 0x7e fun SetExTimer <1:Sys:00126, 1> ('time') ('time', 'counter')
1056 */
1057
1058 } else if (cmd.cmd3 == 0x6e || cmd.cmd3 == 0x78) { // set basetime
1059 if (cmd.cmd4 == 1) {
1060 eprintf("set basetime\n");
1061 base_time = old_time;
1062 cmd.clear();
1063 } else if (cmd.cmd4 == 0) { // n-th base time
1064 int index = cmd.args[0].value;
1065 eprintf("set basetime (%d)\n",index);
1066 TimerAtom& atom = timer_var[index];
1067 atom.from = 0;
1068 atom.to = 0;
1069 atom.total_time = 0;
1070 atom.start_time = old_time;
1071 cmd.clear();
1072 }
1073 } else if (cmd.cmd3 == 0x72 || cmd.cmd3 == 0x7c) { // get time
1074 if (cmd.cmd4 == 1) { // get time
1075 eprintf("get time\n");
1076 cmd.SetSysvar(old_time - base_time);
1077 } else if (cmd.cmd4 == 0) { // n-th get time
1078 int index = cmd.args[0].value;
1079 eprintf("get time %dth\n",index);
1080 if (timer_var.find(index) == timer_var.end()) cmd.SetSysvar(0);
1081 else cmd.SetSysvar(old_time - timer_var[index].start_time);
1082 }
1083 } else if (cmd.cmd3 == 0x26c || cmd.cmd3 == 0x270) { // set basetime(multi)
1084 int j = 0;
1085 eprintf("set basetime\n");
1086 int i; for (i=0; i<cmd.argc; i++) {
1087 int cnt = cmd.args[j++].value; // 3なので無視
1088 int num = cmd.args[j++].value;
1089 TimerAtom& atom = timer_var[num];
1090 atom.from = cmd.args[j++].value;
1091 atom.to = cmd.args[j++].value;
1092 atom.total_time = cmd.args[j++].value;
1093 atom.start_time = old_time;
1094 }
1095 cmd.clear();
1096 } else if (cmd.cmd3 == 0x276) { // get time (multi)
1097 int j = 0;
1098 eprintf("get timer value\n");
1099 vector<VarInfo> args = cmd.args;
1100 vector<VarInfo>::iterator it = args.begin();
1101 int argc = cmd.argc;
1102 int active_timers = 0;
1103 int i; for (i=0; i<argc; i++) {
1104 int cnt = (it++)->value;
1105 int num = (it++)->value;
1106
1107 if (timer_var.find(num) == timer_var.end()) {
1108 cmd.SetFlagvar(*it++, 0);
1109 } else {
1110 TimerAtom& atom = timer_var[num];
1111 if (atom.total_time <= 0) atom.total_time = 1;
1112 int cur_tm = old_time - atom.start_time;
1113 if (cur_tm < 0) cur_tm = atom.total_time; // エラーなら最終時間に合わせる
1114 if (cur_tm > atom.total_time) cur_tm = atom.total_time;
1115 // use 'long long'(64bit) or 'double'(80bit) type, since total_time, to and from is 32 bit.
1116 int v = atom.from + (long long)(atom.to-atom.from)*cur_tm/int(atom.total_time);
1117 cmd.SetFlagvar(*it++, v);
1118 if (atom.total_time != -1 && cur_tm < atom.total_time) active_timers++;
1119 }
1120 }
1121 if (active_timers) active_timers = 1;
1122 cmd.SetSysvar(active_timers);
1123 } else if (cmd.cmd3 == 0x1f4) {
1124 TimerAtom& atom = timer_var[cmd.args[0].value];
1125 atom.from = cmd.args[1].value;
1126 atom.to = cmd.args[2].value;
1127 atom.total_time = cmd.args[3].value;
1128 atom.start_time = old_time;
1129 cmd.clear();
1130 } else if (cmd.cmd3 == 0x3e8) {
1131 /* rand() */
1132 int min = 0, max;
1133 if (cmd.args.size() == 2) {
1134 min = cmd.args[0].value;
1135 max = cmd.args[1].value;
1136 } else {
1137 max = cmd.args[1].value;
1138 }
1139 if (min > max) {
1140 int tmp = max;
1141 max = min;
1142 min = tmp;
1143 }
1144 int r = random();
1145 if (min == max) r = min;
1146 else r = (r % (max-min)) + min;
1147 cmd.SetSysvar(r);
1148 } else if (cmd.cmd3 == 0x3ea) {
1149 int val = cmd.args[0].value;
1150 if (val < 0) val = -val;
1151 cmd.SetSysvar(val);
1152 } else if (cmd.cmd3 == 0x3ec) {
1153 /* min だよなあ・・・*/
1154 int min = cmd.args[0].value;
1155 int max = cmd.args[1].value;
1156 if (max < min) min = max;
1157 cmd.SetSysvar(min);
1158 } else if (cmd.cmd3 == 0x3ef) {
1159 /* min */
1160 int min = cmd.args[0].value;
1161 int max = cmd.args[1].value;
1162 if (max < min) min = max;
1163 cmd.SetSysvar(min);
1164 } else if (cmd.cmd3 == 0x320) {
1165 /* range conversion : 比率に丸める */
1166 // アルゴリズムは間違えてるような気がする
1167 //
1168 if (cmd.args.size() >= 7) {
1169 int val = cmd.args[0].value;
1170 int offset = cmd.args[1].value;
1171 int r_min = cmd.args[2].value;
1172 int v_min = cmd.args[3].value;
1173 int v_max = cmd.args[4].value;
1174 int r_max = cmd.args[5].value;
1175 int mode = cmd.args[6].value;
1176 // rldev : mode == 1,3 : 'acceralating curve', 2,3: 'decelerating curve'
1177 // 複数の引数リスト(r_minからmodeまでのリスト)もつこともあり、その場合は
1178 // "cancel out in some way" らしい
1179 if (mode == 1 || mode == 3) val += offset;
1180 else if (mode == 2 || mode == 4) val -= offset;
1181 if (cmd.args.size() != 7)
1182 fprintf(stderr,"\n%d/%d: cmd 01-04:0320 : XXXX NOT SUPPORTED LIST : DOUBLE RANGE CONVERSION! XXXXXXXXXXX\n",cmd.scn,cmd.pos);
1183 if (val < v_min) val = v_min;
1184 if (val > v_max) val = v_max;
1185 val = (r_max-r_min)*(val-v_min)/(v_max-v_min) + r_min;
1186 cmd.SetSysvar(val);
1187 }
1188 } else if (cmd.cmd3 == 0x3f1) {
1189 /* range 内に丸める */
1190 int min = cmd.args[0].value;
1191 int val = cmd.args[1].value;
1192 int max = cmd.args[2].value;
1193 if (min > max) {
1194 int tmp = max;
1195 max = min;
1196 min = tmp;
1197 }
1198 if (val < min) val = min;
1199 if (val > max) val = max;
1200 cmd.SetSysvar(val);
1201 } else if (cmd.cmd3 == 0x16c && cmd.cmd4 == 0) {
1202 /* なんかよくわからないけどカーソル形状変更にしとく */
1203 SetCursor(cmd.args[0].value);
1204 cmd.clear();
1205 } else if (cmd.cmd3 == 0x0bc1) { // メニューからのロード
1206 cmd.cmd_type = CMD_LOADREQ;
1207 } else if ( (cmd.cmd3 >= 0x8d4 && cmd.cmd3 <= 0x8d8) || cmd.cmd3 == 0x8db || cmd.cmd3 == 0x93f || cmd.cmd3 == 0xa39) {
1208 // テキストウィンドウの色設定
1209 int r, g, b, a, flag;
1210 if (cmd.cmd3 == 0xa39) { // 元設定を取り出す
1211 config.GetOriginalParam("#WINDOW_ATTR", 5, &r, &g, &b, &a, &flag);
1212 } else {
1213 config.GetParam("#WINDOW_ATTR", 5, &r, &g, &b, &a, &flag);
1214 }
1215 if (cmd.cmd3 == 0xa39 || cmd.cmd3 == 0x93f) { // 設定を変数に取り出す
1216 if (cmd.args.size() != 5) {
1217 fprintf(stderr,"cmd 01-04:%4d : invalid arg size\n", cmd.cmd3);
1218 } else {
1219 vector<VarInfo> args(cmd.args);
1220 cmd.SetFlagvar(args[0], r);
1221 cmd.SetFlagvar(args[1], g);
1222 cmd.SetFlagvar(args[2], b);
1223 cmd.SetFlagvar(args[3], a);
1224 cmd.SetFlagvar(args[4], flag);
1225 }
1226 } else {
1227 switch(cmd.cmd3) {
1228 case 0x8d4: r = cmd.args[0].value; break;
1229 case 0x8d5: g = cmd.args[0].value; break;
1230 case 0x8d6: b = cmd.args[0].value; break;
1231 case 0x8d7: a = cmd.args[0].value; break;
1232 case 0x8d8: flag = cmd.args[0].value; break;
1233 case 0x8db:
1234 r = cmd.args[0].value;
1235 g = cmd.args[1].value;
1236 b = cmd.args[2].value;
1237 a = cmd.args[3].value;
1238 flag = cmd.args[4].value;
1239 break;
1240 }
1241 config.SetParam("#WINDOW_ATTR", 5, r, g, b, a, flag);
1242 SetWindowColor(r, g, b, a, flag);
1243 cmd.clear();
1244 }
1245 } else if (cmd.cmd3 == 0xa28 || cmd.cmd3 == 0xa29 || cmd.cmd3 == 0xa2c || cmd.cmd3 == 0xa2d || cmd.cmd3 == 0xa2e) {
1246 int v = 0;
1247 switch(cmd.cmd3) {
1248 case 0xa28: case 0xa2d: config.GetOriginalParam("#INIT_MESSAGE_SPEED", 1, &v); break;
1249 case 0xa29: config.GetOriginalParam("#INIT_MESSAGE_SPEED_MOD", 1, &v); break;
1250 case 0xa2c: config.GetOriginalParam("#MESSAGE_KEY_WAIT_USE", 1, &v); break;
1251 case 0xa2e: config.GetOriginalParam("#MESSAGE_KEY_WAIT_TIME", 1, &v); break;
1252 }
1253 cmd.SetSysvar(v);
1254 } else if (cmd.cmd3 == 0x913 || cmd.cmd3 == 0x914 || cmd.cmd3 == 0x92f || cmd.cmd3 == 0x8af || cmd.cmd3 == 0x8b0 || cmd.cmd3 == 0x8cb) {
1255 // テキスト表示速度関連
1256 int m, speed;
1257 config.GetParam("#INIT_MESSAGE_SPEED", 1, &speed);
1258 config.GetParam("#INIT_MESSAGE_SPEED_MOD", 1, &m);
1259 if (cmd.cmd3 == 0x913 || cmd.cmd3 == 0x92f) fprintf(stderr,"TEXT speed %d\n",speed);
1260 else if (cmd.cmd3 == 0x914) fprintf(stderr,"TEXT mode %d\n",m);
1261 else if (cmd.cmd3 == 0x8af || cmd.cmd3 == 0x8cb) fprintf(stderr,"TEXT %d, %d <- speed %d\n",m,speed,cmd.args[0].value);
1262 else fprintf(stderr,"TEXT %d, %d <- mode %d\n",m,speed,cmd.args[0].value);
1263 if (cmd.cmd3 == 0x913 || cmd.cmd3 == 0x92f) cmd.SetSysvar(speed);
1264 else if (cmd.cmd3 == 0x914) cmd.SetSysvar(m);
1265 else {
1266 if (cmd.cmd3 == 0x8af || cmd.cmd3 == 0x8cb) speed = cmd.args[0].value;
1267 else m = cmd.args[0].value;
1268 if (speed < 10) speed = 10;
1269 else if (speed > 1000) speed = 1000;
1270 config.SetParam("#INIT_MESSAGE_SPEED", 1, speed);
1271 config.SetParam("#INIT_MESSAGE_SPEED_MOD", 1, m);
1272 if (m) speed = -1;
1273 SetTextSpeed(speed);
1274 cmd.clear();
1275 }
1276 } else if (cmd.cmd3 == 0x92e || cmd.cmd3 == 0x930 || cmd.cmd3 == 0x8ca || cmd.cmd3 == 0x8cc) {
1277 // テキストオートモード関連
1278 int m, wait;
1279 config.GetParam("#MESSAGE_KEY_WAIT_USE", 1, &m);
1280 config.GetParam("#MESSAGE_KEY_WAIT_TIME", 1, &wait);
1281 if (cmd.cmd3 == 0x92e) fprintf(stderr,"AUTO mode %d\n",m);
1282 else if (cmd.cmd3 == 0x930) fprintf(stderr,"AUTO wait %d\n",wait);
1283 else if (cmd.cmd3 == 0x8ca) fprintf(stderr,"AUTO %d,%d <- mode %d\n",m,wait,cmd.args[0].value);
1284 else fprintf(stderr,"AUTO %d,%d <- wait %d\n",m,wait,cmd.args[0].value);
1285
1286 if (cmd.cmd3 == 0x92e) cmd.SetSysvar(m);
1287 else if (cmd.cmd3 == 0x930) cmd.SetSysvar(wait);
1288 else {
1289 if (cmd.cmd3 == 0x8ca) m = cmd.args[0].value;
1290 else wait = cmd.args[1].value;
1291 if (wait < 0) wait = 0;
1292 else if (wait > 60000) wait = 60000;
1293 config.SetParam("#MESSAGE_KEY_WAIT_USE", 1, m);
1294 config.SetParam("#MESSAGE_KEY_WAIT_TIME", 1, wait);
1295 if (m) SetTextWait(wait);
1296 else SetTextWait(-1);
1297 cmd.clear();
1298 }
1299 } else if (cmd.cmd3 == 0x51f && cmd.cmd4 == 0) { // replace_name2 の設定
1300 int n = cmd.args[0].value;
1301 if (n>=0 && n<26) {
1302 replace_name2[n] = cmd.Str(cmd.args[1]);
1303 }
1304 cmd.clear();
1305 } else if (cmd.cmd3 == 0x51e && cmd.cmd4 == 0) { // replace_name2 を得る
1306 int n = cmd.args[0].value;
1307 if (n >= 0 && n < 26) {
1308 cmd.SetStrvar(cmd.args[1], replace_name2[n]);
1309 } else {
1310 cmd.SetStrvar(cmd.args[1], "");
1311 }
1312 } else if (cmd.cmd3 == 0x514 && cmd.cmd4 == 0) { // replace_name を得る
1313 int n = cmd.args[0].value;
1314 if (n >= 0 && n < 26) {
1315 cmd.SetStrvar(cmd.args[1], replace_name[n]);
1316 } else {
1317 cmd.SetStrvar(cmd.args[1], "");
1318 }
1319 }
1320 }
1321
1322 return;
1323 }
1324 extern int print_blit;
1325 bool TextImpl::Wait(unsigned int current_time, Cmd& cmd) {
1326 if (current_time != 0xffffffffUL) old_time = current_time;
1327 /*
1328 if (event.presscount(MOUSE_UP)) {
1329 if (text) text->Pic()->ReBlit();
1330 }
1331 if (event.presscount(MOUSE_DOWN)) {
1332 print_blit^=1;
1333 }
1334 */
1335
1336 if (status == NORMAL && status_mask == NORMAL) return false;
1337
1338 if (status_mask & WAIT_EXTRN_MASK) return true;
1339 if (status_mask & (BACKLOG_MASK|BACKLOG_MASK_FWD) ) {
1340 if (status_mask & BACKLOG_WAIT_MASK) ;
1341 else {
1342 if ( (status == WAIT_TEXT && text != 0) || status == WAIT_SELECT_INBOX || status == WAIT_SELECT_OUTBOX) {
1343 if(text && text->wid->status != WidText::PREPARE && text->wid->status != WidText::WAIT && text->wid->status != WidText::WAIT2) {
1344 text->wid->Flush(); // 表示を最後の状態にする
1345 }
1346 if (status == WAIT_TEXT && text != 0 && kcursor) kcursor->show();
1347 }
1348 }
1349 if (status_mask & BACKLOG_MASK) {
1350 cmd.cmd_type = CMD_BACKLOGREQ;
1351 } else {
1352 cmd.cmd_type = CMD_BACKLOGREQ_FWD;
1353 }
1354 status_mask = Status(status_mask & ~(BACKLOG_MASK|BACKLOG_MASK_FWD));
1355 return false;
1356 }
1357 if ( (status_mask & BACKLOG_WAIT_MASK) && (status_mask & BACKLOG_MASK_KOE)) {
1358 if (drawn_backlog_item.koe != -1) {
1359 cmd.cmd_type = CMD_OTHER;
1360 cmd.cmd1 = 1;
1361 cmd.cmd2 = 0x17;
1362 cmd.cmd3 = 0;
1363 cmd.cmd4 = 1;
1364 cmd.args.clear();
1365 cmd.args.push_back(VarInfo(drawn_backlog_item.koe));
1366 cmd.args.push_back(VarInfo(0));
1367 }
1368 status_mask = Status(status_mask & ~BACKLOG_MASK_KOE);
1369 return false;
1370 }
1371 if (skip_mode & SKIP_IN_MENU) return false;
1372 if (status_mask & SAVEMASK) {
1373 cmd.cmd_type = CMD_SAVEREQ;
1374 status_mask = Status(status_mask & ~SAVEMASK);
1375 return false;
1376 }
1377 if (status_mask & LOADMASK) {
1378 cmd.cmd_type = CMD_LOADREQ;
1379 status_mask = Status(status_mask & ~LOADMASK);
1380 return false;
1381 }
1382 if (status_mask & SKIPEND_MASK) {
1383 if ( (skip_mode & SKIP_TEXT) && (skip_mode & SKIPEND_TEXT)) {
1384 if (skip_mode & SKIPEND_KEY) { // shift skip 中
1385 SkipMode new_mode = SkipMode(skip_mode & (~SKIPEND_TEXT));
1386 if (new_mode & (SKIP_GRP_NOEFFEC || SKIP_GRP_NODRAW))
1387 new_mode = SkipMode(new_mode & (~SKIP_GRP_FAST));
1388 cmd.SetSysvar(TYPE_SYS_SKIPMODE, new_mode);
1389 } else {
1390 cmd.SetSysvar(TYPE_SYS_SKIPMODE, SKIP_NO);
1391 }
1392 }
1393 status_mask = Status(status_mask & ~SKIPEND_MASK);
1394 }
1395 if (status_mask & SKIPMASK) {
1396 if (skip_mode != SKIP_NO) {
1397 cmd.SetSysvar(TYPE_SYS_SKIPMODE, skip_mode | SKIPEND_TEXT);
1398 } else {
1399 cmd.SetSysvar(TYPE_SYS_SKIPMODE, SKIP_TEXT | SKIP_GRP_FAST | SKIPEND_TEXT);
1400 }
1401 status_mask = Status(status_mask & ~SKIPMASK);
1402 return false;
1403 }
1404 if (event.presscount(MOUSE_RIGHT)) {
1405 if ( (status == WAIT_TEXT && text != 0) || status == WAIT_SELECT_INBOX || status == WAIT_SELECT_OUTBOX) {
1406 if(text && text->wid->status != WidText::PREPARE && text->wid->status != WidText::WAIT && text->wid->status != WidText::WAIT2) {
1407 text->wid->Flush(); // 表示を最後の状態にする
1408 }
1409 cmd.cmd_type = CMD_MENUREQ;
1410 if (!(status_mask & STATSAVE_MASK)) {
1411 status_saved = status;
1412 status_mask = Status(status_mask | STATSAVE_MASK);
1413 }
1414 return false;
1415 } else if (status == WAIT_CLICK_MOUSEPOS) {
1416 status = WAIT_CLICK_MOUSEPOSEND_R;
1417 }
1418 }
1419 if (event.presscount(MOUSE_UP)) {
1420 if ( (status == WAIT_TEXT && text != 0) || status == WAIT_SELECT_INBOX || status == WAIT_SELECT_OUTBOX) {
1421 if(text && text->wid->status != WidText::PREPARE && text->wid->status != WidText::WAIT && text->wid->status != WidText::WAIT2) {
1422 text->wid->Flush(); // 表示を最後の状態にする
1423 }
1424 cmd.cmd_type = CMD_BACKLOGREQ;
1425 if (!(status_mask & STATSAVE_MASK)) {
1426 status_saved = status;
1427 status_mask = Status(status_mask | STATSAVE_MASK);
1428 }
1429 return false;
1430 }
1431 }
1432 if (status_mask & CLEARSCR_MASK) {
1433 if ( (status == WAIT_TEXT && text != 0 ) || status == WAIT_SELECT_INBOX || status == WAIT_SELECT_OUTBOX) {
1434 if (skip_mode) skip_mode = SKIP_NO;
1435 if (text && text->wid->status != WidText::PREPARE && text->wid->status != WidText::WAIT && text->wid->status != WidText::WAIT2) {
1436 text->wid->Flush(); // 表示を最後の状態にする
1437 return true;
1438 }
1439 status_mask = Status(status_mask & (~CLEARSCR_MASK) | CLEARSCR_WAIT_MASK);
1440 if (text) text->hide();
1441 if (kcursor) kcursor->hide();
1442 if (sel_widget) sel_widget->hide();
1443 if (backlog_widget) backlog_widget->hide();
1444 return true;
1445 }
1446 status_mask = Status(status_mask & (~CLEARSCR_MASK));
1447 return false;
1448 }
1449 if (status_mask & CLEARSCR_WAIT_MASK) {
1450 return true;
1451 }
1452 if (status == WAIT_TEXT) {
1453 if (text == 0) { status = NORMAL; return false;}
1454 if (skip_mode & SKIP_TEXT) {
1455 } else if (text->wid->status != WidText::PREPARE) {
1456 return true;
1457 }
1458 if (kcursor) kcursor->hide();
1459 text_stream.Clear();
1460 status = NORMAL;
1461 cmd.cmd_type = CMD_TEXTEND;
1462 return false;
1463 }
1464 if (status == WAIT) {
1465 if (skip_mode & SKIP_TEXT) ;
1466 else if (wait_time > current_time) return true;
1467 status = NORMAL;
1468 } else if (status == WAIT_CLICK) {
1469 if (skip_mode & SKIP_TEXT) ;
1470 else if (wait_time > current_time) return true;
1471 status = NORMAL;
1472 cmd.SetSysvar(0);
1473 } else if (status == WAIT_ABORT) {
1474 cmd.SetSysvar(1);
1475 status = NORMAL;
1476 } else if (status == WAIT_CLICK_MOUSEPOS || status == WAIT_CLICK_MOUSEPOSEND_L || status == WAIT_CLICK_MOUSEPOSEND_R) {
1477 if (status == WAIT_CLICK_MOUSEPOS && (skip_mode & SKIP_TEXT) == 0) return true; // keep wait
1478 else {
1479 int x, y;
1480 event.MousePos(x,y);
1481 if (status == WAIT_CLICK_MOUSEPOS) x = y = 0; // skip mode
1482 cmd.clear();
1483 cmd.SetFlagvar(wait_savedvar[0], x);
1484 cmd.SetFlagvar(wait_savedvar[1], y);
1485 if (status == WAIT_CLICK_MOUSEPOSEND_R) cmd.SetSysvar(-1);
1486 else cmd.SetSysvar(0);
1487 status = NORMAL;
1488 }
1489 } else if (status == WAIT_SELECT_INBOX || status == WAIT_SELECT_OUTBOX) {
1490 return true;
1491 } else if ( int(status) >= WAIT_SELECT_VALUE) {
1492 int sel_val = int(status) - WAIT_SELECT_VALUE;
1493 cmd.SetSysvar(sel_val);
1494 selects.clear();
1495 delete sel_widget;
1496 sel_widget = 0;
1497 status = NORMAL;
1498 // CreateSelect() で作成された cur_backlog_item を backlog_item へ反映させる
1499 cur_backlog_item.text.InsertColor(sel_backlog_pos[sel_val], sel_backlog_pos[sel_val+1], 0xff, 0, 0);
1500 backlog_item = cur_backlog_item;
1501 cur_backlog_item.Clear();
1502 }
1503 return false;
1504 }
1505
1506 void clearbtn_press(void* pointer, WidButton* button) {
1507 if (pointer == 0) return;
1508 TextImpl* t = (TextImpl*)pointer;
1509 t->status_mask = TextImpl::Status(t->status_mask | TextImpl::CLEARSCR_MASK);
1510 return;
1511 }
1512 void TextImpl::PressFuncSkip(void* pointer, WidButton* from) {
1513 if (pointer == 0) return;
1514 TextImpl* t = (TextImpl*)pointer;
1515 t->status_mask = TextImpl::Status(t->status_mask | TextImpl::SKIPMASK);
1516 return;
1517 }
1518 void TextImpl::PressFuncLoad(void* pointer, WidButton* from) {
1519 if (pointer == 0) return;
1520 TextImpl* t = (TextImpl*)pointer;
1521 t->status_mask = TextImpl::Status(t->status_mask | TextImpl::LOADMASK);
1522 return;
1523 }
1524 void TextImpl::PressFuncSave(void* pointer, WidButton* from) {
1525 if (pointer == 0) return;
1526 TextImpl* t = (TextImpl*)pointer;
1527 t->status_mask = TextImpl::Status(t->status_mask | TextImpl::SAVEMASK);
1528 return;
1529 }
1530 void TextImpl::PressFuncBacklog(void* pointer, WidButton* from) {
1531 if (pointer == 0) return;
1532 TextImpl* t = (TextImpl*)pointer;
1533 t->status_mask = TextImpl::Status(t->status_mask | TextImpl::BACKLOG_MASK);
1534 return;
1535 }
1536 void TextImpl::PressFuncBacklogFwd(void* pointer, WidButton* from) {
1537 if (pointer == 0) return;
1538 TextImpl* t = (TextImpl*)pointer;
1539 t->status_mask = TextImpl::Status(t->status_mask | TextImpl::BACKLOG_MASK_FWD);
1540 return;
1541 }
1542 void movebtn_drag(int from_x, int from_y, int x, int y, void* pointer, WidButton* button) {
1543 if (pointer == 0) return;
1544 fprintf(stderr,"drag.\n");
1545 }
1546 #define BTNCNT 10
1547 static char* btnname[BTNCNT] = {
1548 "MOVE",
1549 "CLEAR",
1550 "READJUMP",
1551 "AUTOMODE",
1552 "MSGBK",
1553 "MSGBKLEFT",
1554 "MSGBKRIGHT",
1555 "EXBTN_000",
1556 "EXBTN_001",
1557 "EXBTN_002"
1558 };
1559 static int btnpos[BTNCNT] = { // g00 ファイル内のボタン情報の位置
1560 // 0, 1, 13, 12, 2, 3, 4, 5, 6, 7 // princess bride?
1561 0, 1, 13, 14, 2, 3, 4, 5, 6, 7 // tomoyo after?
1562 };
1563 static WidButton::PressFunc btnpress[BTNCNT] = {
1564 0, clearbtn_press, &TextImpl::PressFuncSkip,0,&TextImpl::PressFuncBacklogFwd,&TextImpl::PressFuncBacklog,&TextImpl::PressFuncBacklogFwd,&TextImpl::PressFuncSave,&TextImpl::PressFuncLoad,0
1565 };
1566 static WidButton::DragFunc btndrag[BTNCNT] = {
1567 movebtn_drag, 0,0,0,0, 0,0,0,0, 0
1568 };
1569
1570 void TextImpl::SetTextSpeed(int speed) {
1571 // 100 : 10char / sec
1572 // 10 : 100char / sec
1573 // text widget:
1574 if (speed <= 0) speed = -1;
1575 else if (speed > 1000) speed = 1;
1576 else speed = 1000 / speed;
1577 int i;
1578 for (i=0; i<32; i++)
1579 if (widgets[i]) widgets[i]->wid->SetSpeed(speed);
1580 }
1581 void TextImpl::SetTextWait(int wait) {
1582 int i;
1583 for (i=0; i<32; i++)
1584 if (widgets[i]) widgets[i]->wid->SetWait(wait);
1585 }
1586
1587 void TextImpl::SetWindowColor(int r, int g, int b, int a, bool is_transparent) {
1588 char key[1024];
1589 int w;
1590
1591 for (w=0; w<32; w++) {
1592 if (widgets[w] == 0) continue;
1593 sprintf(key, "#WAKU.%03d.000.BACK", w);
1594 const char* back = config.GetParaStr(key);
1595 if (back == 0 || back[0] == 0) continue;
1596 sprintf(key, "%s.g00", back);
1597 Surface* back_s = parent.Root().NewSurface(key);
1598 if (back_s == 0) continue;
1599 Rect rect(*back_s);
1600 Surface* new_s = parent.Root().NewSurface(rect.width(), rect.height(), ALPHA_MASK);
1601 DSurfaceMove(back_s, rect, new_s, rect);
1602 DSurfaceFillA(new_s, rect, r, g, b, a);
1603 widgets[w]->wid->Pic()->SetSurface(new_s, 0, 0);
1604 widgets[w]->wid->Pic()->SetSurfaceFreeFlag(1);
1605 if (!is_transparent)
1606 widgets[w]->wid->Pic()->SetSurfaceAttribute(PicBase::BLIT_MULTIPLY);
1607 parent.Root().DeleteSurface(back_s);
1608 }
1609 return;
1610 }
1611
1612 void TextImpl::SetCursor(int cursor_no) {
1613 char key[1024];
1614 sprintf(key, "#CURSOR.%03d.NAME", cursor_no);
1615 string path = config.GetParaStr(key);
1616 if (path.length() == 0) return; // 名前なし
1617 path += ".pdt";
1618 int w,h,cont,speed;
1619 sprintf(key, "#CURSOR.%03d.SIZE", cursor_no);
1620 config.GetParam(key, 2, &w, &h);
1621 sprintf(key, "#CURSOR.%03d.CONT", cursor_no);
1622 config.GetParam(key, 1, &cont);
1623 sprintf(key, "#CURSOR.%03d.SPEED", cursor_no);
1624 config.GetParam(key, 1, &speed);
1625
1626 // speed で1周、cont 回変化
1627 if (kcursor) delete kcursor;
1628
1629 kcursor = new WidTimeCursor(event, speed/cont, &parent, path.c_str(), 0, 0, w, 0, cont, Rect(0,0,w,h));
1630 int i;
1631 for (i=0; i<32; i++) {
1632 if (widgets[i]) widgets[i]->wid->SetCursor(kcursor);
1633 }
1634 }
1635
1636 void kconv(const unsigned char* src, unsigned char* dest) {
1637 /* input : sjis output: euc */
1638 while(*src) {
1639 unsigned int high = *src++;
1640 if (high < 0x80) {
1641 /* ASCII */
1642 *dest++ = high; continue;
1643 } else if (high < 0xa0) {
1644 /* SJIS */
1645 high -= 0x71;
1646 } else if (high < 0xe0) {
1647 /* hankaku KANA */
1648 *dest++ = 0x8e; *dest++ = high;
1649 continue;
1650 } else { /* high >= 0xe0 : SJIS */
1651 high -= 0xb1;
1652 }
1653 /* SJIS convert */
1654 high = (high<<1) + 1;
1655
1656 unsigned int low = *src++;
1657 if (low == 0) break; /* incorrect code */
1658 if (low > 0x7f) low--;
1659 if (low >= 0x9e) {
1660 low -= 0x7d;
1661 high++;
1662 } else {
1663 low -= 0x1f;
1664 }
1665 *dest++ = high | 0x80; *dest++ = low | 0x80;
1666 }
1667 *dest = 0;
1668 }
1669 void kconv_rev(const unsigned char* src, unsigned char* dest) {
1670 /* input : euc output: sjis */
1671 while(*src) {
1672 unsigned int high = *src++;
1673 if (high < 0x80) {
1674 /* ASCII */
1675 *dest++ = high; continue;
1676 } else if (high == 0x8e) { /* hankaku KANA */
1677 high = *src;
1678 if (high >= 0xa0 && high < 0xe0)
1679 *dest++ = *src++;
1680 continue;
1681 } else {
1682 unsigned int low = *src++;
1683 if (low == 0) break; /* incorrect code , EOS */
1684 if (low < 0x80) continue; /* incorrect code */
1685 /* convert */
1686 low &= 0x7f; high &= 0x7f;
1687 low += (high & 1) ? 0x1f : 0x7d;
1688 high = (high-0x21)>>1;
1689 high += (high > 0x1e) ? 0xc1 : 0x81;
1690 *dest++ = high;
1691 if (low > 0x7f) low++;
1692 *dest++ = low;
1693 }
1694 }
1695 *dest = 0;
1696 }
1697 string kconv(const string& s) {
1698 char* out = new char[s.length()*2+100];
1699 kconv((const unsigned char*)s.c_str(), (unsigned char*)out);
1700 string ret = out;
1701 delete[] out;
1702 return ret;
1703 }
1704 string kconv_rev(const string& s) {
1705 char* out = new char[s.length()*2+100];
1706 kconv_rev((const unsigned char*)s.c_str(), (unsigned char*)out);
1707 string ret = out;
1708 delete[] out;
1709 return ret;
1710 }
1711
1712 /**************************************************************::
1713 **
1714 ** Text
1715 */
1716 Text::Text(Event::Container& _event, PicContainer& _parent, AyuSysConfig& config) {
1717 pimpl = new TextImpl(_event, _parent, config, backlog, backlog_item);
1718 }
1719 Text::~Text() {
1720 delete pimpl;
1721 }
1722 void Text::InitWindow(void) {
1723 pimpl->InitWindow();
1724 }
1725 void Text::Exec(Cmd& cmd) {
1726 pimpl->Exec(cmd);
1727 }
1728 bool Text::Wait(unsigned int current_time, Cmd& cmd) {
1729 return pimpl->Wait(current_time, cmd);
1730 }
1731 void Text::SetSkipMode(SkipMode mode) {
1732 pimpl->SetSkipMode(mode);
1733 }
1734 void Text::Save(std::string& str, bool select_save) {
1735 pimpl->Save(str, select_save);
1736 }
1737 void Text::Load(const char* str) {
1738 pimpl->Load(str);
1739 }
1740
1741 void Text::hide(void) {
1742 pimpl->hide();
1743 }
1744 void Text::show(void) {
1745 pimpl->show();
1746 }
1747 void Text::show(int num) {
1748 pimpl->show(num);
1749 }
1750 void Text::DrawBacklog(BacklogItem& item, Cmd& cmd) {
1751 pimpl->DrawBacklog(item, cmd);
1752 }
1753 /**************************************************************::
1754 **
1755 ** BacklogItem
1756 */
1757
1758 BacklogItem::BacklogItem(void) {
1759 scn = -1;
1760 pos = -1;
1761 koe = -1;
1762 face = "";
1763 text.kanji_type = TextStream::sjis;
1764 }
1765 void BacklogItem::Clear(void) {
1766 scn = -1;
1767 pos = -1;
1768 koe = -1;
1769 text.Clear();
1770 }
1771 void BacklogItem::AddTextPos(Cmd& cmd) {
1772 if (scn == -1 && pos == -1) {
1773 scn = cmd.scn;
1774 pos = cmd.pos;
1775 return;
1776 }
1777 DeleteTextPos();
1778 }
1779 void BacklogItem::DeleteTextPos(void) {
1780 scn = 0;
1781 pos = -1;
1782 }
1783 BacklogItem& BacklogItem::operator =(const BacklogItem& p) {
1784 scn = p.scn;
1785 pos = p.pos;
1786 koe = p.koe;
1787 face = p.face;
1788 text = p.text;
1789 }
1790 void BacklogItem::SetSavepos(int p) {
1791 Clear();
1792 scn = SaveSelect;
1793 pos = p;
1794 }
1795
1796 Rect TextWindow::WakuSize(PicContainer& pic, int waku_no, const AyuSysConfig& config) {
1797 char key[1024];
1798 sprintf(key, "#WAKU.%03d.000.NAME", waku_no);
1799 const char* name = config.GetParaStr(key);
1800 if (!name) return Rect(0,0,0,0);
1801 std::string str = name; str += ".g00";
1802 Surface* s = pic.Root().NewSurface(str.c_str());
1803 if (!s) return Rect(0,0,0,0);
1804 Rect r(*s);
1805 pic.Root().DeleteSurface(s);
1806 return r;
1807 }
1808 void TextWindow::MakeWaku(PicContainer& pic, Event::Container& event, int waku_no, int window_no, bool* use_btn, const AyuSysConfig& config, void* callback) {
1809 char key[1024];
1810 std::string str;
1811 /* 枠を作成 */
1812 sprintf(key, "#WAKU.%03d.000.NAME", waku_no);
1813 const char* name = config.GetParaStr(key);
1814 if (name && name[0] == 0) name = 0;
1815 sprintf(key, "#WAKU.%03d.000.BACK", waku_no);
1816 const char* back = config.GetParaStr(key);
1817 if (back && back[0] == 0) back = 0;
1818 sprintf(key, "#WAKU.%03d.000.BTN", waku_no);
1819 const char* btn = config.GetParaStr(key);
1820 if (btn && btn[0] == 0) btn = 0;
1821
1822 if (name == 0 && back == 0 && btn == 0) return;
1823
1824 /* まず、テキスト背景を設定 */
1825 if (back) {
1826 str = back; str += ".g00";
1827 int rc,gc,bc,ac, flag;
1828 char key[1024];
1829 sprintf(key, "#WINDOW.%03d.ATTR", window_no);
1830 if (config.GetParam(key, 5, &rc, &gc, &bc, &ac, &flag) == -1) {
1831 config.GetParam("#WINDOW_ATTR", 5, &rc, &gc, &bc, &ac, &flag);
1832 }
1833 Surface* back_s = pic.Root().NewSurface(str.c_str());
1834 if (back_s) {
1835 Rect rect(*back_s);
1836 Surface* s = pic.Root().NewSurface(rect.width(), rect.height(), ALPHA_MASK);
1837 DSurfaceMove(back_s, rect, s, rect);
1838 DSurfaceFillA(s, rect, rc, gc, bc, ac); // 透明度設定
1839 pic.SetSurface(s, 0, 0);
1840 pic.SetSurfaceFreeFlag(1);
1841 if (flag == 0) wid->Pic()->SetSurfaceAttribute(PicBase::BLIT_MULTIPLY);
1842 pic.Root().DeleteSurface(back_s);
1843 }
1844 }
1845 /* その前に枠飾りを設定 */
1846 if (name) {
1847 str = name; str += ".g00";
1848 Surface* s = pic.Root().NewSurface(str.c_str());
1849 if (s) {
1850 Rect rect(*s);
1851 pic.Root().DeleteSurface(s);
1852 PicBase* p = pic.create_leaf(Rect(0, 0, rect.width(), rect.height()),0);
1853 p->SetSurface(str.c_str(), 0, 0);
1854 p->ZMove(ZMOVE_BOTTOM);
1855 p->show();
1856 }
1857 }
1858 if (btn == 0) return;
1859 if (use_btn == 0) return;
1860 // ボタンの作成
1861 // 使用するボタンについては、必要に応じて show() すること
1862
1863 /* ボタンの位置情報を求める */
1864 str = btn; str += ".g00";
1865 ARCINFO* info = file_searcher.Find(FILESEARCH::PDT, str.c_str(), "g00");
1866 if (info == 0) return; // cannot find file
1867 const char* data = info->Read();
1868 /* g00 ファイルのヘッダ部分に位置情報は入っている */
1869 /* 存在しなければボタン画像ではない */
1870 if (data == 0 || *data != 2) {
1871 delete info;
1872 return;
1873 }
1874 int index_count = read_little_endian_int(data+5); // 0x70 == 112 ( 8 個ずつグループなので、14個のボタン ) が標準
1875 int i;
1876 for (i=0; i<BTNCNT; i++) {
1877 if (!use_btn[i]) continue;
1878 if (btnpos[i]*8 >= index_count) {
1879 continue; // ボタンが存在しない
1880 }
1881 int x, y, w, h;
1882 sprintf(key, "#WAKU.%03d.000.%s_BOX", waku_no, btnname[i]);
1883 if (config.GetParam(key, 5, 0, &x, &y, &w, &h) == -1) continue;
1884 int sx, sy, sdx, sdy, cnt;
1885 const char* d = data + 9 + btnpos[i]*24*8;
1886 sx = read_little_endian_int(d);
1887 sy = read_little_endian_int(d+4);
1888 sdx = read_little_endian_int(d+24) - sx;
1889 sdy = read_little_endian_int(d+24 + 4) - sy;
1890 cnt = 2;
1891 if (sx+sdx*2 == read_little_endian_int(d+2*24) && sy+sdy*2 == read_little_endian_int(d+2*24+4)) cnt = 3;
1892 WidButton* wid = new WidButton(event, &pic, str.c_str(), sx, sy, sdx, sdy, cnt, Rect(x, y, x+w, y+h), 1);
1893 if (btnpress[i]) { wid->press_func = btnpress[i]; wid->press_pointer = callback;}
1894 if (btndrag[i]) { wid->drag_func = btndrag[i]; wid->drag_pointer = callback;}
1895 }
1896 delete info;
1897 return;
1898 }
1899
1900 TextWindow::TextWindow(PicContainer& parent, Event::Container& event, int win_no, const AyuSysConfig& config, void* callback) :
1901 wid(0), name_visible(true),name(0),name_container(0), face(0) {
1902 int i; for (i=0; i<8; i++) face_pics[i]=0;
1903 char key[1024];
1904 bool use_btn[BTNCNT];
1905 int size, rep1, rep2, cntw, cnth, mposx, mposy, posd, posx, posy, minx, miny, waku_no, ruby;
1906 sprintf(key, "#WINDOW.%03d.MOJI_SIZE", win_no); if (config.GetParam(key, 1, &size) == -1) return;
1907 sprintf(key, "#WINDOW.%03d.MOJI_REP", win_no); if (config.GetParam(key, 2, &rep1, &rep2) == -1) return;
1908 sprintf(key, "#WINDOW.%03d.MOJI_CNT", win_no); if (config.GetParam(key, 2, &cntw, &cnth) == -1) return;
1909 sprintf(key, "#WINDOW.%03d.POS", win_no); if (config.GetParam(key, 3, &posd, &posx, &posy) == -1) return;
1910 sprintf(key, "#WINDOW.%03d.MOJI_POS", win_no); if (config.GetParam(key, 4, &mposy, 0, &mposx, 0) == -1) return;
1911 sprintf(key, "#WINDOW.%03d.MOJI_MIN", win_no); if (config.GetParam(key, 2, &minx, &miny) == -1) return;
1912 sprintf(key, "#WINDOW.%03d.WAKU_SETNO", win_no);if (config.GetParam(key, 1, &waku_no) == -1) return;
1913 sprintf(key, "#WINDOW.%03d.LUBY_SIZE", win_no); if (config.GetParam(key, 1, &ruby) == -1) return;
1914
1915 /* テキストウィジット:画面の右下一杯まで使用 */
1916 /* posd == 2 なら画面下にひっつくように配置 */
1917 Rect r(0,0);
1918 if (posd == 2) {
1919 r = WakuSize(parent, waku_no, config);
1920 r = Rect(0, parent.Height()-r.height(), r.width(), parent.Height());
1921 posx = 0;
1922 posy = parent.Height()-r.height();
1923 } else /* posd == 0 ? */
1924 r = Rect(posx, posy, parent.Width(), parent.Height());
1925
1926 /* テキストウィンドウの作成 */
1927 int w = size*cntw; int h = (size+ruby+2)*cnth;
1928 wid = new WidText(event, &parent, r, Rect(mposx, mposy, mposx+w, mposy+h), size);
1929 wid->stream.kanji_type = TextStream::sjis;
1930 /* 顔ウィンドウの作成 */
1931 for (i=0; i<8; i++) {
1932 int x,y;
1933 sprintf(key, "#WINDOW.%03d.FACE.%03d", win_no, i);
1934 if (config.GetParam(key, 2, &x, &y) == -1) continue;
1935 /* 顔ウィンドウを作成する */
1936 if (x >= 0 && y >= 0) {
1937 face_pics[i] = wid->PicNode()->create_leaf(Rect(x,y), PicBase::FIT_SURFACE);
1938 } else {
1939 face_pics[i] = parent.create_leaf(Rect(x+posx,y+posy), PicBase::FIT_SURFACE);
1940 }
1941 face_pics[i]->show();
1942 }
1943 face = face_pics[0];
1944 // ボタンの設定
1945 for (i=0; i<BTNCNT; i++) {
1946 int num;
1947 sprintf(key, "#WINDOW.%03d.%s_USE", win_no, btnname[i]);
1948 config.GetParam(key, 1, &num);
1949 use_btn[i] = (num==0) ? false : true;
1950 }
1951 // make name window
1952 int shadow, name_mod, name_size, name_min, name_center, name_posx, name_posy, name_mposx, name_mposy;
1953 sprintf(key, "#WINDOW.%03d.MOJI_SHADOW", win_no); config.GetParam(key, 1, &shadow);
1954 sprintf(key, "#WINDOW.%03d.NAME_MOD", win_no); config.GetParam(key, 1, &name_mod);
1955 sprintf(key, "#WINDOW.%03d.NAME_MOJI_SIZE", win_no); config.GetParam(key, 1, &name_size);
1956 sprintf(key, "#WINDOW.%03d.NAME_MOJI_MIN", win_no); config.GetParam(key, 1, &name_min);
1957 sprintf(key, "#WINDOW.%03d.NAME_MOJI_POS", win_no); config.GetParam(key, 2, &name_mposx, &name_mposy);
1958 sprintf(key, "#WINDOW.%03d.NAME_CENTERING", win_no); config.GetParam(key, 1, &name_center);
1959 sprintf(key, "#WINDOW.%03d.NAME_POS", win_no); config.GetParam(key, 2, &name_posx, &name_posy);
1960 // if name_mode==0 name is in the text window
1961 // if name_mode == 1 open name window
1962 // if name_mode == 2 name is not used
1963 if (name_mod) {
1964 if (name_mod == 1) {
1965 int w = name_size*name_min; int h = name_size;
1966 int name_waku;
1967 sprintf(key, "#WINDOW.%03d.NAME_WAKU_SETNO", win_no);
1968 if (config.GetParam(key, 1, &name_waku) != -1 && name_waku != -1) {
1969 Rect waku_r = WakuSize(parent, name_waku, config);
1970 waku_r.rmove(r.lx, r.ty); // テキストウィンドウ位置に動かす
1971 waku_r.rmove(name_posx, name_posy-waku_r.height()); // NAME_POS へ位置補正
1972 name_container = parent.create_node(waku_r, 0);
1973 MakeWaku(*name_container, event, name_waku, win_no, 0, config, callback);
1974 Rect name_r(0,0,w,h);
1975 name_r.rmove(name_mposx, name_mposy);
1976 name = new WidLabel(name_container, name_r, true, 0, name_size);
1977 name->show();
1978 } else { // 名前専用枠なし
1979 Rect name_r(0, 0, w, h);
1980 name_r.rmove(r.lx, r.ty);
1981 name_r.rmove(name_posx, name_posy-name_size);
1982 name_container = parent.create_node(name_r, 0);
1983 name = new WidLabel(name_container, Rect(0,0,w,h), true, 0, name_size);
1984 name->show();
1985 name_container->show();
1986 }
1987 } else { // name_mod == 2 or 3
1988 name_container = parent.create_node( Rect(0,0,1,1), 0);
1989 }
1990 }
1991 MakeWaku(*wid->PicNode(), event,waku_no, win_no, use_btn, config, callback);
1992 }
1993 void TextImpl::InitWindow(void) {
1994 int i,j,k;
1995 int w;
1996 std::string str;
1997
1998 for (w=0; w<32; w++) {
1999 widgets[w] = new TextWindow(parent, event, w, config, (void*)this);
2000 if (widgets[w]->wid == 0) {
2001 delete widgets[w];
2002 widgets[w] = 0;
2003 }
2004 }
2005 SetCursor(0);
2006 for (i=0; i<26; i++) {
2007 char buf[1024];
2008 sprintf(buf, "#NAME.%c", i+'A');
2009 const char* s = config.GetParaStr(buf);
2010 if (s) replace_name[i] = s;
2011 }
2012 // replace_name2 : 初期設定
2013 // 渚、秋生、渚 (CLANNAD)
2014 char name_nagisa[3] = {0x8f,0x8d,0};
2015 char name_akio[5] = {0x8f, 0x48, 0x90, 0xb6, 0};
2016 replace_name2[0] = name_nagisa;
2017 replace_name2[1] = name_akio;
2018 replace_name2[2] = name_nagisa;
2019 text = 0;
2020 /* テキスト速度の設定 */
2021 int speed, mod, wait, auto_mod;
2022 config.GetParam("#INIT_MESSAGE_SPEED", 1, &speed);
2023 config.GetParam("#INIT_MESSAGE_SPEED_MOD", 1, &mod);
2024 config.GetParam("#MESSAGE_KEY_WAIT_USE", 1, &auto_mod);
2025 config.GetParam("#MESSAGE_KEY_WAIT_TIME", 1, &wait);
2026 if (mod) speed = -1;
2027 if (!auto_mod) wait = -1;
2028 SetTextSpeed(speed);
2029 SetTextWait(wait);
2030 return;
2031 }