#include "game_object.h" #include "animation_block.h" #include "indent.h" #include "gpl_assert.h" #include "error.h" #include using namespace std; // all game objects that have been created but not deleted // used for (1) error checking via valid() (2) drawing all game objects /* static */ vector Game_object::all_game_objects; // all game objects that have been deleted // used for error checking via has_been_deleted() /* static */ vector Game_object::deleted_game_objects; /* static */ bool Game_object::graphics_dirty = true; void Game_object::insert_into_all_game_objects_vector() { // all_game_objects is a vector sorted by drawing_order // Sorted small to large (small drawing_order drawn first/on bottom) // find the first iter w/a larger or equal m_drawing_order compared to this vector::iterator iter = all_game_objects.begin(); while (iter != all_game_objects.end() && (*iter)->m_drawing_order < m_drawing_order) iter++; // this object has a larger drawing_order than any object in vector if (iter == all_game_objects.end()) all_game_objects.push_back(this); // the game object pointed to by iter is the first one in the vector // that has a drawing order larger or equal to this // insert before this object else all_game_objects.insert(iter,this); } // call when the drawing_order for a Game_object changes // removes the object from the all_game_objects vector and // inserts it in the correct position void Game_object::update_order_in_game_objects_vector() { // remove this from all_game_objects vector::iterator iter = find(all_game_objects.begin(), all_game_objects.end(), this); assert(iter != all_game_objects.end()); all_game_objects.erase(iter); // now put it back -- it will go in the correct place this time insert_into_all_game_objects_vector(); } /* static */ bool Game_object::graphics_out_of_date_with_last_rendering() { bool result = graphics_dirty; graphics_dirty = false; return result; } /* static */ void Game_object::animate_all_game_objects() { vector::iterator iter; for (iter = all_game_objects.begin(); iter != all_game_objects.end(); iter++) { Game_object *cur = *iter; if (cur->m_should_animate) cur->animate(); } } /* static */ void Game_object::draw_all_game_objects() { vector::iterator iter; for (iter = all_game_objects.begin(); iter != all_game_objects.end(); iter++) { if ((*iter)->m_should_draw) (*iter)->draw(); } } Game_object::Game_object(double red /* = 0.5 */, double green /* = 0.5 */, double blue /* = 0.5 */ ) { m_x = 0; m_y = 0; m_w = 10; m_h = 10; m_red = red; m_green = green; m_blue = blue; m_animation_block = 0; m_visible = 1; m_proximity = 4; m_drawing_order = 0; m_user_int = 0; m_user_double = 0.0; m_user_string = ""; m_user_int2 = 0; m_user_double2 = 0.0; m_user_string2 = ""; m_user_int3 = 0; m_user_double3 = 0.0; m_user_string3 = ""; m_user_int4 = 0; m_user_double4 = 0.0; m_user_string4 = ""; m_user_int5 = 0; m_user_double5 = 0.0; m_user_string5 = ""; // OpenGL is not initialized enough yet to create our // display list. Use a unique number so that draw() // will know it is the first time it is called. m_display_list = 0; m_display_list_dirty = true; // objects that are place holders for parameters to animation_blocks // should never be drawn or animated (they are not "real" objects) // the functions never_draw() and never_animate() reset these // however, all others objects should be drawn and animated m_should_draw = true; m_should_animate = true; // Set my type name // --> must be done by all that inherit me m_object_type_name = "Game_object"; // register my variable that can be set/get from parser // --> each object that inherits me that wants to have any of // --> its member variables settable, must do the same for each register_member_variable("x", &m_x); register_member_variable("y", &m_y); register_member_variable("w", &m_w); register_member_variable("h", &m_h); register_member_variable("red", &m_red); register_member_variable("green", &m_green); register_member_variable("blue", &m_blue); register_member_variable("animation_block", &m_animation_block); register_member_variable("visible", &m_visible); register_member_variable("proximity", &m_proximity); register_member_variable("drawing_order", &m_drawing_order); register_member_variable("user_int", &m_user_int); register_member_variable("user_double", &m_user_double); register_member_variable("user_string", &m_user_string); register_member_variable("user_int2", &m_user_int2); register_member_variable("user_double2", &m_user_double2); register_member_variable("user_string2", &m_user_string2); register_member_variable("user_int3", &m_user_int3); register_member_variable("user_double3", &m_user_double3); register_member_variable("user_string3", &m_user_string3); register_member_variable("user_int4", &m_user_int4); register_member_variable("user_double4", &m_user_double4); register_member_variable("user_string4", &m_user_string4); register_member_variable("user_int5", &m_user_int5); register_member_variable("user_double5", &m_user_double5); register_member_variable("user_string5", &m_user_string5); insert_into_all_game_objects_vector(); } Game_object::~Game_object() { vector::iterator iter = find(all_game_objects.begin(), all_game_objects.end(), this); assert(iter != all_game_objects.end()); deleted_game_objects.push_back(*iter); all_game_objects.erase(iter); } void Game_object::draw() { if (!m_visible) return; if (m_display_list_dirty) { if (m_display_list == 0) m_display_list = glGenLists(1); build_display_list(); m_display_list_dirty = false; } // it might be more efficient to move the translation for m_x and m_y // out of the display list // a) if most objects move, it will probably be faster to take it out of // the display list // b) if most objects don't move, it will be faster to leave it in // unclear if there is an efficiency problem... glCallList(m_display_list); } Status Game_object::get_member_variable_type(string name, Gpl_type &type) { Typed_void_ptr* typed_void_ptr = lookup_registered_member_variable(name); if (typed_void_ptr) { type = typed_void_ptr->m_type; return OK; } else { return MEMBER_NOT_DECLARED; } } Status Game_object::set_member_variable(string name, int value) { graphics_dirty = true; Typed_void_ptr *variable = lookup_registered_member_variable(name); if (!variable) return MEMBER_NOT_DECLARED; if (variable->m_type != INT) return MEMBER_NOT_OF_GIVEN_TYPE; assert(variable->m_value); if (variable->m_derived) Error::error(Error::CANNOT_CHANGE_DERIVED_ATTRIBUTE, name, m_object_type_name); else *((int *) variable->m_value) = value; // when drawing_order is changed, need to update the drawing order if (name == "drawing_order") update_order_in_game_objects_vector(); updated(name); return OK; } Status Game_object::set_member_variable(string name, double value) { graphics_dirty = true; Typed_void_ptr *variable = lookup_registered_member_variable(name); if (!variable) return MEMBER_NOT_DECLARED; if (variable->m_type != DOUBLE) return MEMBER_NOT_OF_GIVEN_TYPE; assert(variable->m_value); *((double *) variable->m_value) = value; updated(name); return OK; } Status Game_object::set_member_variable(string name, string value) { graphics_dirty = true; Typed_void_ptr *variable = lookup_registered_member_variable(name); if (!variable) return MEMBER_NOT_DECLARED; if (variable->m_type != STRING) return MEMBER_NOT_OF_GIVEN_TYPE; assert(variable->m_value); *((string *) variable->m_value) = value; updated(name); return OK; } Status Game_object::set_member_variable(string name, Animation_block *value) { graphics_dirty = true; Typed_void_ptr *variable = lookup_registered_member_variable(name); if (!variable) return MEMBER_NOT_DECLARED; if (variable->m_type != ANIMATION_BLOCK) return MEMBER_NOT_OF_GIVEN_TYPE; assert(variable->m_value); // note, we don't dereference m_value as above because // we store Animation_blocks as pointers *((Animation_block **) variable->m_value) = value; updated(name); return OK; } Status Game_object::get_member_variable(string name, int &value) { Typed_void_ptr *variable = lookup_registered_member_variable(name); if (!variable) return MEMBER_NOT_DECLARED; if (variable->m_type != INT) return MEMBER_NOT_OF_GIVEN_TYPE; value = *((int *) variable->m_value); return OK; } Status Game_object::get_member_variable(string name, double &value) { Typed_void_ptr *variable = lookup_registered_member_variable(name); if (!variable) return MEMBER_NOT_DECLARED; if (variable->m_type == DOUBLE) { value = *((double *) variable->m_value); return OK; } else return MEMBER_NOT_OF_GIVEN_TYPE; } Status Game_object::get_member_variable(string name, string &value) { Typed_void_ptr *variable = lookup_registered_member_variable(name); if (!variable) return MEMBER_NOT_DECLARED; if (variable->m_type != STRING) return MEMBER_NOT_OF_GIVEN_TYPE; value = *((string *) variable->m_value); return OK; } Status Game_object::get_member_variable(string name, Animation_block *&value) { Typed_void_ptr *variable = lookup_registered_member_variable(name); if (!variable) return MEMBER_NOT_DECLARED; if (variable->m_type != ANIMATION_BLOCK) return MEMBER_NOT_OF_GIVEN_TYPE; // note, we don't dereference m_value as above because // we store Animation_blocks as pointers value = *((Animation_block **) variable->m_value); return OK; } static bool overlap(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) { return !(ax2 < bx1 || ax1 > bx2 || ay1 > by2 || ay2 < by1); } // since gpl does not have a bool, touches() and near() return an int to reduce // confusion between ints and bools int Game_object::touches(Game_object *obj) { /***** LIMITATION touches uses the bounding box of the object which is created at object construction. The bounding box is not a very good representation of triangles or circles. And it does not take into account the rotation of either triangles or rectangles. It would make the touches mechanism better if objects were represented with a more accurate boundary. Idea 1: use a polygon to represent the bounding boxes. Each type of object could create and update their polygon. When line segments of the polygons of two objects intersect the two objects would be considered touching. This approach does not handle containment (when one object is completely inside another). Idea 2: represent each object as a set of triangles. When segments of triangles of two objects intersect, the objects touch. Could also look for a vertex inside of another object's triangle. *****/ // true if the bounding boxes of this and obj overlap return overlap(m_x, m_y, m_x + m_w, m_y + m_h, obj->m_x, obj->m_y, obj->m_x + obj->m_w, obj->m_y+obj->m_h); } int Game_object::near(Game_object *obj) { // expand the bounding boxes of this and obj by their respective m_proximity // true if the expanded bounding boxes of this and obj overlap return overlap(m_x - m_proximity, m_y - m_proximity, m_x + m_w + m_proximity, m_y + m_h + m_proximity, obj->m_x - obj->m_proximity, obj->m_y - obj->m_proximity, obj->m_x + obj->m_w + obj->m_proximity, obj->m_y+obj->m_h + obj->m_proximity); } Game_object::Typed_void_ptr* Game_object::lookup_registered_member_variable(string name) { // Check if name in map of registered_member_variables map::iterator iter; iter = m_variable_registry.find(name); if (iter == m_variable_registry.end()) { return 0; } return (*iter).second; } void Game_object::register_member_variable(Gpl_type type, string name, void *value ) { assert(type == INT || type == DOUBLE || type == STRING || type == ANIMATION_BLOCK); // make sure this member variable has not already been registered assert(!lookup_registered_member_variable(name)); // insert this member variable into the registry as type "type" m_variable_registry[name] = new Typed_void_ptr(type, (void *)value); } Status Game_object::mark_member_variable_as_derived(string name) { Typed_void_ptr *variable = lookup_registered_member_variable(name); if (!variable) return MEMBER_NOT_DECLARED; variable->m_derived = true; return OK; } void Game_object::animate() { if (m_animation_block) { // Call our own animation block // Animation blocks are special type of statement block that ALWAYS // contain a pointer to a symbol in the symbol_table that represent // a parameter. // When this object calls its animation block, it must pass itself // to the execute function of the animation_block if (!m_animation_block->empty()) m_animation_block->execute(this); // else do nothing } } bool Game_object::valid() const { vector::iterator iter = find(all_game_objects.begin(), all_game_objects.end(), this); return iter != all_game_objects.end(); } ostream & Game_object::print(ostream &os) const { os << indent << m_object_type_name /* << "<" << (void *) this << ">" */ << endl << indent << "{" << endl; os.flush(); indent++; map::const_iterator iter; for (iter = m_variable_registry.begin(); iter != m_variable_registry.end(); iter++ ) { if (iter != m_variable_registry.begin()) os << "," << endl; Typed_void_ptr *cur_member = (*iter).second; string cur_name = (*iter).first; os << indent << cur_name << " = "; switch (cur_member->m_type) { case INT: os << "int(" << *((int *) cur_member->m_value) << ")"; break; case DOUBLE: os << "double(" << *((double *) cur_member->m_value) << ")"; break; case STRING: os << "string(\"" << *((string *) cur_member->m_value)<< "\")"; break; case ANIMATION_BLOCK: if (*((Animation_block **) (cur_member->m_value)) == 0) os << "animation_block(0)"; else { indent++; os << endl<< *((Animation_block **) (cur_member->m_value)); indent--; } break; default: // there is a programming bug if this is ever executed assert(false); } } // for loop iterating through all registered member variables os << endl; indent--; os << indent << "}"; return os; } ostream & operator<<(ostream &os, const Game_object &game_object) { return game_object.print(os); } ostream & operator<<(ostream &os, const Game_object *game_object) { if (!game_object) { os << indent << "Game_object{NULL}"; return os; } else return game_object->print(os); }