00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #ifdef HAVE_CONFIG_H
00023 # include <config.h>
00024 #endif
00025
00026 #ifndef ALPHA_LINUX_CXX
00027 # include <iostream>
00028 # include <cstdlib>
00029 # include <cstring>
00030 #endif
00031 #include <algorithm>
00032 #include "Astar.h"
00033 #include "Audio.h"
00034 #include "Gump_manager.h"
00035 #include "Paperdoll_gump.h"
00036 #include "Zombie.h"
00037 #include "actions.h"
00038 #include "actors.h"
00039 #include "bodies.h"
00040 #include "cheat.h"
00041 #include "chunks.h"
00042 #include "combat.h"
00043 #include "combat_opts.h"
00044 #include "dir.h"
00045 #include "egg.h"
00046 #include "exult.h"
00047 #include "frameseq.h"
00048 #include "game.h"
00049 #include "gamewin.h"
00050 #include "gamemap.h"
00051 #include "gameclk.h"
00052 #include "imagewin.h"
00053 #include "items.h"
00054 #include "npctime.h"
00055 #include "ready.h"
00056 #include "ucmachine.h"
00057 #include "party.h"
00058 #include "monstinf.h"
00059 #include "exult_constants.h"
00060 #include "monsters.h"
00061 #include "effects.h"
00062 #include "palette.h"
00063 #include "ucsched.h"
00064 #include "ucscriptop.h"
00065
00066 #ifdef USE_EXULTSTUDIO
00067 #include "server.h"
00068 #include "objserial.h"
00069 #include "mouse.h"
00070 #include "servemsg.h"
00071 #endif
00072
00073 #ifndef UNDER_CE
00074 using std::cerr;
00075 using std::cout;
00076 using std::endl;
00077 using std::memcpy;
00078 using std::rand;
00079 using std::string;
00080 using std::swap;
00081 #endif
00082
00083 Actor *Actor::editing = 0;
00084
00085 extern bool combat_trace;
00086
00087
00088
00089
00090
00091
00092 const short Actor::party_pos[4][10][2] = {
00093
00094 {
00095 { -2, 2 },
00096 { 2, 2 },
00097 { 0, 4 },
00098 { -4, 4 },
00099 { 4, 4 },
00100 { -2, 6 },
00101 { 2, 6 },
00102 { 0, 8 },
00103 { -4, 8 },
00104 { 4, 8 }
00105 },
00106
00107 {
00108 { -2, -2 },
00109 { -2, 2 },
00110 { -4, 0 },
00111 { -4, -4 },
00112 { -4, 4 },
00113 { -6, -2 },
00114 { -6, 2 },
00115 { -8, 0 },
00116 { -8, -4 },
00117 { -8, 4 }
00118 },
00119
00120 {
00121 { -2, -2 },
00122 { 2, -2 },
00123 { 0, -4 },
00124 { -4, -4 },
00125 { 4, -4 },
00126 { -2, -6 },
00127 { 2, -6 },
00128 { 0, -8 },
00129 { -4, -8 },
00130 { 4, -8 }
00131 },
00132
00133 {
00134 { 2, -2 },
00135 { 2, 2 },
00136 { 4, 0 },
00137 { 4, -4 },
00138 { 4, 4 },
00139 { 6, -2 },
00140 { 6, 2 },
00141 { 8, 0 },
00142 { 8, -4 },
00143 { 8, 4 }
00144 }
00145 };
00146
00147
00148 uint8 visible_frames[16] = {
00149 Actor::standing,
00150 Actor::standing,
00151 Actor::standing,
00152 Actor::standing,
00153 Actor::raise2_frame,
00154 Actor::reach2_frame,
00155 Actor::strike2_frame,
00156 Actor::raise1_frame,
00157 Actor::reach1_frame,
00158 Actor::strike1_frame,
00159 Actor::standing,
00160 Actor::kneel_frame,
00161 Actor::bow_frame,
00162 Actor::standing,
00163 Actor::strike2_frame,
00164 Actor::strike2_frame };
00165
00166 Frames_sequence *Actor::avatar_frames[4] = {0, 0, 0, 0};
00167 Frames_sequence *Actor::npc_frames[4] = {0, 0, 0, 0};
00168 const signed char sea_serpent_attack_frames[] = {13, 12, 11, 0, 1, 2, 3, 11, 12,
00169 13, 14};
00170
00171 inline int Is_attack_frame(int i) { return i == 6 || i == 9; }
00172 inline int Get_dir_from_frame(int i)
00173 { return ((((i&16)/8) - ((i&32)/32)) + 4)%4; }
00174
00175
00176
00177
00178
00179 Npc_timer_list *Actor::need_timers
00180 (
00181 )
00182 {
00183 if (!timers)
00184 timers = new Npc_timer_list(this);
00185 return timers;
00186 }
00187
00188
00189
00190
00191
00192 void Actor::init
00193 (
00194 )
00195 {
00196 if (!avatar_frames[0])
00197 init_default_frames();
00198 size_t i;
00199 for (i = 0; i < sizeof(properties)/sizeof(properties[0]); i++)
00200 properties[i] = 0;
00201 for (i = 0; i < sizeof(spots)/sizeof(spots[0]); i++)
00202 spots[i] = 0;
00203 }
00204
00205
00206
00207
00208
00209
00210
00211 int Actor::ready_ammo
00212 (
00213 )
00214 {
00215 int points;
00216 Weapon_info *winf = Actor::get_weapon(points);
00217 int ammo;
00218 if (!winf || (ammo = winf->get_ammo_consumed()) == 0)
00219 return 1;
00220
00221 Game_object *aobj = get_readied(Actor::ammo);
00222 if (aobj && In_ammo_family(aobj->get_shapenum(), ammo))
00223 return 1;
00224 Game_object_vector vec(50);
00225 get_objects(vec, c_any_shapenum, c_any_qual, c_any_framenum);
00226 Game_object *found = 0;
00227 for (Game_object_vector::const_iterator it = vec.begin();
00228 it != vec.end(); ++it)
00229 {
00230 Game_object *obj = *it;
00231 if (In_ammo_family(obj->get_shapenum(), ammo))
00232 found = obj;
00233 }
00234 if (!found)
00235 return 0;
00236 if (aobj)
00237 aobj->remove_this(1);
00238 found->remove_this(1);
00239 add(found, 1);
00240 if (aobj)
00241 add(aobj, 1);
00242 return 1;
00243 }
00244
00245
00246
00247
00248
00249 void Actor::ready_best_weapon
00250 (
00251 )
00252 {
00253 int points;
00254
00255 if (Actor::get_weapon(points) != 0)
00256 {
00257 ready_ammo();
00258 return;
00259 }
00260 Game_object_vector vec(50);
00261 get_objects(vec, c_any_shapenum, c_any_qual, c_any_framenum);
00262 Game_object *best = 0;
00263 int best_damage = -20;
00264 Ready_type wtype=other;
00265 for (Game_object_vector::const_iterator it = vec.begin();
00266 it != vec.end(); ++it)
00267 {
00268 Game_object *obj = *it;
00269 if (obj->get_shapenum() == 595)
00270 continue;
00271
00272 Shape_info& info = obj->get_info();
00273 Weapon_info *winf = info.get_weapon_info();
00274 if (!winf)
00275 continue;
00276
00277 int damage = winf->get_damage();
00278 if (damage > best_damage)
00279 {
00280 wtype = static_cast<Ready_type>(info.get_ready_type());
00281 best = obj;
00282 best_damage = damage;
00283 }
00284 }
00285 if (!best)
00286 return;
00287 Game_object *remove1 = 0, *remove2 = 0;
00288 if (wtype == two_handed_weapon)
00289 {
00290 remove1 = spots[lhand];
00291 remove2 = spots[rhand];
00292 }
00293 else
00294 if (free_hand() == -1)
00295 remove1 = spots[lhand];
00296
00297 if (remove1)
00298 remove1->remove_this(1);
00299 if (remove2)
00300 remove2->remove_this(1);
00301 best->remove_this(1);
00302 add(best, 1);
00303 if (remove1)
00304 add(remove1, 1);
00305 if (remove2)
00306 add(remove2, 1);
00307 ready_ammo();
00308 }
00309
00310
00311
00312
00313
00314 void Actor::unready_weapon
00315 (
00316 int spot
00317 )
00318 {
00319 Game_object *obj = spots[spot];
00320 if (!obj)
00321 return;
00322 Shape_info& info = obj->get_info();
00323 if (!info.get_weapon_info())
00324 return;
00325 if (!spots[belt])
00326 {
00327 obj->remove_this(1);
00328 add_readied(obj, belt);
00329 }
00330 }
00331
00332
00333
00334
00335
00336
00337 int Actor::add_dirty
00338 (
00339 int figure_rect
00340 )
00341 {
00342 if (!gwin->add_dirty(this))
00343 return 0;
00344 int weapon_x, weapon_y, weapon_frame;
00345 if (figure_rect)
00346 if (figure_weapon_pos(weapon_x, weapon_y, weapon_frame))
00347 {
00348 Game_object * weapon = spots[lhand];
00349 int shnum = weapon->get_shapenum();
00350
00351 Shape_frame *wshape = ShapeID(shnum, weapon_frame).get_shape();
00352
00353 if (wshape)
00354 weapon_rect = gwin->get_shape_rect(wshape,
00355 weapon_x, weapon_y);
00356 else
00357 weapon_rect.w = 0;
00358 }
00359 else
00360 weapon_rect.w = 0;
00361 if (weapon_rect.w > 0)
00362 {
00363 Rectangle r = weapon_rect;
00364 int xoff, yoff;
00365 gwin->get_shape_location(this, xoff, yoff);
00366 r.shift(xoff, yoff);
00367 r.enlarge(4);
00368 gwin->add_dirty(gwin->clip_to_win(r));
00369 }
00370 return 1;
00371 }
00372
00373
00374
00375
00376
00377 void Actor::change_frame
00378 (
00379 int frnum
00380 )
00381 {
00382 add_dirty();
00383 set_frame(frnum);
00384 add_dirty(1);
00385 }
00386
00387
00388
00389
00390
00391
00392
00393 int Actor::is_blocked
00394 (
00395 Tile_coord& t,
00396 Tile_coord *f
00397 )
00398 {
00399 Shape_info& info = get_info();
00400
00401 int xtiles = info.get_3d_xtiles(), ytiles = info.get_3d_ytiles();
00402 int ztiles = info.get_3d_height();
00403 if (xtiles == 1 && ytiles == 1)
00404 {
00405 Map_chunk *nlist = gmap->get_chunk(
00406 t.tx/c_tiles_per_chunk, t.ty/c_tiles_per_chunk);
00407 nlist->setup_cache();
00408 int new_lift;
00409 int blocked = nlist->is_blocked(ztiles, t.tz,
00410 t.tx%c_tiles_per_chunk, t.ty%c_tiles_per_chunk,
00411 new_lift, get_type_flags());
00412 t.tz = new_lift;
00413 return blocked;
00414 }
00415 return Map_chunk::is_blocked(xtiles, ytiles, ztiles,
00416 f ? *f : get_tile(), t, get_type_flags());
00417 }
00418
00419
00420
00421
00422 inline void Actor::movef
00423 (
00424 Map_chunk *old_chunk,
00425 Map_chunk *new_chunk,
00426 int new_sx, int new_sy, int new_frame,
00427 int new_lift
00428 )
00429 {
00430 if (old_chunk)
00431 old_chunk->remove(this);
00432 set_shape_pos(new_sx, new_sy);
00433 if (new_frame >= 0)
00434 set_frame(new_frame);
00435 if (new_lift >= 0)
00436 set_lift(new_lift);
00437 new_chunk->add(this);
00438 }
00439
00440
00441
00442
00443
00444 Actor::Actor
00445 (
00446 const std::string &nm,
00447 int shapenum,
00448 int num,
00449 int uc
00450 ) : Container_game_object(), name(nm),usecode(uc),
00451 usecode_assigned(false), unused(false),
00452 npc_num(num), face_num(num), party_id(-1), shape_save(-1),
00453 oppressor(-1), target(0), attack_mode(nearest),
00454 schedule_type(static_cast<int>(Schedule::loiter)), schedule(0),
00455 dormant(true), hit(false), combat_protected(false),
00456 user_set_attack(false), alignment(0),
00457 two_handed(false), two_fingered(false), light_sources(0),
00458 usecode_dir(0), siflags(0), type_flags(0), ident(0),
00459 skin_color(-1), action(0),
00460 frame_time(0), step_index(0), timers(0),
00461 weapon_rect(0, 0, 0, 0), rest_time(0)
00462 {
00463 set_shape(shapenum, 0);
00464 init();
00465 frames = &npc_frames[0];
00466 }
00467
00468
00469
00470
00471
00472 Actor::~Actor
00473 (
00474 )
00475 {
00476 delete schedule;
00477 delete action;
00478 delete timers;
00479 }
00480
00481
00482
00483
00484
00485
00486 void Actor::use_food
00487 (
00488 )
00489 {
00490 if (Game::get_game_type() == SERPENT_ISLE)
00491 {
00492 int shnum = get_shapenum();
00493 if (shnum == 658 || shnum == 734 || shnum == 747)
00494 return;
00495 }
00496 int food = get_property(static_cast<int>(food_level));
00497 food -= rand()%4;
00498 set_property(static_cast<int>(food_level), food);
00499 if (food <= 0)
00500 {
00501 if (rand()%4)
00502 say(first_starving, first_starving + 2);
00503 if (food < 0)
00504 need_timers()->start_hunger();
00505 }
00506 else if (food <= 4)
00507 {
00508 if (rand()%3)
00509 say(first_needfood, first_needfood + 2);
00510 }
00511 else if (food <= 8)
00512 if (rand()%2)
00513 say(first_hunger, first_hunger + 2);
00514 }
00515
00516
00517
00518
00519
00520 void Actor::check_temperature
00521 (
00522 bool freeze
00523 )
00524 {
00525 if (!freeze)
00526 {
00527 if (!temperature)
00528 return;
00529
00530 temperature -= (temperature >= 5 ? 5 : temperature);
00531 if (rand()%3 == 0)
00532 if (temperature >= 30)
00533 say(1218, 1221);
00534 else
00535 say(1214, 1217);
00536 return;
00537 }
00538 int shnum = get_shapenum();
00539 if ((shnum == 658 || shnum == 734 || shnum == 747) && GAME_SI)
00540 return;
00541 if (get_schedule_type() == Schedule::wait)
00542 return;
00543 int warmth = figure_warmth();
00544 if (warmth >= 100)
00545 {
00546 if (!temperature)
00547 return;
00548 int decr = 1 + (warmth - 100)/10;
00549 decr = decr > temperature ? temperature : decr;
00550 temperature -= decr;
00551 if (rand()%3 == 0)
00552 if (temperature >= 30)
00553 say(1201, 1205);
00554 else
00555 say(1194, 1200);
00556 return;
00557 }
00558 int incr = 1 + (100 - warmth)/20;
00559 temperature += incr;
00560 if (temperature > 63)
00561 temperature = 63;
00562 if (rand()%3 == 0) switch (temperature/10)
00563 {
00564 case 0:
00565 say(1182, 1184);
00566 break;
00567 case 1:
00568 say(1185, 1187);
00569 break;
00570 case 2:
00571 say(1188, 1190);
00572 break;
00573 case 3:
00574 say(1191, 1193);
00575 break;
00576 case 4:
00577 say(1206, 1208);
00578 break;
00579 case 5:
00580 say(1209, 1211);
00581 break;
00582 case 6:
00583 say(1212, 1213);
00584 reduce_health(1 + rand()%3);
00585 break;
00586 }
00587 }
00588
00589
00590
00591
00592
00593 static void Get_weapon_frames
00594 (
00595 int weapon,
00596 bool projectile,
00597 bool two_handed,
00598 signed char *frames
00599 )
00600 {
00601
00602 static signed char swing_frames1[3] = {Actor::raise1_frame,
00603 Actor::reach1_frame,
00604 Actor::strike1_frame};
00605 static signed char swing_frames2[3] = {Actor::raise2_frame,
00606 Actor::reach2_frame,
00607 Actor::strike2_frame};
00608 unsigned char frame_flags;
00609 Weapon_info *winfo;
00610 if (weapon &&
00611 (winfo = ShapeID::get_info(weapon).get_weapon_info()) != 0)
00612 frame_flags = winfo->get_actor_frames(projectile);
00613 else
00614 frame_flags = projectile ? 0 : Weapon_info::raise|
00615 Weapon_info::reach;
00616
00617 const signed char *swing_frames = two_handed ? swing_frames2 : swing_frames1;
00618 frames[0] = Actor::ready_frame;
00619
00620 frames[1] = (frame_flags&Weapon_info::raise) ? swing_frames[0]
00621 : Actor::ready_frame;
00622 frames[2] = (frame_flags&Weapon_info::reach) ? swing_frames[1]
00623 : Actor::ready_frame;
00624 frames[3] = swing_frames[2];
00625 }
00626
00627
00628
00629
00630
00631
00632
00633 int Actor::get_attack_frames
00634 (
00635 int weapon,
00636 bool projectile,
00637 int dir,
00638 signed char *frames
00639 ) const
00640 {
00641 signed char baseframes[4];
00642 const signed char *which = baseframes;
00643 int cnt = 4;
00644 switch (get_shapenum())
00645 {
00646 case 525:
00647 which = sea_serpent_attack_frames;
00648 cnt = sizeof(sea_serpent_attack_frames);
00649 break;
00650 case 529:
00651 return 0;
00652 default:
00653 Get_weapon_frames(weapon, projectile, two_handed, baseframes);
00654 break;
00655 }
00656 for (int i = 0; i < cnt; i++)
00657 {
00658 int frame = get_dir_framenum(dir, *which++);
00659
00660 ShapeID id(get_shapenum(), frame, get_shapefile());
00661 Shape_frame *shape = id.get_shape();
00662 if (!shape || shape->is_empty())
00663 {
00664 frame = get_dir_framenum(dir,visible_frames[frame&15]);
00665 id.set_frame(frame);
00666 if (!(shape = id.get_shape()) || shape->is_empty())
00667 frame = get_dir_framenum(dir, Actor::standing);
00668 }
00669 *frames++ = frame;
00670 }
00671 return (cnt);
00672 }
00673
00674
00675
00676
00677
00678 void Actor::init_default_frames
00679 (
00680 )
00681 {
00682
00683
00684
00685 const int FRAME_NUM = 5;
00686 uint8 npc_north_frames[FRAME_NUM] = { 0, 1, 0, 2, 0},
00687 npc_south_frames[FRAME_NUM] = {16, 17, 16, 18, 16},
00688 npc_east_frames[FRAME_NUM] = {48, 49, 48, 50, 48},
00689 npc_west_frames[FRAME_NUM] = {32, 33, 32, 34, 32};
00690 npc_frames[static_cast<int> (north)/2] =
00691 new Frames_sequence(FRAME_NUM, npc_north_frames);
00692 npc_frames[static_cast<int> (south)/2] =
00693 new Frames_sequence(FRAME_NUM, npc_south_frames);
00694 npc_frames[static_cast<int> (east)/2] =
00695 new Frames_sequence(FRAME_NUM, npc_east_frames);
00696 npc_frames[static_cast<int> (west)/2] =
00697 new Frames_sequence(FRAME_NUM, npc_west_frames);
00698
00699 uint8 avatar_north_frames[3] = {0, 1, 2},
00700 avatar_south_frames[3] = {16, 17, 18},
00701 avatar_east_frames[3] = {48, 49, 50},
00702 avatar_west_frames[3] = {32, 33, 34};
00703 avatar_frames[static_cast<int> (north)/2] =
00704 new Frames_sequence(3, avatar_north_frames);
00705 avatar_frames[static_cast<int> (south)/2] =
00706 new Frames_sequence(3, avatar_south_frames);
00707 avatar_frames[static_cast<int> (east)/2] =
00708 new Frames_sequence(3, avatar_east_frames);
00709 avatar_frames[static_cast<int> (west)/2] =
00710 new Frames_sequence(3, avatar_west_frames);
00711 }
00712
00713
00714
00715
00716
00717
00718
00719 void Actor::stand_at_rest
00720 (
00721 )
00722 {
00723 rest_time = 0;
00724 int frame = get_framenum()&0xff;
00725 if (frame == standing || frame == sit_frame || frame == sleep_frame)
00726 return;
00727 if (!is_dead() && schedule_type == Schedule::follow_avatar &&
00728 !get_flag(Obj_flags::asleep))
00729 change_frame(get_dir_framenum(standing));
00730 }
00731
00732
00733
00734
00735
00736 void Actor::set_action
00737 (
00738 Actor_action *newact
00739 )
00740 {
00741 if (newact != action)
00742 {
00743 delete action;
00744 action = newact;
00745 }
00746 if (!action)
00747 frame_time = 0;
00748 }
00749
00750
00751
00752
00753
00754 void Actor::notify_object_gone
00755 (
00756 Game_object *obj
00757 )
00758 {
00759 if (schedule)
00760 schedule->notify_object_gone(obj);
00761 }
00762
00763
00764
00765
00766
00767 Tile_coord Actor::get_dest
00768 (
00769 )
00770 {
00771 Tile_coord dest;
00772 if (action && action->get_dest(dest))
00773 return dest;
00774 else
00775 return get_tile();
00776 }
00777
00778
00779
00780
00781
00782 void Actor::walk_to_tile
00783 (
00784 Tile_coord dest,
00785 int speed,
00786 int delay,
00787
00788 int maxblk
00789 )
00790 {
00791 if (!action)
00792 action = new Path_walking_actor_action(new Zombie(), maxblk);
00793 set_action(action->walk_to_tile(this, get_tile(), dest));
00794 if (action)
00795 start(speed, delay);
00796 else
00797 frame_time = 0;
00798 }
00799
00800
00801
00802
00803
00804
00805
00806 int Actor::walk_path_to_tile
00807 (
00808 Tile_coord src,
00809
00810 Tile_coord dest,
00811 int speed,
00812 int delay,
00813
00814 int dist,
00815 int maxblk
00816 )
00817 {
00818 set_action(new Path_walking_actor_action(new Astar(), maxblk));
00819 set_action(action->walk_to_tile(this, src, dest, dist));
00820 if (action)
00821 {
00822 start(speed, delay);
00823 return (1);
00824 }
00825 frame_time = 0;
00826 return (0);
00827 }
00828
00829
00830
00831
00832
00833 void Actor::start
00834 (
00835 int speed,
00836 int delay
00837
00838 )
00839 {
00840 dormant = false;
00841 frame_time = speed;
00842 if (!in_queue() || delay)
00843 {
00844 if (delay)
00845 gwin->get_tqueue()->remove(this);
00846 uint32 curtime = Game::get_ticks();
00847 gwin->get_tqueue()->add(curtime + delay, this,
00848 reinterpret_cast<long>(gwin));
00849 }
00850 }
00851
00852
00853
00854
00855 void Actor::stop
00856 (
00857 )
00858 {
00859
00860 if (action)
00861 {
00862 action->stop(this);
00863 add_dirty();
00864 }
00865 frame_time = 0;
00866 }
00867
00868
00869
00870
00871
00872 inline int Approach
00873 (
00874 int from,
00875 int to,
00876 int dist
00877 )
00878 {
00879 if (from <= to)
00880 return (to - from <= dist ? from : to - dist);
00881 else
00882 return (from - to <= dist ? from : to + dist);
00883 }
00884
00885
00886
00887
00888
00889 void Actor::follow
00890 (
00891 Actor *leader
00892 )
00893 {
00894 if (Actor::is_dead())
00895 return;
00896 int delay = 0;
00897 int dist;
00898 Tile_coord leaderpos = leader->get_tile();
00899 Tile_coord pos = get_tile();
00900 Tile_coord goal;
00901 if (leader->is_moving())
00902 {
00903 dist = 2 + party_id/3;
00904 goal = leader->get_dest();
00905 goal.tx = Approach(pos.tx, goal.tx, dist);
00906 goal.ty = Approach(pos.ty, goal.ty, dist);
00907 }
00908 else
00909 {
00910 goal = leaderpos;
00911 if (gwin->walk_in_formation && pos.distance(leaderpos) <= 6)
00912 return;
00913
00914
00915 static int xoffs[10] = {-1, 1, -2, 2, -3, 3, -4, 4, -5, 5},
00916 yoffs[10] = {1, -1, 2, -2, 3, -3, 4, -4, 5, -5};
00917 goal.tx += xoffs[party_id] + 1 - rand()%3;
00918 goal.ty += yoffs[party_id] + 1 - rand()%3;
00919 dist = 1;
00920 }
00921
00922 if (is_moving() && action && action->following_smart_path() &&
00923
00924 (leader->is_moving() || goal.distance(get_dest()) <= 5))
00925 return;
00926
00927 int goaldist = goal.distance(pos);
00928 if (goaldist < dist)
00929 {
00930 if (!leader->is_moving())
00931 stop();
00932 return;
00933 }
00934
00935 bool leaderpath = leader->action &&
00936 leader->action->following_smart_path();
00937
00938 int leaderdist = goal.distance(leaderpos);
00939
00940 int speed = leader->get_frame_time();
00941 if (!speed)
00942 {
00943 speed = 100;
00944 if (goaldist < leaderdist)
00945
00946 delay = (1 + leaderdist - goaldist)*100;
00947 }
00948 if (goaldist - leaderdist >= 5)
00949 speed -= 20;
00950
00951 Rectangle wrect = gwin->get_win_tile_rect();
00952 int dist2lead = pos.distance(leaderpos);
00953
00954 if (dist2lead > wrect.w + wrect.w/2 &&
00955 party_id >= 0 &&
00956 !leaderpath)
00957 {
00958
00959 if (approach_another(leader))
00960 return;
00961
00962 goal = Map_chunk::find_spot(
00963 leader->get_tile(), 2, this);
00964 if (goal.tx != -1)
00965 {
00966 move(goal.tx, goal.ty, goal.tz);
00967 if (rand()%2)
00968 say(first_catchup, last_catchup);
00969 gwin->paint();
00970 return;
00971 }
00972 }
00973
00974
00975
00976 walk_to_tile(goal, speed, delay, 0);
00977 }
00978
00979
00980
00981
00982
00983
00984
00985 int Actor::approach_another
00986 (
00987 Actor *other,
00988 bool wait
00989 )
00990 {
00991 Tile_coord dest = other->get_tile();
00992
00993 dest = Map_chunk::find_spot(dest, 8, get_shapenum(), get_framenum());
00994 if (dest.tx == -1)
00995 return 0;
00996
00997 Tile_coord src = get_tile();
00998 if (!gwin->get_win_tile_rect().has_point(src.tx - src.tz/2,
00999 src.ty - src.tz/2))
01000
01001 src = Tile_coord(-1, -1, 0);
01002 Actor_action *action = new Path_walking_actor_action();
01003 if (!action->walk_to_tile(this, src, dest))
01004 {
01005 delete action;
01006 return 0;
01007 }
01008 set_action(action);
01009 int speed = gwin->get_std_delay()/2;
01010 start(speed);
01011 if (wait)
01012 Wait_for_arrival(this, dest, 2*gwin->get_std_delay());
01013 return 1;
01014 }
01015
01016
01017
01018
01019
01020 void Actor::get_tile_info
01021 (
01022 Actor *actor,
01023 Game_window *gwin,
01024 Map_chunk *nlist,
01025 int tx, int ty,
01026 int& water,
01027 int& poison
01028 )
01029 {
01030 ShapeID flat = nlist->get_flat(tx, ty);
01031 if (flat.get_shapenum() == -1)
01032 water = poison = 0;
01033 else
01034 {
01035 Shape_info& finfo = flat.get_info();
01036 water = finfo.is_water();
01037 poison = finfo.is_poisonous();
01038
01039 if (poison && actor)
01040 {
01041 Game_object *boots = actor->Actor::get_readied(
01042 Actor::feet);
01043 if (boots != 0 &&
01044 ((boots->get_shapenum() == 588 &&
01045 Game::get_game_type() == BLACK_GATE) ||
01046 (boots->get_shapenum() == 587 &&
01047
01048 (boots->get_framenum() == 6 ||
01049 boots->get_framenum() == 1) &&
01050 Game::get_game_type() == SERPENT_ISLE)))
01051 poison = 0;
01052 else
01053 {
01054 Monster_info *minf =
01055 actor->get_info().get_monster_info();
01056 if (minf && minf->poison_safe())
01057 poison = 0;
01058 }
01059 }
01060 }
01061 }
01062
01063
01064
01065
01066
01067 void Actor::set_target
01068 (
01069 Game_object *obj,
01070 bool start_combat
01071 )
01072 {
01073 target = obj;
01074 if (start_combat && (schedule_type != Schedule::combat || !schedule))
01075 set_schedule_type(Schedule::combat);
01076 }
01077
01078
01079
01080
01081
01082
01083
01084 bool Actor::fits_in_spot (Game_object *obj, int spot, FIS_Type type)
01085 {
01086
01087 if (spots[spot])
01088 return false;
01089
01090 else if ((type == FIS_2Hand || two_handed) && spot == rhand)
01091 return false;
01092
01093 else if ((type == FIS_2Finger || two_fingered) && spot == rfinger)
01094 return false;
01095
01096 else if (type == FIS_2Hand && spot == lhand && spots[rhand])
01097 return false;
01098
01099 else if (type == FIS_2Finger && spot == lfinger && spots[rfinger])
01100 return false;
01101
01102 else if (spot == lhand || spot == rhand)
01103 return true;
01104
01105 else if (spot == belt)
01106 {
01107 if (type == FIS_Spell)
01108 return true;
01109 else if (Game::get_game_type() == BLACK_GATE)
01110 {
01111 if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), back2h_spot))
01112 return true;
01113 else if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), shield_spot))
01114 return true;
01115 }
01116 }
01117
01118
01119 return Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), spot);
01120 }
01121
01122
01123
01124
01125
01126
01127
01128 void Actor::get_prefered_slots
01129 (
01130 Game_object *obj,
01131 int &prefered,
01132 int &alternate,
01133 FIS_Type &fistype
01134 )
01135 {
01136
01137 Shape_info& info = obj->get_info();
01138
01139
01140 fistype = FIS_Other;
01141 prefered = lhand;
01142 alternate = lhand;
01143
01144 if (Game::get_game_type() == BLACK_GATE)
01145 {
01146 Ready_type type = (Ready_type) info.get_ready_type();
01147
01148 switch (type)
01149 {
01150
01151 default:
01152 if (type == spell || type == other_spell) fistype = FIS_Spell;
01153 else if (type == two_handed_weapon) fistype = FIS_2Hand;
01154
01155 if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), rhand))
01156 prefered = rhand;
01157 else if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), back))
01158 prefered = back;
01159 else
01160 alternate = rhand;
01161 break;
01162
01163
01164 case gloves:
01165 fistype = FIS_2Finger;
01166
01167 case ring:
01168 prefered = lfinger;
01169 alternate = rfinger;
01170 break;
01171
01172 case neck_armor:
01173 prefered = neck;
01174 break;
01175
01176 case torso_armor:
01177 prefered = torso;
01178 alternate = neck;
01179 break;
01180
01181 case ammunition:
01182 prefered = ammo;
01183 break;
01184
01185 case head_armor:
01186 prefered = head;
01187 break;
01188
01189 case leg_armor:
01190 prefered = legs;
01191 break;
01192
01193 case foot_armor:
01194 prefered = feet;
01195 break;
01196 }
01197 }
01198 else if (Game::get_game_type() == SERPENT_ISLE)
01199 {
01200 Ready_type_SI type = (Ready_type_SI) info.get_ready_type();
01201
01202 switch (type)
01203 {
01204
01205 default:
01206 if (type == spell_si|| type == other_spell_si) fistype = FIS_Spell;
01207 else if (type == two_handed_si) fistype = FIS_2Hand;
01208
01209 if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), rhand))
01210 prefered = rhand;
01211 else
01212 alternate = rhand;
01213 break;
01214
01215 case other:
01216 if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), back2h_spot))
01217 prefered = back2h_spot;
01218 else if (Paperdoll_gump::IsObjectAllowed (obj->get_shapenum(), obj->get_framenum(), back))
01219 prefered = back;
01220 else
01221 alternate = rhand;
01222 break;
01223
01224
01225 case helm_si:
01226 prefered = head;
01227 break;
01228
01229 case gloves_si:
01230 prefered = hands2_spot;
01231 break;
01232
01233 case boots_si:
01234 prefered = feet;
01235 break;
01236
01237 case leggings_si:
01238 prefered = legs;
01239 break;
01240
01241 case amulet_si:
01242 prefered = neck;
01243 break;
01244
01245 case armour_si:
01246 prefered = torso;
01247 break;
01248
01249 case ring_si:
01250 prefered = lfinger;
01251 alternate = rfinger;
01252 break;
01253
01254 case ammo_si:
01255 prefered = ammo;
01256 break;
01257
01258 case cloak_si:
01259 prefered = cloak_spot;
01260 break;
01261
01262 case usecode_container_si:
01263 prefered = ucont_spot;
01264 break;
01265
01266 case earrings_si:
01267 prefered = ears_spot;
01268 break;
01269
01270 case belt_si:
01271 prefered = belt;
01272 break;
01273
01274 case backpack_si:
01275 prefered = back;
01276 break;
01277 }
01278 }
01279 }
01280
01281
01282
01283
01284
01285
01286
01287
01288 int Actor::find_best_spot
01289 (
01290 Game_object *obj
01291 )
01292 {
01293 int prefered;
01294 int alternate;
01295 FIS_Type type;
01296 bool SI = Game::get_game_type() == SERPENT_ISLE;
01297
01298
01299 get_prefered_slots (obj, prefered, alternate, type);
01300
01301
01302 if (fits_in_spot (obj, prefered, type)) return prefered;
01303
01304 else if (fits_in_spot (obj, alternate, type)) return alternate;
01305
01306 else if (fits_in_spot (obj, belt, type)) return belt;
01307
01308 else if (fits_in_spot (obj, back, type)) return back;
01309
01310 else if (SI && fits_in_spot (obj, back2h_spot, type)) return back2h_spot;
01311
01312 else if (SI && fits_in_spot (obj, shield_spot, type)) return shield_spot;
01313
01314 else if (fits_in_spot (obj, lhand, type)) return lhand;
01315
01316 else if (fits_in_spot (obj, rhand, type)) return rhand;
01317
01318 return -1;
01319 }
01320
01321
01322
01323
01324
01325
01326
01327 int Actor::get_prev_schedule_type
01328 (
01329 )
01330 {
01331 return schedule ? schedule->get_prev_type() : -1;
01332 }
01333
01334
01335
01336
01337
01338
01339
01340 void Actor::restore_schedule
01341 (
01342 )
01343 {
01344
01345 Map_chunk *olist = gmap->get_chunk_safely(get_cx(), get_cy());
01346
01347 if (olist && party_id < 0)
01348 {
01349 if (next_schedule != 255 &&
01350 schedule_type == Schedule::walk_to_schedule)
01351 set_schedule_and_loc(next_schedule, schedule_loc);
01352 else
01353 set_schedule_type(schedule_type);
01354 }
01355 }
01356
01357
01358
01359
01360
01361 void Actor::set_schedule_type
01362 (
01363 int new_schedule_type,
01364 Schedule *newsched
01365 )
01366 {
01367 stop();
01368 if (schedule)
01369 schedule->ending(new_schedule_type);
01370 set_action(0);
01371
01372 Schedule::Schedule_types old_schedule = (Schedule::Schedule_types)
01373 schedule_type;
01374 delete schedule;
01375 schedule = newsched;
01376 if (!schedule)
01377 switch ((Schedule::Schedule_types) new_schedule_type)
01378 {
01379 case Schedule::combat:
01380 schedule = new Combat_schedule(this, old_schedule);
01381 break;
01382 case Schedule::horiz_pace:
01383 schedule = Pace_schedule::create_horiz(this);
01384 break;
01385 case Schedule::vert_pace:
01386 schedule = Pace_schedule::create_vert(this);
01387 break;
01388 case Schedule::talk:
01389 schedule = new Talk_schedule(this);
01390 break;
01391 case Schedule::dance:
01392 schedule = new Dance_schedule(this);
01393 break;
01394 case Schedule::farm:
01395 schedule = new Tool_schedule(this, 618);
01396 break;
01397 case Schedule::tend_shop:
01398 schedule = new Loiter_schedule(this, 3);
01399 break;
01400 case Schedule::miner:
01401 schedule = new Tool_schedule(this, 624);
01402 break;
01403 case Schedule::hound:
01404 schedule = new Hound_schedule(this);
01405 break;
01406 case Schedule::loiter:
01407 case Schedule::graze:
01408 schedule = new Loiter_schedule(this);
01409 break;
01410 case Schedule::wander:
01411 schedule = new Wander_schedule(this);
01412 break;
01413 case Schedule::blacksmith:
01414 schedule = new Forge_schedule(this);
01415 break;
01416 case Schedule::sleep:
01417 schedule = new Sleep_schedule(this);
01418 break;
01419 case Schedule::wait:
01420 schedule = new Wait_schedule(this);
01421 break;
01422 case Schedule::eat:
01423 case Schedule::sit:
01424 schedule = new Sit_schedule(this);
01425 break;
01426 case Schedule::bake:
01427 schedule = new Bake_schedule(this);
01428 break;
01429 case Schedule::sew:
01430 schedule = new Sew_schedule(this);
01431 break;
01432 case Schedule::shy:
01433 schedule = new Shy_schedule(this);
01434 break;
01435 case Schedule::lab:
01436 schedule = new Lab_schedule(this);
01437 break;
01438 case Schedule::thief:
01439 gwin->add_dirty(this);
01440 unready_weapon(lhand);
01441 unready_weapon(rhand);
01442 set_frame(get_dir_framenum(0, Actor::standing));
01443 gwin->add_dirty(this);
01444 break;
01445 case Schedule::waiter:
01446 schedule = new Waiter_schedule(this);
01447 break;
01448 case Schedule::kid_games:
01449 schedule = new Kid_games_schedule(this);
01450 break;
01451 case Schedule::eat_at_inn:
01452 schedule = new Eat_at_inn_schedule(this);
01453 break;
01454 case Schedule::duel:
01455 schedule = new Duel_schedule(this);
01456 break;
01457 case Schedule::preach:
01458 schedule = new Preach_schedule(this);
01459 break;
01460 case Schedule::patrol:
01461 schedule = new Patrol_schedule(this);
01462 break;
01463 case Schedule::desk_work:
01464 schedule = new Desk_schedule(this);
01465 break;
01466 case Schedule::follow_avatar:
01467 schedule = new Follow_avatar_schedule(this);
01468 break;
01469 case Schedule::walk_to_schedule:
01470 cerr << "Attempted to set a \"walk to schedule\" activity for NPC "<< get_npc_num() << endl;
01471 break;
01472 default:
01473 break;
01474 }
01475
01476 schedule_type = new_schedule_type;
01477
01478
01479 schedule_loc = Tile_coord(0,0,0);
01480 next_schedule = 255;
01481
01482 if (!gmap->is_chunk_read(get_cx(), get_cy()))
01483 dormant = true;
01484 else if (schedule)
01485 {
01486 dormant = false;
01487 schedule->now_what();
01488 }
01489 }
01490
01491
01492
01493
01494
01495 void Actor::cache_out()
01496 {
01497
01498 if (get_schedule_type() != Schedule::walk_to_schedule)
01499 set_schedule_type(get_schedule_type());
01500 }
01501
01502
01503
01504
01505
01506
01507 void Actor::set_schedule_and_loc (int new_schedule_type, Tile_coord dest,
01508 int delay)
01509 {
01510 stop();
01511 if (schedule)
01512 schedule->ending(new_schedule_type);
01513
01514 if (!gmap->is_chunk_read(get_cx(), get_cy()) &&
01515 !gmap->is_chunk_read(dest.tx/c_tiles_per_chunk,
01516 dest.ty/c_tiles_per_chunk))
01517 {
01518 move(dest.tx, dest.ty, dest.tz);
01519 set_schedule_type(new_schedule_type);
01520 return;
01521 }
01522
01523 schedule_loc = dest;
01524 next_schedule = new_schedule_type;
01525 schedule_type = Schedule::walk_to_schedule;
01526 delete schedule;
01527 schedule = new Walk_to_schedule(this, dest, next_schedule, delay);
01528 dormant = false;
01529 schedule->now_what();
01530 }
01531
01532
01533
01534
01535
01536 void Actor::paint
01537 (
01538 )
01539 {
01540
01541 if (!(flags & (1L << Obj_flags::dont_move)) ||
01542 Game::get_game_type() == SERPENT_ISLE)
01543 {
01544 int xoff, yoff;
01545 gwin->get_shape_location(this, xoff, yoff);
01546 if (flags & (1L << Obj_flags::invisible))
01547 paint_invisible(xoff, yoff);
01548 else
01549 paint_shape(xoff, yoff, true);
01550
01551 paint_weapon();
01552 if (hit)
01553 ShapeID::paint_outline(xoff, yoff, HIT_PIXEL);
01554 else if (flags & ((1L<<Obj_flags::protection) |
01555 (1L << Obj_flags::poisoned) | (1 << Obj_flags::cursed)))
01556 {
01557 if (flags & (1L << Obj_flags::poisoned))
01558 ShapeID::paint_outline(xoff,yoff,POISON_PIXEL);
01559 else if (flags & (1L << Obj_flags::cursed))
01560 ShapeID::paint_outline(xoff,yoff,CURSED_PIXEL);
01561 else
01562 ShapeID::paint_outline(xoff, yoff,
01563 PROTECT_PIXEL);
01564 }
01565 }
01566 }
01567
01568
01569
01570 void Actor::paint_weapon
01571 (
01572 )
01573 {
01574 int weapon_x, weapon_y, weapon_frame;
01575 if (figure_weapon_pos(weapon_x, weapon_y, weapon_frame))
01576 {
01577 Game_object * weapon = spots[lhand];
01578 int shnum = weapon->get_shapenum();
01579 ShapeID wsid(shnum, weapon_frame);
01580 Shape_frame *wshape = wsid.get_shape();
01581 if (!wshape)
01582 {
01583 weapon_rect.w = 0;
01584 return;
01585 }
01586
01587 weapon_rect = gwin->get_shape_rect(wshape, weapon_x, weapon_y);
01588
01589 int xoff, yoff;
01590 gwin->get_shape_location(this, xoff, yoff);
01591 xoff += weapon_x;
01592 yoff += weapon_y;
01593
01594 if (flags & (1L<<Obj_flags::invisible))
01595 wsid.paint_invisible(xoff, yoff);
01596 else
01597 wsid.paint_shape(xoff, yoff);
01598 }
01599 else
01600 weapon_rect.w = 0;
01601 }
01602
01603
01604
01605
01606
01607
01608
01609
01610
01611
01612
01613
01614
01615
01616
01617 int Actor::figure_weapon_pos
01618 (
01619 int& weapon_x, int& weapon_y,
01620 int& weapon_frame
01621 )
01622 {
01623 unsigned char actor_x, actor_y;
01624 unsigned char wx, wy;
01625 Game_object * weapon = spots[lhand];
01626 if(weapon == 0)
01627 return 0;
01628
01629 int myframe = get_framenum();
01630 get_info().get_weapon_offset(myframe & 0x1f, actor_x,
01631 actor_y);
01632
01633
01634
01635 weapon_frame = 1;
01636 int baseframe = myframe&0xf;
01637 if (gwin->in_combat() && Is_attack_frame(baseframe))
01638 {
01639 int dir = Get_dir_from_frame(myframe);
01640 if (dir < 3)
01641 weapon_frame = 2 + dir;
01642 else
01643 weapon_frame = 2 | 32;
01644 }
01645 weapon->get_info().get_weapon_offset(weapon_frame&0xf, wx,
01646 wy);
01647
01648
01649 if(actor_x != 255 && wx != 255)
01650 {
01651 weapon_x = wx - actor_x;
01652 weapon_y = wy - actor_y;
01653
01654 if((get_framenum() & 32))
01655 {
01656 swap(weapon_x, weapon_y);
01657
01658 if (weapon_frame == 1)
01659 weapon_frame |= 32;
01660 }
01661 #if 0
01662
01663 int nframes = gwin->get_shape_num_frames(
01664 weapon->get_shapenum());
01665 if ((weapon_frame&31) >= nframes)
01666 weapon_frame = (nframes - 1)|(weapon_frame&32);
01667 #endif
01668 return 1;
01669 }
01670 else
01671 return 0;
01672 }
01673
01674
01675
01676
01677 void Actor::activate
01678 (
01679 int event
01680 )
01681 {
01682 if (edit())
01683 return;
01684
01685 bool serpent = Game::get_game_type()==SERPENT_ISLE||
01686 (sman->can_use_paperdolls() && sman->get_bg_paperdolls());
01687
01688 bool show_party_inv = gumpman->showing_gumps(true) ||
01689 gwin->in_combat();
01690 Schedule::Schedule_types sched =
01691 (Schedule::Schedule_types) get_schedule_type();
01692 if (!npc_num ||
01693 (show_party_inv && party_id >= 0 &&
01694 (serpent || (npc_num >= 1 && npc_num <= 10))) ||
01695
01696 (cheat.in_pickpocket() && event == 1))
01697 show_inventory();
01698
01699 else if ((sched == Schedule::sleep &&
01700 (get_framenum()&0xf) == Actor::sleep_frame) ||
01701 get_flag(Obj_flags::asleep))
01702 return;
01703 else if (sched == Schedule::combat && party_id < 0)
01704 return;
01705
01706
01707 else if (serpent &&
01708 gwin->get_main_actor()->get_flag(Obj_flags::confused))
01709 ucmachine->call_usecode(0x63d, this,
01710 (Usecode_machine::Usecode_events) event);
01711 else if (usecode == -1)
01712 ucmachine->call_usecode(get_shapenum(), this,
01713 (Usecode_machine::Usecode_events) event);
01714 else if (party_id >= 0 || !gwin->is_time_stopped())
01715 ucmachine->call_usecode(usecode, this,
01716 (Usecode_machine::Usecode_events) event);
01717
01718 }
01719
01720
01721
01722
01723
01724
01725
01726 bool Actor::edit
01727 (
01728 )
01729 {
01730 #ifdef USE_EXULTSTUDIO
01731 if (client_socket >= 0 &&
01732 cheat.in_map_editor())
01733 {
01734 editing = 0;
01735 Tile_coord t = get_tile();
01736 unsigned long addr = reinterpret_cast<unsigned long>(this);
01737 int num_schedules;
01738 Schedule_change *changes;
01739 get_schedules(changes, num_schedules);
01740 Serial_schedule schedules[8];
01741 for (int i = 0; i < num_schedules; i++)
01742 {
01743 schedules[i].time = changes[i].get_time();
01744 schedules[i].type = changes[i].get_type();
01745 Tile_coord p = changes[i].get_pos();
01746 schedules[i].tx = p.tx;
01747 schedules[i].ty = p.ty;
01748 }
01749 if (Npc_actor_out(client_socket, addr, t.tx, t.ty, t.tz,
01750 get_shapenum(), get_framenum(), get_face_shapenum(),
01751 name, npc_num, ident, usecode, properties, attack_mode,
01752 alignment, flags, siflags, type_flags,
01753 num_schedules, schedules) != -1)
01754 {
01755 cout << "Sent npc data to ExultStudio" << endl;
01756 editing = this;
01757 }
01758 else
01759 cout << "Error sending npc data to ExultStudio" <<endl;
01760 return true;
01761 }
01762 #endif
01763 return false;
01764 }
01765
01766
01767
01768
01769
01770 void Actor::update_from_studio
01771 (
01772 unsigned char *data,
01773 int datalen
01774 )
01775 {
01776 #ifdef USE_EXULTSTUDIO
01777 unsigned long addr;
01778 int tx, ty, tz;
01779 int shape, frame, face;
01780 std::string name;
01781 short npc_num, ident;
01782 int usecode;
01783 int properties[12];
01784 short attack_mode, alignment;
01785 unsigned long oflags;
01786 unsigned long siflags;
01787 unsigned long type_flags;
01788 short num_schedules;
01789 Serial_schedule schedules[8];
01790 if (!Npc_actor_in(data, datalen, addr, tx, ty, tz, shape, frame,
01791 face, name, npc_num, ident, usecode,
01792 properties, attack_mode, alignment,
01793 oflags, siflags, type_flags, num_schedules, schedules))
01794 {
01795 cout << "Error decoding npc" << endl;
01796 return;
01797 }
01798 Actor *npc = (Actor *) addr;
01799 if (npc && npc != editing)
01800 {
01801 cout << "Npc from ExultStudio is not being edited" << endl;
01802 return;
01803 }
01804 editing = 0;
01805 if (!npc)
01806 {
01807 int x, y;
01808 if (!Get_click(x, y, Mouse::hand, 0))
01809 {
01810 if (client_socket >= 0)
01811 Exult_server::Send_data(client_socket, Exult_server::cancel);
01812 return;
01813 }
01814
01815 npc = new Npc_actor(name, shape, frame, usecode);
01816 npc->set_invalid();
01817 int lift;
01818 for (lift = 0; lift < 12; lift++)
01819 if (gwin->drop_at_lift(npc, x, y, lift))
01820 break;
01821 if (lift == 12)
01822 {
01823 if (client_socket >= 0)
01824 Exult_server::Send_data(client_socket, Exult_server::cancel);
01825 delete npc;
01826 return;
01827 }
01828 npc->npc_num = npc_num;
01829 gwin->add_npc(npc, npc_num);
01830 if (client_socket >= 0)
01831 Exult_server::Send_data(client_socket, Exult_server::user_responded);
01832 }
01833 else
01834 {
01835 npc->add_dirty();
01836 npc->set_shape(shape, frame);
01837 npc->add_dirty();
01838 npc->usecode = usecode;
01839 npc->usecode_assigned = true;
01840 npc->set_npc_name(name.c_str());
01841 }
01842 npc->face_num = face;
01843 npc->set_ident(ident);
01844 int i;
01845 for (i = 0; i < 12; i++)
01846 npc->set_property(i, properties[i]);
01847 npc->set_attack_mode((Actor::Attack_mode) attack_mode);
01848 npc->set_alignment(alignment);
01849 npc->flags = oflags;
01850 npc->siflags = siflags;
01851 npc->type_flags = type_flags;
01852 Schedule_change *scheds = num_schedules ?
01853 new Schedule_change[num_schedules] : 0;
01854 for (i = 0; i < num_schedules; i++)
01855 scheds[i].set(schedules[i].tx, schedules[i].ty,
01856 schedules[i].type, schedules[i].time);
01857 npc->set_schedules(scheds, num_schedules);
01858 cout << "Npc updated" << endl;
01859 #endif
01860 }
01861
01862
01863 void Actor::show_inventory()
01864 {
01865 Gump_manager *gump_man = gumpman;
01866
01867 int shapenum = inventory_shapenum();
01868 if (shapenum)
01869 gump_man->add_gump(this, shapenum);
01870 }
01871
01872 int Actor::inventory_shapenum()
01873 {
01874
01875 bool serpent = Game::get_game_type()==SERPENT_ISLE||(sman->can_use_paperdolls() && sman->get_bg_paperdolls());
01876
01877 if (!npc_num && !serpent && get_type_flag(tf_sex))
01878 return (ACTOR_FIRST_GUMP+1);
01879 else if (!npc_num && !serpent)
01880 return (ACTOR_FIRST_GUMP);
01881 else if (!npc_num && serpent)
01882 return (123);
01883
01884
01885 else if (party_id >= 0 &&
01886 npc_num >= 1 && npc_num <= 10 && !serpent)
01887 return (ACTOR_FIRST_GUMP + 1 + npc_num);
01888
01889 else if (party_id >= 0 && serpent)
01890 return (123);
01891
01892 else if (!serpent && Paperdoll_gump::IsNPCFemale(this->get_shapenum()))
01893 return (66);
01894
01895 else if (!serpent && !Paperdoll_gump::IsNPCFemale(this->get_shapenum()))
01896 return (65);
01897
01898 else
01899 return (123);
01900 }
01901
01902
01903
01904
01905
01906
01907
01908
01909 int Actor::drop
01910 (
01911 Game_object *obj
01912 )
01913 {
01914 if (is_in_party())
01915 return (add(obj, false, true));
01916 else
01917 return 0;
01918 }
01919
01920
01921
01922
01923
01924 string Actor::get_name
01925 (
01926 ) const
01927 {
01928 return !get_flag(Obj_flags::met)?Game_object::get_name():get_npc_name();
01929 }
01930
01931
01932
01933
01934
01935 string Actor::get_npc_name
01936 (
01937 ) const
01938 {
01939 return name.empty() ? Game_object::get_name() : name;
01940 }
01941
01942
01943
01944
01945
01946 void Actor::set_npc_name(const char *n)
01947 {
01948 name = n;
01949 }
01950
01951
01952
01953
01954 void Actor::set_property
01955 (
01956 int prop,
01957 int val
01958 )
01959 {
01960 if (prop == health && ((party_id != -1) || (npc_num == 0)) &&
01961 cheat.in_god_mode() && val < properties[prop])
01962 return;
01963 switch (static_cast<Item_properties>(prop))
01964 {
01965 case exp:
01966 {
01967 int old_level = get_level();
01968 properties[static_cast<int>(exp)] = val;
01969 int delta = get_level() - old_level;
01970 if (delta > 0)
01971 properties[static_cast<int>(training)] += 3*delta;
01972 break;
01973 }
01974 case food_level:
01975 if (val > 36)
01976 val = 36;
01977 else if (val < 0)
01978 val = 0;
01979 properties[prop] = val;
01980 break;
01981 case combat:
01982 case magic:
01983 properties[prop] = val > 30 ? 30 : val;
01984 break;
01985 case training:
01986 properties[prop] = val < 0 ? 0 : val;
01987 break;
01988 default:
01989 if (prop >= 0 && prop < 12)
01990 properties[prop] = val;
01991 break;
01992 }
01993 if (gumpman->showing_gumps())
01994 gwin->set_all_dirty();
01995 }
01996
01997
01998
01999
02000 class Clear_hit : public Time_sensitive
02001 {
02002 public:
02003 Clear_hit()
02004 { }
02005 virtual void handle_event(unsigned long curtime, long udata);
02006 };
02007 void Clear_hit::handle_event(unsigned long curtime, long udata)
02008 {
02009 Actor *a = reinterpret_cast<Actor*>(udata);
02010 a->hit = false;
02011 a->add_dirty();
02012 delete this;
02013 }
02014
02015
02016
02017
02018
02019
02020
02021 bool Actor::reduce_health
02022 (
02023 int delta,
02024 Actor *attacker
02025 )
02026 {
02027 if (cheat.in_god_mode() && ((party_id != -1) || (npc_num == 0)))
02028 return false;
02029 Monster_info *minf = get_info().get_monster_info();
02030 if (minf && minf->cant_die())
02031 return false;
02032
02033 if (npc_num > 0 && Game::get_game_type() == BLACK_GATE &&
02034 get_info().has_translucency() &&
02035 party_id < 0)
02036 return false;
02037
02038 if (attacker && attacker->is_in_party() && GAME_BG &&
02039 npc_num > 0 &&
02040 (alignment == Actor::friendly || alignment == Actor::neutral) &&
02041 get_info().get_shape_class() == Shape_info::human)
02042 {
02043 static long lastcall = 0L;
02044 long curtime = SDL_GetTicks();
02045 long delta = curtime - lastcall;
02046 if (delta > 10000)
02047 {
02048 eman->remove_text_effect(this);
02049 say(first_call_police, last_call_police);
02050 lastcall = curtime;
02051 gwin->attack_avatar(1 + rand()%2);
02052 }
02053 else if (rand()%4 == 0)
02054 {
02055 cout << "Rand()%4" << endl;
02056 gwin->attack_avatar(1 + rand()%2);
02057 }
02058 }
02059 bool defeated = false;
02060 int oldhp = properties[static_cast<int>(health)];
02061 int maxhp = properties[static_cast<int>(strength)];
02062 int val = oldhp - delta;
02063 properties[static_cast<int>(health)] = val;
02064 if (this == gwin->get_main_actor() && val < maxhp/8 &&
02065
02066 rand()%2)
02067 gwin->get_pal()->flash_red();
02068 else
02069 {
02070 hit = true;
02071 add_dirty();
02072 Clear_hit *c = new Clear_hit();
02073 gwin->get_tqueue()->add(Game::get_ticks() + 200, c,
02074 reinterpret_cast<long>(this));
02075 }
02076 if (oldhp >= maxhp/2 && val < maxhp/2 && rand()%2 != 0)
02077 {
02078
02079 if (GAME_SI &&
02080 (get_shapenum() == 0x1de ||
02081 get_shapenum() == 0x2b3 ||
02082 get_shapenum() == 0x2d5 ||
02083 get_shapenum() == 0x2e8))
02084 say(0x4d2, 0x4da);
02085 else if (!minf || !minf->cant_yell())
02086 say(first_ouch, last_ouch);
02087 }
02088 Game_object_vector vec;
02089 const int blood = 912;
02090 if (delta >= 3 && (!minf || !minf->cant_bleed()) &&
02091 rand()%2 && find_nearby(vec, blood, 1, 0) < 2)
02092 {
02093 Game_object *bobj = gmap->create_ireg_object(blood, 0);
02094 bobj->set_flag(Obj_flags::is_temporary);
02095 bobj->move(get_tile());
02096 }
02097 if (Actor::is_dying())
02098 {
02099 if (Game::get_game_type() == SERPENT_ISLE &&
02100
02101 get_flag(Obj_flags::si_tournament))
02102 {
02103 ucmachine->call_usecode(get_usecode(), this,
02104 Usecode_machine::died);
02105
02106 if (!is_dead() && get_flag(Obj_flags::si_tournament) &&
02107 get_property(static_cast<int>(health)) < 1)
02108 {
02109 set_property(static_cast<int>(health), 1);
02110 if (get_attack_mode() == Actor::flee)
02111 defeated = true;
02112 }
02113 }
02114 else
02115 die(attacker);
02116 defeated = defeated || is_dead();
02117 }
02118 else if (val < 0 && !get_flag(Obj_flags::asleep) &&
02119 !get_flag(Obj_flags::si_tournament))
02120 set_flag(Obj_flags::asleep);
02121 return defeated;
02122 }
02123
02124
02125
02126
02127
02128 int Actor::get_effective_prop
02129 (
02130 int prop
02131 ) const
02132 {
02133 int val = properties[prop];
02134 switch (prop)
02135 {
02136 case Actor::dexterity:
02137 case Actor::intelligence:
02138 case Actor::combat:
02139 case Actor::strength:
02140 if (get_flag(Obj_flags::might))
02141 val += (val < 15 ? val : 15);
02142 if (get_flag(Obj_flags::cursed))
02143 val /= 2;
02144 break;
02145 }
02146 return val;
02147 }
02148
02149
02150
02151
02152
02153 void Actor::set_flag
02154 (
02155 int flag
02156 )
02157 {
02158
02159
02160 if (flag >= 0 && flag < 32)
02161 flags |= ((uint32) 1 << flag);
02162 else if (flag >= 32 && flag < 64)
02163 flags2 |= ((uint32) 1 << (flag-32));
02164 switch (flag)
02165 {
02166 case Obj_flags::asleep:
02167
02168
02169 if (schedule_type == Schedule::sleep)
02170 break;
02171
02172 need_timers()->start_sleep();
02173 if ((get_framenum()&0xf) != Actor::sleep_frame &&
02174
02175 !get_info().has_strange_movement() &&
02176 get_shapenum() > 0)
02177
02178 change_frame(Actor::sleep_frame + ((rand()%4)<<4));
02179 set_action(0);
02180 break;
02181 case Obj_flags::poisoned:
02182 need_timers()->start_poison();
02183 break;
02184 case Obj_flags::protection:
02185 need_timers()->start_protection();
02186 break;
02187 case Obj_flags::might:
02188 need_timers()->start_might();
02189 break;
02190 case Obj_flags::cursed:
02191 need_timers()->start_curse();
02192 break;
02193 case Obj_flags::paralyzed:
02194 need_timers()->start_paralyze();
02195 break;
02196 case Obj_flags::invisible:
02197 need_timers()->start_invisibility();
02198 gclock->set_palette();
02199 break;
02200 case Obj_flags::dont_move:
02201 stop();
02202 break;
02203 }
02204
02205 if (gumpman->showing_gumps())
02206 gwin->set_all_dirty();
02207 set_actor_shape();
02208 }
02209
02210 void Actor::set_siflag
02211 (
02212 int flag
02213 )
02214 {
02215 if (flag >= 0 && flag < 32)
02216 siflags |= (static_cast<uint32>(1) << flag);
02217
02218 set_actor_shape();
02219 }
02220
02221 void Actor::set_type_flag
02222 (
02223 int flag
02224 )
02225 {
02226 if (flag >= 0 && flag < 16)
02227 type_flags |= (static_cast<uint32>(1) << flag);
02228
02229 set_actor_shape();
02230 }
02231
02232
02233
02234
02235
02236 void Actor::clear_flag
02237 (
02238 int flag
02239 )
02240 {
02241
02242 if (flag >= 0 && flag < 32)
02243 flags &= ~(static_cast<uint32>(1) << flag);
02244 else if (flag >= 32 && flag < 64)
02245 flags2 &= ~(static_cast<uint32>(1) << (flag-32));
02246 if (flag == Obj_flags::invisible)
02247 gclock->set_palette();
02248 else if (flag == Obj_flags::asleep)
02249 {
02250 if (schedule_type == Schedule::sleep)
02251 set_schedule_type(Schedule::stand);
02252 else if ((get_framenum()&0xf) == Actor::sleep_frame)
02253 {
02254 Tile_coord pos = get_tile();
02255 pos.tz -= pos.tz%5;
02256 pos = Map_chunk::find_spot(pos, 6, get_shapenum(),
02257 Actor::standing, 0);
02258 if (pos.tx >= 0)
02259 move(pos);
02260 change_frame(Actor::standing);
02261 }
02262 }
02263 set_actor_shape();
02264 }
02265
02266 void Actor::clear_siflag
02267 (
02268 int flag
02269 )
02270 {
02271 if (flag >= 0 && flag < 32)
02272 siflags &= ~(static_cast<uint32>(1) << flag);
02273
02274 set_actor_shape();
02275 }
02276
02277 void Actor::clear_type_flag
02278 (
02279 int flag
02280 )
02281 {
02282 if (flag >= 0 && flag < 16)
02283 type_flags &= ~(static_cast<uint32>(1) << flag);
02284
02285 set_actor_shape();
02286 }
02287
02288
02289
02290
02291
02292 int Actor::get_siflag
02293 (
02294 int flag
02295 ) const
02296 {
02297 return (flag >= 0 && flag < 32) ? (siflags & (static_cast<uint32>(1) << flag))
02298 != 0 : 0;
02299 }
02300
02301 int Actor::get_type_flag
02302 (
02303 int flag
02304 ) const
02305 {
02306 return (flag >= 0 && flag < 16) ? (type_flags & (static_cast<uint32>(1) << flag))
02307 != 0 : 0;
02308 }
02309
02310
02311
02312
02313 void Actor::set_type_flags
02314 (
02315 unsigned short tflags
02316 )
02317 {
02318 type_flags = tflags;
02319 set_actor_shape();
02320 }
02321
02322
02323
02324
02325
02326 void Actor::set_temperature
02327 (
02328 int v
02329 )
02330 {
02331 if (v < 0)
02332 v = 0;
02333 else if (v > 63)
02334 v = 63;
02335 temperature = v;
02336 }
02337
02338
02339
02340
02341
02342
02343 int Actor::figure_warmth
02344 (
02345 )
02346 {
02347 static short hats[5] = {10, 35, -8, 20, 35};
02348 static short cloaks[5] = {30, 70, 70, 70, 70};
02349 static short boots[7] = {85, 65, 50, 90, 85, 75, 80};
02350
02351 int warmth = -75;
02352 int frnum;
02353 Game_object *worn = spots[static_cast<int>(head)];
02354 if (worn && worn->get_shapenum() == 1004 &&
02355 (frnum = worn->get_framenum()) < sizeof(hats)/sizeof(hats[0]))
02356 warmth += hats[frnum];
02357 if (worn && worn->get_shapenum() == 1013)
02358 warmth += hats[1];
02359 worn = spots[static_cast<int>(cloak_spot)];
02360 if (worn && worn->get_shapenum() == 227 &&
02361 (frnum = worn->get_framenum()) < sizeof(cloaks)/sizeof(cloaks[0]))
02362 warmth += cloaks[frnum];
02363 worn = spots[static_cast<int>(feet)];
02364 if (worn && worn->get_shapenum() == 587 &&
02365 (frnum = worn->get_framenum()) < sizeof(boots)/sizeof(boots[0]))
02366 warmth += boots[frnum];
02367
02368 worn = spots[static_cast<int>(torso)];
02369 if (worn && worn->get_shapenum() == 569)
02370 warmth += 20;
02371 worn = spots[static_cast<int>(hands2_spot)];
02372 if (worn && worn->get_shapenum() == 579)
02373 warmth += 7;
02374 worn = spots[static_cast<int>(legs)];
02375 if (worn)
02376 switch (worn->get_shapenum())
02377 {
02378 case 686:
02379 warmth += 5; break;
02380 case 574:
02381 warmth += 10; break;
02382 }
02383 return warmth;
02384 }
02385
02386
02387
02388
02389
02390
02391
02392 int Actor::get_max_weight
02393 (
02394 )
02395 {
02396 return 2*get_effective_prop(static_cast<int>(Actor::strength));
02397 }
02398
02399
02400
02401
02402
02403 void Actor::call_readied_usecode
02404 (
02405 int index,
02406 Game_object *obj,
02407 int eventid
02408 )
02409 {
02410
02411 if (Game::get_game_type() == BLACK_GATE)
02412 switch (obj->get_shapenum())
02413 {
02414 case 297:
02415 if (eventid == Usecode_machine::readied)
02416 Actor::set_flag(Obj_flags::protection);
02417 else
02418 Actor::clear_flag(Obj_flags::protection);
02419 return;
02420 case 296:
02421 case 298:
02422 case 701:
02423 case 338:
02424 break;
02425 default:
02426 return;
02427 }
02428 else if (Game::get_game_type() == SERPENT_ISLE)
02429 switch (obj->get_shapenum())
02430 {
02431 case 209:
02432 case 296:
02433 case 701:
02434 case 338:
02435 case 806:
02436 case 990:
02437 case 996:
02438 case 1001:
02439 case 1013:
02440 break;
02441 default:
02442 return;
02443 }
02444
02445 Shape_info& info = obj->get_info();
02446 if (info.get_shape_class() != Shape_info::container)
02447 {
02448 Ready_type type = (Ready_type) info.get_ready_type();
02449 if (type != other)
02450 ucmachine->call_usecode(obj->get_shapenum(),
02451 obj, (Usecode_machine::Usecode_events) eventid);
02452 }
02453 }
02454
02455
02456
02457
02458
02459 void Actor::init_readied
02460 (
02461 )
02462 {
02463 if (spots[lfinger])
02464 call_readied_usecode(lfinger, spots[lfinger],
02465 Usecode_machine::readied);
02466 if (spots[rfinger])
02467 call_readied_usecode(rfinger, spots[rfinger],
02468 Usecode_machine::readied);
02469 if (spots[belt])
02470 call_readied_usecode(belt, spots[belt],
02471 Usecode_machine::readied);
02472 if (spots[neck])
02473 call_readied_usecode(neck, spots[neck],
02474 Usecode_machine::readied);
02475 if (spots[head])
02476 call_readied_usecode(head, spots[head],
02477 Usecode_machine::readied);
02478 if (spots[hands2_spot])
02479 call_readied_usecode(hands2_spot,
02480 spots[hands2_spot], Usecode_machine::readied);
02481 if (spots[lhand])
02482 call_readied_usecode(lhand, spots[lhand],
02483 Usecode_machine::readied);
02484 if (spots[rhand])
02485 call_readied_usecode(rhand, spots[rhand],
02486 Usecode_machine::readied);
02487 }
02488
02489
02490
02491
02492
02493 void Actor::remove
02494 (
02495 Game_object *obj
02496 )
02497 {
02498 int index = Actor::find_readied(obj);
02499
02500
02501
02502
02503 if (!ucmachine->in_usecode() && !is_dead())
02504 call_readied_usecode(index, obj,
02505 Usecode_machine::unreadied);
02506 Container_game_object::remove(obj);
02507 if (index >= 0)
02508 {
02509 if (obj->get_info().is_light_source())
02510 light_sources--;
02511 spots[index] = 0;
02512 if (index == rhand || index == lhand)
02513 two_handed = false;
02514 if (index == rfinger || index == lfinger)
02515 two_fingered = false;
02516 if (index == lhand && schedule)
02517 schedule->set_weapon();
02518 }
02519 }
02520
02521
02522
02523
02524
02525
02526
02527
02528 bool Actor::add
02529 (
02530 Game_object *obj,
02531 bool dont_check,
02532
02533 bool combine
02534
02535 )
02536 {
02537
02538 int index, a;
02539 FIS_Type type;
02540 get_prefered_slots (obj, index, a, type);
02541 index = find_best_spot(obj);
02542
02543 if (index < 0)
02544 {
02545 if (spots[back] && spots[back]->add(obj, false, combine))
02546 return true;
02547 if (spots[belt] && spots[belt]->add(obj, false, combine))
02548 return true;
02549 if (spots[lhand] && spots[lhand]->add(obj, false, combine))
02550 return true;
02551 if (spots[rhand] && spots[rhand]->add(obj, false, combine))
02552 return true;
02553 if (!dont_check)
02554 return false;
02555
02556
02557 if (spots[back] && spots[back]->add(obj, true, combine))
02558 return true;
02559 if (spots[belt] && spots[belt]->add(obj, true, combine))
02560 return true;
02561 if (spots[lhand] && spots[lhand]->add(obj, true, combine))
02562 return true;
02563 if (spots[rhand] && spots[rhand]->add(obj, true, combine))
02564 return true;
02565
02566 if (party_id != -1 || npc_num==0) {
02567 CERR("Warning: adding object (" << obj << ", sh. " << obj->get_shapenum() << ", " << obj->get_name() << ") to actor container (npc " << npc_num << ")");
02568 }
02569 return Container_game_object::add(obj, dont_check, combine);
02570 }
02571
02572 if (!Container_game_object::add(obj, true))
02573 return false;
02574
02575 if (type == FIS_2Hand)
02576 two_handed = true;
02577 if (type == FIS_2Finger) {
02578 index = lfinger;
02579 two_fingered = true;
02580 }
02581
02582 spots[index] = obj;
02583 if (index == lhand && schedule)
02584 schedule->set_weapon();
02585 obj->set_chunk(0, 0);
02586
02587 if (obj->get_info().is_light_source())
02588 light_sources++;
02589 return true;
02590 }
02591
02592
02593
02594
02595
02596
02597
02598 int Actor::add_readied
02599 (
02600 Game_object *obj,
02601 int index,
02602 int dont_check,
02603 int force_pos
02604 )
02605 {
02606
02607 if (index < 0 || index >= static_cast<int>(sizeof(spots)/sizeof(spots[0])))
02608 return (0);
02609
02610
02611
02612
02613 if (spots[index]) return (spots[index]->add(obj));
02614
02615 int prefered;
02616 int alternate;
02617 FIS_Type type;
02618
02619
02620 get_prefered_slots (obj, prefered, alternate, type);
02621
02622
02623 if (!fits_in_spot (obj, index, type) && !force_pos) return 0;
02624
02625
02626 if (!Container_game_object::add(obj, 1)) return 0;
02627
02628
02629 spots[index] = obj;
02630
02631
02632 obj->set_chunk(0, 0);
02633
02634
02635 if (type == FIS_2Hand && index == lhand) two_handed = true;
02636
02637
02638 if (type == FIS_2Finger && index == lfinger) two_fingered = true;
02639
02640
02641
02642
02643
02644
02645
02646 if (obj->get_info().is_light_source()) light_sources++;
02647
02648 if (index == lhand && schedule)
02649 schedule->set_weapon();
02650 return 1;
02651 }
02652
02653
02654
02655
02656
02657
02658
02659 int Actor::find_readied
02660 (
02661 Game_object *obj
02662 )
02663 {
02664 for (size_t i = 0; i < sizeof(spots)/sizeof(spots[0]); i++)
02665 if (spots[i] == obj)
02666 return (i);
02667 return (-1);
02668 }
02669
02670
02671
02672
02673
02674 void Actor::change_member_shape
02675 (
02676 Game_object *obj,
02677 int newshape
02678 )
02679 {
02680 if (obj->get_info().is_light_source())
02681 light_sources--;
02682 Container_game_object::change_member_shape(obj, newshape);
02683 if (obj->get_info().is_light_source())
02684 light_sources++;
02685 }
02686
02687
02688
02689
02690
02691
02692
02693 int Actor::move_aside
02694 (
02695 Actor *for_actor,
02696 int dir
02697 )
02698 {
02699 Tile_coord cur = get_tile();
02700 int opp = (dir + 4)%8;
02701 Tile_coord to(-1, -1, -1);
02702 int i;
02703 for (i = 0; i < 8; i++)
02704 if (i == dir || i == opp)
02705 continue;
02706 else
02707 {
02708 to = cur.get_neighbor(i);
02709
02710 if (!Map_chunk::is_blocked(
02711 to, 3, get_type_flags()))
02712 break;
02713 }
02714 int stepdir = i;
02715 if (i == 8 || to.tx < 0)
02716 return swap_positions(for_actor);
02717
02718 step(to, get_dir_framenum(stepdir,static_cast<int>(Actor::standing)));
02719 Tile_coord newpos = get_tile();
02720 return (newpos.tx == to.tx && newpos.ty == to.ty);
02721 }
02722
02723
02724
02725
02726
02727 int Actor::get_rotated_frame
02728 (
02729 int quads
02730 )
02731 {
02732 int curframe = get_framenum();
02733
02734 int curdir = (4 + 2*((curframe>>4)&1) - ((curframe>>5)&1))%4;
02735 int newdir = (curdir + quads)%4;
02736
02737 return get_dir_framenum(2*newdir, curframe);
02738 }
02739
02740
02741
02742
02743
02744 int Actor::get_armor_points
02745 (
02746 )
02747 {
02748 int points = 0;
02749 static enum Spots aspots[] = {neck, torso, lfinger, rfinger, head,
02750 rhand, legs, feet};
02751 const int num_armor_spots = sizeof(aspots)/sizeof(aspots[0]);
02752 for (int i = 0; i < num_armor_spots; i++)
02753 {
02754 Game_object *armor = spots[static_cast<int>(aspots[i])];
02755 if (armor)
02756 points += armor->get_info().get_armor();
02757 }
02758 return points;
02759 }
02760
02761
02762
02763
02764
02765 Weapon_info *Actor::get_weapon
02766 (
02767 int& points,
02768 int& shape
02769 )
02770 {
02771 points = 1;
02772 Weapon_info *winf = 0;
02773 Game_object *weapon = spots[static_cast<int>(lhand)];
02774 if (weapon)
02775 if ((winf = weapon->get_info().get_weapon_info()) != 0)
02776 {
02777 points = winf->get_damage();
02778 shape = weapon->get_shapenum();
02779 }
02780
02781 weapon = spots[static_cast<int>(rhand)];
02782 if (weapon)
02783 {
02784 Weapon_info *rwinf = weapon->get_info().get_weapon_info();
02785 int rpoints;
02786 if (rwinf && (rpoints = rwinf->get_damage()) > points)
02787 {
02788 winf = rwinf;
02789 points = rpoints;
02790 shape = weapon->get_shapenum();
02791 }
02792 }
02793 return winf;
02794 }
02795
02796
02797
02798
02799
02800
02801 bool Actor::roll_to_win
02802 (
02803 int attacker,
02804 int defender
02805 )
02806 {
02807 const int sides = 25;
02808 int roll = rand()%sides;
02809 if (roll == 0)
02810 return false;
02811 else if (roll == sides - 1)
02812 return true;
02813 else
02814 return roll + attacker - defender >= sides/2;
02815 }
02816
02817
02818
02819
02820
02821 inline int Get_effective_prop
02822 (
02823 Actor *npc,
02824 Actor::Item_properties prop,
02825 int defval = 0
02826 )
02827 {
02828 return npc ? npc->get_effective_prop(prop) : defval;
02829 }
02830
02831
02832
02833
02834
02835
02836
02837 bool Actor::figure_hit_points
02838 (
02839 Actor *attacker,
02840 int weapon_shape,
02841 int ammo_shape
02842 )
02843 {
02844 bool were_party = party_id != -1 || npc_num == 0;
02845
02846 if (were_party && cheat.in_god_mode())
02847 return false;
02848 Monster_info *minf = get_info().get_monster_info();
02849 if (minf && minf->cant_die())
02850 return false;
02851 bool explosion = false;
02852 if (weapon_shape < 0)
02853 { explosion = true; weapon_shape = -weapon_shape; }
02854 bool theyre_party = attacker &&
02855 (attacker->party_id != -1 || attacker->npc_num == 0);
02856 bool instant_death = (cheat.in_god_mode() && theyre_party);
02857
02858 int bias = were_party ? Combat::difficulty :
02859 (theyre_party ? -Combat::difficulty : 0);
02860 int armor = get_armor_points() - bias;
02861 int wpoints;
02862 Weapon_info *winf;
02863 if (weapon_shape > 0)
02864 {
02865 winf = ShapeID::get_info(weapon_shape).get_weapon_info();
02866 wpoints = winf ? winf->get_damage() : 0;
02867 }
02868 else if (ammo_shape > 0)
02869 {
02870 winf = ShapeID::get_info(ammo_shape).get_weapon_info();
02871 wpoints = winf ? winf->get_damage() : 0;
02872 }
02873 else
02874 {
02875 winf = attacker->get_weapon(wpoints, weapon_shape);
02876 if (!wpoints)
02877 wpoints = 1;
02878 }
02879
02880 Ammo_info *ainf = ammo_shape > 0 ?
02881 ShapeID::get_info(ammo_shape).get_ammo_info() : 0;
02882 if (ainf)
02883 wpoints += ainf->get_damage();
02884 int usefun;
02885 if (winf && (usefun = winf->get_usecode()) != 0)
02886 ucmachine->call_usecode(usefun, this,
02887 Usecode_machine::weapon);
02888
02889 if (ammo_shape == 0x238 && Game::get_game_type() == SERPENT_ISLE)
02890
02891 ucmachine->call_usecode(0x7e1, this,
02892 Usecode_machine::weapon);
02893
02894 unsigned char powers = winf ? winf->get_powers() : 0;
02895 if (ainf)
02896 powers |= ainf->get_powers();
02897 if (!explosion && winf && winf->explodes() &&
02898 rand()%4)
02899 eman->add_effect(new Explosion_effect(get_tile() +
02900 Tile_coord(0, 0, get_info().get_3d_height()/2), 0, 0,
02901 weapon_shape));
02902 if (!wpoints && !powers)
02903 return false;
02904
02905 int prob = 55 + 8*bias +
02906 2*Get_effective_prop(attacker, combat, 10) +
02907 Get_effective_prop(attacker, dexterity, 10) -
02908 2*Get_effective_prop(this, combat, 10) -
02909 Get_effective_prop(this, dexterity, 10);
02910 if (get_flag(Obj_flags::protection))
02911 prob -= (40 + rand()%20);
02912 if (prob < 4)
02913 prob = 4;
02914 else if (prob > 96)
02915 prob = 96;
02916
02917 if (GAME_SI && attacker && attacker->npc_num == 294)
02918 {
02919 int pts, sh;
02920 if (get_weapon(pts, sh) && sh == 0xe7)
02921 {
02922 prob -= (70 + rand()%20);
02923 eman->remove_text_effect(attacker);
02924 attacker->say(item_names[0x49b]);
02925 }
02926 }
02927 if (instant_death)
02928 prob = 200;
02929
02930 if (combat_trace) {
02931 cout << "Hit probability is " << prob << endl;
02932 }
02933 if (rand()%100 > prob)
02934 {
02935
02936 if (winf && ammo_shape && attacker &&
02937 ((winf->is_thrown() && !winf->returns()) ||
02938 winf->get_ammo_consumed() > 0))
02939 {
02940 Tile_coord pos = Map_chunk::find_spot(get_tile(), 3,
02941 ammo_shape, 0, 1);
02942 if (pos.tx == -1)
02943 return false;
02944 Game_object *aobj = gmap->create_ireg_object(
02945 ammo_shape, 0);
02946 if (attacker->get_flag( Obj_flags::is_temporary))
02947 aobj->set_flag( Obj_flags::is_temporary);
02948 aobj->set_flag(Obj_flags::okay_to_take);
02949 aobj->move(pos);
02950 }
02951 return false;
02952 }
02953
02954 int attacker_str = Get_effective_prop(attacker, strength, 8);
02955 int hp;
02956 wpoints += 2*bias;
02957 if (wpoints > 0)
02958 {
02959 if (wpoints == 127)
02960 hp = wpoints;
02961 else {
02962 hp = 1 + rand()%wpoints;
02963 if (attacker_str > 2)
02964 hp += 1 + rand()%(attacker_str/3);
02965 if (armor > 0)
02966 hp -= 1 + rand()%armor;
02967 }
02968 if (hp < 1)
02969 hp = 1;
02970 }
02971 else
02972 hp = 0;
02973 if (powers)
02974 {
02975 if ((powers&Weapon_info::poison) && roll_to_win(
02976 Get_effective_prop(attacker, Actor::strength),
02977 Get_effective_prop(this, Actor::dexterity)) &&
02978 !(minf && minf->poison_safe()))
02979 set_flag(Obj_flags::poisoned);
02980 if (powers&Weapon_info::magebane)
02981 {
02982 int mana = properties[static_cast<int>(Actor::mana)];
02983 set_property(static_cast<int>(Actor::mana),
02984 mana > 1 ? rand()%(mana - 1) : 0);
02985
02986 if (npc_num == 294 && GAME_SI)
02987 hp *= 3;
02988 }
02989 if ((powers&Weapon_info::curse) && roll_to_win(
02990 Get_effective_prop(attacker, Actor::intelligence),
02991 Get_effective_prop(this, Actor::intelligence)))
02992 set_flag(Obj_flags::cursed);
02993 if ((powers&Weapon_info::sleep) && roll_to_win(
02994 Get_effective_prop(attacker, Actor::intelligence),
02995 Get_effective_prop(this, Actor::intelligence)))
02996 set_flag(Obj_flags::asleep);
02997 if ((powers&Weapon_info::paralyze) && roll_to_win(
02998 Get_effective_prop(attacker, Actor::intelligence),
02999 Get_effective_prop(this, Actor::intelligence)))
03000 set_flag(Obj_flags::paralyzed);
03001 }
03002 int sfx;
03003 if (winf && (sfx = winf->get_hitsfx()) >= 0 &&
03004
03005 (this == gwin->get_main_actor() ||
03006 attacker == gwin->get_main_actor()))
03007 Audio::get_ptr()->play_sound_effect(sfx);
03008
03009 int oldhealth = properties[static_cast<int>(health)];
03010 int maxhealth = properties[static_cast<int>(strength)];
03011
03012 if (instant_death)
03013 hp = properties[static_cast<int>(health)] +
03014 properties[static_cast<int>(strength)] + 1;
03015 int newhp = oldhealth - hp;
03016
03017 bool defeated = reduce_health(hp, attacker);
03018 if (Combat::show_hits)
03019 {
03020 eman->remove_text_effect(this);
03021 char hpmsg[50];
03022 sprintf(hpmsg, "-%d(%d)", hp, newhp);
03023 eman->add_text(hpmsg, this);
03024 }
03025 string name = "<trap>";
03026 if (attacker)
03027 name = attacker->get_name();
03028
03029 if (combat_trace) {
03030 cout << name << " hits " << get_name()
03031 << " for " << hp << " hit points, leaving "
03032 << properties[static_cast<int>(health)] << " remaining" << endl;
03033 }
03034 if (!defeated && minf && minf->splits() && rand()%2 == 0 &&
03035 properties[static_cast<int>(health)] > 0)
03036 clone();
03037
03038 return defeated;
03039 }
03040
03041
03042
03043
03044
03045
03046
03047 Game_object *Actor::attacked
03048 (
03049 Actor *attacker,
03050 int weapon_shape,
03051
03052 int ammo_shape
03053 )
03054 {
03055 if (is_dead() ||
03056
03057 (party_id >= 0 && gwin->get_main_actor()->is_dead()))
03058 return 0;
03059 if (attacker)
03060 {
03061 if (attacker->get_schedule_type() == Schedule::duel)
03062 return this;
03063 set_oppressor(attacker->get_npc_num());
03064 if (is_combat_protected() && party_id >= 0 &&
03065 rand()%5 == 0)
03066 say(first_need_help, last_need_help);
03067
03068 if (!target && !is_in_party())
03069 set_target(attacker,
03070 attacker->get_schedule_type() != Schedule::duel);
03071 }
03072
03073 if (npc_num > 0 && Game::get_game_type() == BLACK_GATE &&
03074 get_info().has_translucency() &&
03075 party_id < 0)
03076 return this;
03077 bool defeated = figure_hit_points(attacker, weapon_shape, ammo_shape);
03078 if (attacker && defeated)
03079 {
03080
03081 int expval = get_property(static_cast<int>(strength)) +
03082 get_property(static_cast<int>(combat))/4 +
03083 get_property(static_cast<int>(dexterity))/4 +
03084 get_property(static_cast<int>(intelligence))/4;
03085 expval /= 2;
03086
03087 attacker->set_property(static_cast<int>(exp),
03088 attacker->get_property(static_cast<int>(exp)) + expval);
03089 return 0;
03090 }
03091 return this;
03092 }
03093
03094
03095
03096
03097
03098
03099 static int Is_draco
03100 (
03101 Actor *dragon
03102 )
03103 {
03104 Game_object_vector vec;
03105
03106 int cnt = dragon->get_objects(vec, 797, 241, 4);
03107 return cnt > 0;
03108 }
03109
03110
03111
03112
03113
03114 void Actor::die
03115 (
03116 Actor *attacker
03117 )
03118 {
03119
03120 Tile_coord pos = get_tile();
03121 set_action(0);
03122 delete schedule;
03123 schedule = 0;
03124 gwin->get_tqueue()->remove(this);
03125 Actor::set_flag(Obj_flags::dead);
03126
03127
03128 int shnum = get_shapenum();
03129
03130 if (((shnum == 0x1fa || (shnum == 0x1f8 && Is_draco(this))) &&
03131 Game::get_game_type() == BLACK_GATE))
03132 {
03133 ucmachine->call_usecode(shnum, this,
03134 Usecode_machine::internal_exec);
03135 if (get_cx() == 255)
03136 return;
03137 }
03138 properties[static_cast<int>(health)] = -50;
03139 Shape_info& info = get_info();
03140 Monster_info *minfo = info.get_monster_info();
03141 remove_this(1);
03142 set_invalid();
03143 Dead_body *body;
03144 if (!minfo || !minfo->has_no_body())
03145 {
03146 int frnum;
03147 if (!Body_lookup::find(get_shapenum(), shnum, frnum))
03148 {
03149 shnum = 400;
03150 frnum = 3;
03151 }
03152
03153 frnum |= (get_framenum()&32);
03154 body = new Dead_body(shnum, frnum, 0, 0, 0,
03155 npc_num > 0 ? npc_num : -1);
03156 if (npc_num > 0)
03157 {
03158 body->set_quality(1);
03159 gwin->set_body(npc_num, body);
03160 }
03161
03162 if (get_flag(Obj_flags::is_temporary))
03163 body->set_flag(Obj_flags::is_temporary);
03164
03165 body->set_flag_recursively(Obj_flags::okay_to_take);
03166
03167 Tile_coord bp = Map_chunk::find_spot(pos, 1, shnum, frnum, 2);
03168 if (bp.tx == -1)
03169 bp = pos;
03170 body->move(bp);
03171 }
03172 else
03173 body = 0;
03174 Game_object *item;
03175 Game_object_vector tooheavy;
03176 while ((item = objects.get_first()) != 0)
03177 {
03178 remove(item);
03179 item->set_invalid();
03180 if (!item->is_dragable())
03181 {
03182 tooheavy.push_back(item);
03183 continue;
03184 }
03185 if (body)
03186 body->add(item, 1);
03187 else
03188 {
03189 item->set_flag_recursively(Obj_flags::okay_to_take);
03190 Tile_coord pos2 = Map_chunk::find_spot(pos, 5,
03191 item->get_shapenum(), item->get_framenum(), 1);
03192 if (pos.tx != -1)
03193 item->move(pos2);
03194 else
03195 tooheavy.push_back(item);
03196 }
03197 }
03198
03199 for (Game_object_vector::const_iterator it = tooheavy.begin();
03200 it != tooheavy.end(); ++it)
03201 add(*it, 1);
03202 if (body)
03203 gwin->add_dirty(body);
03204 add_dirty();
03205 delete_contents();
03206
03207
03208 if (attacker && attacker->is_in_party() && !is_in_party() &&
03209 alignment != neutral && alignment != friendly)
03210 Combat_schedule::monster_died();
03211
03212 partyman->update_party_status(this);
03213 }
03214
03215
03216
03217
03218
03219
03220
03221 Monster_actor *Actor::clone
03222 (
03223 )
03224 {
03225 Shape_info& info = get_info();
03226
03227 int xs = info.get_3d_xtiles(), ys = info.get_3d_ytiles();
03228
03229 Tile_coord pos = Map_chunk::find_spot(get_tile(),
03230 xs > ys ? xs : ys, get_shapenum(), 0, 1);
03231 if (pos.tx < 0)
03232 return 0;
03233
03234 Monster_actor *monst = Monster_actor::create(
03235 get_shapenum(), pos, get_schedule_type(),
03236 get_alignment(), true, true);
03237 return monst;
03238 }
03239
03240
03241
03242
03243
03244 void Actor::mend_hourly
03245 (
03246 )
03247 {
03248 if (is_dead())
03249 return;
03250 int maxhp = properties[static_cast<int>(strength)];
03251 int hp = properties[static_cast<int>(health)];
03252 if (maxhp > 0 && hp < maxhp)
03253 {
03254 if (maxhp >= 3)
03255 hp += 1 + rand()%(maxhp/3);
03256 else
03257 hp += 1;
03258 if (hp > maxhp)
03259 hp = maxhp;
03260 properties[static_cast<int>(health)] = hp;
03261
03262
03263 }
03264
03265 int maxmana = properties[static_cast<int>(magic)];
03266 int curmana = properties[static_cast<int>(mana)];
03267 if (maxmana > 0 && curmana < maxmana)
03268 {
03269 if (maxmana >= 3)
03270 curmana += 1 + rand()%(maxmana/3);
03271 else
03272 curmana += 1;
03273 properties[static_cast<int>(mana)] = curmana <= maxmana ? curmana
03274 : maxmana;
03275 }
03276 }
03277
03278
03279
03280
03281
03282
03283
03284 Actor *Actor::resurrect
03285 (
03286 Dead_body *body
03287 )
03288 {
03289 if (body->get_owner() ||
03290 npc_num <= 0 || gwin->get_body(npc_num) != body)
03291 return (0);
03292 gwin->set_body(npc_num, 0);
03293 Game_object *item;
03294 while ((item = body->get_objects().get_first()) != 0)
03295 {
03296 body->remove(item);
03297 add(item, 1);
03298 }
03299 gwin->add_dirty(body);
03300 Tile_coord pos = body->get_tile();
03301 body->remove_this();
03302 move(pos);
03303
03304 properties[static_cast<int>(health)] =
03305 properties[static_cast<int>(strength)];
03306 Actor::clear_flag(Obj_flags::dead);
03307 Actor::clear_flag(Obj_flags::poisoned);
03308 Actor::clear_flag(Obj_flags::paralyzed);
03309 Actor::clear_flag(Obj_flags::asleep);
03310
03311 partyman->update_party_status(this);
03312
03313 set_schedule_type(is_in_party() ? Schedule::follow_avatar
03314 : Schedule::loiter);
03315
03316 Usecode_script *scr = new Usecode_script(this);
03317 (*scr) << (Ucscript::npc_frame + Actor::sleep_frame)
03318 << (Ucscript::npc_frame + Actor::kneel_frame)
03319 << (Ucscript::npc_frame + Actor::standing);
03320 scr->start(1);
03321 return (this);
03322 }
03323
03324
03325
03326
03327
03328 void Main_actor::handle_event
03329 (
03330 unsigned long curtime,
03331 long udata
03332 )
03333 {
03334 if (action)
03335 {
03336 int delay = action->handle_event(this);
03337 if (delay)
03338 gwin->get_tqueue()->add(
03339 curtime + delay, this, udata);
03340 else
03341 {
03342 set_action(0);
03343 if (schedule)
03344 schedule->now_what();
03345 }
03346 }
03347 else if (schedule)
03348 schedule->now_what();
03349 }
03350
03351
03352
03353
03354
03355 void Main_actor::get_followers
03356 (
03357 )
03358 {
03359 int cnt = partyman->get_count();
03360 for (int i = 0; i < cnt; i++)
03361 {
03362 Actor *npc = gwin->get_npc(partyman->get_member(i));
03363 if (!npc || npc->get_flag(Obj_flags::asleep) ||
03364 npc->is_dead())
03365 continue;
03366 int sched = npc->get_schedule_type();
03367
03368 if (sched != Schedule::combat &&
03369 sched != Schedule::wait &&
03370
03371 sched != Schedule::loiter)
03372 {
03373 if (sched != Schedule::follow_avatar)
03374 npc->set_schedule_type(
03375 Schedule::follow_avatar);
03376 else
03377 npc->follow(this);
03378 }
03379 }
03380 }
03381
03382
03383
03384
03385
03386
03387
03388 int Main_actor::step
03389 (
03390 Tile_coord t,
03391 int frame
03392 )
03393 {
03394 rest_time = 0;
03395
03396 int cx = t.tx/c_tiles_per_chunk, cy = t.ty/c_tiles_per_chunk;
03397
03398 int tx = t.tx%c_tiles_per_chunk, ty = t.ty%c_tiles_per_chunk;
03399 Map_chunk *nlist = gmap->get_chunk(cx, cy);
03400 int old_lift = get_lift();
03401 int water, poison;
03402 get_tile_info(this, gwin, nlist, tx, ty, water, poison);
03403 Game_object *block;
03404 if (is_blocked(t) &&
03405 (!(block = Game_object::find_blocking(t)) || block == this ||
03406
03407 !block->move_aside(this, get_direction(block)) ||
03408
03409 (t != get_tile() &&
03410
03411 is_blocked(t))))
03412 {
03413 stop();
03414 return (0);
03415 }
03416 if (poison && t.tz == 0)
03417 Actor::set_flag(static_cast<int>(Obj_flags::poisoned));
03418
03419 gwin->scroll_if_needed(this, t);
03420 add_dirty();
03421
03422 Map_chunk *olist = gmap->get_chunk(get_cx(), get_cy());
03423 Tile_coord oldtile = get_tile();
03424
03425 Actor::movef(olist, nlist, tx, ty, frame, t.tz);
03426 add_dirty(1);
03427
03428 if (olist != nlist)
03429 Main_actor::switched_chunks(olist, nlist);
03430 int roof_height = nlist->is_roof (tx, ty, t.tz);
03431 gwin->set_ice_dungeon(nlist->is_ice_dungeon(tx, ty));
03432 if (gwin->set_above_main_actor (roof_height))
03433 {
03434 gwin->set_in_dungeon(nlist->has_dungeon()?
03435 nlist->is_dungeon(tx, ty):0);
03436 gwin->set_all_dirty();
03437 }
03438 else if (roof_height < 31 && gwin->set_in_dungeon(nlist->has_dungeon()?
03439 nlist->is_dungeon(tx, ty):0))
03440 gwin->set_all_dirty();
03441
03442
03443 nlist->activate_eggs(this, t.tx, t.ty, t.tz,
03444 oldtile.tx, oldtile.ty);
03445 return (1);
03446 }
03447
03448
03449
03450
03451
03452 void Main_actor::switched_chunks
03453 (
03454 Map_chunk *olist,
03455 Map_chunk *nlist
03456 )
03457 {
03458 int newcx = nlist->get_cx(), newcy = nlist->get_cy();
03459 int xfrom, xto, yfrom, yto;
03460 if (!olist)
03461 {
03462 xfrom = newcx > 0 ? newcx - 1 : newcx;
03463 xto = newcx < c_num_chunks - 1 ? newcx + 1 : newcx;
03464 yfrom = newcy > 0 ? newcy - 1 : newcy;
03465 yto = newcy < c_num_chunks - 1 ? newcy + 1 : newcy;
03466 }
03467 else
03468 {
03469 int oldcx = olist->get_cx(), oldcy = olist->get_cy();
03470 if (newcx == oldcx + 1)
03471 {
03472 xfrom = newcx;
03473 xto = newcx < c_num_chunks - 1 ? newcx + 1 : newcx;
03474 }
03475 else if (newcx == oldcx - 1)
03476 {
03477 xfrom = newcx > 0 ? newcx - 1 : newcx;
03478 xto = newcx;
03479 }
03480 else
03481 {
03482 xfrom = newcx > 0 ? newcx - 1 : newcx;
03483 xto = newcx < c_num_chunks - 1 ? newcx + 1 : newcx;
03484 }
03485 if (newcy == oldcy + 1)
03486 {
03487 yfrom = newcy;
03488 yto = newcy < c_num_chunks - 1 ? newcy + 1 : newcy;
03489 }
03490 else if (newcy == oldcy - 1)
03491 {
03492 yfrom = newcy > 0 ? newcy - 1 : newcy;
03493 yto = newcy;
03494 }
03495 else
03496 {
03497 yfrom = newcy > 0 ? newcy - 1 : newcy;
03498 yto = newcy < c_num_chunks - 1 ? newcy + 1 : newcy;
03499 }
03500 }
03501 for (int y = yfrom; y <= yto; y++)
03502 for (int x = xfrom; x <= xto; x++)
03503 gmap->get_chunk(x, y)->setup_cache();
03504
03505
03506 if (olist) gwin->emulate_cache(olist->get_cx(), olist->get_cy(), newcx, newcy);
03507 else gwin->emulate_cache(-1, -1, newcx, newcy);
03508 }
03509
03510
03511
03512
03513
03514 void Main_actor::move
03515 (
03516 int newtx,
03517 int newty,
03518 int newlift
03519 )
03520 {
03521
03522 Map_chunk *olist = gmap->get_chunk_safely(
03523 get_cx(), get_cy());
03524
03525 Actor::move(newtx, newty, newlift);
03526 Map_chunk *nlist = gmap->get_chunk(get_cx(), get_cy());
03527 if (nlist != olist)
03528 Main_actor::switched_chunks(olist, nlist);
03529 int tx = get_tx(), ty = get_ty();
03530 gwin->set_ice_dungeon(nlist->is_ice_dungeon(tx, ty));
03531 if (gwin->set_above_main_actor(nlist->is_roof(tx, ty, newlift)))
03532 gwin->set_in_dungeon(nlist->has_dungeon()?
03533 nlist->is_dungeon(tx, ty):0);
03534 }
03535
03536
03537
03538
03539
03540 void Main_actor::die
03541 (
03542 Actor *
03543 )
03544 {
03545 if (gwin->in_combat())
03546 gwin->toggle_combat();
03547 Actor::set_flag(Obj_flags::dead);
03548 gumpman->close_all_gumps();
03549
03550 if (Game::get_game_type() == BLACK_GATE)
03551 ucmachine->call_usecode(0x60e, this, Usecode_machine::weapon);
03552
03553 else
03554 ucmachine->call_usecode(0x400, this,
03555 Usecode_machine::died);
03556 }
03557
03558
03559
03560
03561 void Actor::set_actor_shape()
03562 {
03563 if (get_npc_num() != 0 || get_flag (Obj_flags::polymorph))
03564 return;
03565
03566 int sn;
03567
03568 ShapeFile the_file = SF_SHAPES_VGA;
03569 int female = get_type_flag(tf_sex)?1:0;
03570
03571 if (Game::get_game_type() == SERPENT_ISLE||sman->can_use_multiracial())
03572 {
03573 if (Game::get_game_type() == BLACK_GATE) the_file = SF_BG_SISHAPES_VGA;
03574
03575 if (get_skin_color() == 0)
03576 {
03577 sn = 1028+female+6*get_siflag(naked);
03578 }
03579 else if (get_skin_color() == 1)
03580 {
03581 sn = 1026+female+6*get_siflag(naked);
03582 }
03583 else if (get_skin_color() == 2)
03584 {
03585 sn = 1024+female+6*get_siflag(naked);
03586 }
03587 else if (Game::get_game_type() == SERPENT_ISLE)
03588 {
03589 sn = female?658:747;
03590 }
03591 else
03592 {
03593 the_file = SF_SHAPES_VGA;
03594 sn = female?989:721;
03595 }
03596 }
03597 else if (female)
03598 sn = 989;
03599 else
03600 sn = 721;
03601 #ifdef DEBUG
03602 cerr << "Setting Shape to " << sn << endl;
03603 #endif
03604 set_shape (sn, get_framenum());
03605 set_file(the_file);
03606 }
03607
03608
03609 void Actor::set_polymorph (int shape)
03610 {
03611
03612 if (Game::get_game_type() != SERPENT_ISLE) return;
03613
03614 #ifdef DEBUG
03615 cerr << "Setting polymorph for " << get_npc_num() << endl;
03616 cerr << "Shape " << shape << endl;
03617 cerr << "Save shape " << shape_save << endl;
03618 #endif
03619
03620
03621 if (shape == 721 || shape == 989)
03622 {
03623 Actor *avatar = gwin->get_main_actor();
03624 if (!avatar) return;
03625
03626 if (avatar->get_skin_color() == 0)
03627 shape = 1028+avatar->get_type_flag(tf_sex)+6*avatar->get_siflag(naked);
03628 else if (avatar->get_skin_color() == 1)
03629 shape = 1026+avatar->get_type_flag(tf_sex)+6*avatar->get_siflag(naked);
03630 else
03631 shape = 1024+avatar->get_type_flag(tf_sex)+6*avatar->get_siflag(naked);
03632 }
03633
03634 if (shape == shape_save)
03635 {
03636 set_shape (shape_save);
03637 shape_save = -1;
03638 clear_flag(Obj_flags::polymorph);
03639 return;
03640 }
03641 if (shape_save == -1) shape_save = get_shapenum();
03642
03643
03644 set_shape (shape, get_dir_framenum(Actor::standing));
03645 set_flag (Obj_flags::polymorph);
03646 }
03647
03648
03649 void Actor::set_polymorph_default()
03650 {
03651
03652 if (Game::get_game_type() != SERPENT_ISLE ||
03653 !get_flag(Obj_flags::polymorph)
03654 || (get_npc_num() != 0 && get_npc_num() != 28))
03655 return;
03656
03657 set_actor_shape();
03658
03659 shape_save = get_shapenum();
03660
03661 if (get_npc_num() == 28)
03662 set_polymorph (721);
03663 else if (get_flag(Obj_flags::petra))
03664 set_polymorph (658);
03665 else
03666 set_polymorph (530);
03667 }
03668
03669
03670
03671
03672
03673 int Actor::get_shape_real
03674 (
03675 )
03676 {
03677 #if 0
03678 if (Game::get_game_type() == BLACK_GATE)
03679 return get_shapenum();
03680 #endif
03681 if (npc_num != 0)
03682 return shape_save!=-1?shape_save:get_shapenum();
03683
03684 if (get_type_flag(Actor::tf_sex))
03685 return 989;
03686 else
03687 return 721;
03688 }
03689
03690
03691
03692
03693
03694 Npc_actor::Npc_actor
03695 (
03696 const std::string &nm,
03697 int shapenum,
03698 int num,
03699 int uc
03700 ) : Actor(nm, shapenum, num, uc), nearby(false),
03701 num_schedules(0),
03702 schedules(0)
03703 {
03704 }
03705
03706
03707
03708
03709
03710 Npc_actor::~Npc_actor
03711 (
03712 )
03713 {
03714 delete [] schedules;
03715 }
03716
03717
03718
03719
03720
03721 void Npc_actor::set_schedules
03722 (
03723 Schedule_change *sc_list,
03724 int cnt
03725 )
03726 {
03727 delete [] schedules;
03728 schedules = sc_list;
03729 num_schedules = cnt;
03730 }
03731
03732
03733
03734
03735
03736 void Npc_actor::set_schedule_time_type (int time, int type)
03737 {
03738 Tile_coord tile;
03739 int i;
03740
03741 for (i = 0; i < num_schedules; i++) if (schedules[i].get_time() == time) break;
03742
03743 if (i == num_schedules)
03744 {
03745 Schedule_change *scheds = new Schedule_change[num_schedules+1];
03746
03747 for (i = 0; i < num_schedules; i++)
03748 {
03749 tile = schedules[i].get_pos();
03750 scheds[i].set(tile.tx, tile.ty, schedules[i].get_type(), schedules[i].get_time());
03751 }
03752
03753 scheds[num_schedules].set(0, 0, static_cast<unsigned char>(type),
03754 static_cast<unsigned char>(time));
03755 set_schedules(scheds, num_schedules+1);
03756 }
03757 else
03758 {
03759 tile = schedules[i].get_pos();
03760 schedules[i].set(tile.tx, tile.ty, static_cast<unsigned char>(type),
03761 static_cast<unsigned char>(time));
03762 }
03763 }
03764
03765
03766
03767
03768
03769 void Npc_actor::set_schedule_time_location (int time, int x, int y)
03770 {
03771 int i;
03772
03773 for (i = 0; i < num_schedules; i++) if (schedules[i].get_time() == time) break;
03774
03775 if (i == num_schedules)
03776 {
03777 Tile_coord tile;
03778 Schedule_change *scheds = new Schedule_change[num_schedules+1];
03779
03780 for (i = 0; i < num_schedules; i++)
03781 {
03782 tile = schedules[i].get_pos();
03783 scheds[i].set(tile.tx, tile.ty, schedules[i].get_type(), schedules[i].get_time());
03784 }
03785
03786 scheds[num_schedules].set(x, y, 0, static_cast<unsigned char>(time));
03787 set_schedules(scheds, num_schedules+1);
03788 }
03789 else
03790 {
03791 schedules[i].set(x, y, schedules[i].get_type(), static_cast<unsigned char>(time));
03792 }
03793 }
03794
03795
03796
03797
03798
03799 void Npc_actor::remove_schedule (int time)
03800 {
03801 int i;
03802
03803 for (i = 0; i < num_schedules; i++)
03804 if (schedules[i].get_time() == time) break;
03805 if (i != num_schedules)
03806 {
03807 int todel = i;
03808 Tile_coord tile;
03809 Schedule_change *scheds = new Schedule_change[num_schedules-1];
03810
03811 for (i = 0; i < todel; i++)
03812 {
03813 tile = schedules[i].get_pos();
03814 scheds[i].set(tile.tx, tile.ty,
03815 schedules[i].get_type(), schedules[i].get_time());
03816 }
03817
03818 for (; i < num_schedules - 1; i++)
03819 {
03820 tile = schedules[i+1].get_pos();
03821 scheds[i].set(tile.tx, tile.ty,
03822 schedules[i+1].get_type(),
03823 schedules[i+1].get_time());
03824 }
03825
03826 set_schedules(scheds, num_schedules-1);
03827 }
03828 }
03829
03830
03831
03832
03833
03834 void Npc_actor::get_schedules
03835 (
03836 Schedule_change *&sc_list,
03837 int &cnt
03838 )
03839 {
03840 sc_list = schedules;
03841 cnt = num_schedules;
03842 }
03843
03844
03845
03846
03847 void Npc_actor::movef
03848 (
03849 Map_chunk *old_chunk,
03850 Map_chunk *new_chunk,
03851 int new_sx, int new_sy,
03852 int new_frame,
03853 int new_lift
03854 )
03855 {
03856 Actor::movef(old_chunk, new_chunk,
03857 new_sx, new_sy, new_frame, new_lift);
03858 if (old_chunk != new_chunk)
03859 Npc_actor::switched_chunks(old_chunk, new_chunk);
03860 }
03861
03862
03863
03864
03865
03866
03867
03868
03869 int Npc_actor::find_schedule_change
03870 (
03871 int hour3
03872 )
03873 {
03874 if (party_id >= 0 || is_dead())
03875 return (-1);
03876 for (int i = 0; i < num_schedules; i++)
03877 if (schedules[i].get_time() == hour3)
03878 return i;
03879 return -1;
03880 }
03881
03882
03883
03884
03885
03886 void Npc_actor::update_schedule
03887 (
03888 int hour3,
03889 int backwards,
03890 int delay
03891 )
03892 {
03893 int i = find_schedule_change(hour3);
03894 if (i < 0)
03895 {
03896
03897 long hour = gclock->get_total_hours();
03898 if (hour == 12 && !backwards)
03899 backwards++;
03900 while (backwards-- && i < 0)
03901 i = find_schedule_change((--hour3 + 8)%8);
03902 if (i < 0)
03903 return;
03904
03905
03906
03907
03908 }
03909 set_schedule_and_loc (schedules[i].get_type(), schedules[i].get_pos(),
03910 delay);
03911 }
03912
03913
03914
03915
03916
03917 void Npc_actor::paint
03918 (
03919 )
03920 {
03921 Actor::paint();
03922 if (dormant && schedule &&
03923
03924 (party_id < 0 || !gwin->walk_in_formation ||
03925 schedule_type != Schedule::follow_avatar))
03926 {
03927 dormant = false;
03928 gwin->get_tqueue()->remove(this);
03929
03930
03931 uint32 curtime = Game::get_ticks();
03932 gwin->get_tqueue()->add(curtime + 500, this, reinterpret_cast<long>(gwin));
03933 }
03934 if (!nearby)
03935 gwin->add_nearby_npc(this);
03936 }
03937
03938
03939
03940
03941 void Npc_actor::activate
03942 (
03943 int event
03944 )
03945 {
03946 if (is_dead())
03947 return;
03948
03949 Actor::activate(event);
03950 #if 0
03951
03952
03953
03954 int i;
03955 if (gclock->get_total_hours() >= 18 ||
03956 Game::get_game_type() == SERPENT_ISLE ||
03957
03958 (i = find_schedule_change(gclock->get_hour()/3)) < 0 ||
03959
03960 schedules[i].get_type() == schedule_type)
03961 return;
03962 cout << "Setting '" << get_name() << "' to 1st schedule" << endl;
03963
03964 update_schedule(gclock->get_hour()/3);
03965 #endif
03966 }
03967
03968
03969
03970
03971
03972 void Npc_actor::handle_event
03973 (
03974 unsigned long curtime,
03975 long udata
03976 )
03977 {
03978 if (!action)
03979 {
03980 if (schedule)
03981 schedule->now_what();
03982 else
03983 dormant = true;
03984 }
03985 else
03986 {
03987 int delay = party_id < 0 ? gwin->is_time_stopped() : 0;
03988 if (delay <= 0)
03989 delay = action->handle_event(this);
03990 if (delay)
03991 gwin->get_tqueue()->add(
03992 curtime + delay, this, udata);
03993 else
03994 {
03995 set_action(0);
03996 if (schedule)
03997 if (dormant)
03998 schedule->im_dormant();
03999 else
04000 schedule->now_what();
04001 }
04002 }
04003 }
04004
04005
04006
04007
04008
04009
04010
04011
04012 int Npc_actor::step
04013 (
04014 Tile_coord t,
04015 int frame
04016 )
04017 {
04018 if (get_flag(Obj_flags::paralyzed))
04019 return 0;
04020
04021 Tile_coord oldtile = get_tile();
04022 int old_cx = get_cx(), old_cy = get_cy();
04023
04024 int cx = t.tx/c_tiles_per_chunk, cy = t.ty/c_tiles_per_chunk;
04025
04026 int tx = t.tx%c_tiles_per_chunk, ty = t.ty%c_tiles_per_chunk;
04027
04028 Map_chunk *nlist = gmap->get_chunk_safely(cx, cy);
04029 if (!nlist)
04030 {
04031 stop();
04032 return (0);
04033 }
04034 int water, poison;
04035 get_tile_info(this, gwin, nlist, tx, ty, water, poison);
04036 if (is_blocked(t))
04037 {
04038 if (schedule)
04039 schedule->set_blocked(t);
04040 stop();
04041
04042 if (!gwin->add_dirty(this) && party_id < 0 &&
04043
04044 distance(gwin->get_camera_actor()) > 1 + 320/c_tilesize)
04045 dormant = true;
04046 return (0);
04047 }
04048 if (poison && t.tz == 0)
04049 Actor::set_flag(static_cast<int>(Obj_flags::poisoned));
04050
04051 gwin->scroll_if_needed(this, t);
04052 add_dirty();
04053
04054 Map_chunk *olist = gmap->get_chunk(old_cx, old_cy);
04055
04056 movef(olist, nlist, tx, ty, frame, t.tz);
04057
04058
04059
04060 nlist->activate_eggs(this, t.tx, t.ty, t.tz, oldtile.tx, oldtile.ty);
04061
04062
04063 if (!add_dirty(1) && party_id < 0 &&
04064
04065 distance(gwin->get_camera_actor()) > 1 + 320/c_tilesize &&
04066
04067 get_schedule_type() != Schedule::talk &&
04068 get_schedule_type() != Schedule::street_maintenance)
04069 {
04070 stop();
04071 dormant = true;
04072 return (0);
04073 }
04074 return (1);
04075 }
04076
04077
04078
04079
04080
04081
04082 void Npc_actor::remove_this
04083 (
04084 int nodel
04085 )
04086 {
04087 set_action(0);
04088
04089
04090
04091 gwin->get_tqueue()->remove(this);
04092 gwin->remove_nearby_npc(this);
04093
04094 Map_chunk *olist = gmap->get_chunk_safely(get_cx(), get_cy());
04095 Actor::remove_this(1);
04096 Npc_actor::switched_chunks(olist, 0);
04097 cx = cy = 0xff;
04098 if (!nodel && npc_num > 0)
04099 unused = true;
04100 }
04101
04102
04103
04104
04105
04106 void Npc_actor::switched_chunks
04107 (
04108 Map_chunk *olist,
04109 Map_chunk *nlist
04110 )
04111 {
04112
04113 }
04114
04115
04116
04117
04118
04119 void Npc_actor::move
04120 (
04121 int newtx,
04122 int newty,
04123 int newlift
04124 )
04125 {
04126
04127 Map_chunk *olist = gmap->get_chunk_safely(get_cx(), get_cy());
04128
04129 Actor::move(newtx, newty, newlift);
04130 Map_chunk *nlist = gmap->get_chunk_safely(get_cx(), get_cy());
04131 if (nlist != olist)
04132 {
04133 Npc_actor::switched_chunks(olist, nlist);
04134 if (!olist)
04135 dormant = true;
04136 }
04137 }
04138
04139
04140
04141
04142
04143 Dead_body::~Dead_body
04144 (
04145 )
04146 {
04147 }
04148
04149
04150
04151
04152
04153 int Dead_body::get_live_npc_num
04154 (
04155 )
04156 {
04157 return npc_num;
04158 }
04159
04160
04161
04162
04163
04164 Frames_sequence::Frames_sequence
04165 (
04166 int cnt,
04167 unsigned char *f
04168 ) : num_frames(cnt)
04169 {
04170 frames = new unsigned char[cnt];
04171 memcpy(frames, f, cnt);
04172 }
04173
04174