src/character/body.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002  *  Wormux is a convivial mass murder game.
00003  *  Copyright (C) 2001-2004 Lawrence Azzoug.
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
00018  ******************************************************************************
00019  * Character of a team.
00020  *****************************************************************************/
00021 #include "body.h"
00022 #include <sstream>
00023 #include <iostream>
00024 #include <map>
00025 #include "clothe.h"
00026 #include "member.h"
00027 #include "../team/teams_list.h"
00028 #include "../game/time.h"
00029 #include "../tool/debug.h"
00030 #include "../tool/random.h"
00031 #include "../tool/resource_manager.h"
00032 #include "../tool/xml_document.h"
00033 #include "../particles/body_member.h"
00034 #include "../particles/teleport_member.h"
00035 
00036 Body::Body(xmlpp::Element* xml, Profile* res)
00037 {
00038   need_rebuild = true;
00039   current_clothe = NULL;
00040   current_mvt = NULL;
00041   walk_events = 0;
00042   animation_number = 0;
00043   direction = DIRECTION_RIGHT;
00044   main_rotation_rad = 0;
00045 
00046   // Load members
00047   xmlpp::Node::NodeList nodes = xml -> get_children("sprite");
00048   xmlpp::Node::NodeList::iterator it=nodes.begin();
00049 
00050   while(it != nodes.end())
00051    {
00052     xmlpp::Element *elem = dynamic_cast<xmlpp::Element*> (*it);
00053     std::string name;
00054     XmlReader::ReadStringAttr( elem, "name", name);
00055 
00056     Member* member = new Member(elem, res);
00057     if(members_lst.find(name) != members_lst.end())
00058       std::cerr << "Warning !! The member \""<< name << "\" is defined twice in the xml file" << std::endl;
00059     else
00060       members_lst[name] = member;
00061 
00062     it++;
00063   }
00064 
00065   // Add a special weapon member to the body
00066   weapon_member = new WeaponMember();
00067   members_lst["weapon"] = weapon_member;
00068 
00069   // Load clothes
00070   xmlpp::Node::NodeList nodes2 = xml -> get_children("clothe");
00071   xmlpp::Node::NodeList::iterator it2=nodes2.begin();
00072 
00073   while(it2 != nodes2.end())
00074   {
00075     xmlpp::Element *elem = dynamic_cast<xmlpp::Element*> (*it2);
00076     std::string name;
00077     XmlReader::ReadStringAttr( elem, "name", name);
00078 
00079     Clothe* clothe = new Clothe(elem, members_lst);
00080     if (clothes_lst.find(name) != clothes_lst.end())
00081       std::cerr << "Warning !! The clothe \""<< name << "\" is defined twice in the xml file" << std::endl;
00082     else
00083       clothes_lst[name] = clothe;
00084 
00085     it2++;
00086   }
00087 
00088   // Load movements alias
00089   xmlpp::Node::NodeList nodes4 = xml -> get_children("alias");
00090   xmlpp::Node::NodeList::iterator it4=nodes4.begin();
00091   std::map<std::string, std::string> mvt_alias;
00092   while(it4 != nodes4.end())
00093    {
00094     xmlpp::Element *elem = dynamic_cast<xmlpp::Element*> (*it4);
00095     std::string mvt, corresp;
00096     XmlReader::ReadStringAttr( elem, "movement", mvt);
00097     XmlReader::ReadStringAttr( elem, "correspond_to", corresp);
00098     mvt_alias.insert(std::make_pair(mvt,corresp));
00099     it4++;
00100   }
00101 
00102   // Load movements
00103   xmlpp::Node::NodeList nodes3 = xml -> get_children("movement");
00104   xmlpp::Node::NodeList::iterator it3=nodes3.begin();
00105 
00106   while(it3 != nodes3.end())
00107   {
00108     xmlpp::Element *elem = dynamic_cast<xmlpp::Element*> (*it3);
00109     std::string name;
00110     XmlReader::ReadStringAttr( elem, "name", name);
00111     if(strncmp(name.c_str(),"animation", 9)==0)
00112       animation_number++;
00113 
00114     Movement* mvt = new Movement(elem);
00115     if(mvt_lst.find(name) != mvt_lst.end())
00116       std::cerr << "Warning !! The movement \""<< name << "\" is defined twice in the xml file" << std::endl;
00117     else
00118       mvt_lst[name] = mvt;
00119 
00120     for(std::map<std::string, std::string>::iterator it = mvt_alias.begin();
00121         it != mvt_alias.end();  ++it)
00122     if(it->second == name)
00123     {
00124       Movement* mvt = new Movement(elem);
00125       mvt->type = it->first;
00126       mvt_lst[it->first] = mvt;
00127     }
00128     it3++;
00129   }
00130 }
00131 
00132 Body::Body(Body *_body)
00133 {
00134   need_rebuild = true;
00135   current_clothe = NULL;
00136   current_mvt = NULL;
00137   walk_events = 0;
00138   animation_number = _body->animation_number;
00139   direction = DIRECTION_RIGHT;
00140   main_rotation_rad = 0;
00141 
00142   // Add a special weapon member to the body
00143   weapon_member = new WeaponMember();
00144   members_lst["weapon"] = weapon_member;
00145 
00146   // Make a copy of members
00147   std::map<std::string, Member*>::iterator it1 = _body->members_lst.begin();
00148   while(it1 != _body->members_lst.end())
00149   if(it1->second->name != "weapon")
00150   {
00151     std::pair<std::string,Member*> p;
00152     p.first = it1->first;
00153     p.second = new Member(it1->second);
00154     members_lst.insert(p);
00155     it1++;
00156   }
00157   else
00158     it1++;
00159 
00160   // Make a copy of clothes
00161   std::map<std::string, Clothe*>::iterator it2 = _body->clothes_lst.begin();
00162   while(it2 != _body->clothes_lst.end())
00163   {
00164     std::pair<std::string,Clothe*> p;
00165     p.first = it2->first;
00166     p.second = new Clothe(it2->second, members_lst);
00167     clothes_lst.insert(p);
00168     it2++;
00169   }
00170 
00171   // Movement are shared
00172   std::map<std::string, Movement*>::iterator it3 = _body->mvt_lst.begin();
00173   while(it3 != _body->mvt_lst.end())
00174   {
00175     std::pair<std::string,Movement*> p;
00176     p.first = it3->first;
00177     p.second = it3->second;
00178     mvt_lst.insert(p);
00179     it3++;
00180   }
00181 }
00182 
00183 Body::~Body()
00184 {
00185   // Pointers inside those lists are freed from the body_list
00186   // Clean the members list
00187   std::map<std::string, Member*>::iterator it = members_lst.begin();
00188   while(it != members_lst.end())
00189   {
00190     delete it->second;
00191     it++;
00192   }
00193 
00194   // Clean the clothes list
00195   std::map<std::string, Clothe*>::iterator it2 = clothes_lst.begin();
00196   while(it2 != clothes_lst.end())
00197   {
00198     delete it2->second;
00199     it2++;
00200   }
00201 
00202   members_lst.clear();
00203   clothes_lst.clear();
00204   mvt_lst.clear();
00205 }
00206 
00207 void Body::ResetMovement()
00208 {
00209   for(int layer=0;layer < (int)current_clothe->layers.size() ;layer++)
00210     current_clothe->layers[layer]->ResetMovement();
00211 }
00212 
00213 void Body::ApplyMovement(Movement* mvt, uint frame)
00214 {
00215   // Move each member following the movement description
00216   // We do it using the order of the squeleton, as the movement of each
00217   // member affect the child members as well
00218   std::vector<junction>::iterator member = squel_lst.begin();
00219   for(;member != squel_lst.end();
00220        member++)
00221   {
00222     assert( frame < mvt->frames.size() );
00223     if(mvt->frames[frame].find(member->member->type) != mvt->frames[frame].end())
00224     {
00225       // This member needs to be moved :
00226       member_mvt mb_mvt = mvt->frames[frame].find(member->member->type)->second;
00227       if(mb_mvt.follow_crosshair && ActiveCharacter().body == this && ActiveTeam().AccessWeapon().UseCrossHair())
00228       {
00229         // Use the movement of the crosshair
00230         double angle = owner->GetFiringAngle(); /* Get -2 * M_PI < angle =< 2 * M_PI*/
00231         if(angle < 0)
00232           angle += 2 * M_PI; // so now 0 < angle < 2 * M_PI;
00233         if(ActiveCharacter().GetDirection() == DIRECTION_LEFT)
00234           angle = M_PI - angle;
00235 
00236         mb_mvt.SetAngle(mb_mvt.GetAngle() + angle);
00237       }
00238 
00239       if(mb_mvt.follow_half_crosshair && ActiveCharacter().body == this && ActiveTeam().AccessWeapon().UseCrossHair())
00240       {
00241         // Use the movement of the crosshair
00242         double angle_rad = owner->GetFiringAngle(); // returns -180 < angle < 180
00243         if(ActiveCharacter().GetDirection() == DIRECTION_RIGHT)
00244           angle_rad /= 2; // -90 < angle < 90
00245         else
00246         if(angle_rad > M_PI_2)
00247           angle_rad = M_PI_2 - angle_rad / 2;//formerly in deg to 45 + (90 - angle) / 2;
00248         else
00249           angle_rad = -M_PI_2 - angle_rad / 2;//formerly in deg to -45 + (-90 - angle) / 2;
00250 
00251 
00252 
00253         if(angle_rad < 0)
00254           angle_rad += 2 * M_PI; // so now 0 < angle < 2 * M_PI;
00255 
00256         mb_mvt.SetAngle(mb_mvt.GetAngle() + angle_rad);
00257       }
00258 
00259       if(mb_mvt.follow_speed)
00260       {
00261         // Use the movement of the character
00262         double angle_rad = (owner->GetSpeedAngle());
00263         if(angle_rad < 0)
00264           angle_rad += 2 * M_PI; // so now 0 < angle < 2 * M_PI;
00265         if(owner->GetDirection() == DIRECTION_LEFT)
00266           angle_rad = M_PI - angle_rad;
00267 
00268         mb_mvt.SetAngle(mb_mvt.GetAngle() + angle_rad);
00269       }
00270 
00271       if(mb_mvt.follow_direction)
00272       {
00273         // Use the direction of the character
00274         if(owner->GetDirection() == DIRECTION_LEFT)
00275           mb_mvt.SetAngle(mb_mvt.GetAngle() + M_PI);
00276       }
00277 
00278 
00279       member->member->ApplyMovement(mb_mvt, squel_lst);
00280     }
00281   }
00282 }
00283 
00284 void Body::ApplySqueleton()
00285 {
00286   // Move each member following the squeleton
00287   std::vector<junction>::iterator member = squel_lst.begin();
00288   // The first member is the body, we set it to pos:
00289   member->member->pos = Point2f(0,0);
00290   member->member->SetAngle(0);
00291   member++;
00292 
00293   for(;member != squel_lst.end();
00294        member++)
00295   {
00296     // Place the other members depending of the parent member:
00297     member->member->ApplySqueleton(member->parent);
00298   }
00299 }
00300 
00301 void Body::Build()
00302 {
00303   // Increase frame number if needed
00304   unsigned int last_frame = current_frame;
00305   if(walk_events > 0 || current_mvt->type!="walk")
00306   if(Time::GetInstance()->Read() > last_refresh + current_mvt->speed)
00307   {
00308     current_frame += (Time::GetInstance()->Read()-last_refresh) / current_mvt->speed;
00309     last_refresh += ((Time::GetInstance()->Read()-last_refresh) / current_mvt->speed) * current_mvt->speed;
00310 
00311     if(current_frame >= current_mvt->frames.size())
00312     {
00313       if(play_once_clothe_sauv)
00314         SetClothe(play_once_clothe_sauv->name);
00315       if(play_once_mvt_sauv)
00316       {
00317         SetMovement(play_once_mvt_sauv->type);
00318         current_frame = play_once_frame_sauv;
00319       }
00320     }
00321     current_frame %= current_mvt->frames.size();
00322   }
00323 
00324   need_rebuild |= (last_frame != current_frame);
00325   need_rebuild |= current_mvt->always_moving;
00326 
00327   if(!need_rebuild)
00328     return;
00329 
00330   ResetMovement();
00331   ApplySqueleton();
00332   ApplyMovement(current_mvt, current_frame);
00333 
00334   // Rotate each sprite, because the next part need to know the height of the sprite
00335   // once he is rotated
00336   for(int layer=0;layer < (int)current_clothe->layers.size() ;layer++)
00337   if(current_clothe->layers[layer]->name != "weapon")
00338     current_clothe->layers[layer]->RotateSprite();
00339 
00340   // Move the members to get the lowest member at the bottom of the skin rectangle
00341   member_mvt body_mvt;
00342   float y_max = 0;
00343   for(int lay=0;lay < (int)current_clothe->layers.size() ;lay++)
00344   if(current_clothe->layers[lay]->name != "weapon")
00345   {
00346     Member* member = current_clothe->layers[lay];
00347     if(member->pos.y + member->spr->GetHeightMax() + member->spr->GetRotationPoint().y > y_max
00348     && !member->go_through_ground)
00349       y_max = member->pos.y + member->spr->GetHeightMax() + member->spr->GetRotationPoint().y;
00350   }
00351   body_mvt.pos.y = (float)GetSize().y - y_max + current_mvt->test_bottom;
00352   body_mvt.pos.x = GetSize().x / 2.0 - squel_lst.front().member->spr->GetWidth() / 2.0;
00353   body_mvt.SetAngle(main_rotation_rad);
00354   squel_lst.front().member->ApplyMovement(body_mvt, squel_lst);
00355 
00356   need_rebuild = false;
00357 }
00358 
00359 void Body::Draw(const Point2i& _pos)
00360 {
00361   Build();
00362 
00363   // update the weapon position
00364   if(direction == DIRECTION_RIGHT)
00365     weapon_pos = Point2i((int)weapon_member->pos.x,(int)weapon_member->pos.y);
00366   else
00367     weapon_pos = Point2i(GetSize().x - (int)weapon_member->pos.x,(int)weapon_member->pos.y);
00368   weapon_pos += _pos;
00369 
00370   // Finally draw each layer one by one
00371   for(int layer=0;layer < (int)current_clothe->layers.size() ;layer++)
00372     current_clothe->layers[layer]->Draw(_pos, _pos.x + GetSize().x/2, int(direction));
00373 }
00374 
00375 void Body::AddChildMembers(Member* parent)
00376 {
00377   // Add child members of the parent member to the squeleton
00378   // and continue recursively with child members
00379   for(std::map<std::string, v_attached>::iterator child = parent->attached_members.begin();
00380       child != parent->attached_members.end();
00381       child++)
00382   {
00383     // Find if the current clothe uses this member:
00384     for(uint lay = 0; lay < current_clothe->layers.size(); lay++)
00385     {
00386       if(current_clothe->layers[lay]->type == child->first)
00387       {
00388         // This child member is attached to his parent
00389         junction body;
00390         body.member = current_clothe->layers[lay];
00391         body.parent = parent;
00392         squel_lst.push_back(body);
00393 
00394         // continue recursively
00395         AddChildMembers(current_clothe->layers[lay]);
00396       }
00397     }
00398   }
00399 }
00400 
00401 void Body::BuildSqueleton()
00402 {
00403   // Find each member used by the current clothe
00404   // and set the parent member of each member
00405   squel_lst.clear();
00406 
00407   // Find the "body" member as its the top of the squeleton
00408   for(uint lay = 0; lay < current_clothe->layers.size(); lay++)
00409   if(current_clothe->layers[lay]->type == "body")
00410   {
00411     junction body;
00412     body.member = current_clothe->layers[lay];
00413     body.parent = NULL;
00414     squel_lst.push_back(body);
00415     break;
00416   }
00417 
00418   if(squel_lst.size() == 0)
00419   {
00420     std::cerr << "Unable to find the \"body\" member in the current clothe" << std::endl;
00421     assert(false);
00422   }
00423 
00424   AddChildMembers(squel_lst.front().member);
00425 }
00426 
00427 void Body::SetClothe(std::string name)
00428 {
00429   MSG_DEBUG("body", " %s use clothe %s", owner->GetName().c_str(), name.c_str());
00430   if(current_clothe && current_clothe->name == name) return;
00431 
00432   if(clothes_lst.find(name) != clothes_lst.end())
00433   {
00434     current_clothe = clothes_lst.find(name)->second;
00435     BuildSqueleton();
00436     main_rotation_rad = 0;
00437     need_rebuild = true;
00438   }
00439   else
00440     MSG_DEBUG("body","Clothe not found");
00441 
00442 
00443   play_once_clothe_sauv = NULL;
00444 
00445   assert(current_clothe != NULL);
00446 }
00447 
00448 void Body::SetMovement(std::string name)
00449 {
00450   MSG_DEBUG("body", " %s use movement %s", owner->GetName().c_str(), name.c_str());
00451   if(current_mvt && current_mvt->type == name) return;
00452 
00453   // Dirty trick to get the "black" movement to be played fully
00454   if(current_clothe && current_clothe->name == "black") return;
00455 
00456   if(mvt_lst.find(name) != mvt_lst.end())
00457   {
00458     current_mvt = mvt_lst.find(name)->second;
00459     current_frame = 0;
00460     last_refresh = Time::GetInstance()->Read();
00461     main_rotation_rad = 0;
00462     need_rebuild = true;
00463   }
00464   else
00465     MSG_DEBUG("body","Movement not found");
00466 
00467   play_once_mvt_sauv = NULL;
00468 
00469   assert(current_mvt != NULL);
00470 }
00471 
00472 void Body::PlayAnimation()
00473 {
00474   std::ostringstream name;
00475   name << "animation" << randomObj.GetLong(0, animation_number - 1);
00476   SetClotheOnce(name.str());
00477   SetMovementOnce(name.str());
00478 }
00479 
00480 void Body::SetClotheOnce(std::string name)
00481 {
00482   MSG_DEBUG("body", " %s use clothe %s once", owner->GetName().c_str(), name.c_str());
00483   if(current_clothe && current_clothe->name == name) return;
00484 
00485   if(clothes_lst.find(name) != clothes_lst.end())
00486   {
00487     if(!play_once_clothe_sauv)
00488       play_once_clothe_sauv = current_clothe;
00489     current_clothe = clothes_lst.find(name)->second;
00490     BuildSqueleton();
00491     main_rotation_rad = 0;
00492     need_rebuild = true;
00493   }
00494   else
00495     MSG_DEBUG("body","Clothe not found");
00496 
00497   assert(current_clothe != NULL);
00498 }
00499 
00500 void Body::SetMovementOnce(std::string name)
00501 {
00502   MSG_DEBUG("body", " %s use movement %s once", owner->GetName().c_str(), name.c_str());
00503   if(current_mvt && current_mvt->type == name) return;
00504 
00505   // Dirty trick to get the "black" movement to be played fully
00506   if(current_clothe && current_clothe->name == "black"  && name != "black") return;
00507 
00508   if(mvt_lst.find(name) != mvt_lst.end())
00509   {
00510     if(!play_once_mvt_sauv)
00511     {
00512       play_once_mvt_sauv = current_mvt;
00513       play_once_frame_sauv = current_frame;
00514     }
00515 
00516     current_mvt = mvt_lst.find(name)->second;
00517     current_frame = 0;
00518     last_refresh = Time::GetInstance()->Read();
00519     main_rotation_rad = 0;
00520     need_rebuild = true;
00521   }
00522   else
00523     MSG_DEBUG("body","Movement not found");
00524 
00525   assert(current_mvt != NULL);
00526 }
00527 
00528 void Body::GetTestRect(uint &l, uint&r, uint &t, uint &b)
00529 {
00530   if(direction == DIRECTION_RIGHT)
00531   {
00532     l = current_mvt->test_left;
00533     r = current_mvt->test_right;
00534   }
00535   else
00536   {
00537     r = current_mvt->test_left;
00538     l = current_mvt->test_right;
00539   }
00540   t = current_mvt->test_top;
00541   b = current_mvt->test_bottom;
00542 }
00543 
00544 void Body::SetDirection(Direction_t dir)
00545 {
00546   direction=dir;
00547 }
00548 
00549 const Body::Direction_t &Body::GetDirection() const
00550 {
00551   return direction;
00552 }
00553 
00554 const Point2i &Body::GetHandPosition() const
00555 {
00556   return weapon_pos;
00557 }
00558 
00559 void Body::StartWalk()
00560 {
00561   // walk events happens when the player hits the left/right key
00562   // counting how much they are pressed allow to get skin walking
00563   // if the key was hit while the character was jumping or using an other
00564   // animation than the walk animation
00565   walk_events++;
00566   if(walk_events == 1)
00567     last_refresh = Time::GetInstance()->Read();
00568 }
00569 
00570 void Body::StopWalk()
00571 {
00572   if(walk_events > 0)
00573     walk_events--;
00574   if(current_mvt->type == "walk")
00575     SetFrame(0);
00576 }
00577 
00578 void Body::ResetWalk()
00579 {
00580   walk_events = 0;
00581 }
00582 
00583 uint Body::GetMovementDuration()
00584 {
00585   return current_mvt->frames.size() * current_mvt->speed;
00586 }
00587 
00588 uint Body::GetFrameCount()
00589 {
00590   return current_mvt->frames.size();
00591 }
00592 
00593 void Body::SetFrame(uint no)
00594 {
00595   assert(no < current_mvt->frames.size());
00596   current_frame = no;
00597   need_rebuild = true;
00598 }
00599 
00600 void Body::MakeParticles(const Point2i& pos)
00601 {
00602   Build();
00603 
00604   for(int layer=0;layer < (int)current_clothe->layers.size() ;layer++)
00605   if(current_clothe->layers[layer]->type != "weapon")
00606     ParticleEngine::AddNow(new BodyMemberParticle(current_clothe->layers[layer]->spr,
00607                                                   current_clothe->layers[layer]->GetPos()+pos));
00608 }
00609 
00610 void Body::MakeTeleportParticles(const Point2i& pos, const Point2i& dst)
00611 {
00612   Build();
00613 
00614   for(int layer=0;layer < (int)current_clothe->layers.size() ;layer++)
00615   if(current_clothe->layers[layer]->type != "weapon")
00616   {
00617     ParticleEngine::AddNow(new TeleportMemberParticle(current_clothe->layers[layer]->spr,
00618                                                       current_clothe->layers[layer]->GetPos()+pos,
00619                                                       current_clothe->layers[layer]->GetPos()+dst,
00620                                                       int(direction)));
00621   }
00622 }
00623 
00624 void Body::SetRotation(double angle)
00625 {
00626   MSG_DEBUG("body", "%s -> new angle: %i", owner->GetName().c_str(), angle);
00627   main_rotation_rad = angle;
00628   need_rebuild = true;
00629 }
00630 
00631 const std::string& Body::GetMovement() { return current_mvt->type; }
00632 const std::string& Body::GetClothe() { return current_clothe->name; }

Generated on Mon Jan 1 13:10:55 2007 for Wormux by  doxygen 1.4.7