Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F134480
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
130 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/PlayerEntity.cpp b/src/PlayerEntity.cpp
index ffe60d0..4d91b55 100644
--- a/src/PlayerEntity.cpp
+++ b/src/PlayerEntity.cpp
@@ -1,3196 +1,3211 @@
#include "PlayerEntity.h"
#include "SlimeEntity.h"
#include "SlimePetEntity.h"
#include "FallingRockEntity.h"
#include "BoltEntity.h"
#include "SpiderWebEntity.h"
#include "EvilFlowerEntity.h"
#include "EnemyBoltEntity.h"
#include "ItemEntity.h"
#include "FairyEntity.h"
#include "sfml_game/ImageManager.h"
#include "sfml_game/SoundManager.h"
#include "Constants.h"
#include "WitchBlastGame.h"
#include "TextEntity.h"
#include "TextMapper.h"
#include <iostream>
#include <sstream>
#define SPIRAL_STAIRCASE
const int xHalo[9][3] =
{
{ 13, 13, 18},
{ 47, 45, 37},
{ 48, 47, 44},
{ 15, 15, 16},
{ 45, 45, 45},
{ 46, 44, 42},
{ 13, 13, 13},
{ 13, 24, 36},
{ 13, 17, 13}
};
const int yHalo[9][3] =
{
{ 26, 25, 25},
{ 23, 23, 23},
{ 23, 24, 25},
{ 31, 32, 34},
{ 23, 23, 23},
{ 23, 22, 21},
{ 26, 26, 26},
{ 26, 15, 6},
{ 25, 27, 25}
};
const int KEYS_MOVE_TOLERANCE = 36;
PlayerEntity::PlayerEntity(float x, float y)
: BaseCreatureEntity (ImageManager::getInstance().getImage(IMAGE_PLAYER_0), x, y, 64, 96)
{
currentFireDelay = -1.0f;
randomFireDelay = -1.0f;
invincibleDelay = -1.0f;
divineInterventionDelay = -1.0f;
fireAnimationDelay = -1.0f;
fireAnimationDelayMax = 0.4f;
spellAnimationDelay = -1.0f;
spellAnimationDelayMax = 0.7f;
canFirePlayer = true;
type = ENTITY_PLAYER;
imagesProLine = 8;
playerStatus = playerStatusPlaying;
hp = INITIAL_PLAYER_HP;
#ifdef TEST_MODE
hp = INITIAL_PLAYER_HP * 100;
#endif // TEST_MODE
hpDisplay = hp;
hpMax = hp;
gold = 0;
deathAge = -1.0f;
hiccupDelay = HICCUP_DELAY;
idleAge = 0.0f;
boltLifeTime = INITIAL_BOLT_LIFE;
specialBoltTimer = -1.0f;
bloodColor = BloodRed;
canExplode = false;
// init the equipment (to empty)
for (int i = 0; i < NUMBER_EQUIP_ITEMS; i++) equip[i] = false;
collidingDirection = 0;
// init the shots (to none)
for (int i = 0; i < SPECIAL_SHOT_SLOTS; i++)
{
specialShots[i] = ShotTypeStandard;
specialShotLevel[i] = 0;
}
specialShotIndex = 0;
needInitShotType = false;
computePlayer();
firingDirection = 5;
facingDirection = 2;
keyDirection = 5;
sprite.setOrigin(32, 80);
protection.active = false;
armor = 0.0f;
activeSpell.delay = -1.0f;
activeSpell.spell = SpellNone;
divinity.divinity = -1;
divinity.piety = 0;
divinity.level = 0;
divinity.interventions = 0;
divinity.percentsToNextLevels = 0.0f;
+ shouldBeSavedFromDivinity = false;
isRegeneration = false;
isFairyTransmuted = false;
itemToBuy = NULL;
}
void PlayerEntity::moveTo(float newX, float newY)
{
float dx = newX - x;
float dy = newY - y;
x = newX;
y = newY;
for(int unsigned i = 0; i < fairies.size(); i++)
{
fairies[i]->setX(fairies[i]->getX() + dx);
fairies[i]->setY(fairies[i]->getY() + dy);
}
}
int PlayerEntity::getFacingDirection()
{
return facingDirection;
}
void PlayerEntity::setFacingDirection(int facingDirection)
{
if (facingDirection == 4 || facingDirection == 6 || facingDirection == 2 || facingDirection == 8)
this->facingDirection = facingDirection;
}
float PlayerEntity::getPercentFireDelay()
{
if (canFirePlayer) return 1.0f;
else return (1.0f - currentFireDelay / fireDelay);
}
float PlayerEntity::getLightCone()
{
if (playerStatus == playerStatusPraying)
{
float result = 1.0f;
if (statusTimer < 0.25f)
result = 4 * statusTimer;
else if (statusTimer > WORSHIP_DELAY - 0.25f)
result = (WORSHIP_DELAY - statusTimer) * 4;
return result;
}
else if (divineInterventionDelay > 0.0f)
{
float result = 1.0f;
if (divineInterventionDelay < 0.25f)
result = 4 * divineInterventionDelay;
else if (divineInterventionDelay > WORSHIP_DELAY - 0.25f)
result = (WORSHIP_DELAY - divineInterventionDelay) * 4;
if (isRegeneration) result *= 0.4f;
return result;
}
else return -1.0f;
}
float PlayerEntity::getPercentSpellDelay()
{
if (activeSpell.spell == SpellNone)
return getPercentFireDelay();
else
{
if (activeSpell.delay <= 0.0f) return 1.0f;
else return (1.0f - activeSpell.delay / activeSpell.delayMax);
}
}
bool PlayerEntity::isPoisoned()
{
return (specialState[SpecialStatePoison].active);
}
int PlayerEntity::getCollidingDirection()
{
return collidingDirection;
}
PlayerEntity::playerStatusEnum PlayerEntity::getPlayerStatus()
{
return playerStatus;
}
void PlayerEntity::setPlayerStatus(PlayerEntity::playerStatusEnum playerStatus)
{
this->playerStatus = playerStatus;
}
bool PlayerEntity::isDead()
{
return playerStatus == playerStatusDead;
}
enemyTypeEnum PlayerEntity::getLastHurtingEnemy()
{
return lastHurtingEnemy;
}
sourceTypeEnum PlayerEntity::getLastHurtingSource()
{
return lastHurtingSource;
}
float PlayerEntity::getDeathAge()
{
return deathAge;
}
void PlayerEntity::setDeathAge(float deathAge)
{
this->deathAge = deathAge;
}
bool PlayerEntity::getFairyTransmuted()
{
return isFairyTransmuted;
}
divinityStruct PlayerEntity::getDivinity()
{
return divinity;
}
int PlayerEntity::getPiety()
{
return divinity.piety;
}
void PlayerEntity::stuck()
{
if (playerStatus != playerStatusEntering)
castTeleport();
}
void PlayerEntity::setEntering()
{
playerStatus = playerStatusEntering;
}
void PlayerEntity::setLeavingLevel()
{
playerStatus = playerStatusGoingNext;
}
void PlayerEntity::pay(int price)
{
gold -= price;
displayAcquiredGold(-price);
if (gold < 0) gold = 0;
SoundManager::getInstance().playSound(SOUND_PAY);
}
void PlayerEntity::acquireItemAfterStance()
{
if (acquiredItem >= FirstEquipItem)
{
equip[acquiredItem - FirstEquipItem] = true;
if (acquiredItem != ItemBossKey) game().proceedEvent(EventGetItem);
// familiar
if (items[acquiredItem].familiar != FamiliarNone)
{
setEquiped(acquiredItem - FirstEquipItem, true);
game().proceedEvent(EventGetFamiliar);
}
// shot types
else if (items[acquiredItem].specialShot != (ShotTypeStandard))
{
registerSpecialShot(acquiredItem);
game().proceedEvent(EventGetSpecialShot);
}
// spells
else if (items[acquiredItem].spell != SpellNone)
{
setActiveSpell(items[acquiredItem].spell, false);
game().proceedEvent(EventGetSpell);
}
// pet slime
else if (acquiredItem == ItemPetSlime)
{
new SlimePetEntity();
}
// floor item
else if (acquiredItem == ItemFloorMap)
game().revealFloor();
else if (acquiredItem == ItemAlcohol)
hiccupDelay = HICCUP_DELAY;
// acquirement
if (equip[EQUIP_DISPLACEMENT_GLOVES] && equip[EQUIP_LEATHER_BOOTS] && equip[EQUIP_MAGICIAN_HAT] && equip[EQUIP_MAGICIAN_ROBE])
game().registerAchievement(AchievementCompleteSet);
computePlayer();
}
else
{
if (acquiredItem == ItemBossHeart)
{
int hpBonus = 2 + rand() % 4;
hpMax += hpBonus;
hp += hpBonus;
hpDisplay += hpBonus;
SoundManager::getInstance().playSound(SOUND_EAT);
std::ostringstream oss;
oss << "HP Max +" << hpBonus;
TextEntity* text = new TextEntity(oss.str(), 15, x, y - 50.0f);
text->setColor(TextEntity::COLOR_FADING_GREEN);
text->setAlignment(ALIGN_CENTER);
text->setAge(-1.0f);
text->setLifetime(1.2f);
text->setWeight(-60.0f);
text->setType(ENTITY_FLYING_TEXT);
text->setZ(2000);
}
else if (acquiredItem == ItemBonusHealth)
{
hpMax += 1;
hp = hpMax;
SoundManager::getInstance().playSound(SOUND_EAT);
TextEntity* text = new TextEntity("HP Max + 1", 15, x, y - 50.0f);
text->setColor(TextEntity::COLOR_FADING_GREEN);
text->setAlignment(ALIGN_CENTER);
text->setAge(-1.0f);
text->setLifetime(1.2f);
text->setWeight(-60.0f);
text->setType(ENTITY_FLYING_TEXT);
text->setZ(2000);
}
}
spriteItem->setDying(true);
spriteItemStar->setDying(true);
playerStatus = playerStatusPlaying;
}
void PlayerEntity::resetFloorItem()
{
equip[EQUIP_BOSS_KEY] = false;
equip[EQUIP_FLOOR_MAP] = false;
equip[EQUIP_ALCOHOL] = false;
equip[EQUIP_FAIRY_POWDER] = false;
equip[EQUIP_LUCK] = false;
computePlayer();
}
void PlayerEntity::setItemToBuy(ItemEntity* item)
{
itemToBuy = item;
}
ItemEntity* PlayerEntity::getItemToBuy()
{
return itemToBuy;
}
void PlayerEntity::animate(float delay)
{
// shot timer
if (specialBoltTimer >= 0.0f)
{
specialBoltTimer -= delay;
if (specialBoltTimer <= 0.0f)
{
if (getShotType() == ShotTypeIce) SoundManager::getInstance().playSound(SOUND_ICE_CHARGE);
}
}
if (playerStatus == playerStatusGoingNext)
{
return;
}
if (playerStatus == playerStatusStairs)
{
x += velocity.x * delay;
y += velocity.y * delay;
age += delay;
frame = ((int)(age * 7.0f)) % 4;
if (frame == 3) frame = 1;
if (x < (MAP_WIDTH / 2) * TILE_WIDTH - TILE_WIDTH / 2)
{
facingDirection = 8;
game().moveToOtherMap(8);
}
return;
}
// rate of fire
if (!canFirePlayer)
{
currentFireDelay -= delay;
canFirePlayer = (currentFireDelay <= 0.0f);
}
if (randomFireDelay >= 0.0f) randomFireDelay -= delay;
// spells
if (activeSpell.spell != SpellNone && activeSpell.delay > 0.0f)
{
if (game().getCurrentMap()->isCleared())
activeSpell.delay -= 40 * delay;
else
activeSpell.delay -= delay;
if (activeSpell.spell == SpellProtection && protection.active)
activeSpell.delay = activeSpell.delayMax;
if (activeSpell.delay <= 0.0f) SoundManager::getInstance().playSound(SOUND_SPELL_CHARGE);
}
// protection
if (protection.active)
{
protection.timer -= delay;
if (protection.timer <= 0.0f)
{
protection.active = false;
computePlayer();
}
}
// acquisition animation
if (playerStatus == playerStatusAcquire)
{
statusTimer -= delay;
if (statusTimer <= 0.0f)
{
acquireItemAfterStance();
}
}
else if (equip[EQUIP_ALCOHOL] && playerStatus == playerStatusPlaying)
{
hiccupDelay -= delay;
if (hiccupDelay <= 0.0f)
{
hiccupDelay = 4.0f;
// hiccup
recoil.active = true;
recoil.stun = true;
recoil.velocity = Vector2D(350.0f);
recoil.timer = 0.4f;
for (int i = 0; i < 4; i++)
{
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypePoison, 0);
bolt->setDamages(4);
bolt->setFlying(isFairyTransmuted);
//bolt->setVelocity(recoil.velocity.vectorTo(Vector2D(0, 0), fireVelocity));
bolt->setVelocity(Vector2D(fireVelocity));
}
TextEntity* text = new TextEntity("*hic*", 16, x, y - 30.0f);
text->setColor(TextEntity::COLOR_FADING_GREEN);
text->setAge(-0.6f);
text->setLifetime(0.3f);
text->setWeight(-60.0f);
text->setZ(2000);
text->setAlignment(ALIGN_CENTER);
text->setType(ENTITY_FLYING_TEXT);
SoundManager::getInstance().playSound(SOUND_HICCUP);
SoundManager::getInstance().playSound(SOUND_BLAST_STANDARD);
}
}
if (divineInterventionDelay > 0.0f) divineInterventionDelay -= delay;
if (fireAnimationDelay > 0.0f)
{
fireAnimationDelay -= delay;
if (fireAnimationDelay <= 0.0f) fireAnimationDelay = -1.0f;
}
if (spellAnimationDelay > 0.0f)
{
spellAnimationDelay -= delay;
if (spellAnimationDelay <= 0.0f) spellAnimationDelay = -1.0f;
}
// unlocking animation
else if (playerStatus == playerStatusUnlocking || playerStatus == playerStatusPraying)
{
statusTimer -= delay;
if (statusTimer <= 0.0f)
{
playerStatus = playerStatusPlaying;
}
}
if (playerStatus == playerStatusDead)
{
deathAge += delay;
velocity = Vector2D(0.0f, 0.0f);
}
else
testSpriteCollisions();
// key room collision
if (game().getCurrentMap()->getRoomType() == roomTypeKey && !game().getCurrentMap()->isCleared())
{
sf::IntRect col1;
col1.width = 198;
col1.height = 68;
col1.top = 254;
col1.left = 380;
sf::IntRect col2;
col2.width = 68;
col2.height = 198;
col2.top = 189;
col2.left = 445;
if (boundingBox.intersects(col1) || boundingBox.intersects(col2))
{
recoil.active = true;
recoil.stun = true;
recoil.velocity = Vector2D(GAME_WIDTH / 2, GAME_HEIGHT /2).vectorTo(Vector2D(x, y), 650.0f);
recoil.timer = 0.4f;
game().activateKeyRoomEffect(true);
}
}
collidingDirection = 0;
BaseCreatureEntity::animate(delay);
if (firingDirection != 5)
facingDirection = firingDirection;
// find the frame
if (firingDirection != 5)
{
if (fireAnimationDelay < 0.0f)
fireAnimationDelay = fireAnimationDelayMax;
fireAnimationDirection = firingDirection;
}
else if (isMoving())
{
frame = ((int)(age * 7.0f)) % 4;
if (frame == 3) frame = 1;
}
else if (playerStatus == playerStatusAcquire || playerStatus == playerStatusUnlocking || playerStatus == playerStatusPraying)
frame = 3;
else if (playerStatus == playerStatusDead)
frame = 0;
else // standing
{
frame = 1;
}
if (playerStatus != playerStatusPlaying || isMoving() || firingDirection != 5)
idleAge = 0.0f;
else
idleAge += delay;
if (x < 0)
game().moveToOtherMap(4);
else if (x > MAP_WIDTH * TILE_WIDTH)
game().moveToOtherMap(6);
else if (y < 0)
game().moveToOtherMap(8);
else if (y > MAP_HEIGHT * TILE_HEIGHT)
game().moveToOtherMap(2);
#ifdef SPIRAL_STAIRCASE
else if (playerStatus == playerStatusPlaying
&& game().getCurrentMap()->getRoomType() == roomTypeExit && y < TILE_HEIGHT * 0.6f)
{
playerStatus = playerStatusStairs;
velocity.y = creatureSpeed / 12;
velocity.x = -creatureSpeed / 3;
facingDirection = 4;
SpriteEntity* exitDoorEntity = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_TILES),
(MAP_WIDTH / 2) * TILE_WIDTH - TILE_WIDTH / 2,
TILE_HEIGHT / 2, 64, 64, 1);
exitDoorEntity->setZ(TILE_HEIGHT);
exitDoorEntity->setImagesProLine(24);
exitDoorEntity->setFrame(MAP_WALL_BEGIN + 21 + 24 * game().getCurrentMap()->getWallType());
exitDoorEntity->setType(ENTITY_EFFECT);
SpriteEntity* exitDoorAroundEntity = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_DOORS),
(MAP_WIDTH / 2) * TILE_WIDTH - TILE_WIDTH / 2,
TILE_HEIGHT / 2, 64, 64, 1);
exitDoorAroundEntity->setZ(TILE_HEIGHT + 1);
exitDoorAroundEntity->setImagesProLine(2);
exitDoorAroundEntity->setFrame(3 + 6 * DoorExit);
exitDoorAroundEntity->setType(ENTITY_EFFECT);
SpriteEntity* exitDoorEntityShadow = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_TILES_SHADOW),
(MAP_WIDTH / 2) * TILE_WIDTH - TILE_WIDTH / 2,
TILE_HEIGHT / 2, 64, 64, 1);
exitDoorEntityShadow->setZ(TILE_HEIGHT + 2);
exitDoorEntityShadow->setImagesProLine(10);
exitDoorEntityShadow->setFrame(4);
exitDoorEntityShadow->setType(ENTITY_EFFECT);
}
#endif
if (playerStatus == playerStatusEntering)
{
if (boundingBox.left > TILE_WIDTH
&& (boundingBox.left + boundingBox.width) < TILE_WIDTH * (MAP_WIDTH - 1)
&& boundingBox.top > TILE_HEIGHT
&& (boundingBox.top + boundingBox.height) < TILE_HEIGHT * (MAP_HEIGHT - 1))
{
playerStatus = playerStatusPlaying;
game().closeDoors();
}
else
{
if (x < 2 * TILE_WIDTH)
{
velocity.x = creatureSpeed;
velocity.y = 0.0f;
}
else if (x > (MAP_WIDTH - 3) * TILE_WIDTH)
{
velocity.x = -creatureSpeed;
velocity.y = 0.0f;
}
else if (y < 2 * TILE_HEIGHT)
{
velocity.y = creatureSpeed;
velocity.x = 0.0f;
}
else if (y > (MAP_HEIGHT - 3) * TILE_HEIGHT)
{
velocity.y = -creatureSpeed;
velocity.x = 0.0f;
}
}
}
if (playerStatus != playerStatusDead)
{
if (invincibleDelay >= 0.0f) invincibleDelay -= delay;
if (specialState[SpecialStateConfused].active)
SoundManager::getInstance().playSound(SOUND_VAMPIRE_HYPNOSIS, false);
}
z = y + 4;
}
bool PlayerEntity::canCollide()
{
return invincibleDelay <= 0.0f;
}
void PlayerEntity::setSpecialState(enumSpecialState state, bool active, float timer, float param1, float param2)
{
BaseCreatureEntity::setSpecialState(state, active, timer, param1, param2);
computePlayer();
}
void PlayerEntity::renderPlayer(sf::RenderTarget* app)
{
sf::Color savedColor = sprite.getColor();
if (isPoisoned()) sprite.setColor(sf::Color(180, 255, 180, 255));
// body
if (isMirroring)
sprite.setTextureRect(sf::IntRect( frame * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( frame * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setColor(savedColor);
// boots
if (equip[EQUIP_BOOTS_ADVANCED] && playerStatus != playerStatusDead)
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (21 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (21 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_LEATHER_BOOTS] && playerStatus != playerStatusDead)
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (9 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (9 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_ROBE_ADVANCED] && playerStatus != playerStatusDead)
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (12 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (12 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (15 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (15 + frame) * width, spriteDy * height, width, height));
sprite.setColor(sf::Color(255, 255, 255, 100 + 100 * cosf(game().getAbsolutTime() * 3.5f)));
app->draw(sprite, sf::BlendAdd);
sprite.setColor(sf::Color(255, 255, 255, 255));
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_MAGICIAN_ROBE])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (12 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (12 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_GLOVES_ADVANCED] && playerStatus != playerStatusDead)
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (24 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (24 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_DISPLACEMENT_GLOVES])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (21 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (21 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_CRITICAL_ADVANCED])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (24 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (24 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
else if (equip[EQUIP_CRITICAL])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (18 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (18 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
if (equip[EQUIP_RAGE_AMULET])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (18 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (18 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_LEATHER_BELT])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (15 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (15 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
// staff
int frameDx = equip[EQUIP_MAHOGANY_STAFF] ? 6 : 3;
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (frameDx + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (frameDx + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
if (equip[EQUIP_BLOOD_SNAKE])
{
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (27 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (27 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
}
if (equip[EQUIP_REAR_SHOT])
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
// shot type
if (getShotType() != ShotTypeStandard)
{
switch (getShotType())
{
case ShotTypeIce:
sprite.setColor(sf::Color(100, 220, 255, 255));
break;
case ShotTypeStone:
sprite.setColor(sf::Color(120, 120, 150, 255));
break;
case ShotTypeLightning:
sprite.setColor(sf::Color(255, 255, 0, 255));
break;
case ShotTypeIllusion:
sprite.setColor(sf::Color(240, 180, 250, 255));
break;
case ShotTypeStandard:
sprite.setColor(sf::Color(255, 255, 255, 0));
break;
case ShotTypeFire:
sprite.setColor(sf::Color(255, 180, 0, 255));
break;
case ShotTypePoison:
sprite.setColor(sf::Color(50, 255, 50, 255));
break;
default:
std::cout << "[WARNING] Can not render shot type: " << getShotType() << std::endl;
}
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (3 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (3 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setColor(savedColor);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
// hat
if (equip[EQUIP_HAT_ADVANCED] && playerStatus != playerStatusDead)
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (9 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (9 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
else if (equip[EQUIP_MAGICIAN_HAT] && playerStatus != playerStatusDead)
{
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_1));
if (isMirroring)
sprite.setTextureRect(sf::IntRect( (6 + frame) * width + width, spriteDy * height, -width, height));
else
sprite.setTextureRect(sf::IntRect( (6 + frame) * width, spriteDy * height, width, height));
app->draw(sprite);
sprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_PLAYER_0));
}
}
void PlayerEntity::renderHalo(sf::RenderTarget* app)
{
if (frame > 2 || spriteDy > 8) return;
// gems
if ((getShotType() == ShotTypeIce || getShotType() == ShotTypeLightning || getShotType() == ShotTypeFire || getShotType() == ShotTypePoison) && playerStatus != playerStatusDead)
{
int fade;
sf::Color savedColor = sprite.getColor();
if (getShotType() != ShotTypeIce || specialBoltTimer <= 0.0f) fade = 255;
else fade = ((STATUS_FROZEN_BOLT_DELAY[getShotLevel()] - specialBoltTimer) / STATUS_FROZEN_BOLT_DELAY[getShotLevel()]) * 128;
if (getShotType() == ShotTypeLightning)
fade = 150 + rand() % 105;
if (getShotType() == ShotTypeFire)
fade = 200 + rand() % 40;
if (getShotType() == ShotTypePoison)
fade = 150 + rand() % 40;
if (getShotType() == ShotTypeIce)
sprite.setTextureRect(sf::IntRect(448, 864, 20, 20));
else if (getShotType() == ShotTypeLightning)
sprite.setTextureRect(sf::IntRect(448 + 20, 864, 20, 20));
else if (getShotType() == ShotTypeFire)
sprite.setTextureRect(sf::IntRect(448 + 40, 864, 20, 20));
else if (getShotType() == ShotTypePoison)
sprite.setTextureRect(sf::IntRect(448, 864 + 20, 20, 20));
sprite.setColor(sf::Color(255, 255, 255, fade));
if (isMirroring)
sprite.setPosition(x - 10 + 64 - xHalo[spriteDy][frame], y - 10 + yHalo[spriteDy][frame]);
else
sprite.setPosition(x - 10 + xHalo[spriteDy][frame], y - 10 + yHalo[spriteDy][frame]);
sf::RenderStates r;
r.blendMode = sf::BlendAdd;
app->draw(sprite, r);
sprite.setPosition(x, y);
sprite.setColor(savedColor);
}
}
void PlayerEntity::render(sf::RenderTarget* app)
{
sprite.setPosition(x, y);
spriteDy = 0;
isMirroring = false;
if (idleAge > 8.5f)
{
idleAge -= 8.5f;
}
else if (idleAge >= 7.5)
{
spriteDy = 8;
frame = 2;
}
else if (idleAge >= 7.0)
{
spriteDy = 8;
frame = 1;
}
else if (idleAge >= 6.0)
{
spriteDy = 8;
frame = 0;
}
else if (idleAge >= 5.5f && facingDirection != 2)
{
facingDirection = 2;
idleAge -= 2.0f;
}
else if (fireAnimationDelay <= 0.0f && spellAnimationDelay <= 0.0f)
{
if (facingDirection == 6) spriteDy = 1;
else if (facingDirection == 8) spriteDy = 2;
else if (facingDirection == 4)
{
spriteDy = 1;
isMirroring = true;
}
}
else if (spellAnimationDelay >= 0.0f)
{
spriteDy = 7;
if (spellAnimationDelay < spellAnimationDelayMax * 0.1f) frame = 0;
else if (spellAnimationDelay < spellAnimationDelayMax * 0.2f) frame = 1;
else if (spellAnimationDelay < spellAnimationDelayMax * 0.7f) frame = 2;
else if (spellAnimationDelay < spellAnimationDelayMax * 0.85f) frame = 1;
else frame = 0;
}
else
{
if (fireAnimationDirection == 2) spriteDy = 3;
else if (fireAnimationDirection == 6) spriteDy = 4;
else if (fireAnimationDirection == 8) spriteDy = 5;
else if (fireAnimationDirection == 4)
{
spriteDy = 4;
isMirroring = true;
}
if (fireAnimationDelay < fireAnimationDelayMax * 0.2f) frame = 0;
else if (fireAnimationDelay < fireAnimationDelayMax * 0.4f) frame = 1;
else if (fireAnimationDelay < fireAnimationDelayMax * 0.6f) frame = 2;
else if (fireAnimationDelay < fireAnimationDelayMax * 0.8f) frame = 1;
else frame = 0;
}
if (playerStatus == playerStatusAcquire || playerStatus == playerStatusUnlocking)
{
spriteDy = 6;
frame = ((int)(age * 10.0f)) % 4;
if (frame == 3) frame = 1;
}
else if (playerStatus == playerStatusPraying)
{
spriteDy = 7;
frame = ((int)(age * 10.0f)) % 4;
if (frame == 3) frame = 1;
float delay = playerStatus == playerStatusPraying ? WORSHIP_DELAY : UNLOCK_DELAY;
if (statusTimer < delay * 0.1f) frame = 0;
else if (statusTimer < delay * 0.2f) frame = 1;
else if (statusTimer < delay * 0.7f) frame = 2;
else if (statusTimer < delay * 0.85f) frame = 1;
else frame = 0;
}
if (playerStatus == playerStatusDead)
{
frame = (int)(deathAge / 0.35f);
if (frame > 6) frame = 6;
spriteDy = 9;
sprite.setTextureRect(sf::IntRect( frame * width, spriteDy * height, width, height));
app->draw(sprite);
}
else
{
if (isFairyTransmuted)
{
frame = 0;
if (velocity.x * velocity.x + velocity.y * velocity.y > 400)
frame = ((int)(age * 24.0f)) % 2;
else
frame = ((int)(age * 18.0f)) % 2;
sf::Sprite fairySprite;
fairySprite.setColor(sprite.getColor());
fairySprite.setTexture(*ImageManager::getInstance().getImage(IMAGE_FAIRY));
fairySprite.setPosition(sprite.getPosition());
fairySprite.setOrigin(26, 62);
switch (facingDirection)
{
case 8: fairySprite.setTextureRect(sf::IntRect( (2 + frame) * 48, 5 * 72, 48, 72)); break;
case 4: fairySprite.setTextureRect(sf::IntRect( (4 + frame) * 48, 5 * 72, 48, 72)); break;
case 6: fairySprite.setTextureRect(sf::IntRect( (5 + frame) * 48, 5 * 72, - 48, 72)); break;
default: fairySprite.setTextureRect(sf::IntRect( frame * 48, 5 * 72, 48, 72)); break;
}
app->draw(fairySprite);
}
else
{
renderHalo(app);
renderPlayer(app);
}
// shield
if (playerStatus != playerStatusStairs && (specialState[DivineStateProtection].active || protection.active))
{
int firstFrame = 8;
if (specialState[DivineStateProtection].active && divinity.divinity == DivinityStone) firstFrame = 16;
float timer = specialState[DivineStateProtection].active ? specialState[DivineStateProtection].timer : protection.timer;
sprite.setTextureRect(sf::IntRect( firstFrame * width, 9 * height, width, height));
app->draw(sprite);
sf::Color savedColor = sprite.getColor();
sprite.setColor(sf::Color(255, 255, 255, 100 + cos(age * (timer < 2.0f ? 25 : 10)) * 30 ));
sprite.setTextureRect(sf::IntRect( (firstFrame + 1) * width, 9 * height, width, height));
app->draw(sprite);
sprite.setColor(savedColor);
}
// divine field
if (divinity.level > 1)
{
bool displayField = false;
int fieldFrame;
int fieldFade;
switch (divinity.divinity)
{
case DivinityHealer:
{
displayField = true;
fieldFrame = 10;
fieldFade = 40 * (divinity.level - 1);
break;
}
case DivinityFighter:
{
displayField = true;
fieldFrame = 12;
fieldFade = 40 * (divinity.level - 1);
break;
}
case DivinityIce:
{
if (divinity.level > 2)
{
displayField = true;
fieldFrame = 14;
fieldFade = 80 * (divinity.level - 3);
}
break;
}
case DivinityStone:
{
if (divinity.level > 2)
{
displayField = true;
fieldFrame = 16;
fieldFade = 80 * (divinity.level - 3);
}
break;
}
case DivinityAir:
{
if (divinity.level > 1)
{
displayField = true;
fieldFrame = 18;
fieldFade = 40 * (divinity.level - 1);
}
break;
}
}
if (displayField)
{
sf::Color savedColor = sprite.getColor();
sprite.setColor(sf::Color(255, 255, 255, fieldFade ));
sprite.setTextureRect(sf::IntRect( fieldFrame * width, 9 * height, width, height));
app->draw(sprite);
if (divinity.divinity != DivinityStone && divinity.divinity != DivinityHealer)
{
sprite.setColor(sf::Color(255, 255, 255, 2 + fieldFade / 2 + cos(age * 15) * fieldFade / 2 ));
sprite.setTextureRect(sf::IntRect( (fieldFrame + 1) * width, 9 * height, width, height));
app->draw(sprite);
sprite.setColor(savedColor);
}
if (divinity.divinity == DivinityHealer && divineInterventionDelay > 0.0f && isRegeneration)
{
sprite.setTextureRect(sf::IntRect( (fieldFrame + 1) * width, 9 * height, width, height));
app->draw(sprite);
}
sprite.setColor(savedColor);
}
}
}
if (game().getShowLogical() && playerStatus != playerStatusDead)
{
displayBoundingBox(app);
displayCenterAndZ(app);
}
}
void PlayerEntity::calculateBB()
{
if (isFairyTransmuted)
{
boundingBox.left = (int)x - 10;
boundingBox.width = 20;
boundingBox.top = (int)y - 29;
boundingBox.height = 20;
}
else
{
boundingBox.left = (int)x - 10;
boundingBox.width = 20;
boundingBox.top = (int)y - 29;
boundingBox.height = 33;
}
}
void PlayerEntity::readCollidingEntity(CollidingSpriteEntity* entity)
{
if (playerStatus == playerStatusDead || !canCollide()) return;
EnemyBoltEntity* boltEntity = dynamic_cast<EnemyBoltEntity*>(entity);
if (collideWithEntity(entity))
{
if (boltEntity != NULL && !boltEntity->getDying())
{
boltEntity->collide();
// TODO bolt source
hurt(getHurtParams(boltEntity->getDamages(),
boltEntity->getBoltType(),
boltEntity->getLevel(),
boltEntity->isCritical(),
SourceTypeBolt,
boltEntity->getEnemyType(),
false));
game().generateBlood(x, y, bloodColor);
float xs = (x + boltEntity->getX()) / 2;
float ys = (y + boltEntity->getY()) / 2;
SpriteEntity* star = new SpriteEntity(ImageManager::getInstance().getImage(IMAGE_HURT_IMPACT), xs, ys);
star->setFading(true);
star->setZ(y+ 100);
star->setLifetime(0.7f);
star->setType(ENTITY_EFFECT);
star->setSpin(400.0f);
}
}
}
bool PlayerEntity::willCollideWithMap(int dx, int dy, bool checkMiddle)
{
float oldX = x, oldY = y;
bool collide = true;
x = oldX + dx;
y = oldY + dy;
if (!isCollidingWithMap())
collide = false;
else if (checkMiddle)
{
x = oldX + dx / 2;
y = oldY + dy / 2;
if (!isCollidingWithMap())
collide = false;
}
x = oldX;
y = oldY;
return collide;
}
void PlayerEntity::move(int direction)
{
float oldX = x, oldY = y;
bool touchUp, touchDown, touchLeft, touchRight;
x = oldX + 1;
touchRight = isCollidingWithMap();
x = oldX - 1;
touchLeft = isCollidingWithMap();
x = oldX;
y = oldY + 1;
touchDown = isCollidingWithMap();
y = oldY - 1;
touchUp = isCollidingWithMap();
y = oldY;
if (specialState[SpecialStateConfused].active)
{
switch (direction)
{
case 4: direction = 6; break;
case 6: direction = 4; break;
case 2: direction = 8; break;
case 8: direction = 2; break;
case 1: direction = 9; break;
case 9: direction = 1; break;
case 7: direction = 3; break;
case 3: direction = 7; break;
}
}
keyDirection = direction;
if (playerStatus == playerStatusAcquire && statusTimer < ACQUIRE_DELAY / 2)
{
acquireItemAfterStance();
}
if (playerStatus == playerStatusPlaying)
{
switch (keyDirection)
{
case 1:
if (touchDown && touchLeft)
{
direction = 5;
collidingDirection = 1;
}
else if (touchDown)
{
direction = 4;
collidingDirection = 2;
}
else if (touchLeft)
{
direction = 2;
collidingDirection = 4;
}
break;
case 3:
if (touchDown && touchRight)
{
direction = 5;
collidingDirection = 3;
}
else if (touchDown)
{
direction = 6;
collidingDirection = 2;
}
else if (touchRight)
{
direction = 2;
collidingDirection = 6;
}
break;
case 7:
if (touchUp && touchLeft)
{
direction = 5;
collidingDirection = 7;
}
else if (touchUp)
{
direction = 4;
collidingDirection = 8;
}
- else if (touchRight)
+ else if (touchLeft)
{
direction = 8;
collidingDirection = 1;
}
break;
case 9:
if (touchUp && touchRight)
{
direction = 5;
collidingDirection = 9;
}
else if (touchUp)
{
direction = 6;
collidingDirection = 8;
}
else if (touchRight)
{
direction = 8;
collidingDirection = 6;
}
break;
case 4:
if (touchLeft)
{
x = oldX - 2;
if (!willCollideWithMap(0, KEYS_MOVE_TOLERANCE, true))
direction = 2;
else if (!willCollideWithMap(0, -KEYS_MOVE_TOLERANCE, true))
direction = 8;
else
{
direction = 5;
collidingDirection = 4;
}
x = oldX;
}
break;
case 6:
if (touchRight)
{
x = oldX + 2;
if (!willCollideWithMap(0, KEYS_MOVE_TOLERANCE, true))
direction = 2;
else if (!willCollideWithMap(0, -KEYS_MOVE_TOLERANCE, true))
direction = 8;
else
{
direction = 5;
collidingDirection = 6;
}
x = oldX;
}
break;
case 8:
if (touchUp)
{
y = oldY - 2;
if (!willCollideWithMap(KEYS_MOVE_TOLERANCE, 0, true))
direction = 6;
else if (!willCollideWithMap(-KEYS_MOVE_TOLERANCE, 0, true))
direction = 4;
else
{
direction = 5;
collidingDirection = 8;
}
y = oldY;
}
break;
case 2:
if (touchDown)
{
y = oldY + 2;
if (!willCollideWithMap(KEYS_MOVE_TOLERANCE, 0, true))
direction = 6;
else if (!willCollideWithMap(-KEYS_MOVE_TOLERANCE, 0, true))
direction = 4;
else
{
direction = 5;
collidingDirection = 2;
}
y = oldY;
}
break;
}
float speedx = 0.0f, speedy = 0.0f;
if (direction == 4)
speedx = - creatureSpeed;
else if (direction == 1 || direction == 7)
speedx = - creatureSpeed * 0.7f;
else if (direction == 6)
speedx = creatureSpeed;
else if (direction == 3 || direction == 9)
speedx = creatureSpeed * 0.7f;
if (direction == 2)
speedy = creatureSpeed;
else if (direction == 1 || direction == 3)
speedy = creatureSpeed * 0.7f;
else if (direction == 8)
speedy = - creatureSpeed;
else if (direction == 7 || direction == 9)
speedy = - creatureSpeed * 0.7f;
setVelocity(Vector2D(speedx, speedy));
{
switch (keyDirection)
{
case 8:
facingDirection = 8;
break;
case 2:
facingDirection = 2;
break;
case 4:
facingDirection = 4;
break;
case 6:
facingDirection = 6;
break;
case 7:
if (facingDirection != 4 && facingDirection != 8) facingDirection = 4;
break;
case 1:
if (facingDirection != 4 && facingDirection != 2) facingDirection = 4;
break;
case 9:
if (facingDirection != 6 && facingDirection != 8) facingDirection = 6;
break;
case 3:
if (facingDirection != 6 && facingDirection != 2) facingDirection = 6;
break;
}
}
}
+
+ if (collidingDirection != 5)
+ {
+ game().verifyDoorUnlocking();
+ }
}
bool PlayerEntity::isMoving()
{
if (velocity.x < -1.0f || velocity.x > 1.0f) return true;
if (velocity.y < -1.0f || velocity.y > 1.0f) return true;
return false;
}
bool PlayerEntity::isEquiped(int eq)
{
return equip[eq];
}
bool* PlayerEntity::getEquipment()
{
return equip;
}
void PlayerEntity::setEquiped(int item, bool toggleEquipped)
{
equip[item] = toggleEquipped;
if (toggleEquipped && items[FirstEquipItem + item].familiar > FamiliarNone)
{
FairyEntity* fairy = new FairyEntity(x - 50.0f + rand() % 100,
y - 50.0f + rand() % 100,
items[FirstEquipItem + item].familiar);
fairies.push_back(fairy);
//if (fairies.size() == 3) game().registerAchievement(AchievementFairies);
}
computePlayer();
}
void PlayerEntity::generateBolt(float velx, float vely)
{
enumShotType boltType = ShotTypeStandard;
unsigned int shotLevel = 1;
switch (getShotType())
{
case ShotTypeIce:
if (getShotType() == ShotTypeIce)
{
if (specialBoltTimer <= 0.0f)
{
boltType = ShotTypeIce;
shotLevel = getShotLevel();
needInitShotType = true;
}
else boltType = ShotTypeCold;
}
break;
case ShotTypeStandard:
case ShotTypeIllusion:
case ShotTypeStone:
case ShotTypeLightning:
case ShotTypeFire:
case ShotTypePoison:
boltType = getShotType();
shotLevel = getShotLevel();
break;
default:
std::cout << "[WARNING] Can not generate shot type: " << getShotType() << std::endl;
}
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, boltType, shotLevel);
bolt->setFlying(isFairyTransmuted);
int boltDamage = fireDamages;
if (criticalChance > 0)
if (rand()% 100 < criticalChance)
{
boltDamage *= equip[EQUIP_CRITICAL_ADVANCED] ? 3 : 2;
bolt->setCritical(true);
}
bolt->setDamages(boltDamage);
if (equip[EQUIP_GLOVES_ADVANCED])
{
if (firingDirection == 2 || firingDirection == 8)
velx += velocity.x * 0.7f;
else if (firingDirection == 4 || firingDirection == 6)
vely += velocity.y * 0.7f;
}
bolt->setVelocity(Vector2D(velx, vely));
}
void PlayerEntity::rageFire()
{
for (int i = -1; i <= 1; i += 2)
for (int j = -1; j <= 1; j += 2)
{
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeFire, 0);
bolt->setDamages(10);
bolt->setFlying(isFairyTransmuted);
float velx = fireVelocity * i * 0.42f;
float vely = fireVelocity * j * 0.42f;
bolt->setVelocity(Vector2D(velx, vely));
if (hp <= hpMax / 5)
{
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeFire, 0);
bolt->setDamages(10);
bolt->setFlying(isFairyTransmuted);
float velx = 0.0f;
float vely = 0.0f;
if (i == -1 && j == -1) velx = -fireVelocity * i * 0.6f;
else if (i == -1 && j == 1) velx = fireVelocity * i * 0.6f;
else if (i == 1 && j == -1) vely= -fireVelocity * i * 0.6f;
else if (i == 1 && j == 1) vely = fireVelocity * i * 0.6f;
bolt->setVelocity(Vector2D(velx, vely));
}
}
SoundManager::getInstance().playSound(SOUND_BLAST_FIRE);
}
void PlayerEntity::resestFireDirection()
{
firingDirection = 5;
}
int PlayerEntity::getFireDirection()
{
return firingDirection;
}
void PlayerEntity::fire(int direction)
{
firingDirection = direction;
if (playerStatus != playerStatusDead)
for(int unsigned i = 0; i < fairies.size(); i++)
fairies[i]->fire(direction);
if (canFirePlayer && playerStatus != playerStatusDead && playerStatus != playerStatusAcquire)
{
switch (getShotType())
{
case ShotTypeCold:
case ShotTypeIce:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_ICE);
break;
case ShotTypeFire:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_FIRE);
break;
case ShotTypeIllusion:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_ILLUSION);
break;
case ShotTypeLightning:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_LIGHTNING);
break;
case ShotTypePoison:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_POISON);
break;
case ShotTypeStone:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_STONE);
break;
default:
SoundManager::getInstance().playPitchModSound(SOUND_BLAST_STANDARD);
break;
}
if (equip[EQUIP_BOOK_DUAL] || equip[EQUIP_BOOK_TRIPLE]
|| equip[EQUIP_BOOK_DUAL_QUICK] || equip[EQUIP_BOOK_TRIPLE_QUICK])
{
float shoot_angle = 0.2f;
if ((direction == 4 && velocity.x < -1.0f) || (direction == 6 && velocity.x > 1.0f)
|| (direction == 8 && velocity.y < -1.0f) || (direction == 2 && velocity.y > 1.0f))
shoot_angle = 0.1f;
else if ((direction == 6 && velocity.x < -1.0f) || (direction == 4 && velocity.x > 1.0f)
|| (direction == 2 && velocity.y < -1.0f) || (direction == 8 && velocity.y > 1.0f))
shoot_angle = (equip[EQUIP_BOOK_TRIPLE] || equip[EQUIP_BOOK_TRIPLE_QUICK]) ? 0.35f : 0.2f;
else if (!equip[EQUIP_BOOK_TRIPLE] && !equip[EQUIP_BOOK_TRIPLE_QUICK])
shoot_angle = 0.1f;
switch(direction)
{
case 4:
generateBolt(-fireVelocity * cos(shoot_angle), fireVelocity * sin(shoot_angle));
generateBolt(-fireVelocity * cos(shoot_angle), - fireVelocity * sin(shoot_angle));
break;
case 6:
generateBolt(fireVelocity * cos(shoot_angle), fireVelocity * sin(shoot_angle));
generateBolt(fireVelocity * cos(shoot_angle), - fireVelocity * sin(shoot_angle));
break;
case 8:
generateBolt(fireVelocity * sin(shoot_angle), -fireVelocity * cos(shoot_angle));
generateBolt(-fireVelocity * sin(shoot_angle), - fireVelocity * cos(shoot_angle));
break;
case 2:
generateBolt(fireVelocity * sin(shoot_angle), fireVelocity * cos(shoot_angle));
generateBolt(-fireVelocity * sin(shoot_angle), fireVelocity * cos(shoot_angle));
break;
}
}
if (equip[EQUIP_RAPID_SHOT])
{
Vector2D boltDirection;
switch(direction)
{
case 4:
boltDirection = Vector2D(0, 0).vectorNearlyTo(Vector2D(-1, 0), fireVelocity, 0.2f);
break;
case 6:
boltDirection = Vector2D(0, 0).vectorNearlyTo(Vector2D(1, 0), fireVelocity, 0.2f);
break;
case 8:
boltDirection = Vector2D(0, 0).vectorNearlyTo(Vector2D(0, -1), fireVelocity, 0.2f);
break;
case 2:
boltDirection = Vector2D(0, 0).vectorNearlyTo(Vector2D(0, 1), fireVelocity, 0.2f);
break;
}
generateBolt(boltDirection.x, boltDirection.y);
}
else if (!(equip[EQUIP_BOOK_DUAL] || equip[EQUIP_BOOK_DUAL_QUICK]) || (equip[EQUIP_BOOK_TRIPLE] || equip[EQUIP_BOOK_TRIPLE_QUICK]))
{
switch(direction)
{
case 4:
generateBolt(-fireVelocity, 0.0f);
break;
case 6:
generateBolt(fireVelocity, 0.0f);
break;
case 8:
generateBolt(0.0f, -fireVelocity);
break;
case 2:
generateBolt(0.0f, fireVelocity);
break;
}
}
if (equip[EQUIP_REAR_SHOT])
{
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeStandard, 0);
bolt->setDamages(fireDamages / 2);
bolt->setFlying(isFairyTransmuted);
float velx = 0.0f;
float vely = 0.0f;
switch (direction)
{
case 4:
velx = fireVelocity * 0.75f;
break;
case 6:
velx = -fireVelocity * 0.75f;
break;
case 2:
vely = -fireVelocity * 0.75f;
break;
case 8:
vely = fireVelocity * 0.75f;
break;
}
bolt->setVelocity(Vector2D(velx, vely));
}
if (equip[EQUIP_BOOK_RANDOM] && randomFireDelay <= 0.0f)
{
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeStandard, 0);
bolt->setDamages(fireDamages);
bolt->setFlying(isFairyTransmuted);
float shotAngle = rand() % 360;
bolt->setVelocity(Vector2D(fireVelocity * 0.75f * cos(shotAngle), fireVelocity * 0.75f * sin(shotAngle)));
randomFireDelay = fireDelay * 1.5f;
}
canFirePlayer = false;
currentFireDelay = fireDelay;
if (needInitShotType) initShotType();
}
}
bool PlayerEntity::canMove()
{
return (playerStatus == playerStatusPlaying
|| (playerStatus == playerStatusAcquire && statusTimer < ACQUIRE_DELAY / 2));
}
int PlayerEntity::hurt(StructHurt hurtParam)
{
if (playerStatus == playerStatusDead) return false;
if (isFairyTransmuted && hurtParam.hurtingType != ShotTypeDeterministic)
{
hurtParam.damage *= 2;
}
+ shouldBeSavedFromDivinity = false;
bool divinityInvoked = false;
if (hp - hurtParam.damage <= hpMax / 4 && divinity.divinity >= 0)
{
divinityInvoked = triggerDivinityBefore();
if (divinityInvoked)
+ {
game().testAndAddMessageToQueue((EnumMessages)(MsgInfoDivIntervention));
+ shouldBeSavedFromDivinity = true;
+ }
}
if (invincibleDelay <= 0.0f || hurtParam.hurtingType == ShotTypeDeterministic)
{
SoundManager::getInstance().playSound(SOUND_PLAYER_HIT);
int oldHp = hp;
if (BaseCreatureEntity::hurt(hurtParam) > 0)
{
if (hurtParam.hurtingType != ShotTypeDeterministic)
{
invincibleDelay = INVINCIBLE_DELAY;
if (equip[EQUIP_RAGE_AMULET]) rageFire();
game().generateBlood(x, y, bloodColor);
}
hurtingDelay = HURTING_DELAY * 2.0f;
game().generateBlood(x, y, bloodColor);
game().proceedEvent(EventBeingHurted);
lastHurtingEnemy = hurtParam.enemyType;
lastHurtingSource = hurtParam.sourceType;
// divinity
offerHealth(oldHp - hp);
if (!divinityInvoked && hp <= hpMax / 4 && divinity.divinity >= 0)
{
triggerDivinityAfter();
}
return true;
}
}
return false;
}
void PlayerEntity::setMap(GameMap* map, int tileWidth, int tileHeight, int offsetX, int offsetY)
{
CollidingSpriteEntity::setMap(map, tileWidth, tileHeight, offsetX, offsetY);
//if (slimePet != NULL) slimePet->setMap(map, tileWidth, tileHeight, offsetX, offsetY);
}
void PlayerEntity::loseItem(enumItemType itemType, bool isEquip)
{
CollidingSpriteEntity* itemSprite
= new CollidingSpriteEntity(ImageManager::getInstance().getImage(isEquip ? IMAGE_ITEMS_EQUIP : IMAGE_ITEMS), x, y, 32, 32);
itemSprite->setMap(map, TILE_WIDTH, TILE_HEIGHT, 0, 0);
itemSprite->setZ(-1);
itemSprite->setFrame(itemType);
itemSprite->setImagesProLine(10);
itemSprite->setType(ENTITY_BLOOD);
itemSprite->setVelocity(Vector2D(200 + rand()%450));
itemSprite->setViscosity(0.95f);
itemSprite->setSpin( (rand() % 700) - 350.0f);
}
void PlayerEntity::dying()
{
- if (divinity.divinity > -1 && divinity.interventions < divinity.level - 1)
+ if (shouldBeSavedFromDivinity)
+ {
+ hp = 33 * hpMax / 100;
+ return;
+ }
+ else if (divinity.divinity > -1 && divinity.interventions < divinity.level - 1)
{
hp = 1;
return;
}
playerStatus = playerStatusDead;
deathAge = 0.0f;
hp = 0;
SoundManager::getInstance().playSound(SOUND_PLAYER_DIE);
setVelocity(Vector2D(0.0f, 0.0f));
int i;
for (i = 0; i < gold && i < 10; i++) loseItem(ItemCopperCoin, false);
for (i = 0; i < 5; i++) game().generateBlood(x, y, BloodRed);
for (i = 0; i < NUMBER_EQUIP_ITEMS; i++)
{
if (equip[i])
{
if (items[i + FirstEquipItem].familiar == FamiliarNone)
loseItem(enumItemType(i), true);
}
}
remove(SAVE_FILE.c_str());
game().calculateScore();
}
void PlayerEntity::displayAcquiredGold(int n)
{
std::ostringstream oss;
if (n > 0) oss << "+";
oss << n;
TextEntity* text = new TextEntity(oss.str(), 16, x, y - 30.0f);
text->setColor(TextEntity::COLOR_FADING_YELLOW);
text->setAge(-0.6f);
text->setLifetime(0.3f);
text->setWeight(-60.0f);
text->setZ(2000);
text->setAlignment(ALIGN_CENTER);
text->setType(ENTITY_FLYING_TEXT);
}
void PlayerEntity::acquireItem(enumItemType type)
{
if (items[type].generatesStance) acquireStance(type);
else switch (type)
{
case ItemCopperCoin:
gold++;
displayAcquiredGold(1);
SoundManager::getInstance().playSound(SOUND_COIN_PICK_UP);
game().proceedEvent(EventGetCoin);
break;
case ItemSilverCoin:
gold = gold + 5;
displayAcquiredGold(5);
SoundManager::getInstance().playSound(SOUND_COIN_PICK_UP);
game().proceedEvent(EventGetCoin);
break;
case ItemGoldCoin:
gold = gold + 10;
displayAcquiredGold(10);
SoundManager::getInstance().playSound(SOUND_COIN_PICK_UP);
game().proceedEvent(EventGetCoin);
break;
case ItemHealthVerySmallPoison:
specialState[SpecialStatePoison].active = false;
case ItemHealthVerySmall:
heal(equip[EQUIP_MANUAL_HEALTH] ? 5 : 3);
SoundManager::getInstance().playSound(SOUND_EAT);
break;
case ItemHealthSmall:
heal(equip[EQUIP_MANUAL_HEALTH] ? 10 : 7);
SoundManager::getInstance().playSound(SOUND_EAT);
break;
case ItemHealth:
heal(equip[EQUIP_MANUAL_HEALTH] ? 22 : 15);
SoundManager::getInstance().playSound(SOUND_EAT);
break;
default:
break;
}
}
void PlayerEntity::onClearRoom()
{
if (divinity.divinity == DivinityHealer)
{
if (divinity.level > 1 && hp < hpMax)
{
divineInterventionDelay = WORSHIP_DELAY / 2;
isRegeneration = true;
if (divinity.level >= 5) heal(4);
else if (divinity.level >= 4) heal(3);
else if (divinity.level >= 3) heal(2);
else if (divinity.level >= 2) heal(1);
}
}
}
void PlayerEntity::computePlayer()
{
float boltLifeTimeBonus = 1.0f;
float fireDelayBonus = 1.0f;
float creatureSpeedBonus = 1.0f;
float fireVelocityBonus = 1.0f;
float fireDamagesBonus = 1.0f;
armor = 0.0f;
criticalChance = 0;
for (int i = 0; i < NB_RESISTANCES; i++) resistance[i] = ResistanceStandard;
if (equip[EQUIP_GLOVES_ADVANCED]) fireDelayBonus -= 0.15f;
else if (equip[EQUIP_DISPLACEMENT_GLOVES]) fireDelayBonus -= 0.10f;
if (equip[EQUIP_HAT_ADVANCED])
{
fireDelayBonus -= 0.3f;
resistance[ResistanceIce] = (enumStateResistance)(resistance[ResistanceIce] - 1);
resistance[ResistanceStone] = (enumStateResistance)(resistance[ResistanceStone] - 1);
resistance[ResistanceLightning] = (enumStateResistance)(resistance[ResistanceLightning] - 1);
resistance[ResistanceFire] = (enumStateResistance)(resistance[ResistanceFire] - 1);
}
else if (equip[EQUIP_MAGICIAN_HAT]) fireDelayBonus -= 0.2f;
if (equip[EQUIP_LEATHER_BELT]) fireDelayBonus -= 0.15f;
if (equip[EQUIP_BOOTS_ADVANCED]) creatureSpeedBonus += 0.25f;
else if (equip[EQUIP_LEATHER_BOOTS]) creatureSpeedBonus += 0.15f;
if (equip[EQUIP_BOOK_TRIPLE]) fireDelayBonus += 0.7f;
else if (equip[EQUIP_BOOK_DUAL]) fireDelayBonus += 0.5f;
if (equip[EQUIP_CRITICAL]) criticalChance += 5;
if (equip[EQUIP_MANUAL_STAFF]) boltLifeTimeBonus += 0.4f;
if (equip[EQUIP_MAHOGANY_STAFF])
{
fireVelocityBonus += 0.15f;
fireDamagesBonus += 0.5f;
}
if (equip[EQUIP_BLOOD_SNAKE]) fireDamagesBonus += 0.5f;
if (equip[EQUIP_ROBE_ADVANCED]) armor += 0.2f;
else if (equip[EQUIP_MAGICIAN_ROBE]) armor += 0.15f;
// divinity
switch (divinity.divinity)
{
case (DivinityHealer):
{
break;
}
case (DivinityFighter):
{
if (divinity.level >= 5)
fireDamagesBonus += 0.5f;
else if (divinity.level >= 4)
fireDamagesBonus += 0.375f;
else if (divinity.level >= 3)
fireDamagesBonus += 0.25f;
else if (divinity.level >= 2)
fireDamagesBonus += 0.125f;
break;
}
case (DivinityIce):
{
if (divinity.level >= 5) resistance[ResistanceFrozen] = ResistanceVeryHigh;
if (divinity.level >= 3) resistance[ResistanceIce] = (enumStateResistance)(resistance[ResistanceIce] - 1);
break;
}
case (DivinityStone):
{
if (divinity.level >= 5) resistance[ResistanceRecoil] = ResistanceVeryHigh;
if (divinity.level >= 3) resistance[ResistanceStone] = (enumStateResistance)(resistance[ResistanceStone] - 1);
break;
}
case (DivinityAir):
{
if (divinity.level > 1) creatureSpeedBonus += (divinity.level - 1) * 0.04f;
if (divinity.level >= 3) resistance[ResistanceLightning] = (enumStateResistance)(resistance[ResistanceLightning] - 1);
break;
}
}
fireDelay = INITIAL_PLAYER_FIRE_DELAY * fireDelayBonus;
creatureSpeed = INITIAL_PLAYER_SPEED * creatureSpeedBonus;
fireVelocity = INITIAL_BOLT_VELOCITY * fireVelocityBonus;
fireDamages = INITIAL_BOLT_DAMAGES * fireDamagesBonus;
boltLifeTime = INITIAL_BOLT_LIFE * boltLifeTimeBonus;
// gems
for (int i = 1; i < SPECIAL_SHOT_SLOTS; i++)
{
specialShotLevel[i] = 0;
switch (specialShots[i])
{
case ShotTypeIce:
if (equip[EQUIP_RING_ICE]) specialShotLevel[i]++;
if (divinity.divinity == DivinityIce && divinity.level >= 4) specialShotLevel[i]++;
break;
case ShotTypeStone:
if (equip[EQUIP_RING_STONE]) specialShotLevel[i]++;
if (divinity.divinity == DivinityStone && divinity.level >= 4) specialShotLevel[i]++;
break;
case ShotTypeLightning:
if (equip[EQUIP_RING_LIGHTNING]) specialShotLevel[i]++;
if (divinity.divinity == DivinityAir && divinity.level >= 4) specialShotLevel[i]++;
break;
case ShotTypeIllusion:
if (equip[EQUIP_RING_ILLUSION]) specialShotLevel[i]++;
break;
case ShotTypeFire:
if (equip[EQUIP_RING_FIRE]) specialShotLevel[i]++;
break;
case ShotTypePoison:
if (equip[EQUIP_RING_POISON]) specialShotLevel[i]++;
break;
default:
break;
}
}
if (getShotType() == ShotTypeIllusion) fireDamages *= ILLUSION_DAMAGE_DECREASE[getShotLevel()];
else if (getShotType() == ShotTypeFire) fireDamages *= FIRE_DAMAGE_INCREASE[getShotLevel()];
// divinity
if (specialState[DivineStateProtection].active)
armor += specialState[DivineStateProtection].param1;
// post-computation
if (equip[EQUIP_BOOK_TRIPLE_QUICK]) fireDamages *= 0.65f;
else if (equip[EQUIP_BOOK_DUAL_QUICK]) fireDamages *= 0.75f;
else if (equip[EQUIP_RAPID_SHOT])
{
fireDelay *= 0.20f;
fireDamages *= 0.25f;
}
if (equip[EQUIP_ALCOHOL]) fireDamages *= 1.25f;
// spells
if (protection.active) armor += protection.value;
if (armor > 1.0f) armor = 1.0f;
// fairy ?
if (isFairyTransmuted)
{
fireDamages *= 0.5f;
creatureSpeed *= 1.5f;
movingStyle = movFlying;
}
else
{
movingStyle = movWalking;
}
}
void PlayerEntity::acquireStance(enumItemType type)
{
velocity.x = 0.0f;
velocity.y = 0.0f;
playerStatus = playerStatusAcquire;
statusTimer = ACQUIRE_DELAY;
acquiredItem = (enumItemType)(type);
SoundManager::getInstance().playSound(SOUND_BONUS);
game().showArtefactDescription(type);
enumItemType itemFrame = type;
int itemImage = IMAGE_ITEMS;
if (itemFrame >= FirstEquipItem)
{
itemFrame = (enumItemType)(itemFrame - FirstEquipItem);
itemImage = IMAGE_ITEMS_EQUIP;
}
spriteItem = new SpriteEntity(
ImageManager::getInstance().getImage(itemImage),
x, y - 100.0f, ITEM_WIDTH, ITEM_HEIGHT);
spriteItem->setFrame((int)itemFrame);
spriteItem->setImagesProLine(10);
spriteItem->setZ(z);
spriteItem->setLifetime(ACQUIRE_DELAY);
spriteItemStar = new SpriteEntity(
ImageManager::getInstance().getImage(IMAGE_STAR),
x, y - 100.0f);
spriteItemStar->setScale(4.0f, 4.0f);
spriteItemStar->setZ(z-1.0f);
spriteItemStar->setLifetime(ACQUIRE_DELAY);
spriteItemStar->setSpin(50.0f);
}
void PlayerEntity::collideMapRight()
{
collidingDirection = 6;
if (recoil.active) recoil.velocity.x = -recoil.velocity.x * 0.7f;
}
void PlayerEntity::collideMapLeft()
{
collidingDirection = 4;
if (recoil.active) recoil.velocity.x = -recoil.velocity.x * 0.7f;
}
void PlayerEntity::collideMapTop()
{
collidingDirection = 8;
if (recoil.active) recoil.velocity.y= -recoil.velocity.y * 0.7f;
}
void PlayerEntity::collideMapBottom()
{
collidingDirection = 2;
if (recoil.active) recoil.velocity.y= -recoil.velocity.y * 0.7f;
}
void PlayerEntity::useBossKey()
{
velocity.x = 0.0f;
velocity.y = 0.0f;
playerStatus = playerStatusUnlocking;
statusTimer = UNLOCK_DELAY;
acquiredItem = (enumItemType)(type - FirstEquipItem);
SoundManager::getInstance().playSound(SOUND_DOOR_OPENING_BOSS);
equip[EQUIP_BOSS_KEY] = false;
SpriteEntity* spriteItem = new SpriteEntity(
ImageManager::getInstance().getImage(IMAGE_ITEMS_EQUIP),
x, y - 80.0f, ITEM_WIDTH, ITEM_HEIGHT);
spriteItem->setFrame(EQUIP_BOSS_KEY);
spriteItem->setZ(z);
spriteItem->setAge(-UNLOCK_DELAY * 0.5f);
spriteItem->setLifetime(UNLOCK_DELAY * 0.5f);
spriteItem->setFading(true);
spriteItem->setSpin(300);
}
enumShotType PlayerEntity::getShotType()
{
return specialShots[specialShotIndex];
}
int PlayerEntity::getShotIndex()
{
return specialShotIndex;
}
void PlayerEntity::setShotIndex(int index)
{
specialShotIndex = index;
}
enumShotType PlayerEntity::getShotType(int slot)
{
return specialShots[slot];
}
void PlayerEntity::setShotType(int slot, enumShotType shotType)
{
specialShots[slot] = shotType;
}
void PlayerEntity::registerSpecialShot(int item)
{
bool found = false;
int index = 1;
while (index < SPECIAL_SHOT_SLOTS && !found)
{
found = specialShots[index] == ShotTypeStandard;
if (!found) index++;
}
if (found)
{
this->specialShots[index] = items[item].specialShot;
specialShotIndex = index;
initShotType();
}
}
void PlayerEntity::selectNextShotType()
{
int index = specialShotIndex + 1;
bool found = false;
while (index < SPECIAL_SHOT_SLOTS && !found)
{
if (specialShots[index] == ShotTypeStandard) index++;
else found = true;
}
if (found)
{
specialShotIndex = index;
initShotType();
}
else
specialShotIndex = 0;
SoundManager::getInstance().playSound(SOUND_SHOT_SELECT);
computePlayer();
}
void PlayerEntity::initShotType()
{
specialBoltTimer = STATUS_FROZEN_BOLT_DELAY[getShotLevel()];
needInitShotType = false;
if (getShotType() == ShotTypeLightning)
SoundManager::getInstance().playSound(SOUND_ELECTRIC_CHARGE);
}
unsigned int PlayerEntity::getShotLevel()
{
return specialShotLevel[specialShotIndex];
}
unsigned int PlayerEntity::getShotLevel(int index)
{
return specialShotLevel[index];
}
int PlayerEntity::getFairieNumber()
{
return fairies.size();
}
FairyEntity* PlayerEntity::getFairy(unsigned int n)
{
if (n < fairies.size())
return fairies[n];
else
return NULL;
}
bool PlayerEntity::canGetNewShot(bool advancedShot)
{
int nbSpecial =0;
int nbAdvanced =0;
for (int i = 1; i < SPECIAL_SHOT_SLOTS; i++)
{
switch (specialShots[i])
{
case ShotTypeIce:
case ShotTypeStone:
case ShotTypeLightning:
nbSpecial++;
break;
case ShotTypeFire:
case ShotTypeIllusion:
case ShotTypePoison:
nbAdvanced++;
break;
case ShotTypeStandard:
break;
default:
std::cout << "[WARNING] Can not register shot type: " << getShotType() << std::endl;
}
}
if (advancedShot)
return (nbAdvanced >= SPECIAL_SHOT_SLOTS_ADVANCED);
else
return (nbSpecial >= SPECIAL_SHOT_SLOTS_STANDARD);
}
void PlayerEntity::interact(EnumInteractionType interaction, int id)
{
if (playerStatus == playerStatusPlaying)
{
// praying at the temple
if (interaction == InteractionTypeTemple)
{
if (divinity.divinity == id)
{
// donation
if (gold >= 10)
{
donate(10);
}
}
else
{
worship((enumDivinityType)id);
}
}
else if (interaction == InteractionTypeMerchandise)
{
if (itemToBuy != NULL) itemToBuy->buy();
}
}
}
float PlayerEntity::getBolPositionY()
{
if (isFairyTransmuted)
return y - 25;
else
return y - 20;
}
bool PlayerEntity::collideWithMap(int direction)
{
if (playerStatus == playerStatusEntering)
return false;
else
return BaseCreatureEntity::collideWithMap(direction);
}
// DIVINITY
void PlayerEntity::donate(int n)
{
if (gold >= n)
{
gold -= n;
displayAcquiredGold(-n);
SoundManager::getInstance().playSound(SOUND_PAY);
// standard : 1 gold = 3 piety
int pietyProGold = 3;
if (divinity.divinity == DivinityHealer)
pietyProGold = 5;
addPiety(pietyProGold * n);
// check item invoke
bool divineGift = false;
enumItemType itemType = ItemCopperCoin;
if (divinity.level >= 3 && game().getItemsCount() == 0)
{
if (divinity.divinity == DivinityHealer && !equip[EQUIP_MANUAL_HEALTH])
{
// Healer + level 3 = Health manual
divineGift = true;
itemType = ItemManualHealth;
}
else if (divinity.divinity == DivinityIce && !equip[EQUIP_GEM_ICE])
{
divineGift = true;
itemType = ItemGemIce;
}
else if (divinity.divinity == DivinityStone && !equip[EQUIP_GEM_STONE])
{
divineGift = true;
itemType = ItemGemStone;
}
else if (divinity.divinity == DivinityAir && !equip[EQUIP_GEM_LIGHTNING])
{
divineGift = true;
itemType = ItemGemLightning;
}
}
if (divineGift)
{
float xItem = GAME_WIDTH / 2;
float yItem = GAME_HEIGHT * 0.8f;
new ItemEntity(itemType, xItem, yItem);
SoundManager::getInstance().playSound(SOUND_OM);
divineInterventionDelay = WORSHIP_DELAY / 2;
isRegeneration = false;
for (int i = 0; i < 8; i++)
{
game().generateStar(sf::Color::White, xItem, yItem);
game().generateStar(sf::Color(255, 255, 210), xItem, yItem);
}
game().testAndAddMessageToQueue((EnumMessages)(MsgInfoDivGift));
}
}
}
void PlayerEntity::offerMonster(enemyTypeEnum monster, enumShotType hurtingType)
{
if (divinity.divinity > -1)
{
// standard : 1 monster = 2 piety - 1 boss = 20 piety
int pietyProMonster = 2;
int pietyProBoss = 20;
switch (divinity.divinity)
{
case DivinityHealer:
if (monster == EnemyTypeGhost
|| monster == EnemyTypeZombie
|| monster == EnemyTypeZombieDark
|| monster == EnemyTypeImpBlue
|| monster == EnemyTypeImpRed
|| monster == EnemyTypeWitch
|| monster == EnemyTypeWitchRed
|| monster == EnemyTypeBogeyman)
pietyProMonster = 4;
else
pietyProMonster = 0;
break;
case DivinityFighter:
pietyProMonster = 3;
pietyProBoss = 30;
break;
case DivinityIce:
if (monster == EnemyTypeSlimeRed
|| monster == EnemyTypeImpRed
|| monster == EnemyTypeEvilFlowerFire)
pietyProMonster = 4;
if (hurtingType == ShotTypeCold || hurtingType == ShotTypeIce)
{
pietyProMonster *= 1.5f;
pietyProBoss = 25;
}
break;
case DivinityStone:
if (hurtingType == ShotTypeCold || hurtingType == ShotTypeIce)
{
pietyProMonster = 3;
pietyProBoss = 30;
}
else
{
pietyProBoss = 25;
}
break;
}
if (monster < EnemyTypeButcher) // normal or mini-boss
{
addPiety(pietyProMonster);
}
else if (monster < EnemyTypeBat_invocated) // boss
{
addPiety(pietyProBoss);
}
}
}
void PlayerEntity::offerHealth(int lostHp)
{
if (divinity.divinity == DivinityHealer)
{
addPiety(lostHp * 2.5f);
}
}
void PlayerEntity::offerChallenge()
{
if (divinity.divinity >= 0)
addPiety(30);
}
void PlayerEntity::divineFury()
{
enumShotType shotType = ShotTypeStandard;
if (divinity.divinity == DivinityIce) shotType = ShotTypeIce;
else if (divinity.divinity == DivinityStone) shotType = ShotTypeStone;
else if (divinity.divinity == DivinityAir) shotType = ShotTypeLightning;
int multBonus = 6;
if (divinity.divinity == DivinityFighter) multBonus = 8;
for (int i = 0; i < (divinity.divinity == DivinityAir ? 16 : 32); i ++)
{
BoltEntity* bolt = new BoltEntity(TILE_WIDTH * 1.5f + rand() % (MAP_WIDTH - 3) * TILE_WIDTH ,
TILE_HEIGHT * 1.5f + rand() % (MAP_HEIGHT - 3) * TILE_HEIGHT,
boltLifeTime, shotType, 0);
bolt->setDamages(8 + divinity.level * multBonus);
float velx = 400 * cos(i);
float vely = 400 * sin(i);
bolt->setVelocity(Vector2D(velx, vely));
if (divinity.divinity == DivinityAir)
{
bolt->setFlying(true);
bolt->setLifetime(-1.0f);
}
else
{
bolt->setLifetime(8.0f);
}
bolt->setViscosity(1.0f);
bolt->setGoThrough(true);
bolt->setFromPlayer(false);
}
}
void PlayerEntity::divineDestroyUndead()
{
game().destroyUndead(40);
}
void PlayerEntity::divineIce()
{
EntityManager::EntityList* entityList = EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() >= ENTITY_ENEMY && e->getType() <= ENTITY_ENEMY_MAX)
{
EnemyEntity* enemy = dynamic_cast<EnemyEntity*>(e);
enemy->setSpecialState(SpecialStateIce, true, 10.0f, 0.1f, 0.0f);
}
}
}
void PlayerEntity::divineRepulse()
{
EntityManager::EntityList* entityList = EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
for (it = entityList->begin (); it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if (e->getType() >= ENTITY_ENEMY && e->getType() <= ENTITY_ENEMY_MAX)
{
EnemyEntity* enemy = dynamic_cast<EnemyEntity*>(e);
enemy->giveRecoil(true, Vector2D(x, y).vectorTo(Vector2D(enemy->getX(), enemy->getY()), 700.0f), 2.0f);
}
}
// effect
for (int i = 0; i < 40; i++)
{
SpriteEntity* spriteRock = new SpriteEntity(
ImageManager::getInstance().getImage(IMAGE_CYCLOP),
x, y, 64, 64);
spriteRock->setZ(1000.0f);
spriteRock->setImagesProLine(20);
spriteRock->setFrame(rand() % 2 == 0 ? 38 : 58);
spriteRock->setSpin(-100 + rand()%200);
spriteRock->setVelocity(Vector2D(400 + rand()%400));
spriteRock->setFading(true);
spriteRock->setAge(-0.8f);
spriteRock->setLifetime(2.0f);
spriteRock->setType(ENTITY_EFFECT);
}
game().makeShake(1.0f);
SoundManager::getInstance().playSound(SOUND_EARTHQUAKE);
}
void PlayerEntity::divineProtection(float duration, float armorBonus)
{
setSpecialState(DivineStateProtection, true, 4.0f, 0.8f, 0.0f);
}
void PlayerEntity::divineHeal(int hpHealed)
{
hp += hpHealed;
if (hp > hpMax) hp = hpMax;
specialState[SpecialStatePoison].active = false;
divineInterventionDelay = WORSHIP_DELAY;
isRegeneration = false;
}
bool PlayerEntity::triggerDivinityBefore()
{
if (divinity.divinity > -1 && divinity.interventions < divinity.level - 1)
{
switch (divinity.divinity)
{
case DivinityHealer:
{
if (game().getUndeadCount() > 0 && rand() % 2 == 0)
{
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax / 2);
divineDestroyUndead();
game().makeColorEffect(X_GAME_COLOR_WHITE, 0.45f);
return true;
}
break;
}
case DivinityFighter:
{
int r = rand() % 3;
if (r == 0) return false;
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax / 3);
if (r == 1) divineProtection(5.0f, 0.8f);
else divineFury();
game().makeColorEffect(X_GAME_COLOR_RED, 0.45f);
return true;
break;
}
case DivinityIce:
{
int r = rand() % 3;
if (r == 0) return false;
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax / 3);
if (r == 1)
{
divineIce();
game().makeColorEffect(X_GAME_COLOR_BLUE, 7.5f);
}
else
{
divineFury();
game().makeColorEffect(X_GAME_COLOR_BLUE, 0.5f);
}
return true;
break;
}
case DivinityStone:
{
int r = rand() % 3;
r = 1;
divineProtection(10.0f, 0.5f);
if (r == 0) return false;
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax / 3);
if (r == 1)
{
divineRepulse();
game().makeColorEffect(X_GAME_COLOR_BROWN, 3.0f);
}
else
{
divineFury();
game().makeColorEffect(X_GAME_COLOR_BROWN, 0.5f);
}
return true;
break;
}
case DivinityAir:
{
int r = rand() % 3;
if (r == 0) return false;
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax / 3);
/*if (r == 1)
{
divineIce();
game().makeColorEffect(X_GAME_COLOR_BLUE, 7.5f);
}
else*/
{
divineFury();
game().makeColorEffect(X_GAME_COLOR_VIOLET, 0.5f);
}
return true;
break;
}
}
}
return false;
}
void PlayerEntity::triggerDivinityAfter()
{
if (divinity.divinity > -1 && divinity.interventions < divinity.level - 1)
{
switch (divinity.divinity)
{
case DivinityHealer:
{
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax);
break;
}
//case DivinityFighter:
default:
{
SoundManager::getInstance().playSound(SOUND_OM);
incrementDivInterventions();
divineHeal(hpMax / 2);
break;
}
}
game().testAndAddMessageToQueue((EnumMessages)(MsgInfoDivIntervention));
}
}
void PlayerEntity::addPiety(int n)
{
if (n > 0 && equip[EQUIP_BOOK_PRAYER_I]) n *= 1.5f;
int oldLevel = divinity.level;
divinity.piety += n;
if (divinity.piety >= DIVINITY_LEVEL_TRESHOLD[MAX_DIVINITY_LEVEL - 1])
{
divinity.piety = DIVINITY_LEVEL_TRESHOLD[MAX_DIVINITY_LEVEL - 1];
divinity.percentsToNextLevels = 1.0f;
game().registerAchievement(AchievementPietyMax);
divinity.level = MAX_DIVINITY_LEVEL + 1;
}
else
{
int i = 0;
while (divinity.piety > DIVINITY_LEVEL_TRESHOLD[i] && i < (MAX_DIVINITY_LEVEL + 1)) i++;
divinity.level = i + 1;
if (divinity.level == 1)
divinity.percentsToNextLevels = (float)divinity.piety / (float)DIVINITY_LEVEL_TRESHOLD[0];
else
divinity.percentsToNextLevels
= (float)(divinity.piety - DIVINITY_LEVEL_TRESHOLD[divinity.level - 2])
/ (float)(DIVINITY_LEVEL_TRESHOLD[divinity.level - 1] - DIVINITY_LEVEL_TRESHOLD[divinity.level - 2]);
}
if (divinity.level > oldLevel)
{
SoundManager::getInstance().playSound(SOUND_OM);
divineInterventionDelay = WORSHIP_DELAY / 2;
isRegeneration = false;
pietyLevelUp();
computePlayer();
}
else if (divinity.level < oldLevel)
{
computePlayer();
}
}
void PlayerEntity::pietyLevelUp()
{
std::string label = "";
switch (divinity.divinity)
{
case DivinityFighter:
label = "div_fighter_lvl";
break;
case DivinityHealer:
if (divinity.level == 2) label = "div_healer_lvl_2";
else label = "div_healer_lvl_3";
break;
case DivinityIce:
if (divinity.level == 3) label = "div_ice_lvl_3";
else if (divinity.level == 4) label = "div_ice_lvl_4";
else if (divinity.level == 5) label = "div_ice_lvl_5";
break;
case DivinityStone:
if (divinity.level == 3) label = "div_stone_lvl_3";
else if (divinity.level == 4) label = "div_stone_lvl_4";
else if (divinity.level == 5) label = "div_stone_lvl_5";
break;
case DivinityAir:
if (divinity.level == 4) label = "div_air_lvl_4";
else label = "div_air_lvl";
break;
}
if (label.compare("") != 0) game().addDivLevelMessageToQueue(label);
}
void PlayerEntity::incrementDivInterventions()
{
divinity.interventions++;
addPiety(-divinity.piety * (equip[EQUIP_BOOK_PRAYER_II] ? 0.04f : 0.08f));
}
void PlayerEntity::worship(enumDivinityType id)
{
int oldPiety = divinity.piety;
bool isReconversion = divinity.divinity > -1;
playerStatus = playerStatusPraying;
statusTimer = WORSHIP_DELAY;
SoundManager::getInstance().playSound(SOUND_OM);
divinity.divinity = id;
divinity.piety = 0;
divinity.level = 1;
divinity.percentsToNextLevels = 0.0f;
facingDirection = 2;
// text
float x0 = MAP_WIDTH * 0.5f * TILE_WIDTH;
float y0 = MAP_HEIGHT * 0.5f * TILE_HEIGHT + 140.0f;
std::stringstream ss;
ss << tools::getLabel("worshipping") << " ";
ss << tools::getLabel(divinityLabel[divinity.divinity] + "_0");
TextEntity* text = new TextEntity(ss.str(), 24, x0, y0);
text->setAlignment(ALIGN_CENTER);
text->setLifetime(2.5f);
text->setWeight(-36.0f);
text->setZ(1200);
text->setColor(TextEntity::COLOR_FADING_WHITE);
// reconversion
if (isReconversion)
{
addPiety((equip[EQUIP_BOOK_PRAYER_I]) ? 0.66 * oldPiety : 0.5 * oldPiety);
if (divinity.interventions > divinity.level - 1)
divinity.interventions = divinity.level - 1;
}
else
divinity.interventions = 0;
// message
game().testAndAddMessageToQueue((EnumMessages)(MsgInfoDivHealer + (int)id));
}
void PlayerEntity::loadDivinity(int id, int piety, int level, int interventions)
{
divinity.divinity = id;
divinity.piety = piety;
divinity.level = level;
divinity.interventions = interventions;
if (id >= 0) addPiety(0);
}
// MAGIC
castSpellStruct PlayerEntity::getActiveSpell()
{
return activeSpell;
}
void PlayerEntity::setActiveSpell(enumCastSpell spell, bool fromSaveInFight)
{
if (activeSpell.spell != SpellNone)
{
// drop the old spell
equip[activeSpell.frame] = false;
ItemEntity* newItem = new ItemEntity((enumItemType)(ItemMagicianHat + activeSpell.frame), x, y);
newItem->setVelocity(Vector2D(100.0f + rand()% 250));
newItem->setViscosity(0.96f);
}
activeSpell.spell = spell;
switch (spell)
{
case SpellTeleport:
activeSpell.delayMax = 20.0f;
activeSpell.frame = ItemSpellTeleport - FirstEquipItem;
break;
case SpellSlimeExplode:
activeSpell.delayMax = 40.0f;
activeSpell.frame = ItemSpellSlimeExplode - FirstEquipItem;
break;
case SpellFireball:
activeSpell.delayMax = 20.0f;
activeSpell.frame = ItemSpellFireball - FirstEquipItem;
break;
case SpellFreeze:
activeSpell.delayMax = 40.0f;
activeSpell.frame = ItemSpellFreeze - FirstEquipItem;
break;
case SpellEarthquake:
activeSpell.delayMax = 40.0f;
activeSpell.frame = ItemSpellEarthquake - FirstEquipItem;
break;
case SpellProtection:
activeSpell.delayMax = 40.0f;
activeSpell.frame = ItemSpellProtection - FirstEquipItem;
break;
case SpellWeb:
activeSpell.delayMax = 35.0f;
activeSpell.frame = ItemSpellWeb - FirstEquipItem;
break;
case SpellFlower:
activeSpell.delayMax = 80.0f;
activeSpell.frame = ItemSpellFlower - FirstEquipItem;
break;
case SpellFairy:
activeSpell.delayMax = 10.0f;
activeSpell.frame = ItemSpellFairy - FirstEquipItem;
break;
case SpellNone:
break;
}
if (fromSaveInFight) activeSpell.delay = 1.0f;
else activeSpell.delay = activeSpell.delayMax;
}
void PlayerEntity::castSpell()
{
if (playerStatus != playerStatusPlaying) return;
if (canCastSpell())
{
activeSpell.delay = equip[EQUIP_BOOK_MAGIC_I] ? activeSpell.delayMax * 0.8f : activeSpell.delayMax;
switch (activeSpell.spell)
{
case SpellTeleport:
castTeleport();
break;
case SpellSlimeExplode:
castSummonsSlimeExplode();
break;
case SpellFireball:
castFireball();
break;
case SpellFreeze:
spellAnimationDelay = spellAnimationDelayMax;
castFreeze();
break;
case SpellEarthquake:
spellAnimationDelay = spellAnimationDelayMax;
castEarthquake();
break;
case SpellProtection:
spellAnimationDelay = spellAnimationDelayMax;
castProtection();
break;
case SpellWeb:
castWeb();
break;
case SpellFlower:
spellAnimationDelay = spellAnimationDelayMax;
castSummonsFlower();
break;
case SpellFairy:
castTransmuteFairy();
break;
case SpellNone:
break;
}
}
}
bool PlayerEntity::canCastSpell()
{
return activeSpell.spell != SpellNone && activeSpell.delay <= 0.0f;
}
void PlayerEntity::castTeleport()
{
bool ok = false;
int xm, ym;
float xNew = x, yNew = y;
invincibleDelay = equip[EQUIP_BOOK_MAGIC_II] ? 2.5f : 2.0f;
SoundManager::getInstance().playSound(SOUND_TELEPORT);
game().makeColorEffect(X_GAME_COLOR_VIOLET, 0.3f);
for(int i=0; i < 6; i++)
{
generateStar(sf::Color(50, 50, 255, 255));
generateStar(sf::Color(200, 200, 255, 255));
}
int counter = 150;
while (!ok && counter > 0)
{
counter--;
int distanceMin = 20000;
if (counter < 50) distanceMin = 30000;
else if (counter < 100) distanceMin = 25000;
xm = 1 +rand() % (MAP_WIDTH - 3);
ym = 1 +rand() % (MAP_HEIGHT - 3);
if (game().getCurrentMap()->isWalkable(xm, ym))
{
// enemy or bolt ?
EntityManager::EntityList* entityList =EntityManager::getInstance().getList();
EntityManager::EntityList::iterator it;
bool isBad = false;
xNew = xm * TILE_WIDTH + TILE_WIDTH * 0.5f;
yNew = ym * TILE_HEIGHT+ TILE_HEIGHT * 0.5f;
for (it = entityList->begin (); !isBad && it != entityList->end ();)
{
GameEntity *e = *it;
it++;
if ((e->getType() >= ENTITY_ENEMY && e->getType() <= ENTITY_ENEMY_MAX_COUNT) || e->getType() == ENTITY_ENEMY_BOLT)
isBad = Vector2D(xNew, yNew).distance2(Vector2D(e->getX(), e->getY())) < distanceMin;
}
if (!isBad)
{
x = xNew;
y = yNew;
}
}
}
for(int i=0; i < 6; i++)
{
generateStar(sf::Color(50, 50, 255, 255));
generateStar(sf::Color(200, 200, 255, 255));
}
}
void PlayerEntity::initFallingGrid()
{
for (int i = 0; i < MAP_WIDTH; i++)
for (int j = 0; j < MAP_HEIGHT; j++)
fallingGrid[i][j] = false;
}
void PlayerEntity::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 + TILE_WIDTH / 2,
ry * TILE_HEIGHT + TILE_HEIGHT / 2,
rand() % 3,
true);
}
void PlayerEntity::castSummonsSlimeExplode()
{
SlimeEntity* slime = new SlimeEntity( ((int)(x) / TILE_WIDTH) * TILE_WIDTH + TILE_WIDTH * 0.5f,
y - 5, SlimeTypeViolet, true);
slime->makePet(facingDirection);
game().makeColorEffect(X_GAME_COLOR_VIOLET, 0.3f);
}
void PlayerEntity::castFireball()
{
SoundManager::getInstance().playSound(SOUND_FIREBALL);
game().makeColorEffect(X_GAME_COLOR_RED, 0.3f);
enumShotType boltType = ShotTypeFire;
unsigned int shotLevel = 2;
BoltEntity* bolt = new BoltEntity(x, getBolPositionY(), boltLifeTime + 0.5f, boltType, shotLevel);
int boltDamage = fireDamages * (equip[EQUIP_BOOK_MAGIC_II] ? 4 : 3);
if (equip[EQUIP_BOOK_MAGIC_II] && boltDamage < 16) boltDamage = 16;
else if (!equip[EQUIP_BOOK_MAGIC_II] && boltDamage < 12) boltDamage = 12;
bolt->setDamages(boltDamage);
bolt->setGoThrough(true);
float velx = 0.0f, vely = 0.0f;
if (facingDirection == 4) velx = -fireVelocity;
else if (facingDirection == 8) vely = -fireVelocity;
else if (facingDirection == 2) vely = fireVelocity;
else velx = fireVelocity;
bolt->setVelocity(Vector2D(velx, vely));
}
void PlayerEntity::castFreeze()
{
int iceLevel = equip[EQUIP_BOOK_MAGIC_II] ? 2 : 1;
for (float i = 0.0f; i < 2 * PI; i += PI / 8)
{
BoltEntity* bolt1 = new BoltEntity(x, getBolPositionY(), boltLifeTime, ShotTypeIce, iceLevel);
bolt1->setDamages(1);
float velx = fireVelocity * cos(i);
float vely = fireVelocity * sin(i);
bolt1->setVelocity(Vector2D(velx, vely));
}
game().makeColorEffect(X_GAME_COLOR_BLUE, 0.3f);
SoundManager::getInstance().playSound(SOUND_SPELL_FREEZE);
}
void PlayerEntity::castEarthquake()
{
initFallingGrid();
int nbIterations = equip[EQUIP_BOOK_MAGIC_II] ? 24 : 22;
for (int i = 0; i < nbIterations; i++) fallRock();
game().makeShake(0.25f);
game().makeColorEffect(X_GAME_COLOR_BROWN, 0.3f);
SoundManager::getInstance().playSound(SOUND_EARTHQUAKE);
}
void PlayerEntity::castProtection()
{
protection.active = true;
protection.value = equip[EQUIP_BOOK_MAGIC_II] ? 0.6f : 0.4f;
protection.timer = 10.0f;
computePlayer();
game().makeColorEffect(X_GAME_COLOR_BLUE, 0.3f);
SoundManager::getInstance().playSound(SOUND_SPELL_SHIELD);
}
void PlayerEntity::castWeb()
{
SoundManager::getInstance().playSound(SOUND_SPIDER_WEB);
int nbWeb = equip[EQUIP_BOOK_MAGIC_II] ? 4 : 3;
for (int i = 0; i < nbWeb; i++)
{
SpiderWebEntity* web = new SpiderWebEntity(x, y, true);
float webVel = 100 + rand()% 500;
float webAngle = -60 + rand() % 120;
webAngle = PI * webAngle / 180.0f;
if (facingDirection == 4) webAngle += PI;
else if (facingDirection == 8) webAngle -= PI * 0.5;
else if (facingDirection == 2) webAngle += PI * 0.5;
web->setVelocity(Vector2D(webVel * cos(webAngle), webVel * sin(webAngle)));
}
}
void PlayerEntity::castSummonsFlower()
{
SoundManager::getInstance().playSound(SOUND_INVOKE);
EvilFlowerEntity* flower = new EvilFlowerEntity(x, y, FlowerTypePet);
flower->setLifetime(equip[EQUIP_BOOK_MAGIC_II] ? 45 : 35);
if (equip[EQUIP_BOOK_MAGIC_II]) flower->setFireDelayMax(EVIL_FLOWER_FIRE_DELAY * 0.8f);
}
void PlayerEntity::castTransmuteFairy()
{
if (isFairyTransmuted)
{
movingStyle = movWalking;
if (isCollidingWithMap())
movingStyle = movFlying;
else
{
SoundManager::getInstance().playSound(SOUND_INVOKE);
isFairyTransmuted = false;
computePlayer();
for(int i=0; i < 6; i++)
{
generateStar(sf::Color(50, 50, 255, 255));
generateStar(sf::Color(200, 200, 255, 255));
}
}
}
else
{
SoundManager::getInstance().playSound(SOUND_INVOKE);
isFairyTransmuted = true;
computePlayer();
for(int i=0; i < 6; i++)
{
generateStar(sf::Color(50, 50, 255, 255));
generateStar(sf::Color(200, 200, 255, 255));
}
}
}
diff --git a/src/PlayerEntity.h b/src/PlayerEntity.h
index 085ce9d..d8e7fea 100644
--- a/src/PlayerEntity.h
+++ b/src/PlayerEntity.h
@@ -1,609 +1,610 @@
#ifndef PLAYERSPRITE_H
#define PLAYERSPRITE_H
#include "BaseCreatureEntity.h"
#include "ItemEntity.h"
#include "Constants.h"
class FairyEntity;
struct castSpellStruct
{
enumCastSpell spell;
float delay;
float delayMax;
int frame;
};
struct divinityStruct
{
int divinity;
int piety;
int level;
int interventions;
float percentsToNextLevels;
};
/*! \class PlayerEntity
* \brief Class for the player
*
* It contains the game logic of the player and the rendering.
*/
class PlayerEntity : public BaseCreatureEntity
{
public:
/*!
* \brief Constructor
*
* Constructor of the PlayerEntity class.
*
* \param x : x position of the player
* \param y : y position of the player
*/
PlayerEntity(float x, float y);
/*!
* \brief updates the player
*
* Updates the player.
* Called in the game loop.
*
* \param delay : elapsed time since the last call
*/
virtual void animate(float delay);
/*!
* \brief render the player
*
* Render the player.
* Called in the game loop.
*
* \param app : Rendering target
*/
virtual void render(sf::RenderTarget* app);
/*!
* \brief Moves the player to another place
*
* Moves the player to another place.
* Called when changing room, the "familiers" will move too.
*
* \param newX : target x position of the player
* \param newY : target y position of the player
*/
void moveTo(float newX, float newY);
/*!
* \brief returns the direction the player is facing
*
* Return the direction the player is facing.
* Use to know in which direction he has to fire with the "one button" gameplay.
*
* \return : the facing direction. 4 = left, 8 = north, 6 = right, 2 = south
*/
int getFacingDirection();
void setFacingDirection(int facingDirection);
/*!
* \brief update the bounding box
*
* Update the bounding box of the player.
* Used before testing collision.
*/
virtual void calculateBB();
virtual bool canCollide();
/*!
* \brief Moves the player in the given direction
*
* Moves the player in the given direction.
*
* \param direction : direction of the new map. Numeric pad, diagonals included : 4 = left, 8 = north, 7 = north-west...
*/
void move(int direction);
/*!
* \brief Fires in the given direction
*
* Fires the player in the given direction.
*
* \param direction : direction of the new map. 4 = left, 8 = north, 6 = right, 2 = south
*/
void fire(int direction);
/*!
* \brief reset the fire direction of the player
*/
void resestFireDirection();
/*!
* \brief accessor on the fire direction of the player
*/
int getFireDirection();
/*!
* \brief returns if the player is moving or not
*
* Returns if the player is moving or not.
*
* \return : True if the player is moving
*/
bool isMoving();
/*!
* \brief returns if an item is equipped or not
*
* Returns if an item is equipped or not.
*
* \param eq : the equip item ID
* \return : True if the item is in possession of the player
*/
bool isEquiped(int eq);
bool* getEquipment();
/*!
* \brief returns if the player is poisoned or not
*
* \return : True if the player is poisoned
*/
bool isPoisoned();
/*!
* \brief updates the equipment of the player
*
* Updates the equipment of the player.
*
* \param item : the equip item ID
* \param toggleEquipped : True if the item has to be equipped
*/
void setEquiped(int item, bool toggleEquipped);
divinityStruct getDivinity();
int getPiety();
bool getFairyTransmuted();
/*!
* \brief updates the entering status of the player
*
* Updates the entering status of the player.
* Used when the player is entering in a not yet cleared room.
*/
void setEntering();
/*!
* \brief updates the status of the player to going up
*
* Updates the status of the player to going up.
* Used when the player is leaving a level.
*/
void setLeavingLevel();
/*!
* \brief returns if the player can move or not
*
* Returns if the player can move or not.
*
* \return : True if the player can move
*/
bool canMove();
/*!
* \brief called when the player is dying
*
* Called when the player is dying (HP <= 0).
*/
virtual void dying();
/*!
* \brief hurts the player
*
* Hurts the player.
* Calld when the player is hurt, usually when colliding with a monster or a missile.
*
* \param damages : the inflicted damages
* \param hurtingType : damages type
* \param level : damages level
* \return : True if the player has been hurt
*/
virtual int hurt(StructHurt hurtParam) override;
/*!
* \brief returns if the player is dead or not
*
* Returns if the player is dead or not.
*
* \return : True if the player is dead
*/
bool isDead();
/*!
* \brief returns the fire delay percentage
*
* Returns the fire delay percentage.
* Used (currently) when displaying the blue bar in the HUD.
*
* \return : the percentage (between 0.0f for empty to 1.0 for full)
*/
float getPercentFireDelay();
/*!
* \brief returns the light cone percentage
*
* \return : the percentage (between 0.0f for empty to 1.0 for full) or -1.0f if no light cone
*/
float getLightCone();
/*!
* \brief called when the player get an item
*
* Called when the player get an item.
* When the item is an equip item, he starts an "acquiring stance".
*
* \param type : item ID
*/
void acquireItem(enumItemType type);
/*!
* \brief called when the player get an item and ends the stance
*
*/
void acquireItemAfterStance();
/*!
* \brief makes the player drop an item
*
* Makes the player drop an item.
* Called when the player dies.
*
* \param itemType : item ID
* \param isEquip : True if it's an equip item (not the same texture)
*/
void loseItem(enumItemType itemType, bool isEquip);
/*!
* \brief starts the acquire stance
*
* Starts the acquire stance.
* Called when the player get an equip object. The item is "highlighted" and a description is displayed.
*
* \param type : item ID
*/
void acquireStance(enumItemType type);
/*!
* \brief starts the opening stance
*
* Starts the boss door opening animation.
* Remove the key from the inventory.
*/
void useBossKey();
/*!
* \brief accessor on the gold
*
* Accessor on the gold.
*
* \return : the gold
*/
int getGold() {return gold; }
/*!
* \brief mutator on the gold
*
* Mutator on the gold.
*
* \param gold : the new gold value
*/
void setGold(int gold) { this->gold = gold; }
/*!
* \brief pay some gold
*
* Pay some gold. Usually in shops.
*
* \param gold : the price to pay
*/
void pay(int price);
void displayAcquiredGold(int n);
/** Player status enum
* The different player states.
*/
enum playerStatusEnum
{
playerStatusPlaying, /**< Player is playing "normally" */
playerStatusEntering, /**< Player is entering a not yet cleared room (walking is forced) */
playerStatusAcquire, /**< Player is under acquiring stance */
playerStatusUnlocking, /**< Player is under unlocking stance */
playerStatusPraying, /**< Player is under unlocking stance */
playerStatusStairs, /**< Player walk the stairs */
playerStatusGoingNext, /**< Player goes to next level */
playerStatusDead /**< Player RIP */
};
/*!
* \brief accessor on the player status
*
* Accessor on the player status.
*
* \return : the player status
*/
playerStatusEnum getPlayerStatus();
/*!
* \brief mutator on the player status
*
* Mutator on the player status.
*
* \param player status : the new player status value
*/
void setPlayerStatus(playerStatusEnum playerStatus);
/*!
* \brief accessor on the colliding direction
*
* Accessor on the colliding direction.
*
* \return : the colliding direction
*/
int getCollidingDirection();
/*!
* \brief compute all the player data (stats / items)
*/
void computePlayer();
/*!
* \brief register a new special shot and select it
*
* \param item : Item which provides the shot
*/
void registerSpecialShot(int item);
/*!
* \brief accessor on current shot type
*
* \return : the current shot type
*/
enumShotType getShotType();
/*!
* \brief accessor on current shot level
*
* \return : the current shot level
*/
unsigned int getShotLevel();
/*!
* \brief accessor a shot level
* \param index : the index of the shot
* \return : the shot level
*/
unsigned int getShotLevel(int index);
/*!
* \brief accessor on current shot index
*
* Accessor on the current shot index.
*
* \return : the current shot index
*/
int getShotIndex();
/*!
* \brief mutator on the shot index
*
* Mutator on the shot index.
*
* \param index : the new shot index
*/
void setShotIndex(int index);
/*!
* \brief accessor on current shot type of a slot
*
* \param slot : The slot number
* \return : the current shot type
*/
enumShotType getShotType(int slot);
/*!
* \brief mutator on a shot slot
*
* Mutator on a shot slot.
*
* \param index : the shot index
* \param shotType : the shot type to set
*/
void setShotType(int slot, enumShotType shotType);
/*!
* \brief select the next shot type
*
* Select the next shot type.
*/
void selectNextShotType();
/*!
* \brief accessor on the fairies number
*/
int getFairieNumber();
/*!
* \brief accessor on a fairy
* \param n : index of the fairy in the vector
*/
FairyEntity* getFairy(unsigned int n);
/*!
* \brief checks if the player can get a new shot type
*
* \param advancedShot : true if the shot type is "advanced"
* \return : true if there is a free slot
*/
bool canGetNewShot(bool advancedShot);
/*!
* \brief returns time since player's death
* \return : time since player's death (in seconds)
*/
float getDeathAge();
/*!
* \brief mutator on the player's death age
* \param deathAge : the new player's death (in seconds)
*/
void setDeathAge(float deathAge);
virtual void setMap(GameMap* map, int tileWidth, int tileHeight, int offsetX, int offsetY) override;
/*!
* \brief casts a spell (if possible)
*/
void castSpell();
void onClearRoom();
void resetFloorItem();
void interact(EnumInteractionType interaction, int id);
void worship(enumDivinityType divinity);
void donate(int n);
void offerMonster(enemyTypeEnum monster, enumShotType hurtingType);
void offerHealth(int lostHp);
void offerChallenge();
void addPiety(int n);
void pietyLevelUp();
void loadDivinity(int id, int piety, int level, int interventions);
bool triggerDivinityBefore();
void triggerDivinityAfter();
void divineFury();
void divineDestroyUndead();
void divineIce();
void divineRepulse();
void divineProtection(float duration, float armorBonus);
void divineHeal(int hpHealed);
void setActiveSpell(enumCastSpell spell, bool fromSaveInFight);
castSpellStruct getActiveSpell();
float getPercentSpellDelay();
bool canCastSpell();
enemyTypeEnum getLastHurtingEnemy();
sourceTypeEnum getLastHurtingSource();
virtual void setSpecialState(enumSpecialState state, bool active, float timer, float param1, float param2) override;
void setItemToBuy(ItemEntity* item);
ItemEntity* getItemToBuy();
void castTeleport();
virtual bool collideWithMap(int direction) override;
protected:
virtual void readCollidingEntity(CollidingSpriteEntity* entity);
void generateBolt(float velx, float vely);
void rageFire();
virtual void collideMapRight();
virtual void collideMapLeft();
virtual void collideMapTop();
virtual void collideMapBottom();
virtual void stuck() override;
private:
int fireDamages;
float fireVelocity;
float fireDelay;
float fireAnimationDelay;
float fireAnimationDelayMax;
int fireAnimationDirection;
float spellAnimationDelay;
float spellAnimationDelayMax;
float currentFireDelay;
float randomFireDelay;
float hiccupDelay;
float boltLifeTime;
int gold;
int criticalChance;
float invincibleDelay;
float divineInterventionDelay;
bool isRegeneration;
bool isFairyTransmuted;
bool canFirePlayer;
playerStatusEnum playerStatus;
float statusTimer;
enumItemType acquiredItem;
bool equip[NUMBER_EQUIP_ITEMS];
SpriteEntity* spriteItem;
SpriteEntity* spriteItemStar;
float specialBoltTimer;
enumShotType specialShots[SPECIAL_SHOT_SLOTS];
unsigned int specialShotLevel[SPECIAL_SHOT_SLOTS];
int specialShotIndex;
bool needInitShotType;
int collidingDirection; /*!< Colliding direction (4, 8, 6, 2) to detect collision with closed doors */
int firingDirection;
int keyDirection;
bool willCollideWithMap(int dx, int dy, bool checkMiddle);
std::vector<FairyEntity*> fairies;
float idleAge;
float deathAge;
enemyTypeEnum lastHurtingEnemy;
sourceTypeEnum lastHurtingSource;
int spriteDy;
void renderPlayer(sf::RenderTarget* app);
void renderHalo(sf::RenderTarget* app);
divinityStruct divinity;
+ bool shouldBeSavedFromDivinity;
void fallRock();
void initFallingGrid();
bool fallingGrid[MAP_WIDTH][MAP_HEIGHT];
/*!
* \brief init the current shot type.
*
* Init the current shot type.
* Called when the player get a new shot, or after a switch.
*/
void initShotType();
castSpellStruct activeSpell;
struct protectionStruct
{
bool active;
float value;
float timer;
} protection;
void castSummonsSlimeExplode();
void castFireball();
void castFreeze();
void castEarthquake();
void castProtection();
void castWeb();
void castSummonsFlower();
void castTransmuteFairy();
ItemEntity* itemToBuy;
float getBolPositionY();
void incrementDivInterventions();
};
#endif // PLAYERSPRITE_H
diff --git a/src/WitchBlastGame.h b/src/WitchBlastGame.h
index 330d408..de53020 100644
--- a/src/WitchBlastGame.h
+++ b/src/WitchBlastGame.h
@@ -1,989 +1,989 @@
/** 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 "DungeonMapEntity.h"
#include "EnemyEntity.h"
#include "DoorEntity.h"
#include "GameFloor.h"
#include "Config.h"
#include "Achievements.h"
#include <queue>
// for tests
//#define TEST_MODE
const int ALIGN_LEFT = 0; /*!< Text alignment left */
const int ALIGN_RIGHT = 1; /*!< Text alignment right */
const int ALIGN_CENTER = 2; /*!< Text alignment centered */
const int X_GAME_FADE_IN = 0; /*!< Fade In effect ID */
const int X_GAME_FADE_OUT = 1; /*!< Fade out effect ID */
const int X_GAME_COLOR_RED = 0; /*!< Red light color effect ID */
const int X_GAME_COLOR_GREEN = 1; /*!< Green light color effect ID */
const int X_GAME_COLOR_BLUE = 2; /*!< Blue light color effect ID */
const int X_GAME_COLOR_VIOLET = 3; /*!< Violet light color effect ID */
const int X_GAME_COLOR_BROWN = 4; /*!< Brown light color effect ID */
const int X_GAME_COLOR_WHITE = 5; /*!< White light color effect ID */
unsigned const int NumberKeys = 13; /*!< Number of input keys on the game */
/** Input key string
* Keys in the config file.
*/
const std::string inputKeyString[NumberKeys] =
{
"key_move_up",
"key_move_down",
"key_move_left",
"key_move_right",
"key_fire_up",
"key_fire_down",
"key_fire_left",
"key_fire_right",
"key_fire_select",
"key_spell",
"key_interact",
"key_time",
"key_fire"
};
/** Credits: coder */
const std::string creditsCode[] =
{
"Seby",
"END"
};
/** Credits: 2D artists */
const std::string credits2D[] =
{
"Pierre \"dejam0rt\" Baron",
"END"
};
/** Credits: Sound */
const std::string creditsSound[] =
{
"www.freesound.org/",
"www.freesfx.co.uk/",
"www.universal-soundbank.com/",
"END"
};
/** Credits: Music */
const std::string creditsMusic[] =
{
"Michael Ghelfi",
"JappeJ",
"SteveSyz",
"CinTer",
"cazok",
"ET16",
"END"
};
/** Credits: Translation */
const std::string creditsTranslate[] =
{
"achpile (russian)",
"AFS (spanish)",
"Geheim (german)",
"END"
};
/** Struct for game parameters */
struct parameterStruct
{
int language; /*!< Language ID (english = 0) */
int musicVolume; /*!< music volume (0 to 100) */
int soundVolume; /*!< sound volume (0 to 100) */
bool zoom; /*!< zoom effect (false = disabled) */
bool vsync; /*!< monitor vsync (false = disabled) */
bool bloodSpread; /*!< blood spread (false = disabled) */
bool fullscreen; /*!< full screen (false = disabled) */
bool displayBossPortrait;
std::string playerName; /*!< player name */
};
/*! \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:
// DATA TYPES
/** Savegame header data */
struct saveHeaderStruct
{
bool ok; /**< Save game OK ? */
int level; /**< Level the save game */
float gameTime; /**< How long it has been played */
int shotType; /**< Current special shot type */
std::string date; /**< Date of the save game */
std::string time; /**< Time of the save game */
};
// PUBLIC METHODS
/*!
* \brief Constructor
*/
WitchBlastGame();
/*!
* \brief Destructor
*/
virtual ~WitchBlastGame();
/*!
* \brief Accessor on the current dungeon map
* \return a pointer to the current dungeon map
*/
DungeonMap* getCurrentMap();
GameFloor* getCurrentFloor();
int getFloorX();
int getFloorY();
/*!
* \brief Accessor on the current dungeon map entity
* \return a pointer to the current dungeon map entity
*/
DungeonMapEntity* getCurrentMapEntity();
/*!
* \brief Accessor on the player
* \return a pointer to the player
*/
PlayerEntity* getPlayer();
/*!
* \brief Accessor on the player's position
* \return a Vector2D of the player's position
*/
Vector2D getPlayerPosition();
/*!
* \brief accessor on the level
* \return : the level
*/
int getLevel();
/*!
* \brief accessor on the level
* \return : the challenge level
*/
int getChallengeLevel();
/*!
* \brief accessor on showLogical flag
* \return : the value of the flag
*/
bool getShowLogical();
float getDeltaTime();
/*!
* \brief accessor on the parameters
* \return : the parameters
*/
parameterStruct getParameters();
/*!
* \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 getEnemyCount();
int getUndeadCount();
void animateEffects();
EnemyEntity* getBoss();
void destroyUndead(int damage);
int getItemsCount();
/*!
* \brief Return the position of the nearest enemy
* \param x : x position of the source
* \param y : y position of the source
* \return position of the nearest enemy (negative position when no enemy found)
*/
Vector2D getNearestEnemy(float x, float y);
/*!
* \brief Generates blood
* \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 Add a corpse on the map
* \param x : x position of the corpse
* \param y : y position of the corpse
* \param frame : frame of the corpse in the spritesheet
*/
void addCorpse(float x, float y, int frame);
/*!
* \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);
/*!
* \brief Make a "color fade" effet
* \param color : the color of the effect
* \param duration : duration of the effect
*/
void makeColorEffect(int color, float duration);
/*!
* \brief Write a string on screen
* \param str : the string
* \param size : the character size
* \param x : x position
* \param y : y position
* \param align : alignment (ALIGN_LEFT, ALIGN_CENTER or ALIGN_RIGHT)
* \param color : color of the string
* \param app : the rendering target
* \param xShadow : offset of the shadow (x)
* \param yShadow : offset of the shadow (y)
*/
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();
/*!
* \brief Save the game data (general)
*/
void saveGameData();
/*!
* \brief Load the game data (general)
*/
void loadGameData();
/*!
* \brief Load the savegame data
* \return the savegame data
*/
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 Returns a random spell object
*/
enumItemType getItemSpell();
/*!
* \brief Generate a random challenge loot
*
* \param x : x position of the bonus
* \param y : y position of the bonus
*/
void generateChallengeBonus(float x, float y);
/*!
* \brief Adds monsters
*
* Adds monsters to the room in suitable places.
* \param monsterType : monster type
* \param amount : amount of monsters
*/
void findPlaceMonsters(enemyTypeEnum monsterType, int amount);
/*!
* \brief Add a monster to the "killed monsters" table
* \param enemyType : ID of the monster
*/
void addKilledEnemy(enemyTypeEnum enemyType, enumShotType damageType);
/*!
* \brief Proceed an event
* \param event : ID of the event
*/
void proceedEvent(EnumWorldEvents event);
/*!
* \brief Proceed a message
* \param event : ID of the message
*/
void testAndAddMessageToQueue(EnumMessages type);
/*!
* \brief Proceed a divinity level up message
* \param label : the message to proceed
*/
void addDivLevelMessageToQueue(std::string label);
std::string enemyToString(enemyTypeEnum enemyType);
std::string sourceToString(sourceTypeEnum sourceType, enemyTypeEnum enemyType);
void saveScreen();
void saveDeathScreen();
struct StructScore
{
std::string name;
int score;
int level;
int shotType;
bool equip[NUMBER_EQUIP_ITEMS];
};
void calculateScore();
void addLifeBarToDisplay(std::string label, int hp, int hpMax);
void revealFloor();
void activateKeyRoomEffect(bool withColorEffect);
void generateStar(sf::Color starColor, float xStar, float yStar);
void registerAchievement(enumAchievementType achievement);
void renderDoors();
bool isPresentItem(int n);
void addPresentItem(int n);
+ /*!
+ * \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();
+
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);
void killArtefactDescription();
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 */
bool showGameTime; /*!< True if showing the game time */
// game play
int level; /*!< Level (floor) */
int score; /*!< score (calculated at the end of the game) */
int bodyCount; /*!< killed monsters (calculated at the end of the game) */
int challengeLevel; /*!< Level (challenge) */
float gameTime; /*!< "age" of the current game */
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 */
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 */
DungeonMapEntity* dungeonEntity; /*!< TileMap of the room (main game board) + blood, items, etc...*/
TileMapEntity* miniMapEntity;
// displaying objects
DoorEntity* doorEntity[4]; /*!< Pointers to the door graphical entity */
sf::Font font; /*!< The font used for displaying text */
sf::Text myText; /*!< The text to be displayed */
sf::Sprite introScreenSprite;
sf::Sprite titleSprite;
struct uiSpritesStruct
{
sf::Sprite gui;
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::Sprite topLayer;
sf::Sprite msgBoxSprite;
sf::Sprite iconSprite;
sf::Sprite mapBgSprite;
} uiSprites;
struct lifeBarStruct
{
bool toDisplay;
std::string label;
int hp;
int hpMax;
} lifeBar;
float xOffset, yOffset; /*!< Main game client position in the GUI */
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 */
MusicChallenge /**< Challenge music - for epic fights ! */
};
/** Game states enum
* Used for the different game states
*/
enum gameStateEnum
{
gameStateInit, /**< Game initialization */
gameStateIntro, /** < Intro animation */
gameStateMenu, /**< Menu */
gameStateKeyConfig, /**< Key config */
gameStatePlaying, /**< Playing */
gameStatePlayingPause, /**< Playing / Pause */
gameStatePlayingDisplayBoss, /**< Playing / DisplayBoss */
};
gameStateEnum gameState; /*!< Store the game state */
float bossDisplayTimer;
int bossDisplayState;
/** Special game states enum
* Used for effects such as fade in...
*/
enum xGameTypeEnum
{
xGameTypeFade, /**< Fade effect */
xGameTypeShake, /**< Shake effect */
xGameTypeFadeColor, /**< Color fade effect */
NB_X_GAME
};
struct xGameStruct
{
bool active;
int param;
float timer;
float duration;
} xGame[NB_X_GAME];
/** Input Keys enum
* Used for the input binding
*/
enum inputKeyEnum
{
KeyUp,
KeyDown,
KeyLeft,
KeyRight,
KeyFireUp,
KeyFireDown,
KeyFireLeft,
KeyFireRight,
KeyFireSelect,
KeySpell,
KeyInteract,
KeyTimeControl,
KeyFire
};
sf::Keyboard::Key input[NumberKeys]; /*!< Input key array */
std::string scoreSaveFile;
/*!
* \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, int startingLevel);
/*!
* \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.
* \param isFight : true if loading in fight
*/
void playLevel(bool isFight);
/*!
* \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 map items and sprites.
*/
void refreshMap();
/*!
* \brief Check if a door is present, and its style
*
* \param doorId : index of the door
* \param roomCurrent : type of the current door
* \param roomNeighbour : type of the neighbour door
*/
void checkDoor(int doorId, roomTypeEnum roomCurrent, roomTypeEnum roomNeighbour);
/*!
* \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(enemyTypeEnum 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 Checks if player can interact with something
*/
void checkInteraction();
/*!
* \brief Plays a music
*
* Plays a music.
*
* \param musicChoice : music track ID
*/
void playMusic(musicEnum musicChoice);
/*!
* \brief Update the music volume (with parameters.volumeMusic)
*/
void updateMusicVolume();
/*!
* \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();
void renderGame();
void renderHud();
void renderLifeBar();
void renderMessages();
void renderBossPortrait();
/*!
* \brief Update the menu
*/
void updateMenu();
/*!
* \brief Render the menu
*/
void renderMenu();
/*!
* \brief Render the menu
*/
void renderInGameMenu();
/*!
* \brief initialize the intro
*/
void prepareIntro();
/*!
* \brief Update the intro
*/
void updateIntro();
/*!
* \brief Render the intro
*/
void renderIntro();
/*!
* \brief Render the credits screen
*/
void renderCredits();
/*!
* \brief Render the achievements screen
*/
void renderAchievements();
/*!
* \brief Render the scores screen
*/
void renderHiScores();
/** Menu keys enum
* Identify the various keys of the menu.
*/
enum menuItemEnum
{
MenuStartNew, /**< When starting the game */
MenuStartOld, /**< When restoring the game */
MenuConfig, /**< When configuring the game */
MenuKeys, /**< When configuring keys */
MenuConfigBack, /**< Back to the main menu */
MenuTutoReset, /**< Reset the tutorials */
MenuLanguage, /**< When configuring the language */
MenuExit, /**< When exiting the game */
MenuAchievements, /**< Display the Achievements */
MenuCredits, /**< Display the credits screen */
MenuHiScores, /**< Display the hi-scores screen */
MenuPlayerName, /**< To enter/change the player name */
MenuVolumeSound,
MenuVolumeMusic,
MenuContinue, /**< Continue the game */
MenuSaveAndQuit, /**< Save and return to main */
};
/** Menu states enum
* Identify the various states of the menu.
*/
enum menuStateEnum
{
MenuStateMain,
MenuStateConfig,
MenuStateKeys,
MenuStateHiScores,
MenuStateChangeName,
MenuStateCredits,
MenuStateAchievements,
MenuStateFirst /**< First time, we choose language and keyboard */
};
menuStateEnum menuState;
/*!
* \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 */
float age; /**< Age of the menu */
};
menuStuct menuMain;
menuStuct menuFirst;
menuStuct menuConfig;
menuStuct menuInGame;
unsigned int menuKeyIndex;
unsigned int menuAchIndex;
/*!
* \brief Build the menu items
*/
void buildMenu(bool rebuild);
/*!
* \brief Build the menu items (in game)
*/
void buildInGameMenu();
/*!
* \brief Switch to the menu
*/
void switchToMenu();
/*!
* \brief Save language config and default keys configuration
*/
void registerLanguage();
parameterStruct parameters;
void resetKilledEnemies();
std::queue <messageStruct> messagesQueue;
bool worldEvent[NB_EVENTS];
void initEvents();
bool gameMessagesToSkip[NB_MESSAGES];
struct achievementStruct
{
enumAchievementType type;
std::string message;
float timer;
bool hasStarted;
int counter;
};
std::queue <achievementStruct> achievementsQueue;
void renderPlayer(float x, float y, bool equip[NUMBER_EQUIP_ITEMS], int shotType,
int frame, int spriteDy);
void renderDeathScreen(float x, float y);
bool equipToDisplay[NUMBER_EQUIP_ITEMS];
bool equipNudeToDisplay[NUMBER_EQUIP_ITEMS];
saveHeaderStruct saveHeader;
struct StructMonster
{
enemyTypeEnum id;
float x;
float y;
};
/*!
* \brief Data for "in fight" game saving
*/
struct StructSaveInFight
{
bool isFight; /**< True if loading during a fight */
float x; /**< Player x position */
float y; /**< Player y position */
int direction; /**< Player direction */
std::vector<StructMonster> monsters; /**< Monsters */
};
StructSaveInFight saveInFight; /**< Data for "in fight" game saving */
SpriteEntity* introSprites[8];
int introState;
int introSoundState;
std::vector <StructScore> scores;
StructScore lastScore;
void loadHiScores();
void saveHiScores();
struct StructInteraction
{
bool active;
EnumInteractionType type;
int id;
std::string label;
} interaction;
void enableAA(bool enable);
enum achievementStatus { AchievementDone, AchievementUndone, AchievementPending};
achievementStatus achievementState[NB_ACHIEVEMENTS];
bool isItemLocked(enumItemType item);
bool isFunctionalityLocked(enumFunctionalityType func);
int getAchievementsPercents();
struct globalDataStruct
{
int killedMonster[NB_ENEMY];
} globalData;
void generateUiParticle(float x, float y);
bool presentItems[NUMBER_EQUIP_ITEMS];
void resetPresentItems();
void checkJoypad();
};
/*!
* \brief Returns the game reference
*
* Returns the game reference.
*
* \return a reference to the game
*/
WitchBlastGame& game();
#endif // WITCH_BLAST_GAME_H
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Jun 17, 9:33 PM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
69883
Default Alt Text
(130 KB)
Attached To
Mode
R78 witchblast
Attached
Detach File
Event Timeline