Permalink
Browse files

coins & monsters

  • Loading branch information...
1 parent cf03aca commit 2cb02209e0117a7701e2097f539b7cc2b314f150 Dmytro Lytovchenko committed Oct 1, 2011
Showing with 203 additions and 23 deletions.
  1. +1 −1 bin/level_01.txt
  2. +72 −0 src/creature.cpp
  3. +30 −2 src/creature.h
  4. +45 −11 src/player.cpp
  5. +5 −4 src/player.h
  6. +36 −0 src/world.cpp
  7. +14 −5 src/world.h
View
@@ -4,7 +4,7 @@
# ################ $ ####### ############# ##########
# $ # ############ #### ### #
# @ ##^^#### A ## $ # #### #
-# # $ ##^######## ### ########### #### # #
+# #A $ ##^######## ### ########### #### # #
# ###^^#### $ A $ $ A $ # ### $ ### $ A #
#############^###^^###################^^^^^^^^^^^##########^^^^^^^######################^^^^^^^^##############################
View
@@ -11,6 +11,7 @@
// TODO: more various creature behaviours like ability to shoot, jump, fly?
#include "creature.h"
#include "world.h"
+#include "player.h"
WorldObject::WorldObject( World * owner, float x, float y ): m_world(owner)
@@ -47,6 +48,7 @@ WorldObject_Money::~WorldObject_Money()
delete m_sprite[1];
}
+
hgeSprite * WorldObject_Money::GetSprite()
{
uint32_t milliseconds = GetTickCount();
@@ -56,3 +58,73 @@ hgeSprite * WorldObject_Money::GetSprite()
return m_sprite[f];
}
+
+
+bool WorldObject_Money::TouchPlayer( Player * pl )
+{
+ pl->m_money += 10;
+
+ // false to indicate that object is consumed
+ return false;
+}
+
+WorldObject_Enemy1::WorldObject_Enemy1( World * owner, float x, float y )
+ : WorldObject( owner,x,y ), m_facing(FACING_RIGHT)
+{
+ m_sprite[0][0] = owner->m_sprite_manager.GetSprite( "textures/enemy1_r1.png" );
+ m_sprite[0][1] = owner->m_sprite_manager.GetSprite( "textures/enemy1_r2.png" );
+ m_sprite[1][0] = owner->m_sprite_manager.GetSprite( "textures/enemy1_r1.png" );
+ m_sprite[1][1] = owner->m_sprite_manager.GetSprite( "textures/enemy1_r2.png" );
+}
+
+
+WorldObject_Enemy1::~WorldObject_Enemy1()
+{
+ delete m_sprite[0][0];
+ delete m_sprite[0][1];
+ delete m_sprite[1][0];
+ delete m_sprite[1][1];
+}
+
+
+hgeSprite * WorldObject_Enemy1::GetSprite()
+{
+ uint32_t milliseconds = GetTickCount();
+ // we want frames to change every 333 msec from 0 to 1
+ // total of 2 frames, hence the modulo of 2
+ uint32_t f = (milliseconds / 333) % 2;
+
+ return m_sprite[m_facing][f];
+}
+
+
+void WorldObject_Enemy1::Think()
+{
+ float delta = m_world->m_hge->Timer_GetDelta();
+ if (m_facing == FACING_RIGHT) {
+ // probe the box right of our face
+ bool solid_right = m_world->IsSolidAtXY( m_box.x2+5, m_box.y1+World::CELL_BOX_SIZE/2 );
+ // probe the box under our feet
+ bool solid_bottom_right = m_world->IsSolidAtXY( m_box.x2, m_box.y2+2 );
+ if( solid_bottom_right && ! solid_right ) {
+ m_box.x1 += MOVE_SPEED * delta;
+ m_box.x2 += MOVE_SPEED * delta;
+ } else {
+ m_facing = FACING_LEFT;
+ return;
+ }
+ }
+ else if (m_facing == FACING_LEFT) {
+ // probe the box left of our face
+ bool solid_left = m_world->IsSolidAtXY( m_box.x1-5, m_box.y1+World::CELL_BOX_SIZE/2 );
+ // probe the box under our feet
+ bool solid_bottom_left = m_world->IsSolidAtXY( m_box.x1, m_box.y2+2 );
+ if( solid_bottom_left && ! solid_left) {
+ m_box.x1 -= MOVE_SPEED * delta;
+ m_box.x2 -= MOVE_SPEED * delta;
+ } else {
+ m_facing = FACING_RIGHT;
+ return;
+ }
+ }
+}
View
@@ -11,6 +11,7 @@
#include <hgesprite.h>
class World;
+class Player;
// base Creature class
// represents a walking or stationary creature which is able to kill player
@@ -31,7 +32,15 @@ class WorldObject
virtual ~WorldObject() {}
virtual hgeSprite * GetSprite() { return NULL; }
- void Render();
+ virtual void Render();
+
+ virtual bool CanKillPlayerOnTouch() { return false; }
+
+ // Perform interaction between creature/object and the player. Return false
+ // if the object is consumed or destroyed by player
+ virtual bool TouchPlayer( Player * pl ) { return true; }
+
+ virtual void Think() {}
};
@@ -45,9 +54,28 @@ class WorldObject_Money: public virtual WorldObject
virtual ~WorldObject_Money();
virtual hgeSprite * GetSprite();
+ virtual bool TouchPlayer( Player * pl );
};
-class WorldObject_Creature: public virtual WorldObject
+class WorldObject_Enemy1: public virtual WorldObject
{
+protected:
+ hgeSprite * m_sprite[2][2];
+
+ static const int MOVE_SPEED = 120;
+
+ enum {
+ FACING_RIGHT = 0,
+ FACING_LEFT = 1
+ };
+
+ int m_facing;
+public:
+ WorldObject_Enemy1( World * owner, float x, float y );
+ virtual ~WorldObject_Enemy1();
+ virtual hgeSprite * GetSprite();
+ virtual bool CanKillPlayerOnTouch() { return true; }
+
+ virtual void Think();
};
View
@@ -9,6 +9,7 @@
#include "player.h"
#include "world.h"
#include "game.h"
+#include "creature.h"
#undef min
#include <algorithm>
@@ -19,10 +20,10 @@ Player::Player()
, m_position(0, 0, World::CELL_BOX_SIZE-1, World::CELL_BOX_SIZE-1), m_speed(0,0)
, m_last_facing(FACING_RIGHT), m_is_dead(false), m_money(0)
{
- m_character_right[0][0] = m_sprite_manager.GetSprite( "textures/mario_r1.png" );
- m_character_right[0][1] = m_sprite_manager.GetSprite( "textures/mario_r2.png" );
- m_character_right[1][0] = m_sprite_manager.GetSprite( "textures/mario_l1.png" );
- m_character_right[1][1] = m_sprite_manager.GetSprite( "textures/mario_l2.png" );
+ m_sprite[0][0] = m_sprite_manager.GetSprite( "textures/mario_r1.png" );
+ m_sprite[0][1] = m_sprite_manager.GetSprite( "textures/mario_r2.png" );
+ m_sprite[1][0] = m_sprite_manager.GetSprite( "textures/mario_l1.png" );
+ m_sprite[1][1] = m_sprite_manager.GetSprite( "textures/mario_l2.png" );
// this will not create another HGE, instead we will get the global
// unique HGE object which is already started
@@ -32,10 +33,10 @@ Player::Player()
Player::~Player()
{
- delete m_character_right[0][0];
- delete m_character_right[0][1];
- delete m_character_right[1][0];
- delete m_character_right[1][1];
+ delete m_sprite[0][0];
+ delete m_sprite[0][1];
+ delete m_sprite[1][0];
+ delete m_sprite[1][1];
}
@@ -78,7 +79,7 @@ hgeSprite * Player::GetSprite()
// total of 2 frames, hence the modulo of 2
uint32_t f = (milliseconds / 333) % 2;
- return m_character_right[m_last_facing][f];
+ return m_sprite[m_last_facing][f];
}
@@ -250,7 +251,7 @@ void Player::Think()
// if we fall below the world
if( m_position.y1 >= m_world->m_world_height * World::CELL_BOX_SIZE ) {
Die();
- m_world->OnPlayerDied();
+ return;
}
// if we step at least 25% cell deep into the spikes, we die
@@ -260,10 +261,39 @@ void Player::Think()
|| m_world->IsKillOnTouch( right_foot ) )
{
Die();
- m_world->OnPlayerDied();
+ return;
}
+
+ World::object_list_t touching_objects;
+
+ // shrink our box slightly for more realistic collisions
+ hgeRect shrunken_position = m_position;
+ shrunken_position.x1 += 12; // 20% from left
+ shrunken_position.x2 -= 12; // 20% from right
+ shrunken_position.y1 += 20; // 30% from top
+ shrunken_position.y2 -= 10; // 15% from bottom
+
+ m_world->FindIntersectingObjects( shrunken_position, touching_objects );
+
+ // scan what we have found
+ for( World::object_list_t::iterator i = touching_objects.begin(); i != touching_objects.end(); ++i )
+ {
+ // if it kills us - ouch
+ if( (*i)->CanKillPlayerOnTouch() ) {
+ Die();
+ return;
+ } else {
+ if( false == (*i)->TouchPlayer( this ) ) {
+ // if touch player returned false - we consume the object
+ m_world->RemoveObject( *i );
+ }
+ }
+ }
+ // we do not own the contents of 'touching_objects', so we don't delete
+ // its contents and the list clears itself automatically
}
+
void Player::MoveTo( float x, float y )
{
m_position.Set(
@@ -275,9 +305,13 @@ void Player::MoveTo( float x, float y )
void Player::Die()
{
+ // can't die twice in a moment
+ if( m_is_dead) return;
+
// TODO: invent the way for player to inform the gamestate or the world about
// level game restart or scroll back to allow player to continue
m_is_dead = true;
+ m_world->OnPlayerDied();
}
View
@@ -34,8 +34,6 @@ class Player
// Hack this value for infinite lives
int m_lives;
- // Hack this value for infinite gold, u jelly american treasury?
- int m_money;
// flag to render death animation/effects/blood splash when player died
bool m_is_dead;
@@ -59,12 +57,15 @@ class Player
SpriteManager m_sprite_manager;
- hgeSprite * m_character_right[2][2];
+ hgeSprite * m_sprite[2][2];
HGE * m_hge;
public:
- // A bounding box for character sprite used for rendering and collisions
+ // Hack this value for infinite gold, u jelly american treasury?
+ int m_money;
+
+ // A bounding box for character sprite used for rendering and collisions
// for simplicity player has same size as world cells
hgeRect m_position;
View
@@ -96,6 +96,10 @@ void World::LoadWorld( const std::string & filename )
WorldObject * o = new WorldObject_Money( this, col * CELL_BOX_SIZE, row * CELL_BOX_SIZE );
m_objects.push_back( o );
}
+ else if( contents == WORLD_CELL_ENEMY1 ) {
+ WorldObject * o = new WorldObject_Enemy1( this, col * CELL_BOX_SIZE, row * CELL_BOX_SIZE );
+ m_objects.push_back( o );
+ }
else {
this->At( row, col ) = contents;
}
@@ -120,6 +124,11 @@ void World::Think()
m_player->Die();
OnPlayerDied();
}
+
+ for( object_list_t::iterator i = m_objects.begin(); i != m_objects.end(); ++i )
+ {
+ (*i)->Think();
+ }
}
void World::OnPlayerDied()
@@ -208,7 +217,34 @@ bool World::IsSolid( CellType contents )
return contents == WORLD_CELL_WALL1;
}
+
bool World::IsKillOnTouch( CellType contents )
{
return contents == WORLD_CELL_SPIKES;
}
+
+
+void World::FindIntersectingObjects( const hgeRect & rc, object_list_t & result )
+{
+ result.clear(); // just in case
+ for( object_list_t::iterator i = m_objects.begin(); i != m_objects.end(); ++i )
+ {
+ if( (*i)->m_box.Intersect( & rc ) ) {
+ result.push_back( *i );
+ }
+ }
+}
+
+
+void World::RemoveObject( WorldObject * o )
+{
+ // Linear search for the object. On a large world this can be speeded up if
+ // you change to using std::map for fast key searches.
+ for( object_list_t::iterator i = m_objects.begin(); i != m_objects.end(); ++i )
+ {
+ if (*i == o) {
+ m_objects.erase( i );
+ return;
+ }
+ }
+}
Oops, something went wrong.

0 comments on commit 2cb0220

Please sign in to comment.