Browse Source

Added fire bomb, force phase, fire dragon, control wizard, adjusted balance...

TracyIntegration
Macoy Madson 5 years ago
parent
commit
f6ad585b72
  1. 33
      TODO.tasks
  2. 222
      src/Abilities.cpp
  3. 30
      src/Abilities.hpp
  4. 78
      src/Enemies.cpp
  5. 20
      src/Enemies.hpp
  6. 22
      src/Entity.cpp
  7. 1
      src/Entity.hpp
  8. 2
      src/Gameplay.cpp
  9. 35
      src/Globals.hpp

33
TODO.tasks

@ -6,36 +6,19 @@ enemyImpl
Potential Features
-------
Nice fog effect by having radius around the player plus some noise, changing transparency of every char
Ignore input when not focused
Speed doesn't actually work
Text wrap on small windows
Enemies pathfind to player (actually, just randomly go in a dir sometimes to not get stuck)
Make color scheme consistent!!!
Sound FX!
Random spawn in cube doesn't work as expected
Todo
-------
Figure out why it seems to crash (is it a sublime problem?)
Improve position description
Random spawn in cube doesn't work as expected
REMOVE ENTER CLOSES GAME!
Second restart lighting hitting caller no stairs appeared
Show last screen on death
@ -50,6 +33,20 @@ NEXT: Add abilities!
Done
-------
Show last screen on death
REMOVE ENTER CLOSES GAME!
Improve position description
Figure out why it seems to crash (is it a sublime problem?)
Nice fog effect by having radius around the player plus some noise, changing transparency of every char
Ignore input when not focused
Text wrap on small windows
Window resize
Fix melee combat moving into enemy on death

222
src/Abilities.cpp

