Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
127 KB
Referenced Files
None
Subscribers
None
diff --git a/src/CyclopEntity.cpp b/src/CyclopEntity.cpp
index 04e1d6c..2b49153 100644
--- a/src/CyclopEntity.cpp
+++ b/src/CyclopEntity.cpp
@@ -1,380 +1,375 @@
#include "CyclopEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "RockMissileEntity.h"
#include "FallingRockEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include <iostream>
CyclopEntity::CyclopEntity(float x, float y)
: EnnemyEntity (ImageManager::getImageManager()->getImage(IMAGE_CYCLOP), x, y)
{
width = 128;
height = 192;
creatureSpeed = CYCLOP_SPEED[0];
velocity = Vector2D(creatureSpeed);
hp = CYCLOP_HP;
hpDisplay = CYCLOP_HP;
hpMax = CYCLOP_HP;
meleeDamages = CYCLOP_DAMAGES;
type = ENTITY_ENNEMY_BOSS;
bloodColor = bloodRed;
shadowFrame = 8;
dyingFrame = 5;
deathFrame = FRAME_CORPSE_CYCLOP;
dyingSound = SOUND_CYCLOP_DIE;
frame = 0;
if (game().getPlayerPosition().x > x) isMirroring = true;
sprite.setOrigin(64.0f, 128.0f);
nextRockMissile = 0;
destroyLevel = 0;
state = 0;
timer = 2.0f;
counter = 10;
age = -1.5f;
enemyType = EnemyTypeCyclops;
resistance[ResistanceFrozen] = ResistanceVeryHigh;
resistance[ResistanceRecoil] = ResistanceVeryHigh;
}
int CyclopEntity::getHealthLevel()
{
int healthLevel = 0;
if (hp <= hpMax * 0.25) healthLevel = 3;
else if (hp <= hpMax * 0.5) healthLevel = 2;
else if (hp <= hpMax * 0.75) healthLevel = 1;
return healthLevel;
}
void CyclopEntity::fire()
{
new RockMissileEntity(x, y - 62, nextRockMissile);
SoundManager::getSoundManager()->playSound(SOUND_THROW);
}
void CyclopEntity::initFallingGrid()
{
for (int i = 0; i < MAP_WIDTH; i++)
for (int j = 0; j < MAP_HEIGHT; j++)
fallingGrid[i][j] = false;
}
void CyclopEntity::fallRock()
{
int rx, ry;
do
{
rx = 1 + rand() % (MAP_WIDTH - 2);
ry = 1 + rand() % (MAP_HEIGHT - 2);
}
while (fallingGrid[rx][ry]);
fallingGrid[rx][ry] = true;
new FallingRockEntity(rx * TILE_WIDTH + OFFSET_X + TILE_WIDTH / 2,
ry * TILE_HEIGHT + OFFSET_Y + TILE_HEIGHT / 2,
rand() % 3);
}
void CyclopEntity::computeNextRockMissile()
{
if (getHealthLevel() == 0)
nextRockMissile = rand()%5 == 0 ? 1 : 0;
else if (getHealthLevel() == 1)
nextRockMissile = rand()%3 == 0 ? 1 : 0;
else if (getHealthLevel() == 2)
nextRockMissile = rand()%2 == 0 ? 0 : 1;
else
nextRockMissile = rand()%3 == 0 ? 0 : 1;
}
void CyclopEntity::computeStates(float delay)
{
timer -= delay;
if (timer <= 0.0f)
{
if (state == 0) // walking
{
if (counter > 0)
{
counter--;
timer = 0.5f;
creatureSpeed = CYCLOP_SPEED[getHealthLevel()];
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), creatureSpeed ));
}
else
{
velocity.x = 0.0f;
velocity.y = 0.0f;
if (destroyLevel < getHealthLevel())
{
state = 3; // charge to destroy
destroyLevel++;
counter = destroyLevel + 1;
timer = 0.9f;
SoundManager::getSoundManager()->playSound(SOUND_CYCLOP_00);
initFallingGrid();
}
else
{
state = 1; // charge to fire
timer = 0.9f;
counter = CYCLOP_NUMBER_ROCKS[getHealthLevel()];
SoundManager::getSoundManager()->playSound(SOUND_CYCLOP_00);
computeNextRockMissile();
}
}
}
else if (state == 1) // fire
{
state = 2;
fire();
timer = CYCLOP_FIRE_DELAY[getHealthLevel()];
}
else if (state == 2) // fire end
{
if (counter <= 1)
{
state = 0;
timer = 0.2f;
counter = 10;
}
else
{
counter--;
state = 1;
timer = 0.2f;
computeNextRockMissile();
}
}
else if (state == 3)
{
state = 4; // destroy
timer = 0.2;
game().makeShake(0.4f);
SoundManager::getSoundManager()->playSound(SOUND_WALL_IMPACT);
for (int i = 0; i < 10 ; i++) fallRock();
}
else if (state == 4)
{
if (counter <= 1)
{
state = 0;
timer = 0.2f;
counter = 10;
}
else
{
counter--;
state = 3;
timer = 0.3f;
}
}
}
}
void CyclopEntity::animate(float delay)
{
if (age <= 0.0f)
{
age += delay;
return;
}
if (isAgonising)
{
if (h < -0.01f)
{
isDying = true;
SpriteEntity* corpse;
corpse = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_CORPSES_BIG), x, y, 128, 128);
corpse->setFrame(deathFrame - FRAME_CORPSE_KING_RAT);
corpse->setZ(OFFSET_Y);
corpse->setType(ENTITY_CORPSE);
if (dyingSound != SOUND_NONE) SoundManager::getSoundManager()->playSound(dyingSound);
}
else
{
frame = dyingFrame;
hVelocity -= 700.0f * delay;
h += hVelocity * delay;
}
return;
}
// special states
if (specialState[SpecialStateIce].active) delay *= specialState[SpecialStateIce].parameter;
// IA
computeStates(delay);
// collisions
if (canCollide()) testSpriteCollisions();
BaseCreatureEntity::animate(delay);
// current frame
if (state == 0)
{
int r = ((int)(age * 5.0f)) % 4;
if (r == 2) frame = 0;
else if (r == 3) frame = 2;
else frame = r;
}
else if (state == 1)
{
isMirroring = game().getPlayer()->getX() > x;
frame = 3;
}
else if (state == 2)
{
frame = 4;
}
else if (state == 3)
{
frame = 6;
}
else if (state == 4)
{
frame = 7;
}
// frame's mirroring
if (velocity.x > 1.0f)
isMirroring = true;
else if (velocity.x < -1.0f)
isMirroring = false;
z = OFFSET_Y + y + 46;
}
bool CyclopEntity::hurt(int damages, enumShotType hurtingType, int level)
{
if (destroyLevel < getHealthLevel()) damages /= 3;
return EnnemyEntity::hurt(damages, hurtingType, level);
}
void CyclopEntity::calculateBB()
{
boundingBox.left = OFFSET_X + (int)x - 32;
boundingBox.width = 58;
boundingBox.top = OFFSET_Y + (int)y - 42;
boundingBox.height = 90;
}
void CyclopEntity::afterWallCollide()
{
}
void CyclopEntity::collideMapRight()
{
velocity.x = -velocity.x;
afterWallCollide();
}
void CyclopEntity::collideMapLeft()
{
velocity.x = -velocity.x;
afterWallCollide();
}
void CyclopEntity::collideMapTop()
{
velocity.y = -velocity.y;
afterWallCollide();
}
void CyclopEntity::collideMapBottom()
{
velocity.y = -velocity.y;
afterWallCollide();
}
-void CyclopEntity::dying()
-{
- EnnemyEntity::dying();
-}
-
void CyclopEntity::drop()
{
ItemEntity* newItem = new ItemEntity(itemBossHeart, x, y);
newItem->setVelocity(Vector2D(100.0f + rand()% 250));
newItem->setViscosity(0.96f);
}
void CyclopEntity::render(sf::RenderTarget* app)
{
EnnemyEntity::render(app);
// stones
if (state == 1)
{
if (nextRockMissile == 0) // small rock
{
sprite.setTextureRect(sf::IntRect(1152, 0, 64, 64));
if (isMirroring)
sprite.setPosition(x + 60, y);
else
sprite.setPosition(x + 4, y);
}
else // medium rock
{
sprite.setTextureRect(sf::IntRect(1152, 64, 64, 64));
if (isMirroring)
sprite.setPosition(x + 60, y - 12);
else
sprite.setPosition(x + 4, y - 12);
}
app->draw(sprite);
sprite.setPosition(x, y);
}
float l = hpDisplay * ((MAP_WIDTH - 1) * TILE_WIDTH) / hpMax;
sf::RectangleShape rectangle(sf::Vector2f((MAP_WIDTH - 1) * TILE_WIDTH, 25));
rectangle.setFillColor(sf::Color(0, 0, 0,128));
rectangle.setPosition(sf::Vector2f(OFFSET_X + TILE_WIDTH / 2, OFFSET_Y + 25 + (MAP_HEIGHT - 1) * TILE_HEIGHT));
app->draw(rectangle);
rectangle.setSize(sf::Vector2f(l, 25));
rectangle.setFillColor(sf::Color(190, 20, 20));
rectangle.setPosition(sf::Vector2f(OFFSET_X + TILE_WIDTH / 2, OFFSET_Y + 25 + (MAP_HEIGHT - 1) * TILE_HEIGHT));
app->draw(rectangle);
game().write( "Cimmerian Cyclops",
18,
OFFSET_X + TILE_WIDTH / 2 + 10.0f,
OFFSET_Y + 25 + (MAP_HEIGHT - 1) * TILE_HEIGHT + 1.0f,
ALIGN_LEFT,
sf::Color(255, 255, 255),
app, 0 , 0);
}
void CyclopEntity::collideWithEnnemy(GameEntity* collidingEntity)
{
EnnemyEntity* entity = static_cast<EnnemyEntity*>(collidingEntity);
if (entity->getMovingStyle() == movWalking)
{
inflictsRecoilTo(entity);
}
}
void CyclopEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity)
{
}
diff --git a/src/CyclopEntity.h b/src/CyclopEntity.h
index 9647729..1414e66 100644
--- a/src/CyclopEntity.h
+++ b/src/CyclopEntity.h
@@ -1,44 +1,43 @@
#ifndef CYCLOPENTITY_H
#define CYCLOPENTITY_H
#include "EnnemyEntity.h"
#include "PlayerEntity.h"
class CyclopEntity : public EnnemyEntity
{
public:
CyclopEntity(float x, float y);
virtual void animate(float delay);
virtual void render(sf::RenderTarget* app);
virtual void calculateBB();
virtual void inflictsRecoilTo(BaseCreatureEntity* targetEntity);
protected:
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
void afterWallCollide();
virtual bool hurt(int damages, enumShotType hurtingType, int level);
virtual void collideWithEnnemy(GameEntity* collidingEntity);
- virtual void dying();
virtual void drop();
void computeStates(float delay);
int getHealthLevel();
private:
float timer;
int state;
int counter;
int destroyLevel;
int nextRockMissile;
void computeNextRockMissile();
void fire();
void fallRock();
void initFallingGrid();
bool fallingGrid[MAP_WIDTH][MAP_HEIGHT];
};
#endif // CYCLOPENTITY_H
diff --git a/src/EnnemyEntity.cpp b/src/EnnemyEntity.cpp
index 1da7dfa..118edfd 100644
--- a/src/EnnemyEntity.cpp
+++ b/src/EnnemyEntity.cpp
@@ -1,290 +1,291 @@
#include "EnnemyEntity.h"
#include "BoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include <iostream>
#include "WitchBlastGame.h"
EnnemyEntity::EnnemyEntity(sf::Texture* image, float x, float y)
: BaseCreatureEntity (image, x, y, 64, 64)
{
type = ENTITY_ENNEMY;
bloodColor = bloodRed;
z = y;
h = 0;
age = -0.001f * (rand()%800) - 0.4f;
deathFrame = -1;
dyingFrame = -1;
dyingSound = SOUND_ENNEMY_DYING;
agonizingSound = SOUND_NONE;
hurtingSound = SOUND_NONE;
isAgonising = false;
enemyType = NB_ENEMY;
}
void EnnemyEntity::animate(float delay)
{
if (isAgonising)
{
if (h < -0.01f)
{
isDying = true;
SpriteEntity* corpse;
if (deathFrame >= FRAME_CORPSE_KING_RAT)
{
corpse = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_CORPSES_BIG), x, y, 128, 128);
corpse->setFrame(deathFrame - FRAME_CORPSE_KING_RAT);
}
else
{
corpse = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_CORPSES), x, y, 64, 64);
corpse->setFrame(deathFrame);
corpse->setImagesProLine(10);
}
corpse->setZ(OFFSET_Y);
corpse->setType(ENTITY_CORPSE);
if (dyingSound != SOUND_NONE) SoundManager::getSoundManager()->playSound(dyingSound);
}
else
{
frame = dyingFrame;
hVelocity -= 700.0f * delay;
h += hVelocity * delay;
}
return;
}
if (canCollide()) testSpriteCollisions();
if (age > 0.0f)
BaseCreatureEntity::animate(delay);
else
age += delay;
}
void EnnemyEntity::calculateBB()
{
boundingBox.left = (int)x - width / 2;
boundingBox.width = width;
boundingBox.top = (int)y - height / 2;
boundingBox.height = height;
}
void EnnemyEntity::collideMapRight()
{
velocity.x = 0.0f;
}
void EnnemyEntity::collideMapLeft()
{
velocity.x = 0.0f;
}
void EnnemyEntity::collideMapTop()
{
velocity.y = 0.0f;
}
void EnnemyEntity::collideMapBottom()
{
velocity.y = 0.0f;
}
void EnnemyEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (!isDying && !isAgonising && collideWithEntity(entity))
{
if (entity->getType() == ENTITY_PLAYER || entity->getType() == ENTITY_BOLT )
{
PlayerEntity* playerEntity = dynamic_cast<PlayerEntity*>(entity);
BoltEntity* boltEntity = dynamic_cast<BoltEntity*>(entity);
if (playerEntity != NULL && !playerEntity->isDead())
{
if (playerEntity->hurt(meleeDamages, ShotTypeStandard, 0))
{
float xs = (x + playerEntity->getX()) / 2;
float ys = (y + playerEntity->getY()) / 2;
SpriteEntity* star = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_STAR_2), xs, ys);
star->setFading(true);
star->setZ(y+ 100);
star->setLifetime(0.7f);
star->setType(16);
star->setSpin(400.0f);
}
inflictsRecoilTo(playerEntity);
}
else if (boltEntity != NULL && !boltEntity->getDying() && boltEntity->getAge() > 0.05f)
{
float xs = (x + boltEntity->getX()) / 2;
float ys = (y + boltEntity->getY()) / 2;
boltEntity->collide();
hurt(boltEntity->getDamages(), boltEntity->getBoltType(), boltEntity->getLevel());
if (bloodColor > bloodNone) game().generateBlood(x, y, bloodColor);
SoundManager::getSoundManager()->playSound(SOUND_IMPACT);
SpriteEntity* star = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_STAR_2), xs, ys);
star->setFading(true);
star->setZ(y+ 100);
star->setLifetime(0.7f);
star->setType(16);
star->setSpin(400.0f);
if (boltEntity->getBoltType() == ShotTypeStone)
{
float recoilVelocity = STONE_DECOIL_VELOCITY[boltEntity->getLevel()];
float recoilDelay = STONE_DECOIL_DELAY[boltEntity->getLevel()];
if (resistance[ResistanceRecoil] == ResistanceHigh)
{
recoilVelocity *= 0.75f;
recoilDelay *= 0.75f;
}
else if (resistance[ResistanceRecoil] == ResistanceVeryHigh)
{
recoilVelocity *= 0.5f;
recoilDelay *= 0.5f;
}
Vector2D recoilVector = Vector2D(0, 0).vectorTo(boltEntity->getVelocity(),
recoilVelocity );
giveRecoil(true, recoilVector, recoilDelay);
}
}
}
else // collision with other enemy ?
{
if (entity->getType() >= ENTITY_ENNEMY && entity->getType() <= ENTITY_ENNEMY_MAX)
{
if (this != entity)
{
EnnemyEntity* ennemyEntity = static_cast<EnnemyEntity*>(entity);
if (ennemyEntity->canCollide()) collideWithEnnemy(entity);
}
}
}
}
}
void EnnemyEntity::collideWithEnnemy(GameEntity* collidingEntity)
{
// To implement the behaviour when colliding with another ennemy
}
bool EnnemyEntity::hurt(int damages, enumShotType hurtingType, int level)
{
bool hurted = BaseCreatureEntity::hurt(damages, hurtingType, level);
if (hurted && hurtingSound != SOUND_NONE && hp > 0)
SoundManager::getSoundManager()->playSound(hurtingSound);
return hurted;
}
void EnnemyEntity::dying()
{
if (dyingFrame == -1)
{
isDying = true;
SpriteEntity* corpse = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_CORPSES), x, y, 64, 64);
corpse->setZ(OFFSET_Y);
corpse->setImagesProLine(10);
corpse->setFrame(deathFrame);
corpse->setType(ENTITY_CORPSE);
if (dyingSound != SOUND_NONE) SoundManager::getSoundManager()->playSound(dyingSound);
}
else
{
isAgonising = true;
hVelocity = 200.0f;
if (agonizingSound != SOUND_NONE) SoundManager::getSoundManager()->playSound(agonizingSound);
}
if (bloodColor != bloodNone) for (int i = 0; i < 4; i++) game().generateBlood(x, y, bloodColor);
drop();
+ game().addKilledEnemy(enemyType);
}
void EnnemyEntity::drop()
{
if (rand() % 5 == 0)
{
ItemEntity* newItem = new ItemEntity(ItemCopperCoin, x, y);
newItem->setMap(map, TILE_WIDTH, TILE_HEIGHT, OFFSET_X, OFFSET_Y);
newItem->setVelocity(Vector2D(100.0f + rand()% 250));
newItem->setViscosity(0.96f);
}
if (rand() % 25 == 0)
{
ItemEntity* newItem = new ItemEntity(itemHealthVerySmall, x, y);
newItem->setMap(map, TILE_WIDTH, TILE_HEIGHT, OFFSET_X, OFFSET_Y);
newItem->setVelocity(Vector2D(100.0f + rand()% 250));
newItem->setViscosity(0.96f);
}
}
bool EnnemyEntity::canCollide()
{
return (!isAgonising);
}
void EnnemyEntity::render(sf::RenderTarget* app)
{
if (isAgonising || (isDying && dyingFrame > -1))
{
if (shadowFrame > -1)
{
// shadow
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(shadowFrame * width, 0, width, height));
app->draw(sprite);
}
int nx = dyingFrame;
int ny = 0;
if (imagesProLine > 0)
{
nx = dyingFrame % imagesProLine;
ny = dyingFrame / imagesProLine;
}
sprite.setPosition(x, y - h);
if (isMirroring)
sprite.setTextureRect(sf::IntRect(nx * width + width, ny * height, -width, height));
else
sprite.setTextureRect(sf::IntRect(nx * width, ny * height, width, height));
app->draw(sprite);
}
else
BaseCreatureEntity::render(app);
}
void EnnemyEntity::displayLifeBar(std::string name, float posY, sf::RenderTarget* app)
{
float l = hpDisplay * ((MAP_WIDTH - 1) * TILE_WIDTH) / hpMax;
sf::RectangleShape rectangle(sf::Vector2f((MAP_WIDTH - 1) * TILE_WIDTH, 25));
rectangle.setFillColor(sf::Color(0, 0, 0,128));
rectangle.setPosition(sf::Vector2f(OFFSET_X + TILE_WIDTH / 2, posY));
app->draw(rectangle);
rectangle.setSize(sf::Vector2f(l, 25));
rectangle.setFillColor(sf::Color(190, 20, 20));
rectangle.setPosition(sf::Vector2f(OFFSET_X + TILE_WIDTH / 2, posY));
app->draw(rectangle);
game().write( name,
18,
OFFSET_X + TILE_WIDTH / 2 + 10.0f,
posY + 1.0f,
ALIGN_LEFT,
sf::Color(255, 255, 255),
app, 0, 0);
}
diff --git a/src/EvilFlowerEntity.cpp b/src/EvilFlowerEntity.cpp
index 89434d9..df06ac6 100644
--- a/src/EvilFlowerEntity.cpp
+++ b/src/EvilFlowerEntity.cpp
@@ -1,100 +1,101 @@
#include "EvilFlowerEntity.h"
#include "BoltEntity.h"
#include "EnnemyBoltEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include <math.h>
EvilFlowerEntity::EvilFlowerEntity(float x, float y)
: EnnemyEntity (ImageManager::getImageManager()->getImage(IMAGE_FLOWER), x, y)
{
hp = EVIL_FLOWER_HP;
meleeDamages = EVIL_FLOWER_MELEE_DAMAGES;
setSpin(50.0f);
frame = 0;
type = 23;
bloodColor = bloodGreen;
enemyType = EnemyTypeEvilFlower;
fireDelay = EVIL_FLOWER_FIRE_DELAY;
age = -1.0f + (rand() % 2500) * 0.001f;
}
void EvilFlowerEntity::animate(float delay)
{
float flowerDelay = delay;
if (specialState[SpecialStateIce].active) flowerDelay = delay * specialState[SpecialStateIce].parameter;
if (fireDelay < 0.7f) setSpin(500.0f);
else if (fireDelay < 1.4f) setSpin(120.0f);
else setSpin(50.0f);
EnnemyEntity::animate(delay);
angle += spin * flowerDelay;
if (age > 0.0f)
{
fireDelay -= flowerDelay;
if (fireDelay <= 0.0f)
{
fireDelay = EVIL_FLOWER_FIRE_DELAY;
fire();
}
}
}
void EvilFlowerEntity::calculateBB()
{
boundingBox.left = (int)x - width / 2 + EVIL_FLOWER_BB_LEFT;
boundingBox.width = width - EVIL_FLOWER_BB_WIDTH_DIFF;
boundingBox.top = (int)y - height / 2 + EVIL_FLOWER_BB_TOP;
boundingBox.height = height - EVIL_FLOWER_BB_HEIGHT_DIFF;
}
void EvilFlowerEntity::dying()
{
isDying = true;
+ game().addKilledEnemy(enemyType);
SpriteEntity* deadFlower = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_CORPSES), x, y, 64, 64);
deadFlower->setZ(OFFSET_Y);
deadFlower->setFrame(FRAME_CORPSE_FLOWER);
deadFlower->setType(ENTITY_CORPSE);
for (int i = 0; i < 4; i++) game().generateBlood(x, y, bloodColor);
drop();
SoundManager::getSoundManager()->playSound(SOUND_ENNEMY_DYING);
}
void EvilFlowerEntity::fire()
{
SoundManager::getSoundManager()->playSound(SOUND_BLAST_FLOWER);
EnnemyBoltEntity* bolt = new EnnemyBoltEntity
(x, y + 10, ShotTypeStandard, 0);
bolt->setFrame(1);
bolt->setMap(map, TILE_WIDTH, TILE_HEIGHT, OFFSET_X, OFFSET_Y);
float flowerFireVelocity = EVIL_FLOWER_FIRE_VELOCITY;
if (specialState[SpecialStateIce].active) flowerFireVelocity *= 0.5f;
bolt->setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), flowerFireVelocity ));
}
void EvilFlowerEntity::render(sf::RenderTarget* app)
{
sprite.setPosition(x, y);
float savedAngle = sprite.getRotation();
sprite.setRotation(0.0f);
// shadow
sprite.setTextureRect(sf::IntRect(width * 2, 0, width, height));
app->draw(sprite);
sprite.setTextureRect(sf::IntRect(width, 0, width, height));
app->draw(sprite);
sprite.setRotation(savedAngle);
EnnemyEntity::render(app);
}
diff --git a/src/FallingRockEntity.cpp b/src/FallingRockEntity.cpp
index 8b9569e..64474d5 100644
--- a/src/FallingRockEntity.cpp
+++ b/src/FallingRockEntity.cpp
@@ -1,130 +1,131 @@
#include "FallingRockEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
FallingRockEntity::FallingRockEntity(float x, float y, int rockType)
: EnnemyEntity (ImageManager::getImageManager()->getImage(IMAGE_CYCLOP), x, y)
{
imagesProLine = 20;
type = ENTITY_ENNEMY;
movingStyle = movFlying;
bloodColor = bloodNone; // stones don't bleed
age = 0.0f;
h = 1800 + rand() % 1000;
hp = 24;
jumping = false;
hVelocity = 0.0f;
this->rockType = rockType;
enemyType = EnemyTypeRockFalling;
switch (rockType)
{
case 0: meleeDamages = 8; frame = 18; break;
case 1: meleeDamages = 10; frame = 38; break;
case 2: meleeDamages = 12; frame = 58; break;
}
}
void FallingRockEntity::animate(float delay)
{
if (jumping)
{
hVelocity -= 700.0f * delay;
h += hVelocity * delay;
if (h <= 0.0f) dying();
}
else
{
h -= delay * 750.0f;
if (canCollide()) testSpriteCollisions();
if (h <= 0.0f)
{
hVelocity = 250.0f;
jumping = true;
}
}
}
void FallingRockEntity::render(sf::RenderTarget* app)
{
int nx = frame % imagesProLine;
int ny = frame / imagesProLine;
// shadow
if (h <= 1600)
{
int f = 1600 - h;
if (f > 255) f = 255;
sprite.setColor(sf::Color(255, 255, 255, f));
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect((nx + 1) * width, ny * height, width, height));
app->draw(sprite);
sprite.setColor(sf::Color(255, 255, 255, 255));
}
sprite.setPosition(x, y - h);
sprite.setTextureRect(sf::IntRect(nx * width, ny * height, width, height));
app->draw(sprite);
#ifdef SHOW_BOUNDING_BOX
sf::Vertex line[] =
{
sf::Vertex(sf::Vector2f(boundingBox.left, boundingBox.top)),
sf::Vertex(sf::Vector2f(boundingBox.left + boundingBox.width, boundingBox.top)),
sf::Vertex(sf::Vector2f(boundingBox.left + boundingBox.width, boundingBox.top)),
sf::Vertex(sf::Vector2f(boundingBox.left + boundingBox.width, boundingBox.top + boundingBox.height)),
sf::Vertex(sf::Vector2f(boundingBox.left + boundingBox.width, boundingBox.top + boundingBox.height)),
sf::Vertex(sf::Vector2f(boundingBox.left, boundingBox.top + boundingBox.height)),
sf::Vertex(sf::Vector2f(boundingBox.left, boundingBox.top + boundingBox.height)),
sf::Vertex(sf::Vector2f(boundingBox.left, boundingBox.top))
};
app->draw(line, 8, sf::Lines);
#endif
}
void FallingRockEntity::calculateBB()
{
int w = 30;
if (rockType == 1) w = 40;
else if (rockType == 2) w = 50;
boundingBox.left = (int)x - w / 2;
boundingBox.width = w;
boundingBox.top = (int)y - w / 2;
boundingBox.height = w;
}
bool FallingRockEntity::canCollide()
{
return h < 70;
}
void FallingRockEntity::dying()
{
isDying = true;
+ game().addKilledEnemy(enemyType);
SoundManager::getSoundManager()->playSound(SOUND_ROCK_IMPACT);
game().makeShake(0.1f);
for (int i = 0; i < 4; i++)
{
SpriteEntity* blood = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_BLOOD), x, y, 16, 16, 6);
blood->setZ(OFFSET_Y - 1);
blood->setFrame(12 + rand()%6);
blood->setType(ENTITY_BLOOD);
blood->setVelocity(Vector2D(rand()%150));
blood->setViscosity(0.95f);
float bloodScale = 1.0f + (rand() % 10) * 0.1f;
blood->setScale(bloodScale, bloodScale);
}
}
diff --git a/src/GiantSlimeEntity.cpp b/src/GiantSlimeEntity.cpp
index 6a64ab3..da126f5 100644
--- a/src/GiantSlimeEntity.cpp
+++ b/src/GiantSlimeEntity.cpp
@@ -1,467 +1,468 @@
#include "GiantSlimeEntity.h"
#include "BoltEntity.h"
#include "EnnemyBoltEntity.h"
#include "PlayerEntity.h"
#include "SlimeEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include <iostream>
GiantSlimeEntity::GiantSlimeEntity(float x, float y)
: EnnemyEntity (ImageManager::getImageManager()->getImage(IMAGE_GIANT_SLIME), x, y)
{
width = 128;
height = 128;
creatureSpeed = GIANT_SLIME_SPEED;
velocity = Vector2D(creatureSpeed);
hp = GIANT_SLIME_HP;
hpDisplay = hp;
hpMax = GIANT_SLIME_HP;
meleeDamages = GIANT_SLIME_DAMAGES;
missileDelay = GIANT_SLIME_MISSILE_DELAY;
type = ENTITY_ENNEMY_BOSS;
enemyType = EnemyTypeSlimeBoss;
bloodColor = bloodGreen;
shadowFrame = 3;
frame = 0;
sprite.setOrigin(64.0f, 64.0f);
h = 0.0f;
age = -2.0f;
changeToState(0);
slimeCounter = 0;
slimeTimer =5.0f;
resistance[ResistanceFrozen] = ResistanceVeryHigh;
resistance[ResistanceRecoil] = ResistanceVeryHigh;
sprite.setOrigin(64, 84);
}
void GiantSlimeEntity::changeToState(int n)
{
if (n == 0) // walking
{
state = 0;
counter = 8 + rand() % 7;
timer = -1.0f;
viscosity = 1.0f;
}
else if (n == 1 || n == 3 || n == 5 || n == 8) // waiting
{
state = n;
timer = 1.2f;
setVelocity(Vector2D(0.0f, 0.0f));
}
else if (n == 2) // jumping
{
state = 2;
timer = 4.0f;
viscosity = 0.991f;
SoundManager::getSoundManager()->playSound(SOUND_SLIME_JUMP);
hVelocity = 420.0f + rand() % 380;
isFirstJumping = true;
float randVel = 350.0f + rand() % 200;
if (rand() % 2 == 0)
{
float tan = (game().getPlayer()->getX() - x) / (game().getPlayer()->getY() - y);
float angle = atan(tan);
if (game().getPlayer()->getY() > y)
setVelocity(Vector2D(sin(angle) * randVel,
cos(angle) * randVel));
else
setVelocity(Vector2D(-sin(angle) * randVel,
-cos(angle) * randVel));
}
else
velocity = Vector2D(randVel);
}
else if (n == 4) // walking
{
state = 4;
if (hp <= hpMax / 4)
counter = 26;
if (hp <= hpMax / 2)
counter = 18;
else
counter = 12;
timer = GIANT_SLIME_MISSILE_DELAY;
}
else if (n == 6) // jumping
{
state = 6;
timer = 1.2f;
viscosity = 1.0f;
SoundManager::getSoundManager()->playSound(SOUND_SLIME_JUMP);
hVelocity = 1200.0f;
}
else if (n == 7) // falling
{
isFalling = false;
state = 7;
timer = 4.0f;
hVelocity = -1500.0f;
h = 1500;
}
}
void GiantSlimeEntity::animate(float delay)
{
slimeTimer -= delay;
if (slimeTimer <= 0.0f)
{
switch (slimeCounter)
{
case 0: new SlimeEntity(OFFSET_X + TILE_WIDTH * 1.5f, OFFSET_Y + TILE_HEIGHT * 1.5f, SlimeTypeStandard, true); break;
case 1: new SlimeEntity(OFFSET_X + TILE_WIDTH * (MAP_WIDTH - 2) + TILE_WIDTH * 0.5f, OFFSET_Y + TILE_HEIGHT * 1.5f, SlimeTypeStandard, true); break;
case 2: new SlimeEntity(OFFSET_X + TILE_WIDTH * (MAP_WIDTH - 2) + TILE_WIDTH * 0.5f, OFFSET_Y + TILE_HEIGHT * (MAP_HEIGHT - 2) + TILE_HEIGHT * 0.5f, SlimeTypeStandard, true); break;
case 3: new SlimeEntity(OFFSET_X + TILE_WIDTH * 1.5f, OFFSET_Y + TILE_HEIGHT * (MAP_HEIGHT - 2) + TILE_HEIGHT * 0.5f, SlimeTypeStandard, true); break;
}
slimeTimer = 7.0f;
slimeCounter ++;
if (slimeCounter == 4) slimeCounter = 0;
}
if (age <= 0.0f)
{
age += delay;
return;
}
EnnemyEntity::animate(delay);
if (specialState[SpecialStateIce].active) delay *= specialState[SpecialStateIce].parameter;
timer -= delay;
if (timer <= 0.0f)
{
if (state == 0) // walking
{
counter--;
if (counter >= 0)
{
timer = 0.5f;
if (hp <= hpMax / 4)
creatureSpeed = GIANT_SLIME_SPEED * 1.4f;
if (hp <= hpMax / 2)
creatureSpeed = GIANT_SLIME_SPEED * 1.2f;
else
creatureSpeed = GIANT_SLIME_SPEED;
setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(), GIANT_SLIME_SPEED ));
}
else
{
int r = rand() % 3;
if (r == 0) changeToState(1);
else if (r == 1) changeToState(3);
else changeToState(5);
}
}
else if (state == 1) // waiting for jumping
{
changeToState(2);
}
else if (state == 2) // jumping
{
changeToState(8);
}
else if (state == 3)
{
changeToState(4);
}
else if (state == 4) // walking
{
counter--;
if (counter >= 0)
{
if (hp <= hpMax / 4)
timer = missileDelay * 0.6f;
if (hp <= hpMax / 2)
timer = missileDelay * 0.8f;
else
timer = missileDelay;
fire();
}
else
{
changeToState(8);
}
}
else if (state == 5)
{
changeToState(6);
}
else if (state == 6) // jump
{
changeToState(7); // fall
}
else if (state == 7) // jump
{
}
else if (state == 8) // jump
{
changeToState(0); // fall
}
}
if (state == 0) // walking
{
frame = ((int)(age * 2.0f)) % 2;
}
else if (state == 1 || state == 5) // waiting to jump
{
if (timer < 0.25f)
frame = 1;
else
frame = 0;
}
else if (state == 2) // jumping
{
hVelocity -= 700.0f * delay;
h += hVelocity * delay;
if (h <= 0.0f)
{
if (hp <= 0)
dying();
else
{
h = 0.0f;
if (isFirstJumping)
{
isFirstJumping = false;
hVelocity = 160.0f;
SoundManager::getSoundManager()->playSound(SOUND_SLIME_IMAPCT);
}
else
{
SoundManager::getSoundManager()->playSound(SOUND_SLIME_IMAPCT_WEAK);
viscosity = 0.96f;
changeToState(0);
}
}
}
if (hVelocity > 0.0f) frame = 2;
else frame = 0;
}
else if (state == 6) // ultra jump
{
if (h < 2000)
h += hVelocity * delay;
}
else if (state == 7) // ultra jump
{
if (!isFalling && timer <= 2.2f)
{
isFalling = true;
x = game().getPlayer()->getX();
y = game().getPlayer()->getY();
// to prevent collisions
float x0 = OFFSET_X + TILE_WIDTH + 1;
float xf = OFFSET_X + TILE_WIDTH * (MAP_WIDTH - 1) - 1;
float y0 = OFFSET_Y + TILE_HEIGHT + 1;
float yf = OFFSET_Y + TILE_HEIGHT * (MAP_HEIGHT - 1) - 1;
calculateBB();
if (boundingBox.left < x0) x += (x0 - boundingBox.left);
else if (boundingBox.left + boundingBox.width > xf) x -= (boundingBox.left + boundingBox.width - xf);
if (boundingBox.top < y0) y += (y0 - boundingBox.top);
else if (boundingBox.top + boundingBox.height > yf) y -= (boundingBox.top + boundingBox.height - yf);
}
if (timer < 2.3f)
{
h += hVelocity * delay;
if (h <= 0)
{
h = 0;
changeToState(8);
game().makeShake(0.8f);
SoundManager::getSoundManager()->playSound(SOUND_WALL_IMPACT);
}
}
}
if (state == 6 && timer < 0.5f)
{
int fade = timer * 512;
if (fade < 0) fade = 0;
sprite.setColor(sf::Color(255, 255, 255, fade));
}
else if (state == 7 && timer < 1.5f)
sprite.setColor(sf::Color(255, 255, 255, 255));
else if (state == 7 && timer < 2.0f)
sprite.setColor(sf::Color(255, 255, 255, (2.0f - timer) * 512));
else if (state == 7)
sprite.setColor(sf::Color(255, 255, 255, 0));
z = y + 26;
}
void GiantSlimeEntity::calculateBB()
{
boundingBox.left = (int)x - width / 2 + GIANT_SLIME_BB_LEFT;
boundingBox.width = width - GIANT_SLIME_BB_WIDTH_DIFF;
boundingBox.top = (int)y - height / 2 + 40;
boundingBox.height = height - 76;
}
void GiantSlimeEntity::collideMapRight()
{
velocity.x = -velocity.x;
}
void GiantSlimeEntity::collideMapLeft()
{
velocity.x = -velocity.x;
}
void GiantSlimeEntity::collideMapTop()
{
velocity.y = -velocity.y;
}
void GiantSlimeEntity::collideMapBottom()
{
velocity.y = -velocity.y;
}
void GiantSlimeEntity::dying()
{
isDying = true;
+ game().addKilledEnemy(enemyType);
SpriteEntity* deadRat = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_CORPSES_BIG), x, y, 128, 128);
deadRat->setZ(OFFSET_Y);
deadRat->setFrame(FRAME_CORPSE_GIANT_SLIME - FRAME_CORPSE_KING_RAT);
deadRat->setType(ENTITY_CORPSE);
float xSlime = x;
float ySlime = y;
if (x <= OFFSET_X + 1.5 * TILE_WIDTH) x = OFFSET_X + 1.5f * TILE_WIDTH + 2;
else if (x >= OFFSET_X + TILE_WIDTH * MAP_WIDTH - 1.5f * TILE_WIDTH) x = OFFSET_X + TILE_WIDTH * MAP_WIDTH - 1.5f * TILE_WIDTH -3;
if (y <= OFFSET_Y + 1.5 * TILE_HEIGHT) y = OFFSET_Y + 1.5 * TILE_HEIGHT + 2;
else if (y >= OFFSET_Y + TILE_HEIGHT * MAP_HEIGHT - 1.5f * TILE_HEIGHT) x = OFFSET_Y + TILE_HEIGHT * MAP_HEIGHT - 1.5f * TILE_HEIGHT -3;
for (int i = 0; i < 9; i++)
{
game().generateBlood(xSlime, ySlime, bloodColor);
new SlimeEntity(x, y, SlimeTypeStandard, true);
}
game().makeShake(1.0f);
SoundManager::getSoundManager()->playSound(SOUND_SLIME_SMASH);
ItemEntity* newItem = new ItemEntity(itemBossHeart, x, y);
newItem->setVelocity(Vector2D(100.0f + rand()% 250));
newItem->setViscosity(0.96f);
SpriteEntity* star = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_GIANT_SLIME), x, y, 128, 128, 8);
star->setFrame(4);
star->setFading(true);
star->setZ(y+ 100);
star->setAge(-0.4f);
star->setLifetime(0.3f);
star->setType(16);
star->setSpin(400.0f);
}
void GiantSlimeEntity::render(sf::RenderTarget* app)
{
if (!isDying)
{
// shadow
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(shadowFrame * width, 0, width, height));
app->draw(sprite);
}
sprite.setPosition(x, y - h);
sprite.setTextureRect(sf::IntRect(frame * width, 0, width, height));
app->draw(sprite);
float l = hpDisplay * ((MAP_WIDTH - 1) * TILE_WIDTH) / GIANT_SLIME_HP;
sf::RectangleShape rectangle(sf::Vector2f((MAP_WIDTH - 1) * TILE_WIDTH, 25));
rectangle.setFillColor(sf::Color(0, 0, 0,128));
rectangle.setPosition(sf::Vector2f(OFFSET_X + TILE_WIDTH / 2, OFFSET_Y + 25 + (MAP_HEIGHT - 1) * TILE_HEIGHT));
app->draw(rectangle);
rectangle.setSize(sf::Vector2f(l, 25));
rectangle.setFillColor(sf::Color(190, 20, 20));
rectangle.setPosition(sf::Vector2f(OFFSET_X + TILE_WIDTH / 2, OFFSET_Y + 25 + (MAP_HEIGHT - 1) * TILE_HEIGHT));
app->draw(rectangle);
game().write( "Giant Slime",
18,
OFFSET_X + TILE_WIDTH / 2 + 10.0f,
OFFSET_Y + 25 + (MAP_HEIGHT - 1) * TILE_HEIGHT + 1.0f,
ALIGN_LEFT,
sf::Color(255, 255, 255),
app, 0, 0);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void GiantSlimeEntity::collideWithEnnemy(GameEntity* collidingEntity)
{
EnnemyEntity* entity = static_cast<EnnemyEntity*>(collidingEntity);
if (entity->getMovingStyle() == movWalking)
{
inflictsRecoilTo(entity);
}
}
void GiantSlimeEntity::inflictsRecoilTo(BaseCreatureEntity* targetEntity)
{
if (state == 7)
{
Vector2D recoilVector = Vector2D(x, y).vectorTo(Vector2D(targetEntity->getX(), targetEntity->getY()), KING_RAT_RUNNING_RECOIL );
targetEntity->giveRecoil(true, recoilVector, 1.0f);
}
}
bool GiantSlimeEntity::canCollide()
{
return h <= 70.0f;
}
BaseCreatureEntity::enumMovingStyle GiantSlimeEntity::getMovingStyle()
{
if (h <= 70.0f)
return movWalking;
else
return movFlying;
}
void GiantSlimeEntity::fire()
{
SoundManager::getSoundManager()->playSound(SOUND_BLAST_FLOWER);
EnnemyBoltEntity* bolt = new EnnemyBoltEntity
(x, y + 10, ShotTypeStandard, 0);
bolt->setFrame(1);
bolt->setMap(map, TILE_WIDTH, TILE_HEIGHT, OFFSET_X, OFFSET_Y);
bolt->setVelocity(Vector2D(x, y).vectorTo(game().getPlayerPosition(),GIANT_SLIME_FIRE_VELOCITY ));
}
diff --git a/src/RockMissileEntity.cpp b/src/RockMissileEntity.cpp
index 2e01827..97d1897 100644
--- a/src/RockMissileEntity.cpp
+++ b/src/RockMissileEntity.cpp
@@ -1,132 +1,133 @@
#include "RockMissileEntity.h"
#include "PlayerEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
RockMissileEntity::RockMissileEntity(float x, float y, int rockType)
: EnnemyEntity (ImageManager::getImageManager()->getImage(IMAGE_CYCLOP), x, y)
{
Vector2D targetPos = game().getPlayerPosition();
imagesProLine = 20;
collisionDirection = -1;
type = ENTITY_ENNEMY;
enemyType = EnemyTypeRockMissile;
movingStyle = movFlying;
bloodColor = bloodNone; // stones don't bleed
hasCollided = false;
age = 0.0f;
this->rockType = rockType;
if (rockType == 0)
{
creatureSpeed = 500.0f;
hp = 12;
meleeDamages = 8;
frame = 18;
}
else
{
creatureSpeed = 450.0f;
hp = 24;
meleeDamages = 10;
frame = 38;
}
setVelocity(Vector2D(x, y).vectorNearlyTo(targetPos, creatureSpeed, 0.4f));
}
void RockMissileEntity::animate(float delay)
{
EnnemyEntity::animate(delay);
if (x < -60 || x > 1050 || y < - 50 || y > 800) isDying = true;
}
void RockMissileEntity::calculateBB()
{
int w;
if (rockType == 0) w = 20;
else w = 24;
boundingBox.left = (int)x - w / 2;
boundingBox.width = w;
boundingBox.top = (int)y - w / 2;
boundingBox.height = w;
}
void RockMissileEntity::collideWall()
{
if (rockType == 1 && !hasCollided)
{
hasCollided = true;
if (collisionDirection == DIRECTION_RIGHT || collisionDirection == DIRECTION_LEFT) velocity.x = -velocity.x;
else velocity.y = -velocity.y;
}
else
{
dying();
}
}
void RockMissileEntity::collideMapRight()
{
collisionDirection = DIRECTION_RIGHT;
collideWall();
}
void RockMissileEntity::collideMapLeft()
{
collisionDirection = DIRECTION_LEFT;
collideWall();
}
void RockMissileEntity::collideMapTop()
{
collisionDirection = DIRECTION_TOP;
collideWall();
}
void RockMissileEntity::collideMapBottom()
{
collisionDirection = DIRECTION_BOTTOM;
collideWall();
}
void RockMissileEntity::collideWithEnnemy(GameEntity* collidingEntity)
{
}
void RockMissileEntity::dying()
{
isDying = true;
+ game().addKilledEnemy(enemyType);
SoundManager::getSoundManager()->playSound(SOUND_ROCK_IMPACT);
game().makeShake(0.1f);
for (int i = 0; i < 4; i++)
{
SpriteEntity* blood = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_BLOOD), x, y, 16, 16, 6);
blood->setZ(OFFSET_Y - 1);
blood->setFrame(12 + rand()%6);
blood->setType(ENTITY_BLOOD);
blood->setVelocity(Vector2D(rand()%150));
blood->setViscosity(0.95f);
float bloodScale = 1.0f + (rand() % 10) * 0.1f;
blood->setScale(bloodScale, bloodScale);
if ((collisionDirection == DIRECTION_LEFT) && (blood->getVelocity().x < 0.0f))
blood->setVelocity(Vector2D(-blood->getVelocity().x * 0.25f, blood->getVelocity().y));
else if ((collisionDirection == DIRECTION_RIGHT) && (blood->getVelocity().x > 0.0f))
blood->setVelocity(Vector2D(-blood->getVelocity().x * 0.25f, blood->getVelocity().y));
else if ((collisionDirection == DIRECTION_TOP) && (blood->getVelocity().y < 0.0f))
blood->setVelocity(Vector2D(blood->getVelocity().x, -blood->getVelocity().y * 0.25f));
else if ((collisionDirection == DIRECTION_BOTTOM) && (blood->getVelocity().y > 0.0f))
blood->setVelocity(Vector2D(blood->getVelocity().x, -blood->getVelocity().y * 0.25f));
}
}
diff --git a/src/SlimeEntity.cpp b/src/SlimeEntity.cpp
index 8cb99db..eaeffe0 100644
--- a/src/SlimeEntity.cpp
+++ b/src/SlimeEntity.cpp
@@ -1,308 +1,309 @@
#include "SlimeEntity.h"
#include "PlayerEntity.h"
#include "EnnemyBoltEntity.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
SlimeEntity::SlimeEntity(float x, float y, slimeTypeEnum slimeType, bool invocated)
: EnnemyEntity (ImageManager::getImageManager()->getImage(IMAGE_SLIME), x, y)
{
creatureSpeed = 0.0f;
velocity = Vector2D(0.0f, 0.0f);
hp = SLIME_HP;
meleeDamages = SLIME_DAMAGES;
this->slimeType = slimeType;
this->invocated = invocated;
if (invocated)
{
type = ENTITY_ENNEMY_INVOCATED;
jumpingDelay = 0.1f;
age = 0.0f;
}
else
{
type = ENTITY_ENNEMY;
jumpingDelay = 0.6f + 0.1f * (rand() % 20);
}
if (slimeType == SlimeTypeBlue)
{
resistance[ResistanceFrozen] = ResistanceImmune;
resistance[ResistanceIce] = ResistanceHigh;
resistance[ResistanceFire] = ResistanceLow;
enemyType = invocated ? EnemyTypeSlimeBlue_invocated : EnemyTypeSlimeBlue;
}
else if (slimeType == SlimeTypeRed)
{
resistance[ResistanceIce] = ResistanceLow;
resistance[ResistanceFire] = ResistanceHigh;
enemyType = invocated ? EnemyTypeSlimeRed_invocated : EnemyTypeSlimeRed;
}
else
{
enemyType = invocated ? EnemyTypeSlime_invocated : EnemyTypeSlime;
}
bloodColor = bloodGreen;
frame = 0;
shadowFrame = 3;
imagesProLine = 4;
isJumping = false;
h = 0.0f;
viscosity = 0.98f;
sprite.setOrigin(32, 44);
}
void SlimeEntity::animate(float delay)
{
float slimeDelay = delay;
if (specialState[SpecialStateIce].active) slimeDelay = delay * specialState[SpecialStateIce].parameter;
if (isJumping)
{
hVelocity -= 700.0f * slimeDelay;
h += hVelocity * slimeDelay;
if (h <= 0.0f)
{
if (hp <= 0)
dying();
else
{
h = 0.0f;
if (isFirstJumping)
{
isFirstJumping = false;
hVelocity = 160.0f;
SoundManager::getSoundManager()->playSound(SOUND_SLIME_IMAPCT);
if (slimeType == SlimeTypeBlue || slimeType == SlimeTypeRed)
fire();
}
else
{
jumpingDelay = 0.4f + 0.1f * (rand() % 20);
isJumping = false;
SoundManager::getSoundManager()->playSound(SOUND_SLIME_IMAPCT_WEAK);
}
}
}
if (hVelocity > 0.0f) frame = 2;
else frame = 0;
}
else
{
jumpingDelay -= slimeDelay;
if (jumpingDelay < 0.0f)
{
SoundManager::getSoundManager()->playSound(SOUND_SLIME_JUMP);
hVelocity = 350.0f + rand() % 300;
isJumping = true;
isFirstJumping = true;
float randVel = 250.0f + rand() % 250;
if (rand() % 2 == 0)
{
float tan = (game().getPlayer()->getX() - x) / (game().getPlayer()->getY() - y);
float angle = atan(tan);
if (game().getPlayer()->getY() > y)
setVelocity(Vector2D(sin(angle) * randVel,
cos(angle) * randVel));
else
setVelocity(Vector2D(-sin(angle) * randVel,
-cos(angle) * randVel));
}
else
velocity = Vector2D(randVel);
}
else if (jumpingDelay < 0.25f)
frame = 1;
else frame = 0;
}
EnnemyEntity::animate(delay);
z = y + 14;
}
void SlimeEntity::render(sf::RenderTarget* app)
{
if (!isDying && shadowFrame > -1)
{
// shadow
sprite.setPosition(x, y);
sprite.setTextureRect(sf::IntRect(shadowFrame * width, 0, width, height));
app->draw(sprite);
}
sprite.setPosition(x, y - h);
sprite.setTextureRect(sf::IntRect(frame * width, slimeType * height, width, height));
app->draw(sprite);
if (game().getShowLogical())
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void SlimeEntity::calculateBB()
{
boundingBox.left = (int)x - width / 2 + SLIME_BB_LEFT;
boundingBox.width = width - SLIME_BB_WIDTH_DIFF;
boundingBox.top = (int)y - height / 2 + SLIME_BB_TOP - 15;
boundingBox.height = height - SLIME_BB_HEIGHT_DIFF;
}
void SlimeEntity::collideMapRight()
{
// if (x > OFFSET_X + MAP_WIDTH * TILE_WIDTH)
velocity.x = -velocity.x * 0.8f;
}
void SlimeEntity::collideMapLeft()
{
// if (x < OFFSET_X + MAP_WIDTH )
velocity.x = -velocity.x * 0.8f;
}
void SlimeEntity::collideMapTop()
{
// if (y > OFFSET_Y + MAP_HEIGHT * TILE_HEIGHT)
velocity.y = -velocity.y * 0.8f;
}
void SlimeEntity::collideMapBottom()
{
// if (y < OFFSET_Y + MAP_HEIGHT )
velocity.y = -velocity.y * 0.8f;
}
void SlimeEntity::collideWithEnnemy(GameEntity* collidingEntity)
{
if (recoil.active && recoil.stun) return;
EnnemyEntity* entity = static_cast<EnnemyEntity*>(collidingEntity);
if (entity->getMovingStyle() == movWalking)
{
Vector2D vel = Vector2D(entity->getX(), entity->getY()).vectorTo(Vector2D(x, y), 100.0f );
giveRecoil(false, vel, 0.3f);
computeFacingDirection();
}
}
bool SlimeEntity::collideWithMap(int direction)
{
calculateBB();
int xTile0 = (boundingBox.left - offsetX) / tileWidth;
int xTilef = (boundingBox.left + boundingBox.width - offsetX) / tileWidth;
int yTile0 = (boundingBox.top - offsetY) / tileHeight;
int yTilef = (boundingBox.top + boundingBox.height - offsetY) / tileHeight;
if (boundingBox.top < 0) yTile0 = -1;
for (int xTile = xTile0; xTile <= xTilef; xTile++)
for (int yTile = yTile0; yTile <= yTilef; yTile++)
{
if (xTile == 0 || xTile == MAP_WIDTH - 1 || yTile == 0 || yTile == MAP_HEIGHT - 1)
{
switch (direction)
{
case DIRECTION_LEFT:
if (map->isLeftBlocking(xTile, yTile)) return true;
break;
case DIRECTION_RIGHT:
if (map->isRightBlocking(xTile, yTile)) return true;
break;
case DIRECTION_TOP:
if (map->isUpBlocking(xTile, yTile)) return true;
break;
case DIRECTION_BOTTOM:
if (map->isDownBlocking(xTile, yTile)) return true;
break;
}
}
}
return false;
}
void SlimeEntity::dying()
{
isDying = true;
+ game().addKilledEnemy(enemyType);
SpriteEntity* deadSlime = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_CORPSES), x, y, 64, 64);
deadSlime->setZ(OFFSET_Y);
switch (slimeType)
{
case SlimeTypeStandard: deadSlime->setFrame(FRAME_CORPSE_SLIME); break;
case SlimeTypeRed: deadSlime->setFrame(FRAME_CORPSE_SLIME_RED); break;
case SlimeTypeBlue: deadSlime->setFrame(FRAME_CORPSE_SLIME_BLUE); break;
}
deadSlime->setType(ENTITY_CORPSE);
for (int i = 0; i < 4; i++) game().generateBlood(x, y, bloodColor);
if (!invocated) drop();
SoundManager::getSoundManager()->playSound(SOUND_ENNEMY_DYING);
}
void SlimeEntity::prepareDying()
{
if (!isJumping)
dying();
}
bool SlimeEntity::canCollide()
{
return h <= 70.0f;
}
BaseCreatureEntity::enumMovingStyle SlimeEntity::getMovingStyle()
{
if (h <= 70.0f)
return movWalking;
else
return movFlying;
}
void SlimeEntity::fire()
{
for (int i = 0; i < 4; i++)
{
EnnemyBoltEntity* bolt;
if (slimeType == SlimeTypeBlue)
{
bolt = new EnnemyBoltEntity(x, y, ShotTypeIce, 0);
bolt->setDamages(5);
}
else if (slimeType == SlimeTypeRed)
{
bolt = new EnnemyBoltEntity(x, y, ShotTypeFire, 0);
bolt->setDamages(8);
}
else
return;
switch (i)
{
case 0: bolt->setVelocity(Vector2D(SLIME_FIRE_VELOCITY, 0)); break;
case 1: bolt->setVelocity(Vector2D(-SLIME_FIRE_VELOCITY, 0)); break;
case 2: bolt->setVelocity(Vector2D(0, SLIME_FIRE_VELOCITY)); break;
case 3: bolt->setVelocity(Vector2D(0, -SLIME_FIRE_VELOCITY)); break;
}
}
SoundManager::getSoundManager()->playSound(SOUND_BLAST_FLOWER);
}
diff --git a/src/WitchBlastGame.cpp b/src/WitchBlastGame.cpp
index 475c318..6ae5c7e 100644
--- a/src/WitchBlastGame.cpp
+++ b/src/WitchBlastGame.cpp
@@ -1,2015 +1,2037 @@
/** This file is part of Witch Blast.
*
* Witch Blast 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 3 of the License, or
* (at your option) any later version.
*
* Witch Blast 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 Witch Blast. If not, see <http://www.gnu.org/licenses/>.
*/
#include "WitchBlastGame.h"
#include "sfml_game/SpriteEntity.h"
#include "sfml_game/TileMapEntity.h"
#include "DungeonMap.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "sfml_game/EntityManager.h"
#include "Constants.h"
#include "RatEntity.h"
#include "BlackRatEntity.h"
#include "GreenRatEntity.h"
#include "KingRatEntity.h"
#include "CyclopEntity.h"
#include "GiantSpiderEntity.h"
#include "GiantSlimeEntity.h"
#include "ButcherEntity.h"
#include "BatEntity.h"
#include "ImpEntity.h"
#include "SlimeEntity.h"
#include "ChestEntity.h"
#include "EvilFlowerEntity.h"
#include "ItemEntity.h"
#include "ArtefactDescriptionEntity.h"
#include "PnjEntity.h"
#include "TextEntity.h"
#include "StandardRoomGenerator.h"
#include <iostream>
#include <sstream>
#include <fstream>
#include <ctime>
static std::string intToString(int n)
{
std::ostringstream oss;
oss << n;
return oss.str();
}
namespace {
WitchBlastGame* gameptr;
}
WitchBlastGame::WitchBlastGame(): Game(SCREEN_WIDTH, SCREEN_HEIGHT)
{
gameptr = this;
app->setTitle(APP_NAME + " V" + APP_VERSION);
// loading resources
ImageManager::getImageManager()->addImage("media/player_base.png");
ImageManager::getImageManager()->addImage("media/player_equip.png");
ImageManager::getImageManager()->addImage("media/player_collar.png");
ImageManager::getImageManager()->addImage("media/bolt.png");
ImageManager::getImageManager()->addImage("media/tiles.png");
ImageManager::getImageManager()->addImage("media/rat.png");
ImageManager::getImageManager()->addImage("media/minimap.png");
ImageManager::getImageManager()->addImage("media/doors.png");
ImageManager::getImageManager()->addImage("media/items.png");
ImageManager::getImageManager()->addImage("media/items_equip.png");
ImageManager::getImageManager()->addImage("media/chest.png");
ImageManager::getImageManager()->addImage("media/bat.png");
ImageManager::getImageManager()->addImage("media/evil_flower.png");
ImageManager::getImageManager()->addImage("media/slime.png");
ImageManager::getImageManager()->addImage("media/imp.png");
ImageManager::getImageManager()->addImage("media/spider_egg.png");
ImageManager::getImageManager()->addImage("media/spider_web.png");
ImageManager::getImageManager()->addImage("media/little_spider.png");
ImageManager::getImageManager()->addImage("media/butcher.png");
ImageManager::getImageManager()->addImage("media/giant_slime.png");
ImageManager::getImageManager()->addImage("media/king_rat.png");
ImageManager::getImageManager()->addImage("media/cyclop.png");
ImageManager::getImageManager()->addImage("media/giant_spider.png");
ImageManager::getImageManager()->addImage("media/blood.png");
ImageManager::getImageManager()->addImage("media/corpses.png");
ImageManager::getImageManager()->addImage("media/corpses_big.png");
ImageManager::getImageManager()->addImage("media/star.png");
ImageManager::getImageManager()->addImage("media/star2.png");
ImageManager::getImageManager()->addImage("media/interface.png");
ImageManager::getImageManager()->addImage("media/hud_shots.png");
ImageManager::getImageManager()->addImage("media/pnj.png");
ImageManager::getImageManager()->addImage("media/fairy.png");
SoundManager::getSoundManager()->addSound("media/sound/blast00.ogg");
SoundManager::getSoundManager()->addSound("media/sound/blast01.ogg");
SoundManager::getSoundManager()->addSound("media/sound/door_closing.ogg");
SoundManager::getSoundManager()->addSound("media/sound/door_opening.ogg");
SoundManager::getSoundManager()->addSound("media/sound/chest_opening.ogg");
SoundManager::getSoundManager()->addSound("media/sound/impact.ogg");
SoundManager::getSoundManager()->addSound("media/sound/bonus.ogg");
SoundManager::getSoundManager()->addSound("media/sound/drink.ogg");
SoundManager::getSoundManager()->addSound("media/sound/eat.ogg");
SoundManager::getSoundManager()->addSound("media/sound/player_hit.ogg");
SoundManager::getSoundManager()->addSound("media/sound/player_die.ogg");
SoundManager::getSoundManager()->addSound("media/sound/ennemy_dying.ogg");
SoundManager::getSoundManager()->addSound("media/sound/coin.ogg");
SoundManager::getSoundManager()->addSound("media/sound/pay.ogg");
SoundManager::getSoundManager()->addSound("media/sound/wall_impact.ogg");
SoundManager::getSoundManager()->addSound("media/sound/big_wall_impact.ogg");
SoundManager::getSoundManager()->addSound("media/sound/king_rat_cry_1.ogg");
SoundManager::getSoundManager()->addSound("media/sound/king_rat_cry_2.ogg");
SoundManager::getSoundManager()->addSound("media/sound/king_rat_die.ogg");
SoundManager::getSoundManager()->addSound("media/sound/slime_jump.ogg");
SoundManager::getSoundManager()->addSound("media/sound/slime_impact.ogg");
SoundManager::getSoundManager()->addSound("media/sound/slime_impact_weak.ogg");
SoundManager::getSoundManager()->addSound("media/sound/slime_smash.ogg");
SoundManager::getSoundManager()->addSound("media/sound/ice_charge.ogg");
SoundManager::getSoundManager()->addSound("media/sound/electric.ogg");
SoundManager::getSoundManager()->addSound("media/sound/select.ogg");
SoundManager::getSoundManager()->addSound("media/sound/heart.ogg");
SoundManager::getSoundManager()->addSound("media/sound/rat_die.ogg");
SoundManager::getSoundManager()->addSound("media/sound/bat_die.ogg");
SoundManager::getSoundManager()->addSound("media/sound/imp_hurt.ogg");
SoundManager::getSoundManager()->addSound("media/sound/imp_die.ogg");
SoundManager::getSoundManager()->addSound("media/sound/rock_impact.ogg");
SoundManager::getSoundManager()->addSound("media/sound/throw.ogg");
SoundManager::getSoundManager()->addSound("media/sound/cyclop00.ogg");
SoundManager::getSoundManager()->addSound("media/sound/cyclop_die.ogg");
SoundManager::getSoundManager()->addSound("media/sound/butcher_00.ogg");
SoundManager::getSoundManager()->addSound("media/sound/butcher_01.ogg");
SoundManager::getSoundManager()->addSound("media/sound/butcher_hurt.ogg");
SoundManager::getSoundManager()->addSound("media/sound/butcher_die.ogg");
SoundManager::getSoundManager()->addSound("media/sound/vib.ogg");
if (font.loadFromFile("media/DejaVuSans-Bold.ttf"))
{
myText.setFont(font);
}
miniMap = NULL;
menuMap = NULL;
currentMap = NULL;
currentFloor = NULL;
xGameState = xGameStateNone;
isPausing = false;
showLogical = false;
shotsSprite.setTexture(*ImageManager::getImageManager()->getImage(IMAGE_HUD_SHOTS));
configureFromFile();
srand(time(NULL));
}
WitchBlastGame::~WitchBlastGame()
{
// cleaning all entities
EntityManager::getEntityManager()->clean();
// cleaning data
if (miniMap != NULL) delete (miniMap);
if (menuMap != NULL) delete (menuMap);
if (currentFloor != NULL) delete (currentFloor);
}
DungeonMap* WitchBlastGame::getCurrentMap()
{
return currentMap;
}
PlayerEntity* WitchBlastGame::getPlayer()
{
return player;
}
Vector2D WitchBlastGame::getPlayerPosition()
{
return Vector2D(player->getX(), player->getY());
}
int WitchBlastGame::getLevel()
{
return level;
}
bool WitchBlastGame::getShowLogical()
{
return showLogical;
}
void WitchBlastGame::onUpdate()
{
if (!isPausing)
{
checkFallingEntities();
EntityManager::getEntityManager()->animate(deltaTime);
if (sf::Keyboard::isKeyPressed(input[KeyTimeControl]))
{
EntityManager::getEntityManager()->animate(deltaTime);
SoundManager::getSoundManager()->playSound(SOUND_VIB);
}
else
SoundManager::getSoundManager()->stopSound(SOUND_VIB);
if (xGameState != xGameStateNone)
{
xGameTimer -= deltaTime;
if (xGameTimer <= 0.0f)
{
if (xGameState == xGameStateFadeOut)
{
if (player->getPlayerStatus() == PlayerEntity::playerStatusGoingUp)
{
level++;
startNewLevel();
}
else
startNewGame(false);
}
else
xGameState = xGameStateNone;
}
}
if (isPlayerAlive)
{
if (player->getHp() <= 0)
{
isPlayerAlive = false;
playMusic(MusicEnding);
}
}
}
}
void WitchBlastGame::startNewGame(bool fromSaveFile)
{
gameState = gameStateInit;
level = 1;
// cleaning all entities
EntityManager::getEntityManager()->clean();
// cleaning data
if (miniMap != NULL) delete (miniMap);
if (currentFloor != NULL) delete (currentFloor);
miniMap = NULL;
currentFloor = NULL;
// current map (tiles)
currentTileMap = new TileMapEntity(ImageManager::getImageManager()->getImage(IMAGE_TILES), currentMap, 64, 64, 10);
currentTileMap->setX(OFFSET_X);
currentTileMap->setY(OFFSET_Y);
// the interface
SpriteEntity* interface = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_INTERFACE));
interface->setZ(10000.0f);
interface->removeCenter();
interface->setType(0);
// key symbol on the interface
keySprite.setTexture(*ImageManager::getImageManager()->getImage(IMAGE_ITEMS_EQUIP));
keySprite.setTextureRect(sf::IntRect(ITEM_WIDTH * EQUIP_BOSS_KEY, 0, ITEM_WIDTH, ITEM_HEIGHT));
keySprite.setPosition(326, 616);
miniMap = new GameMap(FLOOR_WIDTH, FLOOR_HEIGHT);
// minimap on the interface
TileMapEntity* miniMapEntity = new TileMapEntity(ImageManager::getImageManager()->getImage(IMAGE_MINIMAP), miniMap, 15, 11, 10);
miniMapEntity->setTileBox(16, 12);
miniMapEntity->setX(407);
miniMapEntity->setY(614);
miniMapEntity->setZ(10001.0f);
// doors
doorEntity[0] = new DoorEntity(8);
doorEntity[1] = new DoorEntity(4);
doorEntity[2] = new DoorEntity(2);
doorEntity[3] = new DoorEntity(6);
if (fromSaveFile)
{
if (!loadGame())
fromSaveFile = false;
else
playLevel();
}
if (!fromSaveFile)
{
// the player
player = new PlayerEntity(OFFSET_X + (TILE_WIDTH * MAP_WIDTH * 0.5f),
OFFSET_Y + (TILE_HEIGHT * MAP_HEIGHT * 0.5f));
-
+ resetKilledEnemies();
startNewLevel();
}
}
void WitchBlastGame::startNewLevel()
{
// create the new level
if (currentFloor != NULL) delete currentFloor;
currentFloor = new GameFloor(level);
currentFloor->createFloor();
// center it
floorX = FLOOR_WIDTH / 2;
floorY = FLOOR_HEIGHT / 2;
// move the player
if (level == 1)
player->moveTo(OFFSET_X + (TILE_WIDTH * MAP_WIDTH * 0.5f),
OFFSET_Y + (TILE_HEIGHT * MAP_HEIGHT * 0.5f));
else
player->moveTo(OFFSET_X + (TILE_WIDTH * MAP_WIDTH * 0.5f),
OFFSET_Y + (TILE_HEIGHT * MAP_HEIGHT - 3 * TILE_HEIGHT));
// the boss room is closed
bossRoomOpened = false;
+ // to test
+ displayKilledEnemies();
playLevel();
}
void WitchBlastGame::playLevel()
{
isPlayerAlive = true;
player->setVelocity(Vector2D(0.0f, 0.0f));
player->setPlayerStatus(PlayerEntity::playerStatusPlaying);
// generate the map
refreshMap();
// items from save
currentMap->restoreMapObjects();
// first map is open
roomClosed = false;
// game time counter an state
lastTime = getAbsolutTime();
gameState = gameStatePlaying;
playMusic(MusicDungeon);
// fade in
xGameState = xGameStateFadeIn;
xGameTimer = FADE_IN_DELAY;
float x0 = OFFSET_X + MAP_WIDTH * 0.5f * TILE_WIDTH;
float y0 = OFFSET_Y + MAP_HEIGHT * 0.5f * TILE_HEIGHT + 40.0f;
std::ostringstream oss;
oss << "Level " << level;
TextEntity* text = new TextEntity(oss.str(), 30, x0, y0);
text->setAlignment(ALIGN_CENTER);
text->setLifetime(2.5f);
text->setWeight(-36.0f);
text->setZ(1000);
text->setColor(TextEntity::COLOR_FADING_WHITE);
}
void WitchBlastGame::updateRunningGame()
{
bool backToMenu = false;
// Process events
sf::Event event;
while (app->pollEvent(event))
{
// Close window : exit
if (event.type == sf::Event::Closed)
{
if (gameState == gameStatePlaying && !player->isDead() && currentMap->isCleared()) saveGame();
app->close();
}
if (event.type == sf::Event::MouseWheelMoved)
{
if (gameState == gameStatePlaying && !isPausing) player->selectNextShotType();
}
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Escape)
{
if (player->isDead()) backToMenu = true;
else if (gameState == gameStatePlaying && !isPausing) isPausing = true;
else if (gameState == gameStatePlaying && isPausing) isPausing = false;
}
if (event.key.code == input[KeyFireSelect])
{
if (gameState == gameStatePlaying && !isPausing) player->selectNextShotType();
}
if (event.key.code == input[KeyFire])
{
if (gameState == gameStatePlaying && !isPausing) firingDirection = player->getFacingDirection();
}
if (event.key.code == sf::Keyboard::X)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift)) startNewGame(false);
}
if (event.key.code == sf::Keyboard::F2)
{
showLogical = !showLogical;
}
}
if (event.type == sf::Event::LostFocus && !player->isDead())
isPausing = true;
}
if (gameState == gameStatePlaying && !isPausing)
{
if (player->canMove()) player->setVelocity(Vector2D(0.0f, 0.0f));
if (sf::Keyboard::isKeyPressed(input[KeyLeft]))
{
if (sf::Keyboard::isKeyPressed(input[KeyUp]))
player->move(7);
else if (sf::Keyboard::isKeyPressed(input[KeyDown]))
player->move(1);
else
player->move(4);
}
else if (sf::Keyboard::isKeyPressed(input[KeyRight]))
{
if (sf::Keyboard::isKeyPressed(input[KeyUp]))
player->move(9);
else if (sf::Keyboard::isKeyPressed(input[KeyDown]))
player->move(3);
else
player->move(6);
}
else if (sf::Keyboard::isKeyPressed(input[KeyUp]))
{
player->move(8);
}
else if (sf::Keyboard::isKeyPressed(input[KeyDown]))
{
player->move(2);
}
player->resestFireDirection();
if (sf::Keyboard::isKeyPressed(input[KeyFireLeft]))
player->fire(4);
else if (sf::Keyboard::isKeyPressed(input[KeyFireRight]))
player->fire(6);
else if (sf::Keyboard::isKeyPressed(input[KeyFireUp]))
player->fire(8);
else if (sf::Keyboard::isKeyPressed(input[KeyFireDown]))
player->fire(2);
// alternative "one button" gameplay
else if (sf::Keyboard::isKeyPressed(input[KeyFire]))
{
player->fire(firingDirection);
}
// alternative "firing with the mouse" gameplay
else if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
sf::Vector2i mousePosition = sf::Mouse::getPosition(*app);
int xm = mousePosition.x - player->getX();
int ym = mousePosition.y - player->getY();
if (abs(xm) >= abs(ym))
{
if (xm > 0) player->fire(6);
else player->fire(4);
}
else
{
if (ym > 0) player->fire(2);
else player->fire(8);
}
}
if (player->isDead() && xGameState == xGameStateNone && sf::Keyboard::isKeyPressed(sf::Keyboard::Return))
{
xGameState = xGameStateFadeOut;
xGameTimer = FADE_OUT_DELAY;
}
}
onUpdate();
verifyDoorUnlocking();
if (roomClosed)
{
if (getEnnemyCount() == 0)
{
currentMap->setCleared(true);
openDoors();
if (currentMap->getRoomType() == roomTypeBoss)
playMusic(MusicDungeon);
}
}
if (backToMenu) switchToMenu();
}
void WitchBlastGame::renderRunningGame()
{
EntityManager::getEntityManager()->sortByZ();
if (xGameState == xGameStateShake)
{
sf::View view = app->getDefaultView();
sf::View viewSave = app->getDefaultView();
view.move(-4 + rand() % 9, -4 + rand() % 9);
app->setView(view);
EntityManager::getEntityManager()->renderUnder(app, 5000);
app->setView(viewSave);
EntityManager::getEntityManager()->renderAfter(app, 5000);
}
else
{
// render the game objects
EntityManager::getEntityManager()->render(app);
}
myText.setColor(sf::Color(255, 255, 255, 255));
myText.setCharacterSize(18);
std::ostringstream oss;
oss << player->getGold();
myText.setString(oss.str());
myText.setPosition(690, 612);
app->draw(myText);
myText.setColor(sf::Color(0, 0, 0, 255));
myText.setCharacterSize(16);
oss.str("");
oss << "Level " << level;
myText.setString(oss.str());
myText.setPosition(410, 692);
app->draw(myText);
sf::RectangleShape rectangle(sf::Vector2f(200, 25));
if (gameState == gameStatePlaying)
{
// life
rectangle.setFillColor(sf::Color(190, 20, 20));
rectangle.setPosition(sf::Vector2f(90, 622));
rectangle.setSize(sf::Vector2f(200.0f * (float)(player->getHpDisplay()) / (float)(player->getHpMax()) , 25));
app->draw(rectangle);
rectangle.setFillColor(sf::Color(255, 190, 190));
rectangle.setPosition(sf::Vector2f(90, 625));
rectangle.setSize(sf::Vector2f(200.0f * (float)(player->getHpDisplay()) / (float)(player->getHpMax()) , 2));
app->draw(rectangle);
oss.str("");
oss << player->getHp() << "/" << player->getHpMax();
myText.setString(oss.str());
myText.setPosition(95, 624);
myText.setColor(sf::Color(255, 255,255, 255));
app->draw(myText);
// mana
rectangle.setFillColor(sf::Color(20, 20, 190));
rectangle.setPosition(sf::Vector2f(90, 658));
rectangle.setSize(sf::Vector2f(200.0f * player->getPercentFireDelay() , 25));
app->draw(rectangle);
rectangle.setFillColor(sf::Color(190, 190, 255));
rectangle.setPosition(sf::Vector2f(90, 661));
rectangle.setSize(sf::Vector2f(200.0f * player->getPercentFireDelay() , 2));
app->draw(rectangle);
// drawing the key on the interface
if (player->isEquiped(EQUIP_BOSS_KEY)) app->draw(keySprite);
// render the shots
renderHudShots(app);
if (isPausing)
{
rectangle.setFillColor(sf::Color(0, 0, 0, 160));
rectangle.setPosition(sf::Vector2f(OFFSET_X, OFFSET_Y));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH, MAP_HEIGHT * TILE_HEIGHT));
app->draw(rectangle);
float x0 = OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2;
int fade = 50 + 205 * (1.0f + cos(3.0f * getAbsolutTime())) * 0.5f;
myText.setColor(sf::Color(255, 255, 255, fade));
myText.setCharacterSize(40);
myText.setString("PAUSE");
myText.setPosition(x0 - myText.getLocalBounds().width / 2, 300);
app->draw(myText);
}
if (player->isDead())
{
float x0 = OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2;
int fade = 255 * (1.0f + cos(2.0f * getAbsolutTime())) * 0.5f;
myText.setColor(sf::Color(255, 255, 255, 255));
myText.setCharacterSize(25);
myText.setString("GAME OVER");
myText.setPosition(x0 - myText.getLocalBounds().width / 2, 400);
app->draw(myText);
myText.setColor(sf::Color(255, 255, 255, fade));
myText.setCharacterSize(20);
myText.setString("Press [ENTER] to play again / [ESC] to go back to the menu");
myText.setPosition(x0 - myText.getLocalBounds().width / 2, 440);
app->draw(myText);
}
else if (currentMap->getRoomType() == roomTypeExit && level > 3)
{
float x0 = OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2;
myText.setColor(sf::Color(255, 255, 255, 255));
myText.setCharacterSize(25);
myText.setString("CONGRATULATIONS !\nYou've challenged this demo and\nmanaged to kill the bosses !\nSee you soon for new adventures !");
myText.setPosition(x0 - myText.getLocalBounds().width / 2, 220);
app->draw(myText);
}
if (sf::Keyboard::isKeyPressed(input[KeyTimeControl]))
{
// effect
//int effectFade = 10 + rand() % 20;
int effectFade = 10 + 20 * (1.0f + cos(12.0f * getAbsolutTime())) * 0.5f;
rectangle.setFillColor(sf::Color(0, 255, 255, effectFade));
rectangle.setPosition(sf::Vector2f(OFFSET_X, OFFSET_Y));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH , MAP_HEIGHT * TILE_HEIGHT));
sf::RenderStates r;
r.blendMode = sf::BlendAlpha ;
app->draw(rectangle, r);
}
if (xGameState == xGameStateFadeIn)
{
// fade in
rectangle.setFillColor(sf::Color(0, 0, 0, 255 - ((FADE_IN_DELAY - xGameTimer) / FADE_IN_DELAY) * 255));
rectangle.setPosition(sf::Vector2f(OFFSET_X, OFFSET_Y));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH , MAP_HEIGHT * TILE_HEIGHT));
app->draw(rectangle);
}
else if (xGameState == xGameStateFadeOut)
{
// fade out
rectangle.setFillColor(sf::Color(0, 0, 0, ((FADE_IN_DELAY - xGameTimer) / FADE_IN_DELAY) * 255));
rectangle.setPosition(sf::Vector2f(OFFSET_X, OFFSET_Y));
rectangle.setSize(sf::Vector2f(MAP_WIDTH * TILE_WIDTH , MAP_HEIGHT * TILE_HEIGHT));
app->draw(rectangle);
}
}
}
void WitchBlastGame::switchToMenu()
{
EntityManager::getEntityManager()->clean();
if (menuMap != NULL) delete menuMap;
menuMap = new GameMap(MENU_MAP_WIDTH, MENU_MAP_HEIGHT);
for (int i = 0; i < MENU_MAP_WIDTH; i++)
for (int j = 0; j < MENU_MAP_HEIGHT; j++)
{
if (rand() % 6 == 0)
menuMap->setTile(i, j, rand() %7 + 1);
}
menuTileMap = new TileMapEntity(ImageManager::getImageManager()->getImage(IMAGE_TILES), menuMap, 64, 64, 10);
menuTileMap->setX(-30.0f);
menuTileMap->setY(-20.0f);
gameState = gameStateMenu;
buildMenu();
if (!config.configFileExists())
{
menu.redefineKey = true;
menu.keyIndex = 0;
}
playMusic(MusicIntro);
}
void WitchBlastGame::updateMenu()
{
EntityManager::getEntityManager()->animate(deltaTime);
menu.age += deltaTime;
float mapY = menuTileMap->getY();
mapY -= 30.0f * deltaTime;
if (mapY < -64.0f)
{
mapY += 64.0f;
for (int i = 0; i < MENU_MAP_WIDTH; i++)
{
for (int j = 0; j < MENU_MAP_HEIGHT - 1; j++)
{
menuMap->setTile(i, j, menuMap->getTile(i, j+1));
}
if (rand() % 6 == 0)
menuMap->setTile(i, MENU_MAP_HEIGHT - 1, rand() %7 + 1);
else
menuMap->setTile(i, MENU_MAP_HEIGHT - 1, 0);
}
}
menuTileMap->setY(mapY);
// Process events
sf::Event event;
while (app->pollEvent(event))
{
// Close window : exit
if (event.type == sf::Event::Closed)
{
app->close();
}
if (event.type == sf::Event::KeyPressed && menu.redefineKey)
{
bool alreadyUsed = false;
if (event.key.code == sf::Keyboard::Escape) alreadyUsed = true;
for (unsigned int i = 0; i < menu.keyIndex; i++)
if (input[i] == event.key.code) alreadyUsed = true;
// TODO more tests
if (!alreadyUsed)
{
input[menu.keyIndex] = event.key.code;
menu.keyIndex++;
if (menu.keyIndex == NumberKeys)
{
menu.redefineKey = false;
saveConfigurationToFile();
}
}
}
else if (event.type == sf::Event::KeyPressed && !menu.redefineKey)
{
if (event.key.code == sf::Keyboard::Escape)
{
app->close();
}
else if (event.key.code == input[KeyDown] || event.key.code == sf::Keyboard::Down)
{
menu.index++;
if (menu.index == menu.items.size()) menu.index = 0;
SoundManager::getSoundManager()->playSound(SOUND_SHOT_SELECT);
}
else if (event.key.code == input[KeyUp] || event.key.code == sf::Keyboard::Up)
{
if (menu.index == 0) menu.index = menu.items.size() - 1;
else menu.index--;
SoundManager::getSoundManager()->playSound(SOUND_SHOT_SELECT);
}
else if (event.key.code == sf::Keyboard::Return)
{
switch (menu.items[menu.index].id)
{
case MenuStartNew: startNewGame(false); remove(SAVE_FILE.c_str()); break;
case MenuStartOld: startNewGame(true); break;
case MenuKeys: menu.redefineKey = true; menu.keyIndex = 0; break;
case MenuExit: app->close(); break;
}
}
}
}
}
void WitchBlastGame::renderMenu()
{
// rendering tiles
EntityManager::getEntityManager()->render(app);
// title
write("Witch Blast", 70, 485, 120, ALIGN_CENTER, sf::Color(255, 255, 255, 255), app, 3, 3);
if (menu.redefineKey)
{
// menu background
sf::RectangleShape rectangle(sf::Vector2f(470 , 400));
rectangle.setFillColor(sf::Color(50, 50, 50, 160));
rectangle.setPosition(sf::Vector2f(250, 240));
app->draw(rectangle);
// menu keys
if (config.configFileExists())
write("Key configuration", 18, 300, 250, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 1, 1);
else
write("Please configure the keys", 18, 300, 250, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 1, 1);
for (unsigned int i = 0; i < NumberKeys; i++)
{
sf::Color itemColor;
if (menu.keyIndex == i) itemColor = sf::Color(255, 255, 255, 255);
else itemColor = sf::Color(180, 180, 180, 255);
std::ostringstream oss;
oss << inputKeyString[i] << ": ";
if (menu.keyIndex == i) oss << "[insert Key]";
else if (menu.keyIndex > i) oss << "DONE";
write(oss.str(), 16, 300, 285 + i * 32, ALIGN_LEFT, itemColor, app, 1, 1);
}
}
else
{
// menu background
sf::RectangleShape rectangle(sf::Vector2f(470 , 390));
rectangle.setFillColor(sf::Color(50, 50, 50, 160));
rectangle.setPosition(sf::Vector2f(250, 240));
if (menu.items.size() == 3) rectangle.setSize(sf::Vector2f(470 , 310));
app->draw(rectangle);
// menu
for (unsigned int i = 0; i < menu.items.size(); i++)
{
sf::Color itemColor;
if (menu.index == i) itemColor = sf::Color(255, 255, 255, 255);
else itemColor = sf::Color(180, 180, 180, 255);
write(menu.items[i].label, 24, 300, 260 + i * 90, ALIGN_LEFT, itemColor, app, 1, 1);
write(menu.items[i].description, 15, 300, 260 + i * 90 + 40, ALIGN_LEFT, itemColor, app, 0, 0);
}
}
std::ostringstream oss;
oss << APP_NAME << " v" << APP_VERSION << " by Seby 2014";
write(oss.str(), 17, 5, 680, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 1, 1);
}
void WitchBlastGame::startGame()
{
lastTime = getAbsolutTime();
switchToMenu();
// Start game loop
while (app->isOpen())
{
deltaTime = getAbsolutTime() - lastTime;
lastTime = getAbsolutTime();
if (deltaTime > 0.05f) deltaTime = 0.05f;
switch (gameState)
{
case gameStateInit:
case gameStateKeyConfig:
case gameStateMenu: updateMenu(); break;
case gameStatePlaying: updateRunningGame(); break;
}
onRender();
}
quitGame();
}
void WitchBlastGame::createFloor()
{
// TODO : extracts from createNewGame
}
void WitchBlastGame::closeDoors()
{
if (!currentMap->isCleared())
{
int i;
for(i = 0; i < MAP_WIDTH; i++)
{
if (currentMap->getTile(i, 0) < 4) currentMap->setTile(i, 0, MAP_DOOR);
if (currentMap->getTile(i, MAP_HEIGHT - 1) < 4) currentMap->setTile(i, MAP_HEIGHT - 1, MAP_DOOR);
}
for(i = 0; i < MAP_HEIGHT; i++)
{
if (currentMap->getTile(0, i) < 4) currentMap->setTile(0, i, MAP_DOOR);
if (currentMap->getTile(MAP_WIDTH - 1, i) < 4) currentMap->setTile(MAP_WIDTH - 1, i, MAP_DOOR);
}
roomClosed = true;
}
}
void WitchBlastGame::openDoors()
{
int i, j;
for(i = 0; i < MAP_WIDTH; i++)
for(j = 0; j < MAP_WIDTH; j++)
if (currentMap->getTile(i, j) == MAP_DOOR) currentMap->setTile(i, j, MAP_DOOR_OPEN);
roomClosed = false;
SoundManager::getSoundManager()->playSound(SOUND_DOOR_OPENING);
if (currentMap->hasNeighbourUp() == 2 && !bossRoomOpened)
currentMap->setTile(MAP_WIDTH/2, 0, MAP_DOOR);
else
doorEntity[0]->openDoor();
if (currentMap->hasNeighbourLeft() == 2 && !bossRoomOpened)
currentMap->setTile(0, MAP_HEIGHT / 2, MAP_DOOR);
else
doorEntity[1]->openDoor();
if (currentMap->hasNeighbourDown() == 2 && !bossRoomOpened)
currentMap->setTile(MAP_WIDTH / 2, MAP_HEIGHT - 1, MAP_DOOR);
else
doorEntity[2]->openDoor();
if (currentMap->hasNeighbourRight() == 2 && !bossRoomOpened)
currentMap->setTile(MAP_WIDTH - 1, MAP_HEIGHT / 2, MAP_DOOR);
else
doorEntity[3]->openDoor();
}
int WitchBlastGame::getEnnemyCount()
{
int n=0;
EntityManager::EntityList* entityList =EntityManager::getEntityManager()->getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() >= 20)
{
n++;
}
}
return n;
}
Vector2D WitchBlastGame::getNearestEnnemy(float x, float y)
{
Vector2D target(-100.0f, -100.0f);
float distanceMin = -1.0f;
EntityManager::EntityList* entityList =EntityManager::getEntityManager()->getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() >= 20)
{
// enemy
EnnemyEntity* enemy = dynamic_cast<EnnemyEntity*>(e);
float d2 = (x - enemy->getX()) * (x - enemy->getX()) + (y - enemy->getY()) * (y - enemy->getY());
if (target.x < -1.0f || d2 < distanceMin)
{
distanceMin = d2;
target.x = enemy->getX();
target.y = enemy->getY();
}
}
}
return target;
}
void WitchBlastGame::refreshMap()
{
// clean the sprites from old map
EntityManager::getEntityManager()->partialClean(10);
// if new map, it has to be randomized
bool generateMap = !(currentFloor->getMap(floorX, floorY)->isVisited());
currentMap = currentFloor->getAndVisitMap(floorX, floorY);
// load the map
currentTileMap->setMap(currentMap);
player->setMap(currentMap, TILE_WIDTH, TILE_HEIGHT, OFFSET_X, OFFSET_Y);
refreshMinimap();
if(generateMap)
this->generateMap();
else
{
if (currentMap->getRoomType() == roomTypeMerchant)
new PnjEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2 - 2) * TILE_HEIGHT,
0);
}
// check doors
doorEntity[0]->setVisible(currentMap->hasNeighbourUp() > 0);
if (currentMap->hasNeighbourUp() == 1) doorEntity[0]->setDoorType(0);
if (currentMap->hasNeighbourUp() == 2 || currentMap->getRoomType()==roomTypeBoss) doorEntity[0]->setDoorType(1);
if (currentMap->hasNeighbourUp() == 2 && !bossRoomOpened)
{
doorEntity[0]->setOpen(false);
currentMap->setTile(MAP_WIDTH/2, 0, MAP_DOOR);
}
else
doorEntity[0]->setOpen(true);
doorEntity[3]->setVisible(currentMap->hasNeighbourRight() > 0);
if (currentMap->hasNeighbourRight() == 1) doorEntity[3]->setDoorType(0);
if (currentMap->hasNeighbourRight() == 2 || currentMap->getRoomType()==roomTypeBoss) doorEntity[3]->setDoorType(1);
if (currentMap->hasNeighbourRight() == 2 && !bossRoomOpened)
{
doorEntity[3]->setOpen(false);
currentMap->setTile(MAP_WIDTH - 1, MAP_HEIGHT / 2, MAP_DOOR);
}
else
doorEntity[3]->setOpen(true);
doorEntity[2]->setVisible(currentMap->hasNeighbourDown() > 0);
if (currentMap->hasNeighbourDown() == 1) doorEntity[2]->setDoorType(0);
if (currentMap->hasNeighbourDown() == 2 || currentMap->getRoomType()==roomTypeBoss) doorEntity[2]->setDoorType(1);
if (currentMap->hasNeighbourDown() == 2 && !bossRoomOpened)
{
doorEntity[2]->setOpen(false);
currentMap->setTile(MAP_WIDTH/2, MAP_HEIGHT - 1, MAP_DOOR);
}
else
doorEntity[2]->setOpen(true);
doorEntity[1]->setVisible(currentMap->hasNeighbourLeft() > 0);
if (currentMap->hasNeighbourLeft() == 1) doorEntity[1]->setDoorType(0);
if (currentMap->hasNeighbourLeft() == 2 || currentMap->getRoomType()==roomTypeBoss) doorEntity[1]->setDoorType(1);
if (currentMap->hasNeighbourLeft() == 2 && !bossRoomOpened)
{
doorEntity[1]->setOpen(false);
currentMap->setTile(0, MAP_HEIGHT / 2, MAP_DOOR);
}
else
doorEntity[1]->setOpen(true);
}
void WitchBlastGame::refreshMinimap()
{
for (int j=0; j < FLOOR_HEIGHT; j++)
for (int i=0; i < FLOOR_WIDTH; i++)
{
int n = currentFloor->getRoom(i, j);
if (n > 0 && currentFloor->getMap(i, j)->isVisited())
{
if (currentFloor->getRoom(i, j) == roomTypeStarting
|| currentFloor->getRoom(i, j) == roomTypeBonus
|| currentFloor->getRoom(i, j) == roomTypeKey
|| currentFloor->getRoom(i, j) == roomTypeStandard)
{
if ( currentFloor->getMap(i, j)->containsHealth())
miniMap->setTile(i, j, 5);
else
miniMap->setTile(i, j, roomTypeStandard);
}
else
miniMap->setTile(i, j, currentFloor->getRoom(i, j));
}
else if (n > 0 && currentFloor->getMap(i, j)->isKnown())
{
if (currentFloor->getRoom(i, j) == roomTypeBoss)
miniMap->setTile(i, j, 7);
else
miniMap->setTile(i, j, 9);
}
else
miniMap->setTile(i, j, 0);
}
miniMap->setTile(floorX, floorY, 8);
}
void WitchBlastGame::checkEntering()
{
if (!currentMap->isCleared())
{
player->setEntering();
SoundManager::getSoundManager()->playSound(SOUND_DOOR_CLOSING);
for (int i=0; i<4; i++)
doorEntity[i]->closeDoor();
}
}
void WitchBlastGame::saveMapItems()
{
EntityManager::EntityList* entityList = EntityManager::getEntityManager()->getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity* e = *it;
it++;
ItemEntity* itemEntity = dynamic_cast<ItemEntity*>(e);
ChestEntity* chestEntity = dynamic_cast<ChestEntity*>(e);
if (itemEntity != NULL)
{
currentMap->addItem(itemEntity->getItemType(), itemEntity->getX(), itemEntity->getY(), itemEntity->getMerchandise());
} // endif
else if (chestEntity != NULL)
{
currentMap->addChest(chestEntity->getChestType(), chestEntity->getOpened(), chestEntity->getX(), chestEntity->getY());
} // endif
else
{
SpriteEntity* spriteEntity = dynamic_cast<SpriteEntity*>(e);
if (spriteEntity != NULL && (e->getType() == ENTITY_BLOOD || e->getType() == ENTITY_CORPSE ) )
{
int spriteFrame = spriteEntity->getFrame();
if (spriteEntity->getWidth() == 128) spriteFrame += FRAME_CORPSE_KING_RAT;
currentMap->addSprite(e->getType(), spriteFrame, e->getX(), e->getY(), spriteEntity->getScaleX());
}
}
} // end for
}
void WitchBlastGame::moveToOtherMap(int direction)
{
// stairs to next level
if (direction == 8 && currentMap->getRoomType() == roomTypeExit)
{
if (player->getPlayerStatus() != PlayerEntity::playerStatusGoingUp)
{
player->setLeavingLevel();
xGameState = xGameStateFadeOut;
xGameTimer = FADE_OUT_DELAY;
player->setVelocity(Vector2D(0.0f, - INITIAL_PLAYER_SPEED / 2));
}
}
// go to another room
else
{
saveMapItems();
switch (direction)
{
case (4): floorX--; player->moveTo((OFFSET_X + MAP_WIDTH * TILE_WIDTH), player->getY()); player->move(4); break;
case (6): floorX++; player->moveTo(OFFSET_X, player->getY()); player->move(6); break;
case (8): floorY--; player->moveTo(player->getX(), OFFSET_Y + MAP_HEIGHT * TILE_HEIGHT/* - 20*/); player->move(8); break;
case (2): floorY++; player->moveTo(player->getX(), OFFSET_Y); break;
}
refreshMap();
checkEntering();
currentMap->restoreMapObjects();
}
}
void WitchBlastGame::onRender()
{
// clear the view
app->clear(sf::Color(32, 32, 32));
switch (gameState)
{
case gameStateInit:
case gameStateKeyConfig:
case gameStateMenu: renderMenu(); break;
case gameStatePlaying: renderRunningGame(); break;
}
app->display();
}
void WitchBlastGame::renderHudShots(sf::RenderTarget* app)
{
int xHud = 640;
int yHud = 650;
int index = 0;
for (int i = 0; i < SPECIAL_SHOT_SLOTS; i++)
{
if (i == 0 || player->getShotType(i) != ShotTypeStandard)
{
int type_shot = player->getShotType(i);
shotsSprite.setPosition(xHud + 48 * index, yHud);
if (index == player->getShotIndex())
{
shotsSprite.setTextureRect(sf::IntRect(0, 0, 48, 48));
app->draw(shotsSprite);
}
shotsSprite.setTextureRect(sf::IntRect(48 * ( 1 + type_shot), 0, 48, 48));
app->draw(shotsSprite);
// level
if (i > 0)
{
std::ostringstream oss;
oss << "lvl " << player->getShotLevel(i) + 1;
write(oss.str(), 10, xHud + 48 * index + 10, yHud + 48, ALIGN_LEFT, sf::Color(255, 255, 255, 255), app, 0, 0);
}
index++;
}
}
}
void WitchBlastGame::generateBlood(float x, float y, BaseCreatureEntity::enumBloodColor bloodColor)
{
// double blood if the "Blood Snake3 object is equipped
int nbIt;
if (player->isEquiped(EQUIP_BLOOD_SNAKE))
nbIt = 2;
else
nbIt = 1;
for (int i=0; i < nbIt; i++)
{
SpriteEntity* blood = new SpriteEntity(ImageManager::getImageManager()->getImage(IMAGE_BLOOD), x, y, 16, 16, 6);
blood->setZ(OFFSET_Y - 1);
int b0 = 0;
if (bloodColor == BaseCreatureEntity::bloodGreen) b0 += 6;
blood->setFrame(b0 + rand()%6);
blood->setType(ENTITY_BLOOD);
blood->setVelocity(Vector2D(rand()%250));
blood->setViscosity(0.95f);
float bloodScale = 1.0f + (rand() % 10) * 0.1f;
blood->setScale(bloodScale, bloodScale);
}
}
void WitchBlastGame::showArtefactDescription(enumItemType itemType)
{
new ArtefactDescriptionEntity(itemType);
}
void WitchBlastGame::generateMap()
{
if (currentMap->getRoomType() == roomTypeStandard)
generateStandardMap();
else if (currentMap->getRoomType() == roomTypeBonus)
{
currentMap->setCleared(true);
Vector2D v = currentMap->generateBonusRoom();
int bonusType = getRandomEquipItem(false, false);
//if (bonusType == EQUIP_FAIRY)
if (items[FirstEquipItem + bonusType].familiar > FamiliarNone)
{
new ChestEntity(v.x, v.y, CHEST_FAIRY + items[FirstEquipItem + bonusType].familiar, false);
}
else
{
new ItemEntity( (enumItemType)(FirstEquipItem + bonusType), v.x ,v.y);
}
}
else if (currentMap->getRoomType() == roomTypeKey)
{
Vector2D v = currentMap->generateKeyRoom();
new ItemEntity( (enumItemType)(ItemBossKey), v.x ,v.y);
initMonsterArray();
int x0 = MAP_WIDTH / 2;
int y0 = MAP_HEIGHT / 2;
monsterArray[x0][y0] = true;
if (level == 1)
{
findPlaceMonsters(MONSTER_RAT, 2);
findPlaceMonsters(MONSTER_BAT, 2);
}
else
{
findPlaceMonsters(MONSTER_RAT, 5);
findPlaceMonsters(MONSTER_BAT, 5);
for (int i = 2; i < level; i++)
{
if (rand()%2 == 0)findPlaceMonsters(MONSTER_IMP_BLUE, 1);
else findPlaceMonsters(MONSTER_IMP_RED, 1);
}
}
}
else if (currentMap->getRoomType() == roomTypeMerchant)
{
currentMap->generateMerchantRoom();
ItemEntity* item1 = new ItemEntity(
itemHealth,
OFFSET_X + (MAP_WIDTH / 2 - 3) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT);
item1->setMerchandise(true);
ItemEntity* item3 = new ItemEntity(
itemHealthSmall,
OFFSET_X + (MAP_WIDTH / 2 + 3) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT);
item3->setMerchandise(true);
int bonusType = getRandomEquipItem(true, true);
ItemEntity* item2 = new ItemEntity(
(enumItemType)(FirstEquipItem + bonusType),
OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT);
item2->setMerchandise(true);
new PnjEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2 - 2) * TILE_HEIGHT,
0);
currentMap->setCleared(true);
}
else if (currentMap->getRoomType() == roomTypeBoss)
{
currentMap->generateRoomWithoutHoles(0);
if (level == 1)
new ButcherEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
else if (level == 2)
new GiantSlimeEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
else if (level == 3)
new KingRatEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
else if (level == 4)
new CyclopEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
else //if (level == 5)
new GiantSpiderEntity(OFFSET_X + (MAP_WIDTH / 2) * TILE_WIDTH + TILE_WIDTH / 2,
OFFSET_Y + (MAP_HEIGHT / 2) * TILE_HEIGHT + TILE_HEIGHT / 2);
playMusic(MusicBoss);
}
else if (currentMap->getRoomType() == roomTypeStarting)
{
currentMap->generateRoomWithoutHoles(0);
currentMap->setCleared(true);
}
else if (currentMap->getRoomType() == roomTypeExit)
{
currentMap->generateExitRoom();
currentMap->setCleared(true);
new ChestEntity(OFFSET_X + (TILE_WIDTH * MAP_WIDTH * 0.5f),
OFFSET_Y + (TILE_HEIGHT * MAP_HEIGHT * 0.5f),
CHEST_EXIT, false);
}
else
currentMap->randomize(currentMap->getRoomType());
}
void WitchBlastGame::write(std::string str, int size, float x, float y, int align, sf::Color color, sf::RenderTarget* app, int xShadow = 0, int yShadow = 0)
{
myText.setString(str);
myText.setCharacterSize(size);
float xFont = x;
if (align == ALIGN_CENTER)
xFont = x - myText.getLocalBounds().width / 2;
if (xShadow != 0 && yShadow != 0)
{
myText.setPosition(xFont + xShadow, y + yShadow);
myText.setColor(sf::Color(0, 0, 0, 255));
app->draw(myText);
}
myText.setPosition(xFont, y);
myText.setColor(color);
app->draw(myText);
}
void WitchBlastGame::initMonsterArray()
{
for (int i = 0; i < MAP_WIDTH; i++)
for (int j = 0; j < MAP_HEIGHT; j++)
monsterArray[i][j] = false;
}
void WitchBlastGame::addMonster(monster_type_enum monsterType, float xm, float ym)
{
switch (monsterType)
{
case MONSTER_RAT: new RatEntity(xm, ym - 2); break;
case MONSTER_BLACK_RAT: new BlackRatEntity(xm, ym - 5); break;
case MONSTER_BAT: new BatEntity(xm, ym); break;
case MONSTER_EVIL_FLOWER: new EvilFlowerEntity(xm, ym - 2); break;
case MONSTER_SLIME: new SlimeEntity(xm, ym, SlimeTypeStandard, false); break;
case MONSTER_IMP_RED: new ImpEntity(xm, ym, ImpEntity::ImpTypeRed); break;
case MONSTER_IMP_BLUE: new ImpEntity(xm, ym, ImpEntity::ImpTypeBlue); break;
case MONSTER_SLIME_RED: new SlimeEntity(xm, ym, SlimeTypeRed, false); break;
case MONSTER_SLIME_BLUE: new SlimeEntity(xm, ym, SlimeTypeBlue, false); break;
case MONSTER_KING_RAT: new KingRatEntity(xm, ym); break;
}
}
void WitchBlastGame::findPlaceMonsters(monster_type_enum monsterType, int amount)
{
// find a suitable place
bool isMonsterFlying = monsterType == MONSTER_BAT;
bool bOk;
int xm, ym;
float xMonster, yMonster;
for (int index = 0; index < amount; index++)
{
bOk = false;
while (!bOk)
{
bOk = true;
xm = 1 +rand() % (MAP_WIDTH - 3);
ym = 1 +rand() % (MAP_HEIGHT - 3);
if (monsterArray[xm][ym])
{
bOk = false;
}
if (bOk && !isMonsterFlying && !currentMap->isWalkable(xm, ym))
{
bOk = false;
}
if (bOk)
{
xMonster = OFFSET_X + xm * TILE_WIDTH + TILE_WIDTH * 0.5f;
yMonster = OFFSET_Y + ym * TILE_HEIGHT+ TILE_HEIGHT * 0.5f;
float dist2 = (xMonster - player->getX())*(xMonster - player->getX()) + (yMonster - player->getY())*(yMonster - player->getY());
if ( dist2 < 75000.0f)
{
bOk = false;
}
else
{
addMonster(monsterType, xMonster, yMonster);
monsterArray[xm][ym] = true;
}
}
}
}
}
void WitchBlastGame::generateStandardMap()
{
initMonsterArray();
generateStandardRoom(level);
}
item_equip_enum WitchBlastGame::getRandomEquipItem(bool toSale = false, bool noFairy = false)
{
std::vector<int> bonusSet;
int setSize = 0;
for (int i = 0; i < NUMBER_EQUIP_ITEMS; i++)
{
bool itemOk = true;
int eq = i + FirstEquipItem;
if (player->isEquiped(i)) itemOk = false;
// TODO item already in floor
if (itemOk && toSale && !items[eq].canBeSold) itemOk = false;
if (itemOk && !toSale && !items[eq].canBeFound) itemOk = false;
if (itemOk && items[eq].level > level) itemOk = false;
if (itemOk && items[eq].requirement >= FirstEquipItem
&& !player->isEquiped(items[eq].requirement - FirstEquipItem)) itemOk = false;
if (itemOk && player->getShotType(SPECIAL_SHOT_SLOTS_STANDARD) != ShotTypeStandard
&& (items[eq].specialShot != ShotTypeStandard && items[eq].level < 4))
itemOk = false;
if (itemOk && noFairy && items[eq].familiar != FamiliarNone) itemOk = false;
if (itemOk)
{
int n = 0;
switch (items[eq].rarity)
{
case RarityCommon: n = 4; break;
case RarityUnommon: n = 2; break;
case RarityRare: n = 1; break;
}
for (int j = 0; j < n; j++)
{
bonusSet.push_back(i);
setSize++;
}
}
}
int bonusType = 0;
if (setSize > 0) bonusType = bonusSet[rand() % setSize];
return (item_equip_enum) bonusType;
}
void WitchBlastGame::verifyDoorUnlocking()
{
int collidingDirection = (player->getCollidingDirection());
if (collidingDirection > 0 && currentMap->isCleared() && !bossRoomOpened && player->isEquiped(EQUIP_BOSS_KEY))
{
int xt = (player->getX() - OFFSET_X) / TILE_WIDTH;
int yt = (player->getY() - OFFSET_Y) / TILE_HEIGHT;
if (yt <= 1 && xt >= MAP_WIDTH / 2 - 1 && xt <= MAP_WIDTH / 2 + 1 && currentMap->hasNeighbourUp() == 2)
{
doorEntity[0]->openDoor();
currentMap->setTile(MAP_WIDTH / 2, 0, 0);
SoundManager::getSoundManager()->playSound(SOUND_DOOR_OPENING);
player->useBossKey();
bossRoomOpened = true;
}
if (yt >= MAP_HEIGHT - 2 && xt >= MAP_WIDTH / 2 - 1 &&xt <= MAP_WIDTH / 2 + 1 && currentMap->hasNeighbourDown() == 2)
{
doorEntity[2]->openDoor();
currentMap->setTile(MAP_WIDTH / 2, MAP_HEIGHT - 1, 0);
SoundManager::getSoundManager()->playSound(SOUND_DOOR_OPENING);
player->useBossKey();
bossRoomOpened = true;
}
if (xt <= 1 && yt >= MAP_HEIGHT / 2 - 1 && yt <= MAP_HEIGHT / 2 + 1 && currentMap->hasNeighbourLeft() == 2)
{
doorEntity[1]->openDoor();
currentMap->setTile(0, MAP_HEIGHT / 2, 0);
SoundManager::getSoundManager()->playSound(SOUND_DOOR_OPENING);
player->useBossKey();
bossRoomOpened = true;
}
if (xt >= MAP_WIDTH - 2 && yt >= MAP_HEIGHT / 2 - 1 && yt <= MAP_HEIGHT / 2 + 1 && currentMap->hasNeighbourRight() == 2)
{
doorEntity[3]->openDoor();
currentMap->setTile(MAP_WIDTH - 1, MAP_HEIGHT / 2, 0);
SoundManager::getSoundManager()->playSound(SOUND_DOOR_OPENING);
player->useBossKey();
bossRoomOpened = true;
}
}
}
void WitchBlastGame::playMusic(musicEnum musicChoice)
{
music.stop();
music.setLoop(true);
bool ok = false;
switch (musicChoice)
{
case MusicDungeon:
ok = music.openFromFile("media/sound/track00.ogg");
music.setVolume(75);
break;
case MusicEnding:
ok = music.openFromFile("media/sound/track_ending.ogg");
music.setVolume(35);
break;
case MusicBoss:
ok = music.openFromFile("media/sound/track_boss.ogg");
music.setVolume(90);
break;
case MusicIntro:
ok = music.openFromFile("media/sound/track_intro.ogg");
music.setVolume(90);
break;
}
if (ok)
music.play();
}
void WitchBlastGame::makeShake(float duration)
{
xGameState = xGameStateShake;
xGameTimer = duration;
}
void WitchBlastGame::saveGame()
{
if (player->getPlayerStatus() == PlayerEntity::playerStatusAcquire)
player->acquireItemAfterStance();
ofstream file(SAVE_FILE.c_str(), ios::out | ios::trunc);
int i, j, k, l;
if (file)
{
// version (for compatibility check)
file << SAVE_VERSION << std::endl;
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
file << (now->tm_year + 1900) << '-';
if (now->tm_mon < 9) file << "0";
file << (now->tm_mon + 1) << '-';
if (now->tm_mday < 9) file << "0";
file << now->tm_mday
<< endl;
if (now->tm_hour <= 9) file << "0";
file << (now->tm_hour) << ':';
if (now->tm_min <= 9) file << "0";
file << (now->tm_min) << endl;
// floor
file << level << std::endl;
int nbRooms = 0;
for (j = 0; j < FLOOR_HEIGHT; j++)
{
for (i = 0; i < FLOOR_WIDTH; i++)
{
file << currentFloor->getRoom(i,j) << " ";
if (currentFloor->getRoom(i,j) > 0) nbRooms++;
}
file << std::endl;
}
// maps
saveMapItems();
file << nbRooms << std::endl;
for (j = 0; j < FLOOR_HEIGHT; j++)
{
for (i = 0; i < FLOOR_WIDTH; i++)
{
if (currentFloor->getRoom(i,j) > 0)
{
file << i << " " << j << " "
<< currentFloor->getMap(i, j)->getRoomType() << " "
<< currentFloor->getMap(i, j)->isKnown() << " "
<< currentFloor->getMap(i, j)->isVisited() << " "
<< currentFloor->getMap(i, j)->isCleared() << std::endl;
if (currentFloor->getMap(i, j)->isVisited())
{
for (l = 0; l < MAP_HEIGHT; l++)
{
for (k = 0; k < MAP_WIDTH; k++)
{
file << currentFloor->getMap(i, j)->getTile(k, l) << " ";
}
file << std::endl;
}
// items, etc...
std::list<DungeonMap::itemListElement> itemList = currentFloor->getMap(i, j)->getItemList();
file << itemList.size() << std::endl;
std::list<DungeonMap::itemListElement>::iterator it;
for (it = itemList.begin (); it != itemList.end ();)
{
DungeonMap::itemListElement ilm = *it;
it++;
file << ilm.type << " " << ilm.x << " " << ilm.y << " " << ilm.merch << std::endl;
}
// chests
std::list<DungeonMap::chestListElement> chestList = currentFloor->getMap(i, j)->getChestList();
file << chestList.size() << std::endl;
std::list<DungeonMap::chestListElement>::iterator itc;
for (itc = chestList.begin (); itc != chestList.end ();)
{
DungeonMap::chestListElement ilm = *itc;
itc++;
file << ilm.type << " " << ilm.x << " " << ilm.y << " " << ilm.state << std::endl;
}
// sprites
std::list<DungeonMap::spriteListElement> spriteList = currentFloor->getMap(i, j)->getSpriteList();
file << spriteList.size() << std::endl;
std::list<DungeonMap::spriteListElement>::iterator its;
for (its = spriteList.begin (); its != spriteList.end ();)
{
DungeonMap::spriteListElement ilm = *its;
its++;
file << ilm.type << " " << ilm.frame << " " << ilm.x << " " << ilm.y << " " << ilm.scale << std::endl;
}
}
}
}
file << std::endl;
}
// game
file << floorX << " " << floorY << std::endl;
file << bossRoomOpened << std::endl;
// boss door !
// player
file << player->getHp() << " " << player->getHpMax() << " " << player->getGold() << std::endl;
for (i = 0; i < NUMBER_EQUIP_ITEMS; i++) file << player->isEquiped(i) << " ";
file << std::endl;
file << player->getX() << " " << player->getY() << std::endl;
file << player->getShotIndex();
for (i = 0; i < SPECIAL_SHOT_SLOTS; i++) file << " " << player->getShotType(i);
file.close();
}
else
{
cerr << "[ERROR] Saving the game..." << endl;
}
}
bool WitchBlastGame::loadGame()
{
ifstream file(SAVE_FILE.c_str(), ios::in);
if (file)
{
int i, j, k, n;
// version
std::string v;
file >> v;
if (v != SAVE_VERSION)
{
file.close();
remove(SAVE_FILE.c_str());
return false;
}
// date an time
file >> v;
file >> v;
// floor
file >> level;
currentFloor = new GameFloor(level);
for (j = 0; j < FLOOR_HEIGHT; j++)
{
for (i = 0; i < FLOOR_WIDTH; i++)
{
int n;
file >> n;
currentFloor->setRoom(i, j, n);
}
}
// maps
int nbRooms;
file >> nbRooms;
for (k = 0; k < nbRooms; k++)
{
file >> i;
file >> j;
file >> n;
DungeonMap* iMap = new DungeonMap(currentFloor, i, j);
currentFloor->setMap(i, j, iMap);
iMap->setRoomType((roomTypeEnum)n);
bool flag;
file >> flag;
iMap->setKnown(flag);
file >> flag;
iMap->setVisited(flag);
file >> flag;
iMap->setCleared(flag);
if (iMap->isVisited())
{
for (j = 0; j < MAP_HEIGHT; j++)
{
for (i = 0; i < MAP_WIDTH; i++)
{
file >> n;
iMap->setTile(i, j, n);
}
}
// items int the map
file >> n;
for (i = 0; i < n; i++)
{
int t;
float x, y;
bool merc;
file >> t >> x >> y >> merc;
iMap->addItem(t, x, y, merc);
}
// chests in the map
file >> n;
for (i = 0; i < n; i++)
{
int t;
float x, y;
bool state;
file >> t >> x >> y >> state;
iMap->addChest(t, state, x, y);
}
// sprites in the map
file >> n;
for (i = 0; i < n; i++)
{
int t, f;
float x, y, scale;
file >> t >> f >> x >> y >> scale;
iMap->addSprite(t, f, x, y, scale);
}
}
}
// game
file >> floorX >> floorY;
currentMap = currentFloor->getMap(floorX, floorY);
file >> bossRoomOpened;
// player
int hp, hpMax, gold;
file >> hp >> hpMax >> gold;
player = new PlayerEntity(OFFSET_X + (TILE_WIDTH * MAP_WIDTH * 0.5f),
OFFSET_Y + (TILE_HEIGHT * MAP_HEIGHT * 0.5f));
player->setHp(hp);
player->setHpMax(hpMax);
player->setGold(gold);
for (i = 0; i < NUMBER_EQUIP_ITEMS; i++)
{
bool eq;
file >> eq;
player->setEquiped(i, eq);
}
float x, y;
file >> x >> y;
player->moveTo(x, y);
file >> n;
player->setShotIndex(n);
for (i = 0; i < SPECIAL_SHOT_SLOTS; i++)
{
file >> n;
player->setShotType(i, (enumShotType)n);
}
player->computePlayer();
file.close();
remove(SAVE_FILE.c_str());
}
else
{
return false;
}
return true;
}
WitchBlastGame::saveHeaderStruct WitchBlastGame::loadGameHeader()
{
saveHeaderStruct saveHeader;
saveHeader.ok = true;
ifstream file(SAVE_FILE.c_str(), ios::in);
if (file)
{
// version
std::string v;
file >> v;
if (v != SAVE_VERSION)
{
file.close();
remove(SAVE_FILE.c_str());
saveHeader.ok = false;
}
else
{
// date an time
file >> saveHeader.date;
file >> saveHeader.time;
// floor
file >> saveHeader.level;
}
}
else
{
saveHeader.ok = false;
}
return saveHeader;
}
void WitchBlastGame::addKey(int logicInput, std::string key)
{
int iKey = config.findInt(key);
if (iKey >= 0)
{
sf::Keyboard::Key k = (sf::Keyboard::Key)iKey;
input[logicInput] = k;
}
}
void WitchBlastGame::saveConfigurationToFile()
{
std::map<std::string, std::string> newMap;
// Keys
newMap["keyboard_move_up"] = intToString(input[KeyUp]);
newMap["keyboard_move_down"] = intToString(input[KeyDown]);
newMap["keyboard_move_left"] = intToString(input[KeyLeft]);
newMap["keyboard_move_right"] = intToString(input[KeyRight]);
newMap["keyboard_fire_up"] = intToString(input[KeyFireUp]);
newMap["keyboard_fire_down"] = intToString(input[KeyFireDown]);
newMap["keyboard_fire_left"] = intToString(input[KeyFireLeft]);
newMap["keyboard_fire_right"] = intToString(input[KeyFireRight]);
newMap["keyboard_fire"] = intToString(input[KeyFire]);
newMap["keyboard_time_control"] = intToString(input[KeyTimeControl]);
newMap["keyboard_fire_select"] = intToString(input[KeyFireSelect]);
config.saveToFile(CONFIG_FILE, newMap);
}
void WitchBlastGame::configureFromFile()
{
// default
input[KeyUp] = sf::Keyboard::Z;
input[KeyDown] = sf::Keyboard::S;
input[KeyLeft] = sf::Keyboard::Q;
input[KeyRight] = sf::Keyboard::D;
input[KeyFireUp] = sf::Keyboard::Up;
input[KeyFireDown] = sf::Keyboard::Down;
input[KeyFireLeft] = sf::Keyboard::Left;
input[KeyFireRight] = sf::Keyboard::Right;
input[KeyFire] = sf::Keyboard::Space;
input[KeyFireSelect] = sf::Keyboard::Tab;
input[KeyTimeControl] = sf::Keyboard::RShift;
// from file
addKey(KeyUp, "keyboard_move_up");
addKey(KeyDown, "keyboard_move_down");
addKey(KeyLeft, "keyboard_move_left");
addKey(KeyRight, "keyboard_move_right");
addKey(KeyFireUp, "keyboard_fire_up");
addKey(KeyFireDown, "keyboard_fire_down");
addKey(KeyFireLeft, "keyboard_fire_left");
addKey(KeyFireRight, "keyboard_fire_right");
addKey(KeyFire, "keyboard_fire");
addKey(KeyTimeControl, "keyboard_time_control");
addKey(KeyFireSelect, "keyboard_fire_select");
}
void WitchBlastGame::buildMenu()
{
menu.items.clear();
menu.redefineKey = false;
menu.age = 0.0f;
WitchBlastGame::saveHeaderStruct saveHeader = loadGameHeader();
if (saveHeader.ok)
{
menuItemStuct itemStart;
itemStart.label = "Start a new game";
itemStart.description = "Old game will be destroyed";
itemStart.id = MenuStartNew;
menu.items.push_back(itemStart);
menuItemStuct itemLoad;
itemStart.label = "Restore game";
std::ostringstream oss;
oss << saveHeader.date << " at " << saveHeader.time << " - level " << saveHeader.level;
itemStart.description = oss.str();
itemStart.id = MenuStartOld;
menu.items.push_back(itemStart);
menu.index = 1;
}
else
{
menuItemStuct itemStart;
itemStart.label = "Start a new game";
itemStart.description = "Begin your journey in a new dungeon";
itemStart.id = MenuStartNew;
menu.items.push_back(itemStart);
menu.index = 0;
}
menuItemStuct itemKeys;
itemKeys.label = "Configure keys";
itemKeys.description = "Redefine player's input";
itemKeys.id = MenuKeys;
menu.items.push_back(itemKeys);
menuItemStuct itemExit;
itemExit.label = "Exit game";
itemExit.description = "Return to the desktop";
itemExit.id = MenuExit;
menu.items.push_back(itemExit);
}
void WitchBlastGame::checkFallingEntities()
{
EntityManager::EntityList* entityList =EntityManager::getEntityManager()->getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getLifetime() < 0.8f && (e->getType() == ENTITY_BLOOD || e->getType() == ENTITY_CORPSE))
{
int tilex = (e->getX() - OFFSET_X) / TILE_WIDTH;
int tiley = (e->getY() - OFFSET_Y) / TILE_HEIGHT;
if (currentMap->getTile(tilex, tiley) >= MAP_HOLE)
{
SpriteEntity* spriteEntity = dynamic_cast<SpriteEntity*>(e);
spriteEntity->setAge(0.0f);
spriteEntity->setLifetime(3.0f);
spriteEntity->setShrinking(true);
spriteEntity->setFading(true);
}
}
}
}
+void WitchBlastGame::resetKilledEnemies()
+{
+ for (int i = 0; i < NB_ENEMY; i++) killedEnemies[i] = 0;
+}
+
+void WitchBlastGame::addKilledEnemy(enemyTypeEnum enemyType)
+{
+ if (enemyType == NB_ENEMY)
+ std::cout << "[ERROR] No enemy type";
+ else
+ killedEnemies[enemyType]++;
+}
+
+void WitchBlastGame::displayKilledEnemies()
+{
+ std::cout<<"KILLED: ";
+ for (int i = 0; i < NB_ENEMY; i++) if (killedEnemies[i] > 0) std::cout << i << "x" << killedEnemies[i] << " ";
+ std::cout << std::endl;
+}
+
WitchBlastGame &game()
{
return *gameptr;
}
diff --git a/src/WitchBlastGame.h b/src/WitchBlastGame.h
index 59341f3..7f1aa5a 100644
--- a/src/WitchBlastGame.h
+++ b/src/WitchBlastGame.h
@@ -1,576 +1,584 @@
/** This file is part of Witch Blast.
*
* Witch Blast 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 3 of the License, or
* (at your option) any later version.
*
* Witch Blast 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 Witch Blast. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WITCH_BLAST_GAME_H
#define WITCH_BLAST_GAME_H
#include "sfml_game/Game.h"
#include "sfml_game/TileMapEntity.h"
#include "PlayerEntity.h"
#include "EnnemyEntity.h"
#include "DoorEntity.h"
#include "GameFloor.h"
#include "Config.h"
const int ALIGN_LEFT = 0; /*!< Text alignment left */
const int ALIGN_RIGHT = 1; /*!< Text alignment right */
const int ALIGN_CENTER = 2; /*!< Text alignment centered */
unsigned const int NumberKeys = 11;
const std::string inputKeyString[NumberKeys] =
{
"move up",
"move down",
"move left",
"move right",
"fire up",
"fire down",
"fire left",
"fire right",
"fire select",
"time control",
"fire (one button)"
};
/*! \class WitchBlastGame
* \brief Main class of the game
*
* WitchBlastGame load the game ressources and do a large part of the game logic (game loop...),
* watches the input, manages the game states, ...
*/
class WitchBlastGame : public Game
{
public:
/*!
* \brief Constructor
*
* Constructor of the WitchBlastGame class.
*/
WitchBlastGame();
/*!
* \brief Destructor
*
* Destructor of the WitchBlastGame class.
*/
virtual ~WitchBlastGame();
/*!
* \brief Accessor on the current dungeon map
*
* Accessor on the current dungeon map (room).
*
* \return a pointer to the current dungeon map
*/
DungeonMap* getCurrentMap();
/*!
* \brief Accessor on the player
*
* Accessor on the player.
*
* \return a pointer to the player
*/
PlayerEntity* getPlayer();
/*!
* \brief Accessor on the player's position
*
* Accessor on the player's position.
*
* \return a Vector2D of the player's position
*/
Vector2D getPlayerPosition();
/*!
* \brief accessor on the level
*
* Accessor on the level.
*
* \return : the level
*/
int getLevel();
/*!
* \brief accessor on showLogical flag
* \return : the value of the flag
*/
bool getShowLogical();
/*!
* \brief Start the game and the game loop
*
* This method starts the game and the game loop.
* The game loop watches the events, the inputs, update and draw the game elements.
*/
virtual void startGame();
/*!
* \brief Move the player to another map (room)
*
* Moves the player to another room of the dungeon.
* It's called when a player walk through an opened door.
*
* \param direction : direction of the new map. 4 = left, 8 = north, 6 = right, 2 = south
*/
void moveToOtherMap(int direction);
/*!
* \brief Closes the doors of the room
*
* Closes the doors of the room.
* It's called when the player enter in an area with monsters.
*/
void closeDoors();
/*!
* \brief Opens the doors of the room
*
* Opens the doors of the room.
* It's called when the player destroy the last monster of a room.
*/
void openDoors();
/*!
* \brief Count the enemies in the room
*
* Count the enemies in the room.
* It's called when the room is closed : 0 enemy = room is cleared and doors shall open.
*
* \return amount of enemies
*/
int getEnnemyCount();
/*!
* \brief Return the position of the nearest enemy
*
* \param x : x position of the source
* \param y : y position of the source
* \return amount of enemies
*/
Vector2D getNearestEnnemy(float x, float y);
/*!
* \brief Generates blood
*
* Generates blood, it's called when someone is hurt or dies.
*
* \param x : x position of the blood
* \param y : y position of the blood
* \param bloodColor : color of the blood (red; green, ...)
*/
void generateBlood(float x, float y, BaseCreatureEntity::enumBloodColor bloodColor);
/*!
* \brief Show a "popup" with artefact's description
*
* Show a "popup" with artefact's description.
* Artefact's description consists of zoomed picture of the item, name and description.
* It's called when a player get an equipment item.
*
* \param itemType : item identifier
*/
void showArtefactDescription(enumItemType itemType);
/*!
* \brief Make a "shake" effet
* \param duration : duration of the effect
*/
void makeShake(float duration);
void write(std::string str, int size, float x, float y, int align, sf::Color color, sf::RenderTarget* app, int xShadow, int yShadow);
/*!
* \brief Save the game
*
* Save the game to file : complete floor and maps, items and blood position, player current equipment and stats....
*/
void saveGame();
/*!
* \brief Load the game
*
* Load the game from file. After restoring the game, the file is destroy.
*
* \return true if succeeded
*/
bool loadGame();
struct saveHeaderStruct
{
bool ok; /**< Save game OK ? */
int level; /**< Level the save game */
std::string date; /**< Date of the save game */
std::string time; /**< Time of the save game */
};
/*!
* \brief Load the savegame data
* \return true if succeeded
*/
saveHeaderStruct loadGameHeader();
/*!
* \brief Returns a random equip object
*
* Returns a random equip object (not an object the player already possesses) .
* \param toSale : true if it's an item for sale
*
* \return the equipment item ID
*/
item_equip_enum getRandomEquipItem(bool toSale, bool noFairy);
/*!
* \brief Adds monsters
*
* Adds monsters to the room in suitable places.
* \param monsterType : monster type
* \param amount : amount of monsters
*/
void findPlaceMonsters(monster_type_enum monsterType, int amount);
+ void addKilledEnemy(enemyTypeEnum enemyType);
+ void displayKilledEnemies();
+
protected:
/*!
* \brief Rendering method
*
* Render all the game objects to the screen.
* Called in the game loop.
*/
virtual void onRender();
/*!
* \brief Update method
*
* Update all the game objects to the screen.
* Called in the game loop.
*/
virtual void onUpdate();
/*!
* \brief render the HUD for shot types
*
* Render the HUD for shot types.
* Display the available shot types and highlight the current one.
*
* \param app : Rendering target
*/
void renderHudShots(sf::RenderTarget* app);
+
+
private:
Config config;
float deltaTime;
// game logic / data
GameMap* miniMap; /*!< Pointer to the logical minimap */
DungeonMap* currentMap; /*!< Pointer to the logical current map */
GameFloor* currentFloor; /*!< Pointer to the logical floor (level) */
bool showLogical; /*!< True if showing bounding boxes, z and center */
// game play
int level; /*!< Level (floor) */
int floorX; /*!< X position of the room in the level */
int floorY; /*!< Y position of the room in the level */
bool roomClosed; /*!< True if the room is closed */
bool bossRoomOpened; /*!< True if the boss gate has been opened in this level */
bool isPausing; /*!< True if the game is currently pausing */
int firingDirection; /*!< Save the firing direction - for the "one button" gameplay */
bool isPlayerAlive; /*!< Dying sets this bool to false (trigger the ending music) */
bool monsterArray[MAP_WIDTH][MAP_HEIGHT]; /*!< use to remember if a case has a monster in monster spawn */
+ int killedEnemies[NB_ENEMY];
// game objects
PlayerEntity* player; /*!< Pointer to the player entity */
TileMapEntity* currentTileMap; /*!< TileMap of the room (main game board) */
GameMap* menuMap; /*!< TileMap of the menu background (logical) */
TileMapEntity* menuTileMap; /*!< TileMap of the menu background (graphical) */
// displaying objects
DoorEntity* doorEntity[4]; /*!< Pointers to the door graphical entity */
sf::Sprite keySprite; /*!< A simple sprite with the boss key (displayed on the HUD) */
sf::Sprite shotsSprite; /*!< A simple sprite for the available shot types (displayed on the HUD) */
sf::Font font; /*!< The font used for displaying text */
sf::Text myText; /*!< The text to be displayed */
sf::Music music; /*!< Current game music */
/** Music enum
* Identify the various music tracks of the game.
*/
enum musicEnum
{
MusicDungeon, /**< Main game music - played when playing the game */
MusicEnding, /**< Ending music - played when the player has died */
MusicBoss, /**< Boss music - for epic fights ! */
MusicIntro /**< Main menu music */
};
/** Game states enum
* Used for the different game states
*/
enum gameStateEnum
{
gameStateInit, /**< Game initialization */
gameStateMenu, /**< Menu */
gameStatePlaying, /**< Playing */
gameStateKeyConfig /**< Key config */
};
gameStateEnum gameState; /*!< Store the game state */
/** Special game states enum
* Used for effects such as fade in...
*/
enum xGameStateEnum
{
xGameStateNone, /**< No effect */
xGameStateFadeIn, /**< Fade in effect - usually when starting a level */
xGameStateFadeOut, /**< Fade out effect - usually when leaving a level */
xGameStateShake /**< Shake effect */
};
xGameStateEnum xGameState; /*!< Store the effect game state */
float xGameTimer; /*!< Effect game timer */
/** Input Keys enum
* Used for the input binding
*/
enum inputKeyEnum
{
KeyUp,
KeyDown,
KeyLeft,
KeyRight,
KeyFireUp,
KeyFireDown,
KeyFireLeft,
KeyFireRight,
KeyFireSelect,
KeyTimeControl,
KeyFire
};
sf::Keyboard::Key input[NumberKeys]; /*!< Input key array */
/*!
* \brief Starts the game
*
* Start a new game or load it from file.
*
* \param fromSaveFile : true if we want to try to load the game from a file
*/
void startNewGame(bool fromSaveFile);
/*!
* \brief Starts a new level
*
* Start a new level.
* Called for each level of the game.
*/
void startNewLevel();
/*!
* \brief Starts the level
*
* Starts the level.
* Called after loading the game or creating a new level.
*/
void playLevel();
/*!
* \brief Creates a level
*
* Creates a random level (a level is a floor that consists of rooms).
*/
void createFloor();
/*!
* \brief Create or refresh the room
*
* Create a room (if this is a new one) or refresh it.
* Called when the player changes room.
* Checks the visibility of the doors and close it if there are monsters.
* Loads the mas items and sprites.
*/
void refreshMap();
/*!
* \brief Refreshes the minimap
*
* Refresh the minimap.
* Called when the player changes room.
*/
void refreshMinimap();
/*!
* \brief Generates a room
*
* Generates a room.
* Called when the player moves to a room for the first time.
*/
void generateMap();
/*!
* \brief Generates a standard room
*
* Generates a standard room with monsters.
* Called during the generation when the map has no particular type.
*/
void generateStandardMap();
/*!
* \brief Checks if the room will be closed
*
* Checks if the room will be closed.
* Called when entering a room. If the room is not clear, closes the doors.
*/
void checkEntering();
/*!
* \brief Saves the map items
*
* Saves the map objects such as items, corpses, blood, chest.
* Called when leaving the door.
*/
void saveMapItems();
/*!
* \brief Initializes the monster array
*
* Initializes the monster array (to empty).
*/
void initMonsterArray();
/*!
* \brief Adds a monster
*
* Adds a monster to the room.
* \param monsterType : monster type
* \param xm : x position of the monster
* \param ym : y position of the monster
*/
void addMonster(monster_type_enum monsterType, float xm, float ym);
/*!
* \brief Checks if player opens a door
*
* Checks if player opens a door (collide with the door and gets the key).
* If positive, opens the door.
*/
void verifyDoorUnlocking();
/*!
* \brief Plays a music
*
* Plays a music.
*
* \param musicChoice : music track ID
*/
void playMusic(musicEnum musicChoice);
/*!
* \brief Add a key to the player input map from a string key (from file)
* \param logicInput : input function (move left, fire up, etc...)
* \param key : Key as string
*/
void addKey(int logicInput, std::string key);
/*!
* \brief Save configuration to "config.dat"
*/
void saveConfigurationToFile();
/*!
* \brief Configure with data from "config.dat"
*/
void configureFromFile();
/*!
* \brief Update the game
*/
void updateRunningGame();
/*!
* \brief Render the game
*/
void renderRunningGame();
/*!
* \brief Update the menu
*/
void updateMenu();
/*!
* \brief Render the menu
*/
void renderMenu();
/** Music enum
* Identify the various music tracks of the game.
*/
enum menuItemEnum
{
MenuStartNew, /**< When starting the game */
MenuStartOld, /**< When restoring the game */
MenuKeys, /**< When configuring keys */
MenuExit /**< When exiting the game */
};
/*!
* \brief Menu item structure
*/
struct menuItemStuct
{
menuItemEnum id; /**< Id of the action */
std::string label; /**< Label of the menu item */
std::string description; /**< Description of the menu item */
};
/*!
* \brief Menu structure
*/
struct menuStuct
{
std::vector<menuItemStuct> items; /**< Menu items */
unsigned int index; /**< Position int the menu */
bool redefineKey; /**< true when configuring input */
unsigned int keyIndex; /**< Position int the key configuration */
float age; /**< Age of the menu */
};
menuStuct menu;
/*!
* \brief Build the menu items
*/
void buildMenu();
/*!
* \brief Switch to the menu
*/
void switchToMenu();
/*!
* \brief Check for falling (in holes) blood or bodies
*/
void checkFallingEntities();
+
+ void resetKilledEnemies();
};
/*!
* \brief Returns the game reference
*
* Returns the game reference.
*
* \return a reference to the game
*/
WitchBlastGame& game();
#endif // WITCH_BLAST_GAME_H

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jun 11, 9:54 AM (3 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
68223
Default Alt Text
(127 KB)

Event Timeline