00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #ifdef HAVE_CONFIG_H
00020 # include <config.h>
00021 #endif
00022
00023 #include "actors.h"
00024 #include "conversation.h"
00025 #include "exult.h"
00026 #include "game.h"
00027 #include "gamewin.h"
00028 #include "mouse.h"
00029 #include "useval.h"
00030 #include "data/exult_bg_flx.h"
00031
00032 #ifndef UNDER_CE
00033 using std::cout;
00034 using std::endl;
00035 using std::size_t;
00036 using std::strcpy;
00037 using std::string;
00038 using std::vector;
00039 #endif
00040
00041
00042
00043
00044
00045
00046
00047
00048 class Npc_face_info {
00049 public:
00050 ShapeID shape;
00051 int face_num;
00052
00053 bool text_pending;
00054
00055 Rectangle face_rect;
00056 Rectangle text_rect;
00057 int last_text_height;
00058 string cur_text;
00059 Npc_face_info(ShapeID &sid, int num) : shape(sid), face_num(num), text_pending(0)
00060 { }
00061 };
00062
00063 Conversation::Conversation() :
00064 num_faces(0), last_face_shown(0), conv_choices(0), avatar_face(0,0,0,0)
00065 {
00066
00067 const int max_faces = sizeof(face_info)/sizeof(face_info[0]);
00068 for (int i = 0; i < max_faces; i++)
00069 face_info[i] = 0;
00070 }
00071
00072 Conversation::~Conversation()
00073 {
00074 delete [] conv_choices;
00075 }
00076
00077
00078 void Conversation::clear_answers(void)
00079 {
00080 answers.clear();
00081 }
00082
00083 void Conversation::add_answer(const char *str)
00084 {
00085 remove_answer(str);
00086 string s(str);
00087 answers.push_back(s);
00088 }
00089
00090
00091
00092
00093
00094 void Conversation::add_answer(Usecode_value& val)
00095 {
00096 const char *str;
00097 int size = val.get_array_size();
00098 if (size)
00099 {
00100 for (int i = 0; i < size; i++)
00101 add_answer(val.get_elem(i));
00102 }
00103 else if ((str = val.get_str_value()) != 0)
00104 add_answer(str);
00105 }
00106
00107 void Conversation::remove_answer(const char *str)
00108 {
00109 std::vector<string>::iterator it;
00110
00111 for(it=answers.begin(); it!=answers.end(); ++it)
00112 if(*it==str)
00113 break;
00114
00115 if(it!=answers.end())
00116 answers.erase(it);
00117 }
00118
00119
00120
00121
00122
00123 void Conversation::remove_answer(Usecode_value& val)
00124 {
00125 const char *str;
00126 if (val.is_array()) {
00127 int size = val.get_array_size();
00128 for (int i=0; i < size; i++) {
00129 str = val.get_elem(i).get_str_value();
00130 if (str) remove_answer(str);
00131 }
00132 } else {
00133 str = val.get_str_value();
00134 remove_answer(str);
00135 }
00136 }
00137
00138
00139
00140
00141
00142 void Conversation::init_faces()
00143 {
00144 const int max_faces = sizeof(face_info)/sizeof(face_info[0]);
00145 for (int i = 0; i < max_faces; i++)
00146 {
00147 if( face_info[i] )
00148 delete face_info[i];
00149 face_info[i] = 0;
00150 }
00151 num_faces = 0;
00152 last_face_shown = -1;
00153 }
00154
00155
00156
00157
00158
00159 static int SI_get_frame
00160 (
00161 Actor *main_actor
00162 )
00163 {
00164 int frame;
00165 if (main_actor->get_skin_color() == 0)
00166 {
00167 frame = 1 - main_actor->get_type_flag(Actor::tf_sex);
00168 }
00169 else if (main_actor->get_skin_color() == 1)
00170 {
00171 frame = 3 - main_actor->get_type_flag(Actor::tf_sex);
00172 }
00173 else if (main_actor->get_skin_color() == 2)
00174 {
00175 frame = 5 - main_actor->get_type_flag(Actor::tf_sex);
00176 }
00177 else
00178 {
00179 frame = main_actor->get_type_flag(Actor::tf_sex);
00180 }
00181 return frame;
00182 }
00183
00184
00185
00186
00187
00188
00189
00190 void Conversation::show_face(int shape, int frame, int slot)
00191 {
00192 ShapeID face_sid(shape, frame, SF_FACES_VGA);
00193
00194 bool SI = Game::get_game_type()==SERPENT_ISLE;
00195 Main_actor* main_actor = gwin->get_main_actor();
00196 const int max_faces = sizeof(face_info)/sizeof(face_info[0]);
00197
00198
00199 Palette *pal = gwin->get_pal();
00200 if (pal->get_brightness() >= 300)
00201 pal->set(-1, 100);
00202
00203 if (SI)
00204 {
00205 if (shape == 28 && main_actor->get_flag(Obj_flags::petra))
00206 {
00207 shape = main_actor->get_face_shapenum();
00208 face_sid.set_shape(shape);
00209 }
00210 if (shape == 0)
00211 {
00212 frame = SI_get_frame(main_actor);
00213 if (main_actor->get_flag(Obj_flags::tattooed))
00214 shape = 299;
00215
00216 face_sid.set_shape(shape, frame);
00217 }
00218 }
00219
00220 else if (shape == 0)
00221 {
00222 if (main_actor->get_skin_color() != 3)
00223 {
00224 shape = EXULT_BG_FLX_MR_FACES_SHP;
00225 frame = SI_get_frame(main_actor);
00226 face_sid.set_file(SF_GAME_FLX);
00227 face_sid.set_shape(shape, frame);
00228 }
00229 }
00230
00231 int screenw = gwin->get_width(), screenh = gwin->get_height();
00232 Npc_face_info *info = 0;
00233
00234 for (int i = 0; i < max_faces; i++)
00235 if (face_info[i] && face_info[i]->face_num == shape)
00236 {
00237 info = face_info[i];
00238 last_face_shown = i;
00239 break;
00240 }
00241 if (!info)
00242 {
00243 if (num_faces == max_faces)
00244
00245 remove_slot_face(max_faces - 1);
00246 info = new Npc_face_info(face_sid, shape);
00247 if (slot == -1)
00248 slot = num_faces;
00249
00250 Npc_face_info *prev = slot ? face_info[slot - 1] : 0;
00251 last_face_shown = slot;
00252 if (!face_info[slot])
00253 num_faces++;
00254 else
00255 delete face_info[slot];
00256 face_info[slot] = info;
00257
00258 int text_height = sman->get_text_height(0);
00259
00260
00261 Shape_frame *face = shape >= 0 ? face_sid.get_shape() : 0;
00262 int face_w = 32, face_h = 32;
00263 if (face)
00264 {
00265 face_w = face->get_width();
00266 face_h = face->get_height();
00267 }
00268 int starty;
00269 if (prev)
00270 {
00271 starty = prev->text_rect.y + prev->last_text_height;
00272 if (starty < prev->face_rect.y + prev->face_rect.h)
00273 starty = prev->face_rect.y + prev->face_rect.h;
00274 starty += 2*text_height;
00275 if (starty + face_h > screenh - 1)
00276 starty = screenh - face_h - 1;
00277 }
00278 else
00279 starty = 1;
00280 info->face_rect = gwin->clip_to_win(Rectangle(8, starty,
00281 face_w + 4, face_h + 4));
00282 Rectangle& fbox = info->face_rect;
00283
00284 info->text_rect = gwin->clip_to_win(Rectangle(
00285 fbox.x + fbox.w + 3, fbox.y + 3,
00286 screenw - fbox.x - fbox.w - 6, 4*text_height));
00287
00288 if (info->text_rect.w < 16 || info->text_rect.h < 16)
00289
00290 info->text_rect = Rectangle(screenw/4, screenh/2,
00291 screenw/2, screenh/4);
00292 info->last_text_height = info->text_rect.h;
00293 }
00294 gwin->get_win()->set_clip(0, 0, screenw, screenh);
00295 paint_faces();
00296 gwin->get_win()->clear_clip();
00297 }
00298
00299
00300
00301
00302
00303 void Conversation::remove_face(int shape)
00304 {
00305 const int max_faces = sizeof(face_info)/sizeof(face_info[0]);
00306 int i;
00307 for (i = 0; i < max_faces; i++)
00308 if (face_info[i] && face_info[i]->face_num == shape)
00309 break;
00310 if (i == max_faces)
00311 return;
00312 remove_slot_face(i);
00313 }
00314
00315
00316
00317
00318
00319 void Conversation::remove_slot_face
00320 (
00321 int slot
00322 )
00323 {
00324 const int max_faces = sizeof(face_info)/sizeof(face_info[0]);
00325 if (slot >= max_faces || !face_info[slot])
00326 return;
00327 Npc_face_info *info = face_info[slot];
00328
00329
00330 gwin->add_dirty(info->face_rect);
00331 gwin->add_dirty(info->text_rect);
00332 delete face_info[slot];
00333 face_info[slot] = 0;
00334 num_faces--;
00335 if (last_face_shown == slot)
00336 {
00337 int j;
00338 for (j = max_faces - 1; j >= 0; j--)
00339 if (face_info[j])
00340 break;
00341 last_face_shown = j;
00342 }
00343 }
00344
00345
00346
00347
00348
00349
00350 void Conversation::show_npc_message(const char *msg)
00351 {
00352 if (last_face_shown == -1)
00353 return;
00354 Npc_face_info *info = face_info[last_face_shown];
00355 info->cur_text = "";
00356 Rectangle& box = info->text_rect;
00357
00358
00359 gwin->paint();
00360 int height;
00361 while ((height = sman->paint_text_box(0, msg, box.x,box.y,box.w,box.h,
00362 -1, 1, gwin->get_text_bg())) < 0)
00363 {
00364 info->cur_text = string(msg, -height);
00365 int x, y; char c;
00366 gwin->paint();
00367 Get_click(x, y, Mouse::hand, &c, false, this);
00368
00369 gwin->paint();
00370 msg += -height;
00371 }
00372
00373 info->last_text_height = height;
00374 info->cur_text = msg;
00375 info->text_pending = 1;
00376 gwin->set_painted();
00377
00378 }
00379
00380
00381
00382
00383
00384
00385 bool Conversation::is_npc_text_pending()
00386 {
00387 const int max_faces = sizeof(face_info)/sizeof(face_info[0]);
00388 for (int i = 0; i < max_faces; i++)
00389 if (face_info[i] && face_info[i]->text_pending)
00390 return true;
00391 return false;
00392 }
00393
00394
00395
00396
00397
00398 void Conversation::clear_text_pending()
00399 {
00400 const int max_faces = sizeof(face_info)/sizeof(face_info[0]);
00401 for (int i = 0; i < max_faces; i++)
00402 if (face_info[i])
00403 face_info[i]->text_pending = 0;
00404 }
00405
00406
00407
00408
00409
00410 void Conversation::show_avatar_choices(int num_choices, char **choices)
00411 {
00412 bool SI = Game::get_game_type()==SERPENT_ISLE;
00413 Main_actor *main_actor = gwin->get_main_actor();
00414 const int max_faces = sizeof(face_info)/sizeof(face_info[0]);
00415
00416 Rectangle sbox = gwin->get_win_rect();
00417 int x = 0, y = 0;
00418 int height = sman->get_text_height(0);
00419 int space_width = sman->get_text_width(0, " ");
00420
00421
00422
00423 int shape = main_actor->get_face_shapenum();
00424 int frame;
00425 ShapeID face_sid(shape, 0, SF_FACES_VGA);
00426
00427
00428 if (SI && (main_actor->get_flag(Obj_flags::petra) || (main_actor->get_skin_color() == 3 && main_actor->get_type_flag(Actor::tf_sex))))
00429 {
00430 shape = 28;
00431 frame = 0;
00432 face_sid.set_shape(shape, frame);
00433 }
00434 else if (SI && main_actor->get_skin_color() == 3)
00435 {
00436 shape = 298;
00437 frame = 0;
00438 face_sid.set_shape(shape, frame);
00439 }
00440 else if (SI)
00441 {
00442 frame = SI_get_frame(main_actor);
00443 if (shape == 0 && main_actor->get_flag(Obj_flags::tattooed))
00444 {
00445 shape = 299;
00446 face_sid.set_shape(shape);
00447 }
00448
00449 }
00450 else
00451 {
00452
00453 if (main_actor->get_skin_color() >= 0 && main_actor->get_skin_color() <= 2 && GAME_BG)
00454 {
00455 shape = EXULT_BG_FLX_MR_FACES_SHP;
00456 frame = SI_get_frame(main_actor);
00457 face_sid.set_file(SF_GAME_FLX);
00458 face_sid.set_shape(shape, frame);
00459 }
00460 else
00461 frame = main_actor->get_type_flag(Actor::tf_sex);
00462 }
00463
00464
00465 face_sid.set_frame(frame);
00466
00467 Shape_frame *face = face_sid.get_shape();
00468 int empty;
00469 for (empty = 0; empty < max_faces; empty++)
00470 if (!face_info[empty])
00471 break;
00472
00473 Npc_face_info *prev = empty ? face_info[empty - 1] : 0;
00474 int fx = prev ? prev->face_rect.x + prev->face_rect.w + 4 : 16;
00475 int fy;
00476 if (SI)
00477 {
00478 if (num_faces == max_faces)
00479
00480 remove_slot_face(max_faces - 1);
00481 fy = sbox.h - 2 - face->get_height();
00482 fx = 8;
00483 }
00484 else if (!prev)
00485 fy = sbox.h - face->get_height() - 3*height;
00486 else
00487 {
00488 fy = prev->text_rect.y + prev->last_text_height;
00489 if (fy < prev->face_rect.y + prev->face_rect.h)
00490 fy = prev->face_rect.y + prev->face_rect.h;
00491 fy += height;
00492 }
00493 Rectangle mbox(fx, fy, face->get_width(), face->get_height());
00494 mbox = mbox.intersect(sbox);
00495 avatar_face = mbox;
00496
00497 Rectangle tbox(mbox.x + mbox.w + 8, mbox.y + 4,
00498 sbox.w - mbox.x - mbox.w - 16,
00499
00500 5*height);
00501 tbox = tbox.intersect(sbox);
00502
00503
00504 sman->paint_shape(mbox.x + face->get_xleft(),
00505 mbox.y + face->get_yabove(), face);
00506 delete [] conv_choices;
00507 conv_choices = new Rectangle[num_choices + 1];
00508 for (int i = 0; i < num_choices; i++)
00509 {
00510 char text[256];
00511 text[0] = 127;
00512 strcpy(&text[1], choices[i]);
00513 int width = sman->get_text_width(0, text);
00514 if (x > 0 && x + width >= tbox.w)
00515 {
00516 x = 0;
00517 y += height - 1;
00518 }
00519
00520 conv_choices[i] = Rectangle(tbox.x + x, tbox.y + y,
00521 width, height);
00522 conv_choices[i] = conv_choices[i].intersect(sbox);
00523 avatar_face = avatar_face.add(conv_choices[i]);
00524 sman->paint_text_box(0, text, tbox.x + x, tbox.y + y,
00525 width + space_width, height, 0, 0, gwin->get_text_bg());
00526 x += width + space_width;
00527 }
00528 avatar_face.enlarge(6);
00529 avatar_face = avatar_face.intersect(sbox);
00530
00531 conv_choices[num_choices] = Rectangle(0, 0, 0, 0);
00532 clear_text_pending();
00533 gwin->set_painted();
00534 }
00535
00536 void Conversation::show_avatar_choices()
00537 {
00538 char **result;
00539 size_t i;
00540
00541 result=new char *[answers.size()];
00542 for(i=0;i<answers.size();i++)
00543 {
00544 result[i]=new char[answers[i].size()+1];
00545 strcpy(result[i],answers[i].c_str());
00546 }
00547 show_avatar_choices(answers.size(),result);
00548 for(i=0;i<answers.size();i++)
00549 {
00550 delete [] result[i];
00551 }
00552 delete [] result;
00553 }
00554
00555 void Conversation::clear_avatar_choices()
00556 {
00557
00558 gwin->add_dirty(avatar_face);
00559 avatar_face.w = 0;
00560 }
00561
00562
00563
00564
00565
00566
00567
00568
00569 int Conversation::conversation_choice(int x, int y)
00570 {
00571 int i;
00572 for (i = 0; conv_choices[i].w != 0 &&
00573 !conv_choices[i].has_point(x, y); i++)
00574 ;
00575 if (conv_choices[i].w != 0)
00576 return (i);
00577 else
00578 return (-1);
00579 }
00580
00581
00582
00583
00584
00585 void Conversation::paint
00586 (
00587 )
00588 {
00589 paint_faces(true);
00590 if (avatar_face.w)
00591 show_avatar_choices();
00592 }
00593
00594
00595
00596
00597
00598 void Conversation::paint_faces
00599 (
00600 bool text
00601 )
00602 {
00603 if (!num_faces)
00604 return;
00605 const int max_faces = sizeof(face_info)/sizeof(face_info[0]);
00606 for (int i = 0; i < max_faces; i++)
00607 {
00608 Npc_face_info *finfo = face_info[i];
00609 if (!finfo)
00610 continue;
00611 Shape_frame *face = finfo->face_num >= 0 ?
00612 finfo->shape.get_shape() : 0;
00613 int face_xleft = 0, face_yabove = 0;
00614 if (face)
00615 {
00616 face_xleft = face->get_xleft();
00617 face_yabove = face->get_yabove();
00618
00619 sman->paint_shape(
00620 finfo->face_rect.x + face_xleft,
00621 finfo->face_rect.y + face_yabove, face, 1);
00622 }
00623 if (text)
00624 {
00625 Rectangle& box = finfo->text_rect;
00626 sman->paint_text_box(0, finfo->cur_text.c_str(),
00627 box.x,box.y,box.w,box.h, -1, 1,
00628 gwin->get_text_bg());
00629 }
00630 }
00631 }
00632
00633
00634
00635
00636
00637
00638 int Conversation::locate_answer(const char *str)
00639 {
00640 int num;
00641 std::vector<string>::iterator it;
00642 num = 0;
00643 for(it=answers.begin(); it!=answers.end(); ++it) {
00644 if(*it==str)
00645 return num;
00646 num++;
00647 }
00648
00649 return -1;
00650 }
00651
00652 void Conversation::push_answers()
00653 {
00654 answer_stack.push_front(answers);
00655 answers.clear();
00656 }
00657
00658 void Conversation::pop_answers()
00659 {
00660 answers=answer_stack.front();
00661 answer_stack.pop_front();
00662 gwin->paint();
00663 }