//============================================= // Movable.C // // Class for moving-objects (worms, etc.) // // ZNibbles // Vincent Mallet 1997, 1998 //============================================= // $Id: Movable.C,v 1.7 1999/05/11 02:15:20 vmallet Exp $ /* ZNibbles - a small multiplayer game * Copyright (C) 1997, 1998 Vincent Mallet - vmallet@enst.fr * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include "Movable.H" #include "BaseInterface.H" #include "Map.H" #include "Nibble.H" #include "Player.H" #include "Trame.H" #include "World.H" // add player description to frame t void Movable::add_description(Trame& t) { t.put_char(NEW_MOVABLE); add_description0(t); } void Movable::read_description(Trame& t) { if (t.get_char() != NEW_MOVABLE) { std::cerr << "Movable::read_description(): ohhh le bordel!\n"; exit(1); } read_description0(t); } void Movable::add_description0(Trame& t) { LongObject::add_description0(t); t.put_char(speed); t.put_char(direction); t.put_short(full_length); t.put_int(player_id); t.put_char(paused); t.put_char(baby); t.put_char(chacal); t.put_char(cut); } void Movable::read_description0(Trame& t) { LongObject::read_description0(t); speed = t.get_char(); direction = t.get_char(); full_length = t.get_short(); player_id = t.get_int(); // attention: ne pas oublier de mettre // a jour le champ player plus tard! paused = t.get_char(); baby = t.get_char(); chacal = t.get_char(); cut = t.get_char(); } // Display text representation of this Movable. // for debug purposes void Movable::display() { std::cout << " ID:"<< id; printf(" %04x", classtype); std::cout <<" Movable " << pos.x << "x" << pos.y << " sp=" << speed << " dir=" << direction << " len=" << length << " full=" << full_length << " pid=" << player_id << " paused=" << paused << " q=[ " ; Pix pix = queue.first(); for (int i = length - 1; i > 0; i--) { std::cout << queue(pix) << " "; queue.next(pix); } std::cout << "]" << std::endl; } void Movable::draw(Map& map) { _Position p = pos; char col = 100; if (player_id) col += player->get_number(); if (dying) { map.draw_point(p.x, p.y, 'X'); for (Pix q = queue.first(); q; queue.next(q)) { update_pos(p, queue(q)); map.draw_point(p.x, p.y, 'X'); } } else { if (world.playcycle & 1) // make the head move! switch (direction) { case D_STOPPED: if (world.playcycle & 2) map.draw_point(p.x, p.y, 'T'); else map.draw_point(p.x, p.y, 'G'); break; case D_DOWN: map.draw_point(p.x, p.y, 'D'); break; case D_UP: map.draw_point(p.x, p.y, 'U'); break; case D_LEFT: map.draw_point(p.x, p.y, 'L'); break; case D_RIGHT: map.draw_point(p.x, p.y, 'R'); break; } else switch (direction) { case D_STOPPED: map.draw_point(p.x, p.y, (player) ? player->get_number() : 0); break; case D_DOWN: map.draw_point(p.x, p.y, 'B'); break; case D_UP: map.draw_point(p.x, p.y, 'H'); break; case D_LEFT: map.draw_point(p.x, p.y, 'G'); break; case D_RIGHT: map.draw_point(p.x, p.y, 'T'); break; } for (Pix q = queue.first(); q; queue.next(q)) { update_pos(p, queue(q)); map.draw_point(p.x, p.y, col); } } } int Movable::move(Direction dir) { if (!direction) return 0; if (length < full_length) return grow(dir); else { int ret = update_pos(pos, dir); if (length>1) queue.remove_rear(); queue.prepend(- dir); return ret; } } /* * given a new direction (left or right), return the * new direction deducted from current direction (ie * for two_keys mode) */ Direction Movable::two_key_translate(Direction input) { if (direction == D_STOPPED) /* we can start in either four directions */ return input; switch(input) { case D_LEFT: switch(direction) { case D_LEFT: return D_DOWN; case D_RIGHT: return D_UP; case D_DOWN: return D_RIGHT; case D_UP: return D_LEFT; } break; case D_RIGHT: switch(direction) { case D_LEFT: return D_UP; case D_RIGHT: return D_DOWN; case D_DOWN: return D_LEFT; case D_UP: return D_RIGHT; } break; } return direction; } // here's the computer brain... NOT! :-) int Movable::auto_dir() { int dochange; _Position p = pos; if (update_pos(p, direction)) dochange = 1; else dochange = !(rand()%5); if (dochange) { int newdir; int olddir; int tries = 0; if (length == 1) olddir = 20; else olddir = queue.front(); do { p = pos; do { newdir = (rand() % 6 - 2); } while (newdir == olddir || newdir == 0 || newdir == - olddir); // @@ // @@ there must be better way to do that. } while (tries++ < 2 && update_pos(p, newdir)); p = pos; if (update_pos(p, newdir)) if (D_DOWN != olddir && D_DOWN != newdir && !update_pos(p = pos, D_DOWN)) newdir = D_DOWN; else if (D_UP != olddir && D_UP != newdir && !update_pos(p = pos, D_UP)) newdir = D_UP; else if (D_LEFT != olddir && D_LEFT != newdir && !update_pos(p = pos, D_LEFT)) newdir = D_LEFT; else if (D_RIGHT != olddir && D_RIGHT != newdir && !update_pos(p = pos, D_RIGHT)) newdir = D_RIGHT; // else // std::cout << "Movable::auto_dir(): couldn't find a way to go!" << std::endl; if (direction != newdir) { direction = newdir; return 1; } } return 0; } // collision check // quick'n'dirty. Hu, we'll clean that later. // redondance a mort pour eviter de bugger les parties qui // tournent ok quand on rajoute un nouveau flag... // faudra nettoyer ca aussi, clairement. void Movable::check_collide() { if (dying) return; MapType * maptype = world.map.get_type(pos.x, pos.y); int cnt = 0; if (baby) { for (MapType *mt = maptype; mt && cnt<2; mt=mt->next) { if (mt->object->instance_of(CL_LONGOBJECT)) cnt++; if (cnt >= 2) { self_die(); return; } } } else if (cut) { for (MapType *mt = maptype; mt; mt=mt->next) { if (mt->object->instance_of(CL_LONGOBJECT)) { if (!mt->object->instance_of(CL_MOVABLE) || ((Movable *) mt->object)->pos == pos) cnt++; else { // charcute! ((Movable *) mt->object)->getcut(pos); } } if (cnt >= 2) { self_die(); return; } } } else { for (MapType *mt = maptype; mt; mt=mt->next) { if (mt->object->instance_of(CL_LONGOBJECT)) { if (!mt->object->instance_of(CL_MOVABLE) || !( ((Movable *) mt->object)->baby )) cnt++; } if (cnt >= 2) { self_die(); return; } } } // on ne mange rien quand on est baby if (!baby) for (MapType *mt = maptype; mt; mt=mt->next) { if (mt->object != this && mt->object->instance_of(CL_NIBBLE) && !(mt->object->dying) ) eat( * (Nibble *) mt->object ); } } // Miam! void Movable::eat(Nibble& nib) { switch(nib.type) { case NT_STANDARD: full_length += nib.value; if (player) player->add_score(nib.value); break; case NT_CHACAL: world.interface->display_system_message("got the CHACAL POWER! " \ "Watch out!\n", player); chacal = 100; // hahha, et les parametres ils sont ou??? break; case NT_CUT: world.interface->display_system_message("got the CUT Power! " \ "Rrrrzzzzzzzzzzz(attention, " \ "ca bug)\n", player); cut = 100; break; } nib.dying = 1; } // here we get cut in two pieces. Glurps! void Movable::getcut(_Position cutpos) { _Position pos2 = pos; if (length == 1) { std::cerr << "getcut too short" << std::endl; return; } Pix p = queue.first(); update_pos(pos2, queue(p)); while (pos2 != cutpos && p) { queue.next(p); update_pos(pos2, queue(p)); } // should never happen. (should be an assertion) if (!p) { std::cerr << "Movable::cut(): reached end of queue!" << std::endl; return; } queue.del(p, 1); length--; full_length--; if (p) { Movable worm(world); Direction d = queue(p); while (p) { queue.del(p, 1); length--; full_length--; if (p) { update_pos(pos2, d); worm.queue.prepend(- d); worm.length++; d = queue(p); } } worm.pos = pos2; worm.full_length = worm.length; if (worm.length > 1) worm.direction = - worm.queue.front(); //evidemment ca bug! world.add_object(worm); } //else // std::cerr << "petard de pas de p" << std::endl; } void Movable::cycle() { if (!paused) { if (baby) // bon, baby+chacal+cut = a mega revoir if (direction) // (on reste baby tant qu'on ne bouge pas.. baby--; // et encore pendant baby cycles) if (chacal) { chacal--; if (!chacal) world.interface->display_system_message("lost chacal\n"); } if (cut) { cut--; if (!cut) world.interface->display_system_message("lost cut\n"); } if (move(direction)) self_die(); } } void Movable::server_cycle() { if (!paused) { cycle(); if (!player_id) if (auto_dir()) { world.cycle_trame.put_char(CHANGE_DIRECTION); world.cycle_trame.put_int(id); world.cycle_trame.put_char(direction); } } } void Movable::self_die() { dying = 1; if (player_id) player->die(); }