@ -79,16 +79,39 @@ void Ability::FxUpdate(float frameTime)
Ability* getNewRandomAbility()
{
int numOptions = 2;
switch (rand() % numOptions)
if (gameState.currentLevel <= LEVEL_NUM_FOREST)
return new PhaseDoor();
else if (gameState.currentLevel <= LEVEL_NUM_BARREN)
{
case 0:
return new LightningAbility();
case 1:
return new PhaseDoor();
default:
return new PhaseDoor();
int numOptions = 3;
switch (rand() % numOptions)
{
case 0:
return new LightningAbility();
case 1:
return new PhaseDoor();
case 2:
return new PhaseTarget();
default:
return new PhaseDoor();
}
}
else if (gameState.currentLevel <= LEVEL_NUM_HELLSCAPE)
{
int numOptions = 3;
switch (rand() % numOptions)
{
case 0:
return new LightningAbility();
case 1:
return new FireBomb();
case 2:
return new PhaseTarget();
default:
return new PhaseDoor();
}
}
return new PhaseDoor();
}
@ -127,6 +150,8 @@ void LightningAbility::EnemyActivate(Enemy* enemyActivator)
gameState.abilitiesUpdatingFx.push_back(this);
}
sfxLightning.play();
}
void LightningAbility::PlayerActivateWithTarget(int targetX, int targetY)
@ -281,6 +306,187 @@ void PhaseDoor::PlayerActivateNoTarget()
// Called every frame if Active
void PhaseDoor::FxUpdate(float frameTime)
{
// if (TotalFrameTimeAlive > 1.f)
// return;
}
PhaseTarget::PhaseTarget()
{
RequiresTarget = true;
CooldownTime = 14;
Name = "Phase Target";
Description = "Phase an enemy to a random location";
Damage = 10;
}
bool PhaseTarget::CanActivateOnPlayer(Enemy* enemy)
{
return IsCooldownDone() &&
manhattanTo(enemy->X, enemy->Y, gameState.player.X, gameState.player.Y) <
RANGED_ENEMY_MAX_DIST_MANHATTAN;
}
void PhaseTarget::EnemyActivate(Enemy* enemyActivator)
{
AbilityActivate();
enemyAbilityDamagePlayer(enemyActivator, this);
RLTile* tileAt = gameState.vfx.At(gameState.player.X, gameState.player.Y);
if (tileAt)
{
// tileAt->Type = '!';
tileAt->Color = {FX_PHASE_DOOR};
gameState.abilitiesUpdatingFx.push_back(this);
}
placeEntityWithinSquareRandomSensibly(&gameState.player, enemyActivator->X, enemyActivator->Y,
PHASE_TARGET_ON_PLAYER_RADIUS);
LOGI << "A " << enemyActivator->Description.c_str() << " force teleported you!";
}
void PhaseTarget::PlayerActivateWithTarget(int targetX, int targetY)
{
AbilityActivate();
std::vector<RLEntity*> damageEntities = getEntitiesAtPosition(targetX, targetY);
for (RLEntity* entity : damageEntities)
{
if (!entity->IsTraversable)
{
playerAbilityDamageEntity(this, entity);
placeEntityWithinSquareRandomSensibly(entity, entity->X, entity->Y,
PHASE_TARGET_ON_ENEMY_RADIUS);
}
}
RLTile* tileAt = gameState.vfx.At(targetX, targetY);
if (tileAt)
{
// tileAt->Type = '!';
tileAt->Color = {FX_PHASE_DOOR};
gameState.abilitiesUpdatingFx.push_back(this);
}
}
void PhaseTarget::PlayerActivateNoTarget()
{
LOGE << "Player somehow activated PhaseTarget despite RequiresTarget";
AbilityActivate();
RLTile* tileAt = gameState.vfx.At(gameState.player.X, gameState.player.Y);
if (tileAt)
{
// tileAt->Type = '!';
tileAt->Color = {FX_PHASE_DOOR};
gameState.abilitiesUpdatingFx.push_back(this);
}
}
// Called every frame if Active
void PhaseTarget::FxUpdate(float frameTime)
{
// if (TotalFrameTimeAlive > 1.f)
// return;
}
FireBomb::FireBomb()
{
RequiresTarget = true;
CooldownTime = 20;
Name = "Fire Bomb";
Description = "Engulf a target in a ball of fire";
Damage = 50;
}
bool FireBomb::CanActivateOnPlayer(Enemy* enemy)
{
return IsCooldownDone() &&
manhattanTo(enemy->X, enemy->Y, gameState.player.X, gameState.player.Y) <
RANGED_ENEMY_MAX_DIST_MANHATTAN;
}
void fireBombActivate(int X, int Y, FireBomb* bomb, bool isActivatorPlayer)
{
for (int fireY = Y - FIREBOMB_RADIUS; fireY < Y + FIREBOMB_RADIUS; ++fireY)
{
for (int fireX = X - FIREBOMB_RADIUS; fireX < X + FIREBOMB_RADIUS; ++fireX)
{
std::vector<RLEntity*> damageEntities = getEntitiesAtPosition(fireX, fireY);
for (RLEntity* entity : damageEntities)
{
if (!entity->IsTraversable)
{
if (isActivatorPlayer)
enemyAbilityDamageEntity(bomb, entity);
else
playerAbilityDamageEntity(bomb, entity);
}
}
RLTile* tileAt = gameState.vfx.At(fireX, fireY);
if (tileAt)
{
// tileAt->Type = '!';
tileAt->Color = {FX_FIREBOMB};
}
}
}
}
void FireBomb::EnemyActivate(Enemy* enemyActivator)
{
AbilityActivate();
enemyAbilityDamagePlayer(enemyActivator, this);
fireBombActivate(enemyActivator->X, enemyActivator->Y, this, false);
gameState.abilitiesUpdatingFx.push_back(this);
}
void FireBomb::PlayerActivateWithTarget(int targetX, int targetY)
{
AbilityActivate();
std::vector<RLEntity*> damageEntities = getEntitiesAtPosition(targetX, targetY);
for (RLEntity* entity : damageEntities)
{
if (!entity->IsTraversable)
playerAbilityDamageEntity(this, entity);
}
fireBombActivate(targetX, targetY, this, true);
gameState.abilitiesUpdatingFx.push_back(this);
}
void FireBomb::PlayerActivateNoTarget()
{
LOGE << "Player somehow activated FireBomb despite RequiresTarget";
AbilityActivate();
RLTile* tileAt = gameState.vfx.At(gameState.player.X, gameState.player.Y);
if (tileAt)
{
// tileAt->Type = '!';
tileAt->Color = {FX_FIREBOMB};
gameState.abilitiesUpdatingFx.push_back(this);
}
}
// Called every frame if Active
void FireBomb::FxUpdate(float frameTime)
{
// if (TotalFrameTimeAlive > 1.f)
// return;

30
src/Abilities.hpp

@ -76,6 +76,36 @@ struct PhaseDoor : public Ability
virtual void PlayerActivateWithTarget(int targetX, int targetY);
virtual void PlayerActivateNoTarget();
// Called every frame if Active
virtual void FxUpdate(float frameTime);
};
struct PhaseTarget : public Ability
{
PhaseTarget();
virtual ~PhaseTarget() = default;
virtual bool CanActivateOnPlayer(Enemy* enemy);
virtual void EnemyActivate(Enemy* enemyActivator);
virtual void PlayerActivateWithTarget(int targetX, int targetY);
virtual void PlayerActivateNoTarget();
// Called every frame if Active
virtual void FxUpdate(float frameTime);
};
struct FireBomb : public Ability
{
FireBomb();
virtual ~FireBomb() = default;
virtual bool CanActivateOnPlayer(Enemy* enemy);
virtual void EnemyActivate(Enemy* enemyActivator);
virtual void PlayerActivateWithTarget(int targetX, int targetY);
virtual void PlayerActivateNoTarget();
// Called every frame if Active
virtual void FxUpdate(float frameTime);
};

