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 "monsters.h"
00026 #include "monstinf.h"
00027 #include "gamewin.h"
00028 #include "animate.h"
00029 #include "schedule.h"
00030 #include "chunks.h"
00031 #include "Audio.h"
00032 #include "gamemap.h"
00033 #include "game.h"
00034 #include "effects.h"
00035
00036 #ifndef UNDER_CE
00037 using std::rand;
00038 #endif
00039
00040
00041
00042
00043 class Slime_actor : public Monster_actor
00044 {
00045 void update_frames(Tile_coord src, Tile_coord dest);
00046 public:
00047 Slime_actor(const std::string &nm, int shapenum, int num = -1,
00048 int uc = -1)
00049 : Monster_actor(nm, shapenum, num, uc)
00050 { }
00051
00052 virtual int step(Tile_coord t, int frame);
00053
00054 virtual void remove_this(int nodel = 0);
00055
00056 virtual void move(int newtx, int newty, int newlift);
00057 };
00058
00059
00060
00061
00062 class Quaking_actor : public Monster_actor
00063 {
00064 int qsteps;
00065 int steps;
00066 public:
00067 Quaking_actor(const std::string &nm, int shapenum, int qs = 5,
00068 int num = -1, int uc = -1)
00069 : Monster_actor(nm, shapenum, num, uc), qsteps(qs), steps(0)
00070 { }
00071
00072 virtual int step(Tile_coord t, int frame);
00073 };
00074
00075 Monster_actor *Monster_actor::in_world = 0;
00076
00077
00078
00079
00080
00081 void Monster_actor::link_in
00082 (
00083 )
00084 {
00085 if (prev_monster || in_world == this)
00086 return;
00087 if (in_world)
00088 in_world->prev_monster = this;
00089 next_monster = in_world;
00090 in_world = this;
00091 }
00092
00093
00094
00095
00096
00097 void Monster_actor::link_out
00098 (
00099 )
00100 {
00101 if (next_monster)
00102 next_monster->prev_monster = prev_monster;
00103 if (prev_monster)
00104 prev_monster->next_monster = next_monster;
00105 else
00106 if (in_world == this)
00107 in_world = next_monster;
00108 next_monster = prev_monster = 0;
00109 }
00110
00111
00112
00113
00114
00115 Monster_actor::Monster_actor
00116 (
00117 const std::string &nm,
00118 int shapenum,
00119 int num,
00120 int uc
00121 ) : Npc_actor(nm, shapenum, num, uc), prev_monster(0),
00122 next_monster(0), animator(0)
00123 {
00124
00125 Shape_info& info = get_info();
00126 if (info.is_animated())
00127 animator = Animator::create(this);
00128 }
00129
00130
00131
00132
00133
00134 Monster_actor::~Monster_actor
00135 (
00136 )
00137 {
00138 delete animator;
00139 link_out();
00140 }
00141
00142
00143
00144
00145
00146
00147 static int Monster_food[] = {
00148 498, 10,
00149 500, 9,
00150 502, 14,
00151 509, 12,
00152 811, 9,
00153 970, 8,
00154 727, 23,
00155 329, 11
00156 };
00157
00158
00159
00160
00161
00162
00163
00164 static int Find_monster_food
00165 (
00166 int shnum
00167 )
00168 {
00169 const int cnt = sizeof(Monster_food)/(2*sizeof(Monster_food[0]));
00170 for (int i = 0; i < cnt; i++)
00171 if (Monster_food[2*i] == shnum)
00172 return Monster_food[2*i + 1];
00173 return rand()%32;
00174 }
00175
00176
00177
00178
00179
00180 Monster_actor *Monster_actor::create
00181 (
00182 int shnum
00183 )
00184 {
00185 if (shnum == 529)
00186
00187 return new Slime_actor("", shnum, -1, shnum);
00188 else if (shnum == 501 || (shnum == 380 && GAME_BG))
00189 return new Quaking_actor("", shnum, 5, -1, shnum);
00190 else
00191 return new Monster_actor("", shnum, -1, shnum);
00192 }
00193
00194
00195
00196
00197
00198 Monster_actor *Monster_actor::create
00199 (
00200 int shnum,
00201 Tile_coord pos,
00202
00203 int sched,
00204 int align,
00205 bool temporary,
00206 bool equipment
00207 )
00208 {
00209
00210 const Monster_info *inf = ShapeID::get_info(shnum).get_monster_info();
00211 if (!inf)
00212 inf = Monster_info::get_default();
00213 Monster_actor *monster = create(shnum);
00214 monster->set_alignment(align == static_cast<int>(Actor::neutral)
00215 ? inf->alignment : align);
00216
00217 if ((inf->flags >> Monster_info::fly)&1)
00218 {
00219 monster->set_type_flag(Actor::tf_fly);
00220 }
00221 if ((inf->flags >> Monster_info::swim)&1)
00222 {
00223 monster->set_type_flag(Actor::tf_swim);
00224 }
00225 if ((inf->flags >> Monster_info::walk)&1)
00226 {
00227 monster->set_type_flag(Actor::tf_walk);
00228 }
00229 if ((inf->flags >> Monster_info::ethereal)&1)
00230 {
00231 monster->set_type_flag(Actor::tf_ethereal);
00232 }
00233 monster->set_property(Actor::strength, inf->strength);
00234
00235 monster->set_property(Actor::health, inf->strength);
00236 monster->set_property(Actor::dexterity, inf->dexterity);
00237 monster->set_property(Actor::intelligence, inf->intelligence);
00238 monster->set_property(Actor::combat, inf->combat);
00239
00240
00241 if (temporary) monster->set_flag (Obj_flags::is_temporary);
00242 monster->set_invalid();
00243 if (pos.tx >= 0)
00244 monster->move(pos.tx, pos.ty, pos.tz);
00245 if (equipment) {
00246
00247 int equip_offset = inf->equip_offset;
00248 Equip_record *equip = inf->equip;
00249 if (equip_offset && equip_offset - 1 < inf->equip_cnt)
00250 {
00251 Equip_record& rec = equip[equip_offset - 1];
00252 for (size_t i = 0;
00253 i < sizeof(equip->elements)/sizeof(
00254 equip->elements[0]);
00255 i++)
00256 {
00257 Equip_element& elem = rec.elements[i];
00258 if (!elem.shapenum || 1 + rand()%100 >
00259 elem.probability)
00260 continue;
00261 int frnum = (elem.shapenum == 377) ?
00262 Find_monster_food(shnum) : 0;
00263 monster->create_quantity(elem.quantity,
00264 elem.shapenum, c_any_qual,
00265 frnum, temporary);
00266 }
00267 }
00268 }
00269
00270 if (sched < 0)
00271 sched = static_cast<int>(Schedule::loiter);
00272 monster->set_schedule_type(sched);
00273 return (monster);
00274 }
00275
00276
00277
00278
00279
00280 void Monster_actor::delete_all
00281 (
00282 )
00283 {
00284 while (in_world)
00285 delete in_world;
00286 }
00287
00288
00289
00290
00291
00292 void Monster_actor::paint
00293 (
00294 )
00295 {
00296
00297 if (animator)
00298 animator->want_animation();
00299 Npc_actor::paint();
00300 }
00301
00302
00303
00304
00305
00306
00307
00308
00309 int Monster_actor::step
00310 (
00311 Tile_coord t,
00312 int frame
00313 )
00314 {
00315
00316
00317 if (!gwin->emulate_is_move_allowed(t.tx, t.ty))
00318 return (0);
00319 if (get_flag(Obj_flags::paralyzed))
00320 return 0;
00321
00322 int old_cx = get_cx(), old_cy = get_cy();
00323
00324 int cx = t.tx/c_tiles_per_chunk, cy = t.ty/c_tiles_per_chunk;
00325
00326 Map_chunk *nlist = gmap->get_chunk(cx, cy);
00327 nlist->setup_cache();
00328
00329 if (is_blocked(t))
00330 {
00331 if (schedule)
00332 schedule->set_blocked(t);
00333 stop();
00334 if (!gwin->add_dirty(this))
00335 dormant = true;
00336 return (0);
00337 }
00338
00339 gwin->scroll_if_needed(this, t);
00340 add_dirty();
00341
00342 Map_chunk *olist = gmap->get_chunk(old_cx, old_cy);
00343
00344
00345 int tx = t.tx%c_tiles_per_chunk, ty = t.ty%c_tiles_per_chunk;
00346 movef(olist, nlist, tx, ty, frame, t.tz);
00347 if (!add_dirty(1) &&
00348
00349 distance(gwin->get_camera_actor()) > 1 + 320/c_tilesize)
00350 {
00351 stop();
00352 dormant = true;
00353 return (0);
00354 }
00355 return (1);
00356 }
00357
00358
00359
00360
00361
00362
00363 void Monster_actor::remove_this
00364 (
00365 int nodel
00366 )
00367 {
00368 link_out();
00369 Npc_actor::remove_this(nodel);
00370 }
00371
00372
00373
00374
00375
00376 void Monster_actor::move
00377 (
00378 int newtx,
00379 int newty,
00380 int newlift
00381 )
00382 {
00383 Npc_actor::move(newtx, newty, newlift);
00384 link_in();
00385 }
00386
00387
00388
00389
00390
00391
00392
00393
00394 bool Monster_actor::add
00395 (
00396 Game_object *obj,
00397 bool dont_check,
00398 bool combine
00399
00400 )
00401 {
00402
00403 if (Npc_actor::add(obj, true, combine))
00404 return (true);
00405
00406 return Container_game_object::add(obj, true, combine);
00407 }
00408
00409
00410
00411
00412
00413 int Monster_actor::get_armor_points
00414 (
00415 )
00416 {
00417 Monster_info *inf = get_info().get_monster_info();
00418
00419 return Actor::get_armor_points() + (inf ? inf->armor : 0);
00420 }
00421
00422
00423
00424
00425
00426 Weapon_info *Monster_actor::get_weapon
00427 (
00428 int& points,
00429 int& shape
00430 )
00431 {
00432 Monster_info *inf = get_info().get_monster_info();
00433
00434 Weapon_info *winf = Actor::get_weapon(points, shape);
00435 if (!winf)
00436 {
00437 shape = 0;
00438 winf = get_info().get_weapon_info();
00439 if (winf)
00440 {
00441 shape = get_shapenum();
00442 points = winf->get_damage();
00443 }
00444 else if (inf)
00445 points = inf->weapon;
00446 }
00447 return winf;
00448 }
00449
00450
00451
00452
00453
00454 void Monster_actor::die
00455 (
00456 Actor *attacker
00457 )
00458 {
00459 Actor::die(attacker);
00460
00461
00462 }
00463
00464
00465
00466
00467
00468
00469 static void Get_slime_neighbors
00470 (
00471 Tile_coord pos,
00472 Tile_coord *neighbors
00473 )
00474 {
00475
00476 static int offsets[8] = {0,-2, 2,0, 0,2, -2,0};
00477 for (int dir = 0; dir < 4; dir++)
00478 neighbors[dir] = pos +Tile_coord(offsets[2*dir],
00479 offsets[2*dir + 1], 0);
00480 }
00481
00482
00483
00484
00485
00486
00487
00488 int Find_neighbor
00489 (
00490 Game_object *slime,
00491 Tile_coord *neighbors
00492 )
00493 {
00494 Tile_coord pos = slime->get_tile();
00495 for (int dir = 0; dir < 4; dir++)
00496 if (pos == neighbors[dir])
00497 return dir;
00498 return -1;
00499 }
00500
00501
00502
00503
00504
00505
00506
00507
00508 void Slime_actor::update_frames
00509 (
00510 Tile_coord src,
00511 Tile_coord dest
00512
00513
00514 )
00515 {
00516 Tile_coord neighbors[4];
00517 int dir;
00518 Game_object_vector nearby;
00519 if (src.tx != -1)
00520 if (dest.tx != -1)
00521 Game_object::find_nearby(nearby, dest, 529, 4, 8);
00522 else
00523 Game_object::find_nearby(nearby, src, 529, 2, 8);
00524 else
00525 Game_object::find_nearby(nearby, dest, 529, 2, 8);
00526 if (src.tx != -1)
00527 {
00528 Get_slime_neighbors(src, neighbors);
00529 for (Game_object_vector::const_iterator it = nearby.begin();
00530 it != nearby.end(); ++it)
00531 {
00532 Game_object *slime = *it;
00533 if (slime != this &&
00534 (dir = Find_neighbor(slime, neighbors)) >= 0)
00535 {
00536 int ndir = (dir+2)%4;
00537
00538
00539 slime->change_frame((slime->get_framenum()&
00540 ~(((1<<ndir)*2)|1)) |(rand()%2));
00541 }
00542 }
00543 }
00544 if (dest.tx != -1)
00545 {
00546 int frnum = 0;
00547 Get_slime_neighbors(dest, neighbors);
00548 for (Game_object_vector::const_iterator it = nearby.begin();
00549 it != nearby.end(); ++it)
00550 {
00551 Game_object *slime = *it;
00552 if (slime != this &&
00553 (dir = Find_neighbor(slime, neighbors)) >= 0)
00554 {
00555 frnum |= (1<<dir)*2;
00556 int ndir = (dir+2)%4;
00557
00558
00559 slime->change_frame((slime->get_framenum()&~1)|
00560 ((1<<ndir)*2)|(rand()%2));
00561 }
00562 }
00563 change_frame(frnum|(rand()%2));
00564 }
00565 }
00566
00567
00568
00569
00570
00571
00572
00573
00574 int Slime_actor::step
00575 (
00576 Tile_coord t,
00577 int
00578 )
00579 {
00580
00581 Tile_coord oldpos = get_tile();
00582 int ret = Monster_actor::step(t, -1);
00583
00584 Tile_coord newpos = get_tile();
00585 update_frames(oldpos, newpos);
00586 Game_object_vector blood;
00587 if (newpos != oldpos && rand()%9 == 0 &&
00588 !find_nearby(blood, oldpos, 912, 1, 0))
00589 {
00590
00591 Game_object *b = gmap->create_ireg_object(912, 4 + rand()%8);
00592 b->set_flag(Obj_flags::is_temporary);
00593 b->move(oldpos);
00594 }
00595 return ret;
00596 }
00597
00598
00599
00600
00601
00602
00603 void Slime_actor::remove_this
00604 (
00605 int nodel
00606 )
00607 {
00608 Tile_coord pos = get_tile();
00609 Monster_actor::remove_this(nodel);
00610
00611 update_frames(pos, Tile_coord(-1, -1, -1));
00612 }
00613
00614
00615
00616
00617
00618
00619 void Slime_actor::move
00620 (
00621 int newtx,
00622 int newty,
00623 int newlift
00624 )
00625 {
00626
00627 Tile_coord pos = get_tile();
00628 if (get_cx() == 255)
00629 pos = Tile_coord(-1, -1, -1);
00630 Monster_actor::move(newtx, newty, newlift);
00631
00632 update_frames(pos, get_tile());
00633 }
00634
00635
00636
00637
00638
00639
00640
00641
00642 int Quaking_actor::step
00643 (
00644 Tile_coord t,
00645 int frame
00646 )
00647 {
00648 int ret = Monster_actor::step(t, frame);
00649 if (ret)
00650 {
00651 steps = (steps + 1)%qsteps;
00652 if (!steps)
00653 gwin->get_tqueue()->add(Game::get_ticks() + 10,
00654 new Earthquake(2), 0L);
00655 }
00656 return ret;
00657 }