00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #ifdef HAVE_CONFIG_H
00022 # include <config.h>
00023 #endif
00024
00025 #include "combat.h"
00026 #include "combat_opts.h"
00027 #include "gamewin.h"
00028 #include "gameclk.h"
00029 #include "gamemap.h"
00030 #include "actors.h"
00031 #include "paths.h"
00032 #include "Astar.h"
00033 #include "actions.h"
00034 #include "items.h"
00035 #include "effects.h"
00036 #include "Audio.h"
00037 #include "ready.h"
00038 #include "game.h"
00039 #include "monstinf.h"
00040 #include "ucmachine.h"
00041 #include "game.h"
00042 #include "Gump_manager.h"
00043 #include "spellbook.h"
00044
00045 #ifndef UNDER_CE
00046 using std::cout;
00047 using std::endl;
00048 using std::rand;
00049 #endif
00050
00051 unsigned long Combat_schedule::battle_time = (unsigned long) (-30000);
00052 unsigned long Combat_schedule::battle_end_time = 0;
00053
00054 bool Combat::paused = false;
00055 int Combat::difficulty = 0;
00056 Combat::Mode Combat::mode = Combat::original;
00057 bool Combat::show_hits = false;
00058
00059 extern bool combat_trace;
00060
00061
00062
00063
00064
00065 bool In_ammo_family(int shnum, int family)
00066 {
00067 if (shnum == family)
00068 return true;
00069 Ammo_info *ainf = ShapeID::get_info(shnum).get_ammo_info();
00070 return (ainf != 0 && ainf->get_family_shape() == family);
00071 }
00072
00073
00074
00075
00076
00077 void Combat_schedule::start_battle
00078 (
00079 )
00080 {
00081 if (started_battle)
00082 return;
00083
00084 if (gwin->get_camera_actor() != gwin->get_main_actor())
00085 return;
00086 unsigned long curtime = Game::get_ticks();
00087
00088 if (curtime - battle_time >= 30000)
00089 {
00090 Audio::get_ptr()->start_music_combat(rand()%2 ?
00091 CSAttacked1 : CSAttacked2, 0);
00092 battle_time = curtime;
00093 battle_end_time = curtime - 1;
00094 }
00095 started_battle = true;
00096 }
00097
00098
00099
00100
00101
00102
00103
00104 void Combat_schedule::monster_died
00105 (
00106 )
00107 {
00108 if (battle_end_time >= battle_time)
00109 return;
00110 Actor_queue nearby;
00111 gwin->get_nearby_npcs(nearby);
00112 for (Actor_queue::const_iterator it = nearby.begin();
00113 it != nearby.end(); ++it)
00114 {
00115 Actor *actor = *it;
00116 if (!actor->is_dead() &&
00117 actor->get_attack_mode() != Actor::flee &&
00118 actor->get_alignment() >= Npc_actor::hostile)
00119 return;
00120 }
00121 battle_end_time = Game::get_ticks();
00122
00123 unsigned long len = (battle_end_time - battle_time)/1000;
00124 bool hard = len > 15 && (rand()%60 < len);
00125 Audio::get_ptr()->start_music_combat (hard ? CSBattle_Over
00126 : CSVictory, 0);
00127 }
00128
00129
00130
00131
00132
00133 inline bool Can_teleport
00134 (
00135 Actor *npc
00136 )
00137 {
00138 switch (npc->get_shapenum())
00139 {
00140 case 534:
00141 case 445:
00142 case 446:
00143 case 519:
00144 return true;
00145 default:
00146 return false;
00147 }
00148 }
00149
00150
00151
00152
00153
00154 bool Combat_schedule::teleport
00155 (
00156 )
00157 {
00158 Game_object *trg = npc->get_target();
00159 if (!trg)
00160 return false;
00161 unsigned int curtime = SDL_GetTicks();
00162 if (curtime < teleport_time)
00163 return false;
00164 teleport_time = curtime + 2000 + rand()%2000;
00165 Tile_coord dest = trg->get_tile();
00166 dest.tx += 4 - rand()%8;
00167 dest.ty += 4 - rand()%8;
00168 dest = Map_chunk::find_spot(dest, 3, npc, 1);
00169 if (dest.tx == -1)
00170 return false;
00171 Tile_coord src = npc->get_tile();
00172 if (dest.distance(src) > 6 && rand()%5 != 0)
00173 return false;
00174
00175
00176 src.tz = npc->get_chunk()->get_highest_blocked(src.tz,
00177 src.tx%c_tiles_per_chunk, src.ty%c_tiles_per_chunk);
00178 if (src.tz < 0)
00179 src.tz = 0;
00180 eman->add_effect(new Fire_field_effect(src));
00181 npc->move(dest.tx, dest.ty, dest.tz);
00182
00183 eman->add_effect(new Sprites_effect(7, npc, 0, 0, 0, 0));
00184 return true;
00185 }
00186
00187
00188
00189
00190
00191 inline bool Off_screen
00192 (
00193 Game_window *gwin,
00194 Game_object *npc
00195 )
00196 {
00197
00198 Tile_coord t = npc->get_tile();
00199 Rectangle screen = gwin->get_win_tile_rect().enlarge(2);
00200 return (!screen.has_point(t.tx, t.ty));
00201 }
00202
00203
00204
00205
00206
00207 void Combat_schedule::find_opponents
00208 (
00209 )
00210 {
00211 opponents.clear();
00212 Game_window *gwin = Game_window::get_instance();
00213 if (npc->get_alignment() >= Npc_actor::hostile)
00214 {
00215 Actor *party[10];
00216 int cnt = gwin->get_party(party, 1);
00217 Actor *cact = gwin->get_camera_actor();
00218
00219 if (cact && cact != gwin->get_main_actor() &&
00220 cact->get_alignment() == Npc_actor::friendly &&
00221 cact->get_flag(Obj_flags::si_tournament))
00222 party[cnt++] = cact;
00223 for (int i = 0; i < cnt; i++)
00224
00225 if (!party[i]->get_flag(Obj_flags::invisible) &&
00226 party[i] != npc)
00227 opponents.push(party[i]);
00228 return;
00229 }
00230 Actor_queue nearby;
00231 gwin->get_nearby_npcs(nearby);
00232
00233 bool in_party = npc->is_in_party();
00234 for (Actor_queue::const_iterator it = nearby.begin();
00235 it != nearby.end(); ++it)
00236 {
00237 Actor *actor = *it;
00238 if (actor->is_dead() || actor->get_flag(Obj_flags::invisible))
00239 continue;
00240 if (actor->get_alignment() >= Npc_actor::hostile)
00241 {
00242 opponents.push(actor);
00243 }
00244 else if (in_party)
00245 {
00246 Game_object *t = actor->get_target();
00247 if (t && t->get_flag(Obj_flags::in_party))
00248 opponents.push(actor);
00249 }
00250 }
00251
00252 if (opponents.empty() && npc->is_in_party() &&
00253 npc != gwin->get_main_actor())
00254 {
00255 Game_object *opp = gwin->get_main_actor()->get_target();
00256 Actor *oppnpc = opp ? opp->as_actor() : 0;
00257 if (oppnpc && oppnpc != npc)
00258 opponents.push(oppnpc);
00259 }
00260 }
00261
00262
00263
00264
00265
00266
00267
00268 Actor *Combat_schedule::find_protected_attacker
00269 (
00270 )
00271 {
00272 if (!npc->is_in_party())
00273 return 0;
00274 Game_window *gwin = Game_window::get_instance();
00275 Actor *party[9];
00276 int cnt = gwin->get_party(party, 1);
00277 Actor *prot_actor = 0;
00278 for (int i = 0; i < cnt; i++)
00279 if (party[i]->is_combat_protected())
00280 {
00281 prot_actor = party[i];
00282 break;
00283 }
00284 if (!prot_actor)
00285 return 0;
00286
00287 int dist, best_dist = 4*c_tiles_per_chunk;
00288 Actor *best_opp = 0;
00289 for (Actor_queue::const_iterator it = opponents.begin();
00290 it != opponents.end(); ++it)
00291 {
00292 Actor *opp = *it;
00293 if (opp->get_target() == prot_actor &&
00294 (dist = npc->distance(opp)) < best_dist)
00295 {
00296 best_dist = dist;
00297 best_opp = opp;
00298 }
00299 }
00300 if (!best_opp)
00301 return 0;
00302 if (failures < 5 && yelled && rand()%2 && npc != prot_actor)
00303 npc->say(first_will_help, last_will_help);
00304 return best_opp;
00305 }
00306
00307
00308
00309
00310
00311
00312
00313 Game_object *Combat_schedule::find_foe
00314 (
00315 int mode
00316 )
00317 {
00318 if (combat_trace) {
00319 cout << "'" << npc->get_name() << "' is looking for a foe" << endl;
00320 }
00321
00322 for (Actor_queue::const_iterator it = opponents.begin();
00323 it != opponents.end(); )
00324 {
00325 Actor_queue::const_iterator next = it;
00326 ++next;
00327 if ((*it)->is_dead())
00328 opponents.remove(*it);
00329 it = next;
00330 }
00331 if (opponents.empty())
00332 {
00333 find_opponents();
00334 if (practice_target)
00335 return practice_target;
00336 }
00337 Actor *new_opponent = 0;
00338 switch ((Actor::Attack_mode) mode)
00339 {
00340 case Actor::weakest:
00341 {
00342 int str, least_str = 100;
00343 for (Actor_queue::const_iterator it = opponents.begin();
00344 it != opponents.end(); ++it)
00345 {
00346 Actor *opp = *it;
00347 str = opp->get_property(Actor::strength);
00348 if (str < least_str)
00349 {
00350 least_str = str;
00351 new_opponent = opp;
00352 }
00353 }
00354 break;
00355 }
00356 case Actor::strongest:
00357 {
00358 int str, best_str = -100;
00359 for (Actor_queue::const_iterator it = opponents.begin();
00360 it != opponents.end(); ++it)
00361 {
00362 Actor *opp = *it;
00363 str = opp->get_property(Actor::strength);
00364 if (str > best_str)
00365 {
00366 best_str = str;
00367 new_opponent = opp;
00368 }
00369 }
00370 break;
00371 }
00372 case Actor::nearest:
00373 {
00374 int best_dist = 4*c_tiles_per_chunk;
00375 for (Actor_queue::const_iterator it = opponents.begin();
00376 it != opponents.end(); ++it)
00377 {
00378 Actor *opp = *it;
00379 int dist = npc->distance(opp);
00380 if (opp->get_attack_mode() == Actor::flee)
00381 dist += 16;
00382 if (dist < best_dist)
00383 {
00384 best_dist = dist;
00385 new_opponent = opp;
00386 }
00387 }
00388 break;
00389 }
00390 case Actor::protect:
00391 new_opponent = find_protected_attacker();
00392 if (new_opponent)
00393 break;
00394
00395 case Actor::random:
00396 default:
00397 if (!new_opponent && !opponents.empty())
00398 new_opponent = opponents.front();
00399 break;
00400 }
00401 if (new_opponent)
00402 opponents.remove(new_opponent);
00403 return new_opponent;
00404 }
00405
00406
00407
00408
00409
00410
00411
00412 inline Game_object *Combat_schedule::find_foe
00413 (
00414 )
00415 {
00416 if (npc->get_attack_mode() == Actor::manual)
00417 return 0;
00418 return find_foe(static_cast<int>(npc->get_attack_mode()));
00419 }
00420
00421
00422
00423
00424
00425 void Combat_schedule::approach_foe
00426 (
00427 )
00428 {
00429 Game_object *opponent = npc->get_target();
00430
00431 if (!opponent && !(opponent = find_foe()))
00432 {
00433 failures++;
00434 npc->start(200, 400);
00435 return;
00436 }
00437 npc->set_target(opponent);
00438 Actor::Attack_mode mode = npc->get_attack_mode();
00439 Game_window *gwin = Game_window::get_instance();
00440
00441 if (mode == Actor::flee ||
00442 (mode != Actor::beserk &&
00443 (npc->get_type_flags()&MOVE_ALL) != 0 &&
00444 npc != gwin->get_main_actor() &&
00445 npc->get_property(Actor::health) < 3))
00446 {
00447 run_away();
00448 return;
00449 }
00450 if (Can_teleport(npc) && rand()%4 == 0 &&
00451 teleport())
00452 {
00453 start_battle();
00454 npc->start(gwin->get_std_delay(), gwin->get_std_delay());
00455 return;
00456 }
00457 PathFinder *path = new Astar();
00458
00459 Monster_pathfinder_client cost(npc, max_range, opponent);
00460 Tile_coord pos = npc->get_tile();
00461 if (!path->NewPath(pos, opponent->get_tile(), &cost))
00462 {
00463 failures++;
00464 bool retry_ok = false;
00465 if (npc->get_attack_mode() != Actor::manual)
00466 {
00467 Game_object *closest = find_foe(Actor::nearest);
00468 if (closest && closest != opponent)
00469 {
00470 opponent = closest;
00471 npc->set_target(opponent);
00472 Monster_pathfinder_client cost(npc, max_range,
00473 opponent);
00474 retry_ok = (opponent != 0 && path->NewPath(
00475 pos, opponent->get_tile(), &cost));
00476 }
00477 }
00478 if (!retry_ok)
00479 {
00480 delete path;
00481
00482
00483 Tile_coord pos = opponent->get_tile();
00484 if (rand()%3 == 0)
00485 pos = pos + Tile_coord(rand()%12 - 6,
00486 rand()%12 - 6, 0);
00487 npc->walk_to_tile(pos, 2*gwin->get_std_delay(),
00488 500 + rand()%500);
00489 failures++;
00490 return;
00491 }
00492 }
00493 failures = 0;
00494 start_battle();
00495 if (combat_trace) {
00496 cout << npc->get_name() << " is pursuing " << opponent->get_name()
00497 << endl;
00498 }
00499
00500 if (!yelled && gwin->add_dirty(npc))
00501 {
00502 yelled++;
00503 if (can_yell && rand()%2)
00504 {
00505
00506 if (Game::get_game_type() == SERPENT_ISLE &&
00507 (npc->get_shapenum() == 0x1de ||
00508 npc->get_shapenum() == 0x2b3 ||
00509 npc->get_shapenum() == 0x2d5 ||
00510 npc->get_shapenum() == 0x2e8))
00511 npc->say(0x4c9, 0x4d1);
00512 else
00513 npc->say(first_to_battle, last_to_battle);
00514 }
00515 }
00516
00517 npc->set_action(new Approach_actor_action(path, opponent));
00518
00519
00520 npc->start(gwin->get_std_delay(), Off_screen(gwin, opponent) ?
00521 5*gwin->get_std_delay() : gwin->get_std_delay());
00522 }
00523
00524
00525
00526
00527
00528
00529
00530 static int Swap_weapons
00531 (
00532 Actor *npc
00533 )
00534 {
00535 Game_object *bobj = npc->get_readied(Actor::belt);
00536 if (!bobj)
00537 {
00538 bobj = npc->get_readied(Actor::back2h_spot);
00539 if (!bobj)
00540 return 0;
00541 }
00542 Shape_info& info = bobj->get_info();
00543 Weapon_info *winf = info.get_weapon_info();
00544 if (!winf)
00545 return 0;
00546 int ammo = winf->get_ammo_consumed();
00547 if (ammo)
00548 {
00549 Game_object *aobj = npc->get_readied(Actor::ammo);
00550 if (!aobj || !In_ammo_family(aobj->get_shapenum(), ammo))
00551 return 0;
00552 }
00553 if (info.get_ready_type() == two_handed_weapon &&
00554 npc->get_readied(Actor::rhand) != 0)
00555 return 0;
00556 Game_object *oldweap = npc->get_readied(Actor::lhand);
00557 if (oldweap)
00558 npc->remove(oldweap);
00559 npc->remove(bobj);
00560 npc->add(bobj, 1);
00561 if (oldweap)
00562 npc->add(oldweap, 1);
00563 return 1;
00564 }
00565
00566
00567
00568
00569
00570 void Combat_schedule::start_strike
00571 (
00572 )
00573 {
00574 Game_object *opponent = npc->get_target();
00575 Rectangle npctiles = npc->get_footprint(),
00576 opptiles = opponent->get_footprint();
00577 Rectangle stiles = npctiles,
00578 ptiles = npctiles;
00579
00580 int dz = npc->get_lift() - opponent->get_lift();
00581 if (dz < 0)
00582 dz = -dz;
00583
00584 if (strike_range && dz < 5 &&
00585 stiles.enlarge(strike_range).intersects(opptiles))
00586 state = strike;
00587 else if (dz >= 5 ||
00588
00589 !projectile_range ||
00590
00591 !ptiles.enlarge(projectile_range).intersects(opptiles))
00592 {
00593 state = approach;
00594 approach_foe();
00595 return;
00596 }
00597 else
00598 {
00599 Game_object *aobj;
00600 bool weapon_dead = false;
00601 if (spellbook)
00602 weapon_dead = !spellbook->can_do_spell(npc);
00603 else if (ammo_shape &&
00604 (!(aobj = npc->get_readied(Actor::ammo)) ||
00605 !In_ammo_family(aobj->get_shapenum(), ammo_shape)))
00606 weapon_dead = true;
00607 if (weapon_dead)
00608 {
00609 if (npc->get_schedule_type() != Schedule::duel)
00610 {
00611 if (spellbook || !npc->ready_ammo())
00612 Swap_weapons(npc);
00613 Combat_schedule::set_weapon();
00614 }
00615 state = approach;
00616 npc->set_target(0);
00617 npc->start(200, 500);
00618 return;
00619 }
00620 Tile_coord pos = npc->get_tile();
00621 Tile_coord opos = opponent->get_tile();
00622 if (opos.tx < pos.tx)
00623 pos.tx = npctiles.x;
00624 else
00625 opos.tx = opptiles.x;
00626 if (opos.ty < pos.ty)
00627 pos.ty = npctiles.y;
00628 else
00629 opos.ty = opptiles.y;
00630 if (!no_blocking &&
00631 !Fast_pathfinder_client::is_straight_path(pos, opos))
00632 {
00633 pos.tx += rand()%7 - 3;
00634 pos.ty += rand()%7 - 3;
00635 npc->walk_to_tile(pos, gwin->get_std_delay(), 0);
00636 state = approach;
00637 npc->set_target(0);
00638 return;
00639 }
00640 if (!started_battle)
00641 start_battle();
00642 state = fire;
00643 }
00644 if (combat_trace) {
00645 cout << npc->get_name() << " attacks " << opponent->get_name() << endl;
00646 }
00647 int dir = npc->get_direction(opponent);
00648 signed char frames[12];
00649 int cnt = npc->get_attack_frames(weapon_shape, projectile_range > 0,
00650 dir, frames);
00651 if (cnt)
00652 npc->set_action(new Frames_actor_action(frames, cnt));
00653 npc->start();
00654 int sfx;
00655 Game_window *gwin = Game_window::get_instance();
00656 Weapon_info *winf = ShapeID::get_info(weapon_shape).get_weapon_info();
00657 if (winf && (sfx = winf->get_sfx()) >= 0 &&
00658
00659 (npc == gwin->get_main_actor() ||
00660 opponent == gwin->get_main_actor()))
00661 Audio::get_ptr()->play_sound_effect(sfx);
00662 }
00663
00664
00665
00666
00667
00668 void Combat_schedule::run_away
00669 (
00670 )
00671 {
00672 Game_window *gwin = Game_window::get_instance();
00673 fleed++;
00674
00675 int rx = rand();
00676 int ry = rand();
00677 int dirx = 2*(rx%2) - 1;
00678 int diry = 2*(ry%2) - 1;
00679 Tile_coord pos = npc->get_tile();
00680 pos.tx += dirx*(8 + rx%8);
00681 pos.ty += diry*(8 + ry%8);
00682 npc->walk_to_tile(pos, gwin->get_std_delay(), 0);
00683 if (fleed == 1 && !npc->get_flag(Obj_flags::si_tournament) &&
00684 rand()%3 && gwin->add_dirty(npc))
00685 {
00686 yelled++;
00687 if (can_yell)
00688 npc->say(first_flee, last_flee);
00689 }
00690 }
00691
00692
00693
00694
00695
00696
00697
00698
00699 Spellbook_object *Combat_schedule::readied_spellbook
00700 (
00701 )
00702 {
00703 Spellbook_object *book = 0;
00704
00705 Game_object *obj = npc->get_readied(Actor::lhand);
00706 if (obj && obj->get_info().get_shape_class() == Shape_info::spellbook)
00707 {
00708 book = static_cast<Spellbook_object*> (obj);
00709 if (book->can_do_spell(npc))
00710 return book;
00711 }
00712 obj = npc->get_readied(Actor::rhand);
00713 if (obj && obj->get_info().get_shape_class() == Shape_info::spellbook)
00714 {
00715 book = static_cast<Spellbook_object*> (obj);
00716 if (book->can_do_spell(npc))
00717 return book;
00718 }
00719 return 0;
00720 }
00721
00722
00723
00724
00725
00726 void Combat_schedule::set_weapon
00727 (
00728 )
00729 {
00730 int points;
00731 spellbook = 0;
00732 Weapon_info *info = npc->get_weapon(points, weapon_shape);
00733 if (!info &&
00734 !(spellbook = readied_spellbook()) &&
00735
00736 !gwin->is_dragging() &&
00737
00738 npc->get_schedule_type() != Schedule::duel &&
00739 state != wait_return)
00740 {
00741 npc->ready_best_weapon();
00742 info = npc->get_weapon(points, weapon_shape);
00743 }
00744 if (!info)
00745 {
00746 projectile_shape = ammo_shape = 0;
00747 projectile_range = 0;
00748 strike_range = 1;
00749 is_thrown = returns = no_blocking = false;
00750 if (spellbook)
00751 {
00752 projectile_range = 10;
00753 no_blocking = true;
00754 }
00755 }
00756 else
00757 {
00758 projectile_shape = info->get_projectile();
00759 ammo_shape = info->get_ammo_consumed();
00760 strike_range = info->get_striking_range();
00761 projectile_range = info->get_projectile_range();
00762
00763 returns = info->returns();
00764 is_thrown = info->is_thrown();
00765 no_blocking = info->no_blocking();
00766 if (ammo_shape)
00767 {
00768 Ammo_info *ainfo =
00769 ShapeID::get_info(ammo_shape).get_ammo_info();
00770 if (ainfo)
00771 no_blocking =
00772 no_blocking || ainfo->no_blocking();
00773 }
00774 }
00775 max_range = projectile_range > strike_range ? projectile_range
00776 : strike_range;
00777 if (state == strike || state == fire)
00778 state = approach;
00779 }
00780
00781
00782
00783
00784
00785 inline int Need_new_opponent
00786 (
00787 Game_window *gwin,
00788 Actor *npc
00789 )
00790 {
00791 Game_object *opponent = npc->get_target();
00792 Actor *act;
00793
00794 if (!opponent ||
00795 ((act = opponent->as_actor()) != 0 && act->is_dead()) ||
00796
00797 opponent->get_flag(Obj_flags::invisible))
00798 return 1;
00799
00800 return Off_screen(gwin, opponent) && !Off_screen(gwin, npc);
00801 }
00802
00803
00804
00805
00806
00807
00808
00809
00810 static int Use_ammo
00811 (
00812 Actor *npc,
00813 int ammo,
00814 int proj
00815 )
00816 {
00817 Game_object *aobj = npc->get_readied(Actor::ammo);
00818 if (!aobj)
00819 return 0;
00820 int actual_ammo = aobj->get_shapenum();
00821 if (!In_ammo_family(actual_ammo, ammo))
00822 return 0;
00823 npc->remove(aobj);
00824 int quant = aobj->get_quantity();
00825 aobj->modify_quantity(-1);
00826 if (quant > 1)
00827 npc->add_readied(aobj, Actor::ammo);
00828
00829
00830 return ammo == proj ? actual_ammo : proj;
00831 }
00832
00833
00834
00835
00836
00837
00838 Combat_schedule::Combat_schedule
00839 (
00840 Actor *n,
00841 Schedule_types
00842 prev_sched
00843 ) : Schedule(n), state(initial), prev_schedule(prev_sched),
00844 weapon_shape(0),
00845 ammo_shape(0), projectile_shape(0), spellbook(0),
00846 strike_range(0), projectile_range(0), max_range(0),
00847 practice_target(0), is_thrown(false), yelled(0),
00848 no_blocking(false),
00849 started_battle(false), fleed(0), failures(0), teleport_time(0)
00850 {
00851 Combat_schedule::set_weapon();
00852
00853 Game_window *gwin = Game_window::get_instance();
00854 Monster_info *minf = npc->get_info().get_monster_info();
00855 can_yell = !minf || !minf->cant_yell();
00856 }
00857
00858
00859
00860
00861
00862
00863 void Combat_schedule::now_what
00864 (
00865 )
00866 {
00867 Game_window *gwin = Game_window::get_instance();
00868 if (state == initial)
00869 {
00870
00871 if (npc->distance(gwin->get_camera_actor()) > 50)
00872 {
00873 npc->set_dormant();
00874 return;
00875 }
00876 state = approach;
00877 npc->start(200, 200);
00878 return;
00879 }
00880 if (npc->get_flag(Obj_flags::asleep))
00881 {
00882 npc->start(200, 1000);
00883 return;
00884 }
00885
00886 if (npc->get_attack_mode() == Actor::flee)
00887 {
00888 if (fleed > 2 && !gwin->in_combat() &&
00889 npc->get_party_id() >= 0)
00890
00891 npc->set_schedule_type(Schedule::follow_avatar);
00892 else
00893 run_away();
00894 return;
00895 }
00896
00897 if (Need_new_opponent(gwin, npc))
00898 {
00899 npc->set_target(0);
00900 state = approach;
00901 }
00902 Game_object *opponent = npc->get_target();
00903
00904 bool strange = npc->get_info().has_strange_movement() != false;
00905 switch (state)
00906 {
00907 case approach:
00908 if (opponent)
00909 start_strike();
00910 else
00911 approach_foe();
00912 break;
00913 case strike:
00914 state = approach;
00915
00916 npc->start(gwin->get_std_delay(), strange
00917 ? 4*gwin->get_std_delay() : gwin->get_std_delay());
00918 if (npc->get_footprint().enlarge(strike_range).intersects(
00919 opponent->get_footprint()))
00920 {
00921 int dir = npc->get_direction(opponent);
00922 if (!strange)
00923 npc->change_frame(npc->get_dir_framenum(dir,
00924 Actor::standing));
00925
00926 if (weapon_shape == 604)
00927 {
00928 npc->remove_quantity(1, weapon_shape,
00929 c_any_qual, c_any_framenum);
00930 Combat_schedule::set_weapon();
00931 }
00932
00933 Actor *safenpc = npc;
00934 safenpc->set_target(opponent->attacked(npc));
00935
00936 Game_object *newtarg = safenpc->get_target();
00937 if (newtarg && !newtarg->as_actor())
00938 safenpc->set_target(0);
00939 return;
00940 }
00941 break;
00942 case fire:
00943 {
00944 failures = 0;
00945 state = approach;
00946
00947 int ashape = ammo_shape, wshape = weapon_shape,
00948 pshape = projectile_shape;
00949 int delay = (strange || spellbook) ? 6*gwin->get_std_delay()
00950 : gwin->get_std_delay();
00951 if (is_thrown)
00952 {
00953 if (returns)
00954 {
00955 ashape = wshape;
00956 delay = (1 + npc->distance(opponent))*
00957 gwin->get_std_delay();
00958 state = wait_return;
00959 }
00960 if (npc->remove_quantity(1, wshape,
00961 c_any_qual, c_any_framenum) == 0)
00962 {
00963 npc->add_dirty();
00964 ashape = wshape;
00965 Combat_schedule::set_weapon();
00966 }
00967 }
00968 else if (spellbook)
00969 {
00970 ashape = 0;
00971 if (!spellbook->do_spell(npc, true))
00972 Combat_schedule::set_weapon();
00973 }
00974 else
00975 ashape = ashape ? Use_ammo(npc, ashape, pshape)
00976 : (pshape ? pshape : wshape);
00977 if (ashape > 0)
00978 gwin->get_effects()->add_effect(
00979 new Projectile_effect(npc, opponent,
00980 ashape, wshape));
00981 npc->start(gwin->get_std_delay(), delay);
00982 break;
00983 }
00984 case wait_return:
00985 state = approach;
00986 npc->start(gwin->get_std_delay(), gwin->get_std_delay());
00987 break;
00988 default:
00989 break;
00990 }
00991 if (failures > 5 && npc != gwin->get_camera_actor())
00992 {
00993 if (combat_trace) {
00994 cout << npc->get_name() << " is giving up" << endl;
00995 }
00996 if (npc->get_party_id() >= 0)
00997 {
00998 npc->walk_to_tile(
00999 gwin->get_main_actor()->get_tile(),
01000 gwin->get_std_delay());
01001
01002 npc->set_schedule_type(Schedule::follow_avatar);
01003 }
01004 else if (!gwin->get_win_rect().intersects(
01005 gwin->get_shape_rect(npc)))
01006 {
01007 gwin->get_tqueue()->remove(npc);
01008 npc->set_dormant();
01009 }
01010 else if (npc->get_alignment() == Npc_actor::friendly &&
01011 prev_schedule != Schedule::combat)
01012 {
01013 npc->update_schedule(gclock->get_hour()/3, 7);
01014 if (npc->get_schedule_type() == Schedule::combat)
01015 npc->set_schedule_type(prev_schedule);
01016 }
01017 else
01018 {
01019 Tile_coord t = npc->get_tile();
01020 int dist = 2+rand()%3;
01021 int newx = t.tx - dist + rand()%(2*dist);
01022 int newy = t.ty - dist + rand()%(2*dist);
01023
01024 npc->walk_to_tile(newx, newy, t.tz,
01025 2*gwin->get_std_delay(), rand()%1000);
01026 }
01027 }
01028 }
01029
01030
01031
01032
01033
01034 void Combat_schedule::im_dormant
01035 (
01036 )
01037 {
01038 if (npc->get_alignment() == Npc_actor::friendly &&
01039 prev_schedule != npc->get_schedule_type() && npc->is_monster())
01040
01041 npc->set_schedule_type(prev_schedule);
01042 }
01043
01044
01045
01046
01047
01048 void Combat_schedule::ending
01049 (
01050 int
01051 )
01052 {
01053 Game_window *gwin = Game_window::get_instance();
01054 if (gwin->get_main_actor() == npc &&
01055
01056 !gwin->get_usecode()->in_usecode())
01057 {
01058 find_opponents();
01059 bool found = false;
01060 Tile_coord pos = npc->get_tile();
01061 for (Actor_queue::const_iterator it = opponents.begin();
01062 it != opponents.end(); ++it)
01063 {
01064 Actor *opp = *it;
01065 Tile_coord opppos = opp->get_tile();
01066 if (opppos.distance(pos) < (300/2)/c_tilesize &&
01067 Fast_pathfinder_client::is_grabable(pos, opppos))
01068 {
01069 found = true;
01070 break;
01071 }
01072 }
01073 if (found)
01074 Audio::get_ptr()->start_music_combat(CSRun_Away,
01075 false);
01076 }
01077 }
01078
01079
01080
01081
01082
01083
01084 Duel_schedule::Duel_schedule
01085 (
01086 Actor *n
01087 ) : Combat_schedule(n, duel), start(n->get_tile()),
01088 attacks(0)
01089 {
01090 started_battle = true;
01091 }
01092
01093
01094
01095
01096
01097 void Ready_duel_weapon
01098 (
01099 Actor *npc,
01100 int wshape,
01101 int ashape
01102 )
01103 {
01104 Game_map *gmap = Game_window::get_instance()->get_map();
01105 Game_object *weap = npc->get_readied(Actor::lhand);
01106 if (!weap || weap->get_shapenum() != wshape)
01107 {
01108 Game_object *newweap =
01109 npc->find_item(wshape, c_any_qual, c_any_framenum);
01110 if (newweap)
01111 newweap->remove_this(1);
01112 else
01113 newweap = gmap->create_ireg_object(wshape, 0);
01114 if (weap)
01115 weap->remove_this(1);
01116 npc->add(newweap, 1);
01117 if (weap)
01118 npc->add(weap, 1);
01119 }
01120 if (ashape == -1)
01121 return;
01122
01123 Game_object *aobj = npc->get_readied(Actor::ammo);
01124 if (aobj)
01125 aobj->remove_this();
01126 Game_object *arrows = gmap->create_ireg_object(ashape, 0);
01127 int extra = rand()%3;
01128 if (extra)
01129 arrows->modify_quantity(extra);
01130 npc->add(arrows, 1);
01131 }
01132
01133
01134
01135
01136
01137 void Duel_schedule::find_opponents
01138 (
01139 )
01140 {
01141 opponents.clear();
01142 attacks = 0;
01143 practice_target = 0;
01144 int r = rand()%3;
01145 if (r == 0)
01146 {
01147 practice_target = npc->find_closest(735);
01148 if (practice_target)
01149 Ready_duel_weapon(npc, 597, 722);
01150 }
01151 if (!practice_target)
01152 {
01153 Ready_duel_weapon(npc, 602, -1);
01154 if (r == 1)
01155 practice_target = npc->find_closest(860);
01156 }
01157 Combat_schedule::set_weapon();
01158 if (practice_target)
01159 {
01160 npc->set_target(practice_target);
01161 return;
01162 }
01163 Actor_vector vec;
01164 npc->find_nearby_actors(vec, c_any_shapenum, 24);
01165 for (Actor_vector::const_iterator it = vec.begin(); it != vec.end();
01166 ++it)
01167 {
01168 Actor *opp = *it;
01169 Game_object *oppopp = opp->get_target();
01170 if (opp != npc && opp->get_schedule_type() == duel &&
01171 (!oppopp || oppopp == npc))
01172 if (rand()%2)
01173 opponents.push(opp);
01174 else
01175 opponents.push_front(opp);
01176 }
01177 }
01178
01179
01180
01181
01182
01183 void Duel_schedule::now_what
01184 (
01185 )
01186 {
01187 if (state == strike || state == fire)
01188 {
01189 attacks++;
01190
01191 if (practice_target && practice_target->get_shapenum() == 735
01192 && practice_target->get_framenum() > 0 &&
01193 practice_target->get_framenum()%3 == 0)
01194 {
01195 attacks = 0;
01196
01197 practice_target->change_frame(0);
01198 }
01199 }
01200 else
01201 {
01202 Combat_schedule::now_what();
01203 return;
01204 }
01205 if (attacks%8 == 0)
01206 {
01207 npc->set_target(0);
01208 Tile_coord pos = start;
01209 pos.tx += rand()%24 - 12;
01210 pos.ty += rand()%24 - 12;
01211
01212 Tile_coord dest = Map_chunk::find_spot(pos, 3, npc, 1);
01213 if (dest.tx == -1 || !npc->walk_path_to_tile(dest,
01214 gwin->get_std_delay(), rand()%2000))
01215
01216 npc->start(250, rand()%3000);
01217 }
01218 else
01219 Combat_schedule::now_what();
01220 }
01221
01222
01223
01224
01225
01226 void Combat::toggle_pause
01227 (
01228 )
01229 {
01230 if (!paused && mode == original)
01231 return;
01232 if (paused)
01233 {
01234 resume();
01235 eman->center_text("Combat resumed");
01236 }
01237 else
01238 {
01239 gwin->get_tqueue()->pause(SDL_GetTicks());
01240 eman->center_text("Combat paused");
01241 paused = true;
01242 }
01243 }
01244
01245
01246
01247
01248
01249 void Combat::resume
01250 (
01251 )
01252 {
01253 if (!paused)
01254 return;
01255 gwin->get_tqueue()->resume(SDL_GetTicks());
01256 paused = false;
01257 }