78
src/Enemies.cpp

@ -35,7 +35,7 @@ LevelEnemy::LevelEnemy()
{
Type = DRAGON_TYPE;
Color = {ENEMY_COLOR_NORMAL};
Description = "drake";
Description = "baby drake";
Stats["HP"] = {"Health", 60, 60, 0, 0, -1};
Stats["STR"] = {"Strength", 30, 30, 0, 0, -1};
@ -121,6 +121,30 @@ void Summoner::DoTurn()
LOGD << "Spawn LightningWizard at " << newLightningWizard->X << ", "
<< newLightningWizard->Y << " spawn rate " << spawnRate;
}
// Add offset so they aren't spawned on same turn
if ((TurnCounter + (rand() % 8)) % spawnRate == 0 && NumSpawns < MAX_SINGLE_SUMMONS)
{
NumSpawns++;
ControlWizard* newControlWizard = new ControlWizard();
placeEntityWithinSquareRandomSensibly(newControlWizard, X, Y, SUMMONING_RADIUS);
gameState.npcsToCreate.push_back(newControlWizard);
LOGD << "Spawn ControlWizard at " << newControlWizard->X << ", " << newControlWizard->Y
<< " spawn rate " << spawnRate;
}
}
if (gameState.currentLevel > LEVEL_NUM_BARREN)
{
if ((TurnCounter + (rand() % 8)) % spawnRate == 0 && NumSpawns < MAX_SINGLE_SUMMONS)
{
NumSpawns++;
FireDragon* newFireDragon = new FireDragon();
placeEntityWithinSquareRandomSensibly(newFireDragon, X, Y, SUMMONING_RADIUS);
gameState.npcsToCreate.push_back(newFireDragon);
LOGD << "Spawn FireDragon at " << newFireDragon->X << ", " << newFireDragon->Y
<< " spawn rate " << spawnRate;
}
}
}
@ -146,4 +170,56 @@ void LightningWizard::DoTurn()
if (lightning.CanActivateOnPlayer(this))
lightning.EnemyActivate(this);
}
ControlWizard::ControlWizard()
{
SpawnStairsDown = false;
Speed = 1;
Type = CONTROL_WIZARD_TYPE; // TODO: Replace with define?
Color = {ENEMY_COLOR_NORMAL};
Description = "control mage";
Stats["HP"] = {"Health", 10, 10, 0, 0, -1};
Stats["STR"] = {"Strength", 20, 20, 0, 0, -1};
}
void ControlWizard::DoTurn()
{
CheckDoDeath();
if (!Stats["HP"].Value)
return;
if (manhattanTo(X, Y, gameState.player.X, gameState.player.Y) >
LEVELENEMY_PLAYER_DETECT_MANHATTAN_RADIUS)
RandomWalk();
else
MoveTowardsPlayer();
if (phaseTarget.CanActivateOnPlayer(this))
phaseTarget.EnemyActivate(this);
}
FireDragon::FireDragon()
{
SpawnStairsDown = false;
Speed = 1;
Type = FIRE_DRAGON_TYPE; // TODO: Replace with define?
Color = {ENEMY_COLOR_NORMAL};
Description = "dragon";
Stats["HP"] = {"Health", 80, 80, 0, 0, -1};
Stats["STR"] = {"Strength", 20, 20, 0, 0, -1};
}
void FireDragon::DoTurn()
{
CheckDoDeath();
if (!Stats["HP"].Value)
return;
MoveTowardsPlayer();
if (fireBomb.CanActivateOnPlayer(this) && rand() % DRAGON_FIRE_RATE == 0)
fireBomb.EnemyActivate(this);
}

