src/proto2/Physics.cc
changeset 112 1b9ad61bdf2d
parent 111 d90977611fee
child 114 71f7e9d3d052
equal deleted inserted replaced
111:d90977611fee 112:1b9ad61bdf2d
     8 #include <assert.h>
     8 #include <assert.h>
     9 
     9 
    10 PhysicsWorld::PhysicsWorld (Vector gravity, Vector dimensions)
    10 PhysicsWorld::PhysicsWorld (Vector gravity, Vector dimensions)
    11     : tick_timer(PHYSICS_TICK_MS), tick_counter(0), gravity(gravity), dimensions(dimensions), terrain(dimensions.x, std::vector<TerrainType>(dimensions.y, DIRT)) {
    11     : tick_timer(PHYSICS_TICK_MS), tick_counter(0), gravity(gravity), dimensions(dimensions), terrain(dimensions.x, std::vector<TerrainType>(dimensions.y, DIRT)) {
    12 
    12 
    13 	generateTerrain(1337);
    13     generateTerrain(1337);
    14 
    14 
    15     slots.connect(tick_timer.sig_timer(), this, &PhysicsWorld::tick);
    15     slots.connect(tick_timer.sig_timer(), this, &PhysicsWorld::tick);
    16     tick_timer.enable();
    16     tick_timer.enable();
    17 }
    17 }
    18 
    18 
    46  *
    46  *
    47  * Make the worm walk on the ground.
    47  * Make the worm walk on the ground.
    48  * @return Final position.
    48  * @return Final position.
    49  */
    49  */
    50 Vector PhysicsObject::walk (bool right) {
    50 Vector PhysicsObject::walk (bool right) {
    51 	Vector cursor = right ? this->position + Vector(1,0) : this->position + Vector(-1,0);
    51     Vector cursor = right ? this->position + Vector(1,0) : this->position + Vector(-1,0);
    52 	Vector reached = this->position;
    52     Vector reached = this->position;
    53 
    53 
    54 	//for(int steps = 0; steps < 3; steps++) {
    54     //for(int steps = 0; steps < 3; steps++) {
    55 	
    55     
    56 		// Go up but not if the wall is over two pixels
    56         // Go up but not if the wall is over two pixels
    57 		if(world.getType(cursor) != EMPTY) {
    57         if(world.getType(cursor) != EMPTY) {
    58     		for(int height = 0, max = 3; height < max+42; height++) {
    58             for(int height = 0, max = 3; height < max+42; height++) {
    59 				
    59                 
    60 				if(height >= max)
    60                 if(height >= max)
    61 					return reached;
    61                     return reached;
    62 
    62 
    63 				cursor.y--;
    63                 cursor.y--;
    64 				if(world.getType(cursor) == EMPTY) {
    64                 if(world.getType(cursor) == EMPTY) {
    65 					// Check that the other parts of the worm don't collide with anything
    65                     // Check that the other parts of the worm don't collide with anything
    66 					if(possibleLocation(cursor)) {
    66                     if(possibleLocation(cursor)) {
    67 						reached = cursor;
    67                         reached = cursor;
    68 						continue;
    68                         continue;
    69 					} else {
    69                     } else {
    70 						// Can't get any further
    70                         // Can't get any further
    71 						return reached;
    71                         return reached;
    72 					}
    72                     }
    73 				}
    73                 }
    74 			}
    74             }
    75 		} else {
    75         } else {
    76 			if(possibleLocation(cursor)) {
    76             if(possibleLocation(cursor)) {
    77 				reached = cursor;
    77                 reached = cursor;
    78 			}
    78             }
    79 			// Start checking if the lower squares are empty
    79             // Start checking if the lower squares are empty
    80             for(int depth = 0, max = 3; depth < max+42; depth++) {
    80             for(int depth = 0, max = 3; depth < max+42; depth++) {
    81 				
    81                 
    82 				if(depth >= max) {
    82                 if(depth >= max) {
    83 					// We can start a free fall now
    83                     // We can start a free fall now
    84 					//
    84                     //
    85 				// TODO it should be set inAir if it falls from a cliff
    85                 // TODO it should be set inAir if it falls from a cliff
    86 				//this->inAir = true;
    86                 //this->inAir = true;
    87 
    87 
    88                     // Put some speed there to make loke smoother
    88                     // Put some speed there to make loke smoother
    89 					//this->velocity.y = -5;
    89                     //this->velocity.y = -5;
    90 					return reached;
    90                     return reached;
    91 				}
    91                 }
    92 
    92 
    93 				cursor.y++;
    93                 cursor.y++;
    94 				if(world.getType(cursor) == EMPTY) {
    94                 if(world.getType(cursor) == EMPTY) {
    95 					// Check that the other parts of the worm don't collide with anything
    95                     // Check that the other parts of the worm don't collide with anything
    96 					if(possibleLocation(cursor)) {
    96                     if(possibleLocation(cursor)) {
    97 						reached = cursor;
    97                         reached = cursor;
    98 						continue;
    98                         continue;
    99 					} else {
    99                     } else {
   100 						// Can't get any further
   100                         // Can't get any further
   101 						return reached;
   101                         return reached;
   102 					}
   102                     }
   103 				}
   103                 }
   104 			}
   104             }
   105 		}      
   105         }      
   106 
   106 
   107 	//	cursor.x += right ? 1 : -1;
   107     //    cursor.x += right ? 1 : -1;
   108 	//}
   108     //}
   109 	return reached;
   109     return reached;
   110 }
   110 }
   111 
   111 
   112 void PhysicsObject::jump () {
   112 void PhysicsObject::jump () {
   113  	velocity.y = -100;
   113      velocity.y = -100;
   114 	inAir = true;
   114     inAir = true;
   115 }
   115 }
   116 
   116 
   117 bool PhysicsObject::possibleLocation (Vector loc) {
   117 bool PhysicsObject::possibleLocation (Vector loc) {
   118 	for(unsigned int i = 0; i < this->shape.size(); i++) {
   118     for(unsigned int i = 0; i < this->shape.size(); i++) {
   119 		if(world.getType(loc+shape[i]) != EMPTY)
   119         if(world.getType(loc+shape[i]) != EMPTY)
   120 			return false;
   120             return false;
   121 	}
   121     }
   122 	return true;
   122     return true;
   123 }
   123 }
   124 
   124 
   125 /**
   125 /**
   126  * Updates object speed and position. This function organises force
   126  * Updates object speed and position. This function organises force
   127  * integration and collision detection.
   127  * integration and collision detection.
   134           this->position = moveVertically(forceq.front().x > 0);
   134           this->position = moveVertically(forceq.front().x > 0);
   135           forceq.pop();
   135           forceq.pop();
   136           }
   136           }
   137           } */
   137           } */
   138         return;
   138         return;
   139 	}
   139     }
   140 	// TODO HERE
   140     // TODO HERE
   141 
   141 
   142     // Add gravity to the force queue
   142     // Add gravity to the force queue
   143     forceq.push(world.gravity);
   143     forceq.push(world.gravity);
   144     
   144     
   145     // Go trough every force in the queue
   145     // Go trough every force in the queue
   168     bool collided = false;
   168     bool collided = false;
   169 
   169 
   170     //goes 1 unit forward every step and check if has hit anything
   170     //goes 1 unit forward every step and check if has hit anything
   171     Vector unitVector = (newPosition-position) / (newPosition-position).length();
   171     Vector unitVector = (newPosition-position) / (newPosition-position).length();
   172     
   172     
   173 	Vector tmpVector = position;
   173     Vector tmpVector = position;
   174     Vector reached = position;
   174     Vector reached = position;
   175 	int steps = (int) (newPosition-position).length() + 2;
   175     int steps = (int) (newPosition-position).length() + 2;
   176 		
   176         
   177 	//Engine::log(DEBUG, "physics.update_position") << unitVector-newPosition;
   177     //Engine::log(DEBUG, "physics.update_position") << unitVector-newPosition;
   178 	//Vector foo = position+unitVector*steps-newPosition;
   178     //Vector foo = position+unitVector*steps-newPosition;
   179     //Engine::log(DEBUG, "PhysicsObject.updatePosition") << "Virhe: "<< foo;
   179     //Engine::log(DEBUG, "PhysicsObject.updatePosition") << "Virhe: "<< foo;
   180     
   180     
   181     for(int i = 0; i < steps; i++) {
   181     for(int i = 0; i < steps; i++) {
   182         tmpVector += unitVector;
   182         tmpVector += unitVector;
   183 
   183 
   184 		float minVelocity = 10;
   184         float minVelocity = 10;
   185 		// Check if any of the four corners of the worm collide
   185         // Check if any of the four corners of the worm collide
   186 		for(int sh = 0; sh < 4; sh++) {
   186         for(int sh = 0; sh < 4; sh++) {
   187             if(world.getType(tmpVector+shape[sh]) != EMPTY) {
   187             if(world.getType(tmpVector+shape[sh]) != EMPTY) {
   188 				reached = position + unitVector*(i-1);
   188                 reached = position + unitVector*(i-1);
   189 				collided = true;
   189                 collided = true;
   190 				this->bounce(world.getNormal(tmpVector+shape[sh], tmpVector-unitVector+shape[sh]));
   190                 this->bounce(world.getNormal(tmpVector+shape[sh], tmpVector-unitVector+shape[sh]));
   191 				this->velocity *= 0.4;
   191                 this->velocity *= 0.4;
   192 				if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   192                 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   193 					this->inAir = false;
   193                     this->inAir = false;
   194 					this->velocity = Vector(0,0);
   194                     this->velocity = Vector(0,0);
   195 				}
   195                 }
   196 				break;
   196                 break;
   197 			}
   197             }
   198 		}
   198         }
   199 		if(collided)
   199         if(collided)
   200 			break;
   200             break;
   201 		/*
   201         /*
   202 		if(velocity.y > 0) {
   202         if(velocity.y > 0) {
   203         	if(world.getType(tmpVector+shape[2]) != EMPTY) {
   203             if(world.getType(tmpVector+shape[2]) != EMPTY) {
   204             	reached = position + unitVector*(i-1);
   204                 reached = position + unitVector*(i-1);
   205             	collided = true;
   205                 collided = true;
   206 				this->velocity.y *= -0.3;
   206                 this->velocity.y *= -0.3;
   207 				this->velocity.x *= 0.7;
   207                 this->velocity.x *= 0.7;
   208 				if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   208                 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   209             		this->inAir = false;
   209                     this->inAir = false;
   210 					this->velocity = Vector(0,0);
   210                     this->velocity = Vector(0,0);
   211 				}
   211                 }
   212             	break;
   212                 break;
   213 			}
   213             }
   214 		} else {
   214         } else {
   215         	if(world.getType(tmpVector+shape[0]) != EMPTY) {
   215             if(world.getType(tmpVector+shape[0]) != EMPTY) {
   216             	reached = position + unitVector*(i-1);
   216                 reached = position + unitVector*(i-1);
   217             	collided = true;
   217                 collided = true;
   218 				this->velocity.y *= -0.3;
   218                 this->velocity.y *= -0.3;
   219 				this->velocity.x *= 0.7;
   219                 this->velocity.x *= 0.7;
   220 				if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   220                 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   221             		this->inAir = false;
   221                     this->inAir = false;
   222 					this->velocity = Vector(0,0);
   222                     this->velocity = Vector(0,0);
   223 				}
   223                 }
   224             	break;
   224                 break;
   225 			}
   225             }
   226         }
   226         }
   227 
   227 
   228 		if(velocity.x > 0) {
   228         if(velocity.x > 0) {
   229         	if(world.getType(tmpVector+shape[1]) != EMPTY) {
   229             if(world.getType(tmpVector+shape[1]) != EMPTY) {
   230             	reached = position + unitVector*(i-1);
   230                 reached = position + unitVector*(i-1);
   231             	collided = true;
   231                 collided = true;
   232 				this->velocity.x *= -0.6;
   232                 this->velocity.x *= -0.6;
   233 				this->velocity.y *= 0.7;
   233                 this->velocity.y *= 0.7;
   234 				if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   234                 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   235             		this->inAir = false;
   235                     this->inAir = false;
   236 					this->velocity = Vector(0,0);
   236                     this->velocity = Vector(0,0);
   237 				}
   237                 }
   238             	break;
   238                 break;
   239 			}
   239             }
   240 		} else {
   240         } else {
   241         	if(world.getType(tmpVector+shape[3]) != EMPTY) {
   241             if(world.getType(tmpVector+shape[3]) != EMPTY) {
   242             	reached = position + unitVector*(i-1);
   242                 reached = position + unitVector*(i-1);
   243             	collided = true;
   243                 collided = true;
   244 				this->velocity.x *= -0.6;
   244                 this->velocity.x *= -0.6;
   245 				this->velocity.y *= 0.7;
   245                 this->velocity.y *= 0.7;
   246 				if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   246                 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   247             		this->inAir = false;
   247                     this->inAir = false;
   248 					this->velocity = Vector(0,0);
   248                     this->velocity = Vector(0,0);
   249 				}
   249                 }
   250             	break;
   250                 break;
   251 			}
   251             }
   252         }*/
   252         }*/
   253 		
   253         
   254 		// This col. det. doesn't let worms inside the ground, but on the other hand the worms get often stuck
   254         // This col. det. doesn't let worms inside the ground, but on the other hand the worms get often stuck
   255 		/*if(world.getType(tmpVector+shape[0]) != EMPTY || (world.getType(tmpVector+shape[2]) != EMPTY)) {
   255         /*if(world.getType(tmpVector+shape[0]) != EMPTY || (world.getType(tmpVector+shape[2]) != EMPTY)) {
   256             reached = position + unitVector*(i-1);
   256             reached = position + unitVector*(i-1);
   257             collided = true;
   257             collided = true;
   258 			this->velocity.y *= -0.3;
   258             this->velocity.y *= -0.3;
   259 			if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   259             if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   260             	this->inAir = false;
   260                 this->inAir = false;
   261 				this->velocity = Vector(0,0);
   261                 this->velocity = Vector(0,0);
   262 			}
   262             }
   263             break;
   263             break;
   264         }
   264         }
   265 		if(world.getType(tmpVector+shape[1]) != EMPTY || (world.getType(tmpVector+shape[3]) != EMPTY)) {
   265         if(world.getType(tmpVector+shape[1]) != EMPTY || (world.getType(tmpVector+shape[3]) != EMPTY)) {
   266             reached = position + unitVector*(i-1);
   266             reached = position + unitVector*(i-1);
   267             collided = true;
   267             collided = true;
   268 			this->velocity.x *= -0.6;
   268             this->velocity.x *= -0.6;
   269 			if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   269             if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) {
   270             	this->inAir = false;
   270                 this->inAir = false;
   271 				this->velocity = Vector(0,0);
   271                 this->velocity = Vector(0,0);
   272 			}
   272             }
   273             break;
   273             break;
   274 		}*/
   274         }*/
   275 		
   275         
   276 			//Engine::log(DEBUG, "physics.update_position") << "didnt hit";
   276             //Engine::log(DEBUG, "physics.update_position") << "didnt hit";
   277     }
   277     }
   278 
   278 
   279     // In case of some float error check the final coordinate
   279     // In case of some float error check the final coordinate
   280     if(!collided) {
   280     if(!collided) {
   281         if(world.getType(newPosition+shape[0]) != EMPTY || (world.getType(newPosition+shape[1]) != EMPTY) 
   281         if(world.getType(newPosition+shape[0]) != EMPTY || (world.getType(newPosition+shape[1]) != EMPTY) 
   282 			|| (world.getType(newPosition+shape[2]) != EMPTY) || (world.getType(newPosition+shape[3]) != EMPTY)) {
   282             || (world.getType(newPosition+shape[2]) != EMPTY) || (world.getType(newPosition+shape[3]) != EMPTY)) {
   283             
   283             
   284 			Engine::log(DEBUG, "physics.update_position") << "didnt hit";
   284             Engine::log(DEBUG, "physics.update_position") << "didnt hit";
   285 			// There was error, and there is ground
   285             // There was error, and there is ground
   286             //newPosition = tmpVector;
   286             //newPosition = tmpVector;
   287         } else {
   287         } else {
   288             // This means everything was ok, so no need to do anything
   288             // This means everything was ok, so no need to do anything
   289         }
   289         }
   290     } else {
   290     } else {
   324 
   324 
   325 /**
   325 /**
   326  * Computes hitten wall's normal. Calculated from 3*3 grid
   326  * Computes hitten wall's normal. Calculated from 3*3 grid
   327  */
   327  */
   328 Vector PhysicsWorld::getNormal (Vector hitPoint, Vector prevPoint) {
   328 Vector PhysicsWorld::getNormal (Vector hitPoint, Vector prevPoint) {
   329 	// Search free points with bfs and put them to vector
   329     // Search free points with bfs and put them to vector
   330 	std::vector<Vector> frees;
   330     std::vector<Vector> frees;
   331 	Vector hit = Vector((int)hitPoint.x, (int)hitPoint.y);
   331     Vector hit = Vector((int)hitPoint.x, (int)hitPoint.y);
   332 	Vector prev = Vector((int)prevPoint.x, (int)prevPoint.y);
   332     Vector prev = Vector((int)prevPoint.x, (int)prevPoint.y);
   333 
   333 
   334 	assert(hit != prev);
   334     assert(hit != prev);
   335 	
   335     
   336 	int dirIdx = getDirectionIndex(prev-hit);
   336     int dirIdx = getDirectionIndex(prev-hit);
   337 	float tmp1 = hit.x-prev.x;
   337     float tmp1 = hit.x-prev.x;
   338 	float tmp2 = hit.y-prev.y;
   338     float tmp2 = hit.y-prev.y;
   339 //    Engine::log(DEBUG, "physics.getNormal ") << dirIdx << " " << tmp1 << " " << tmp2;
   339 //    Engine::log(DEBUG, "physics.getNormal ") << dirIdx << " " << tmp1 << " " << tmp2;
   340 
   340 
   341 	for(int i = 1; i <= 2; i++) {
   341     for(int i = 1; i <= 2; i++) {
   342 		if(getType(hit+DIRECTIONS[(dirIdx+i) % 8]) == EMPTY)
   342         if(getType(hit+DIRECTIONS[(dirIdx+i) % 8]) == EMPTY)
   343 			frees.push_back(DIRECTIONS[(dirIdx+i) % 8]);
   343             frees.push_back(DIRECTIONS[(dirIdx+i) % 8]);
   344 		else 
   344         else 
   345 			break;
   345             break;
   346 	}
   346     }
   347 	for(int i = 1; i <= 2; i++) {
   347     for(int i = 1; i <= 2; i++) {
   348 		if(getType(hit+DIRECTIONS[(dirIdx-i+8) % 8]) == EMPTY)
   348         if(getType(hit+DIRECTIONS[(dirIdx-i+8) % 8]) == EMPTY)
   349 			frees.push_back(DIRECTIONS[(dirIdx-i+8) % 8]);
   349             frees.push_back(DIRECTIONS[(dirIdx-i+8) % 8]);
   350 		else
   350         else
   351 			break;
   351             break;
   352 	}
   352     }
   353     frees.push_back(DIRECTIONS[dirIdx]);
   353     frees.push_back(DIRECTIONS[dirIdx]);
   354 
   354 
   355 	Vector normal(0,0);
   355     Vector normal(0,0);
   356 	for(unsigned int i = 0; i < frees.size(); i++) {
   356     for(unsigned int i = 0; i < frees.size(); i++) {
   357 		normal += frees[i];
   357         normal += frees[i];
   358 	}
   358     }
   359     Engine::log(DEBUG, "physics.getNormal ") << "normal: " << normal;
   359     Engine::log(DEBUG, "physics.getNormal ") << "normal: " << normal;
   360 	return normal;
   360     return normal;
   361 }
   361 }
   362 
   362 
   363 /**
   363 /**
   364  * Bounces from straight wall in any direction.
   364  * Bounces from straight wall in any direction.
   365  * Direction given as normal of that wall
   365  * Direction given as normal of that wall
   371     Vector nprojection = normal*(velocity * normal) / (normal.length()*normal.length());
   371     Vector nprojection = normal*(velocity * normal) / (normal.length()*normal.length());
   372     velocity = tprojection - nprojection;
   372     velocity = tprojection - nprojection;
   373 }
   373 }
   374 
   374 
   375 /*bool PhysicsWorld::collided (Vector oldPos, Vector newPos) {
   375 /*bool PhysicsWorld::collided (Vector oldPos, Vector newPos) {
   376 	return false;
   376     return false;
   377 }*/
   377 }*/
   378 
   378 
   379 /**
   379 /**
   380  * Integrates given force over time and stores new position to
   380  * Integrates given force over time and stores new position to
   381  * posAfterTick and new velocity to velAfterTick.
   381  * posAfterTick and new velocity to velAfterTick.
   418     // XXX: dt is not used? It is here because we might want forces to
   418     // XXX: dt is not used? It is here because we might want forces to
   419     // apply longer than one tick in the future.
   419     // apply longer than one tick in the future.
   420     
   420     
   421     // Add applied force to the queue
   421     // Add applied force to the queue
   422     forceq.push(force);
   422     forceq.push(force);
   423 	this->inAir = true;
   423     this->inAir = true;
   424 }
   424 }
   425 
   425 
   426 void PhysicsObject::changeAim(float da) {
   426 void PhysicsObject::changeAim(float da) {
   427     this->aim += da;
   427     this->aim += da;
   428 
   428 
   457 std::vector<Vector>& PhysicsObject::getShape () {
   457 std::vector<Vector>& PhysicsObject::getShape () {
   458     return this->shape;
   458     return this->shape;
   459 }
   459 }
   460 
   460 
   461 void PhysicsObject::setShape (std::vector<Vector> shape) {
   461 void PhysicsObject::setShape (std::vector<Vector> shape) {
   462  	this->shape = shape;
   462      this->shape = shape;
   463 }
   463 }
   464 
   464 
   465 void PhysicsObject::tick () {
   465 void PhysicsObject::tick () {
   466     this->updatePosition();
   466     this->updatePosition();
   467 }
   467 }
   488         int midx = rand()%(int)dimensions.x;
   488         int midx = rand()%(int)dimensions.x;
   489         int midy = rand()%(int)dimensions.y;
   489         int midy = rand()%(int)dimensions.y;
   490         int range = rand()%(max_range-min_range)+min_range;
   490         int range = rand()%(max_range-min_range)+min_range;
   491 
   491 
   492         // put first circle in the middle of the cave
   492         // put first circle in the middle of the cave
   493 		// so that we have some area we can certainly spawn into 
   493         // so that we have some area we can certainly spawn into 
   494 		if(i == 0) {
   494         if(i == 0) {
   495 			midx = dimensions.x/2;
   495             midx = dimensions.x/2;
   496 			midy = dimensions.y/2;
   496             midy = dimensions.y/2;
   497 			range = 150;
   497             range = 150;
   498 		}
   498         }
   499 
   499 
   500         TerrainType type = EMPTY;
   500         TerrainType type = EMPTY;
   501         if(rand()%rock_rarity == 0) {
   501         if(rand()%rock_rarity == 0) {
   502             type = ROCK;
   502             type = ROCK;
   503         }
   503         }
   516 
   516 
   517 /**
   517 /**
   518  * Returns terrainType in given tile. ROCK if tile is out of area
   518  * Returns terrainType in given tile. ROCK if tile is out of area
   519  * @param pos - coordinate of tile
   519  * @param pos - coordinate of tile
   520  */
   520  */
       
   521 TerrainType PhysicsWorld::getType(int x, int y) const {
       
   522     if(x < 0 || y < 0 || x >= dimensions.x || y >= dimensions.y) {
       
   523         return ROCK;
       
   524     }
       
   525     return terrain[x][y];
       
   526 }
   521 TerrainType PhysicsWorld::getType(Vector pos) const {
   527 TerrainType PhysicsWorld::getType(Vector pos) const {
   522     int x = (int)(pos.x);
   528     int x = (int)(pos.x);
   523     int y = (int)(pos.y);
   529     int y = (int)(pos.y);
   524     if(x < 0 || y < 0 || x >= dimensions.x || y >= dimensions.y) {
   530     return getType(x, y);
   525         return ROCK;
   531 }
   526     }
   532 
   527     return terrain[x][y];
   533 /**
   528 }
   534  * Removes ground from given circle. ROCK is not removed.
       
   535  * @param (x, y) or pos - center of circle.
       
   536  * @param r - radius of circle
       
   537  */
       
   538 void PhysicsWorld::removeGround(int x, int y, float r) {
       
   539     for(int i = x-(int)r; i < x+r; i++) {
       
   540         for(int j = y-(int)r; j < y+r; j++) {
       
   541             if(getType(i, j) != ROCK) {
       
   542                 if((i-x)*(i-x)+(j-y)*(j-y) < r*r) {
       
   543                     // Safe because getType returns ROCK if tile is out of bounds
       
   544                     terrain[i][j] = EMPTY;
       
   545                 }
       
   546             }
       
   547         }
       
   548     }
       
   549 }
       
   550 void PhysicsWorld::removeGround(Vector pos, float r) {
       
   551     removeGround((int)pos.x, (int)pos.y, r);
       
   552 }