20
src/Enemies.hpp

@ -41,5 +41,25 @@ public:
LightningWizard();
virtual ~LightningWizard() = default;
virtual void DoTurn() override;
};
class ControlWizard : public Enemy
{
PhaseTarget phaseTarget;
public:
ControlWizard();
virtual ~ControlWizard() = default;
virtual void DoTurn() override;
};
class FireDragon : public Enemy
{
FireBomb fireBomb;
public:
FireDragon();
virtual ~FireDragon() = default;
virtual void DoTurn() override;
};

22
src/Entity.cpp

@ -62,9 +62,8 @@ void Player::Initialize()
for (Ability* ability : Abilities)
ability = nullptr;
// Testing only TODO: Remove!! ez pz
Abilities[0] = new LightningAbility();
Abilities[0] = new PhaseDoor();
// Abilities[0] = new LightningAbility();
// Abilities[0] = new PhaseDoor();
}
Player::Player()
@ -181,13 +180,12 @@ void Player::DoTurn()
ForStatName(stat, statName, this)
{
// Special case: HP doesn't restore if stamina isn't full
if (statName == "HP" && Stats["SP"].Value != Stats["SP"].Max)
if (statName == "HP" &&
(ENABLE_ONLY_HEAL_FULL_STAMINA && Stats["SP"].Value != Stats["SP"].Max))
stat.RestoreOnTurn++;
else
stat.RestoreOnTurn -= restoreTurnBonus;
// LOGD << "Stat " << statName.c_str() << " Value " << stat.Value << " Restore on turn "
// << stat.RestoreOnTurn;
if (TurnCounter >= stat.RestoreOnTurn)
{
stat.Add(stat.RestoreAmount);
@ -417,6 +415,18 @@ void enemyAbilityDamagePlayer(RLEntity* entity, Ability* ability)
}
}
void enemyAbilityDamageEntity(Ability* ability, RLEntity* entity)
{
int damage = -ability->Damage;
if (damage)
{
entity->Stats["HP"].Add(damage);
LOGI << "A " << entity->Description.c_str() << " takes " << abs(damage) << " damage "
<< " from " << ability->Name.c_str();
}
}
void playerAbilityDamageEntity(Ability* ability, RLEntity* entity)
{
int damage = -ability->Damage;

1
src/Entity.hpp

@ -132,6 +132,7 @@ bool playerCanUseStairsNow(std::string* stairsDescriptionOut);
void enemyAbilityDamagePlayer(RLEntity* entity, Ability* ability);
void enemyMeleeAttackPlayer(RLEntity* entity);
void enemyAbilityDamageEntity(Ability* ability, RLEntity* entity);
void playerAbilityDamageEntity(Ability* ability, RLEntity* entity);
void playerMeleeAttackEnemy(RLEntity* entity);

2
src/Gameplay.cpp

@ -626,6 +626,8 @@ bool PlayGame()
if (gameState.currentLevel > NUM_LEVELS_TO_WIN)
sfxPlayerVictory.play();
else
sfxNextLevel.play();
gameState.player.LevelUp();

35
src/Globals.hpp

@ -42,8 +42,9 @@
#define STAIRS_COLOR_NORMAL 252, 145, 57, 255
// FX
#define FX_LIGHTNING 0, 0, 255, 155
#define FX_PHASE_DOOR 0, 255, 0, 155
#define FX_LIGHTNING 41, 94, 243, 150
#define FX_PHASE_DOOR 249, 212, 35, 150
#define FX_FIREBOMB 232, 30, 34, 150
//
// Tile/Entity Types
@ -55,8 +56,10 @@
#define SUMMONER_TYPE 'c'
#define GOBLIN_TYPE 'g'
#define BANDIT_TYPE 'h'
#define DRAGON_TYPE 'd'
#define DRAGON_TYPE 'b'
#define FIRE_DRAGON_TYPE 'd'
#define WIZARD_TYPE 'w'
#define CONTROL_WIZARD_TYPE 'm'
#define CORPSE_TYPE '%'
#define ABILITY_TYPE '!'
@ -77,16 +80,17 @@
#define PLAYER_DEFAULT_RESTORE_RATE_HEALTH 2
#define PLAYER_STARTING_MAX_STAMINA 100
#define PLAYER_STARTING_RESTORE_STAMINA 10
#define PLAYER_DEFAULT_RESTORE_RATE_STAMINA 5
#define PLAYER_STARTING_RESTORE_STAMINA 5
#define PLAYER_DEFAULT_RESTORE_RATE_STAMINA 3
#define PLAYER_STARTING_MAX_STRENGTH 10
#define ENABLE_ONLY_HEAL_FULL_STAMINA false
#define ENABLE_OVEREXERTION false
// If the player rests, the number of turns to restore goes down by this number
#define PLAYER_RESTING_BONUS 1
#define PLAYER_MELEE_ATTACKING_BONUS -1
#define PLAYER_RESTING_BONUS 2
#define PLAYER_MELEE_ATTACKING_BONUS 0
#define PLAYER_NUM_ABILITY_SLOTS 3
@ -95,8 +99,8 @@
// The absolute max a single summoner can spawn
#define MAX_SINGLE_SUMMONS 100
#define SUMMONING_RADIUS 3
#define SUMMONER_SPAWN_RATE_TURNS 40
#define SUMMONING_RADIUS 5
#define SUMMONER_SPAWN_RATE_TURNS 25
// Each level, spawn rate turns cooldown decreases by this * level (0.5 = every other level lower
// spawn rate by 1)
#define SUMMONER_SPAWN_RATE_LEVEL_MULTIPLIER 0.5f
@ -108,10 +112,17 @@
// Each death, chance of ability dropping = this * level
#define DEATH_ABILITY_DROP_LEVEL_MULTIPLIER 5.f
// Dragons don't shoot fire too often
#define DRAGON_FIRE_RATE 18
//
// Abilities
//
#define PHASE_DOOR_SQUARE_RADIUS 20
#define PHASE_TARGET_ON_PLAYER_RADIUS 15
#define PHASE_TARGET_ON_ENEMY_RADIUS 15
#define FIREBOMB_RADIUS 2
//
// Levels
@ -126,12 +137,12 @@
// Number of level enemies spawned = this * level
#define LEVELENEMY_SPAWN_NUM_MULTIPLIER_FOREST 7.f
#define LEVELENEMY_SPAWN_NUM_MULTIPLIER_BARREN 10.f
#define LEVELENEMY_SPAWN_NUM_MULTIPLIER_HELLSCAPE 11.f
#define LEVELENEMY_SPAWN_NUM_MULTIPLIER_HELLSCAPE 5.f
// Size of one axis e.g. total tiles = FOREST_SIZE * FOREST_SIZE
#define FOREST_SIZE 80
#define BARREN_SIZE 120
#define HELLSCAPE_SIZE 100
#define BARREN_SIZE 100
#define HELLSCAPE_SIZE 80
//
// Only display once in log strings

Loading…
Cancel
Save