-
Notifications
You must be signed in to change notification settings - Fork 0
Buffs & Debuffs
Buffs & Debuffs allow for the player to gain temporary enhancements and ailments which provide a platform for more interesting gameplay. Buffs will allow the player to counteract the increasing rate and frequency of obstacles and enemies as they progress through the game. Debuffs will provide the player with ailments which makes gameplay harder for a period of time.
To see the list of buffs and debuffs which are currently available, see the Buff Catalogue and Debuff Catalogue
A BuffManager
handles the tracking, removal, spawning and despawning of buffs and debuffs on the map. Buffs and debuffs are all Entity
objects with particular Component
s applied. A BuffFactory
handles the instantiation of the Entity
, and registers the buff or debuff with the BuffManager
. All buffs / debuffs have a BuffInformation
object which keeps track of the buff and it's relevant attributes, such as it's Type
, effectTime
and Entity
ID
. Buffs and debuffs are retrieved by the player colliding with them.
A single buff / debuff spawns on the map every 3 seconds, and will time-out and disappear every 10 seconds. Some buffs are 'instant' and some are 'timed', ie, they impact the player exactly once immediately, or the effects of the buff remain on the player for a set period, respectively. The selection of the buff is entirely random. In future, the spawn and despawn rates may differ, and weights may be added to the selection process for more impactful buffs.
A PlayerBuffs
class centrally holds the functionality of all buffs & debuffs which spawn on the map. This class also handles removing the lasting effects of timed buffs.
Currently effective timed buffs are displayed in the top-left player UI
.
Instant buffs are those which are applied to the player once, at the time of impact. Some examples are:
- Set the players health to full
- Increase the players health
- Decrease the players health
Timed buffs are those whose effects remain with the player for a set period. Currently, the standard time-out period of these effects is 5 seconds. Some examples are:
- Make the player invincible for 5 seconds
- Make all damage taken by the player doubled for 5 seconds
- Make the player inflict double damage to enemies for 5 seconds.
For readability, buffs & debuffs are just referred to as 'buffs' here.
In order for the buffs to spawn on uneven terrain, a function within TerrainFactory
was written to retrieve the y-coordinate of the surface tile with the given x-coordinate. The function reads the same file that was used when filling the surface tiles on the terrain, which gives information on the y-coordinate depending on the x-coordinate.
public int getYOfSurface(int x, GdxGame.ScreenType screenType) {
int y = 0;
String filename = null;
if (screenType == GdxGame.ScreenType.MAIN_GAME) {
filename = "level-floors/levelOne.txt";
} else if (screenType == GdxGame.ScreenType.LEVEL_TWO_GAME) {
filename = "level-floors/levelTwo.txt";
}
try(BufferedReader br = new BufferedReader(new FileReader(filename))) {
String line = br.readLine();
int referenceX = 0, referenceY = 0, width = 0, distance = 0;
try(BufferedReader br = new BufferedReader(new FileReader(filename))) {
String line = br.readLine();
int referenceX = 0, referenceY = 0, width = 0, distance = 0;
while (line != null) {
String[] values = line.split(" ");
width = Integer.parseInt(values[0]);
referenceX = Integer.parseInt(values[1]);
referenceY = Integer.parseInt(values[2]);
distance = (width * 2) + referenceX;
// Checks if the given x-coordinate is between the x range given on this line of the file.
if (x >= referenceX && x <= distance) {
y = referenceY;
break;
} else {
line = br.readLine();
}
} ...
return y;
}
When spawning the buffs in ForestGameArea
or any other game a random x-position within the bounds of the map is chosen and it's corresponding y-coordinate is retrieved using the function above and the buff is spawned at that position.
If you would like to create a new buff, go to the BuffManager
class and add your buff name to the BuffTypes
enum
. For consistency, the naming convention of buffs & debuffs is:
- Buffs are preceded by a 'B' if they are not timed
- Buffs which are timed are preceded by a 'BT'
- Debuffs are preceded by a 'D' if they are not timed
- Debuffs which are timed are preceded by a 'DT'
For example, the BuffTypes
enum
may look like:
public enum BuffTypes {
B_HP_UP, // Increase the players HP on touch
D_HP_DOWN, // Decrease the players HP on touch
BT_INVINCIBLE, // Make the player invincible for 5 seconds
DT_DOUBLE_HURT // Make the player take double damage for 5 seconds
}
After the name is created, go to the BuffManager
getTexture
function. This function handles returning a filepath to the image for the buff type. Add your new BuffType
to the switch
statement with your filepath as the return. This file must have been loaded by the MainGameScreen
for this to work. If a new file is not specified for a new buff, it will be given a default image.
For example, the switch
statement inside the getTexture
method may look like:
switch (type) {
case BT_INVINCIBLE:
return "images/invincibleBuff.png";
case B_HP_UP:
return "images/increaseHPBuff.png";
}
To specify the behaviour of the buff, a function must be created in PlayerBuffs
. PlayerBuffs
contains static functions which control the behaviour of buffs, including removing their effects (for timed buffs). Find the correct section of PlayerBuffs
(either buffs or debuffs) and add in your new function.
For example, PlayerBuffs
may look like:
/** --- Buffs --- ** /
public static void increasePlayerHP(Entity player) {
// Actions to take to increase the players HP once
}
...
/** --- Debuffs --- ** /
public static void setDoubleHurt(Entity player) {
// Actions to take to make the player take double damage
}
Once a PlayerBuffs
function is defined, add this function call to the appropriate switch
statement in the BuffManager
s selectBuffFunctionality
method. For example, selectBuffFunctionality
may look like:
// Method calls for instant buffs
switch (type) {
case B_HP_UP:
PlayerBuffs.increasePlayerHP(this.player);
return;
case D_HP_DOWN:
PlayerBuffs.reducePlayerHP(this.player);
return;
}
...
// Method calls for timed buffs
switch (type) {
case BT_INVINCIBLE:
PlayerBuffs.applyInvincibility(this.player);
break;
case DT_NO_JUMP:
PlayerBuffs.noJumping(this.player);
break;
}
Note that the PlayerBuffs
function should only take the player
as an argument.
And that's it! You've just created a new buff! Your buff is automatically added to the pool of buffs that will spawn. However..
Timed buffs require some extra additions before they will work correctly. First, when adding new buff functionality to the PlayerBuffs
class, you may have noticed the removeTimedBuff
method. Add a new method below this which controls the behaviour of removing the effects of your buff. Note that this method should only take the player
as an argument. For example;
/** --- Removing timed effects --- ** /
public static void removeInvincibility(Entity player) {
// Actions to take to remove invincibility
}
Note: the PlayerBuffs
class does not handle checking for buff time-outs. If a function which is removing the effects is called, then the timer is already up.
Add your new BuffType
to the switch
statement in the removeTimedBuff
method, and call the buff-removal function with the player
that is passed into removeTimedBuff
as an argument.
For example, removeTimedBuff
may look like:
switch (type) {
case BT_INVINCIBLE:
removeInvincibility(player);
break;
case DT_NO_JUMP:
removeNoJumping(player);
break;
}
The BuffInformation
constructor must also be updated, to allow the UI
to display the name of the currently active buffs. Add your new BuffType
to the BuffInformation
constructors' switch
statement, and set your BuffName
and effectTimeout
.
The BuffName
is a string which will be displayed when the player is under the effect of the timed buff or debuff. The effectTimeout
is how long the timed buffs' effects will affect the player for.
For example, the switch
statement may look like:
switch (type) {
case BT_INVINCIBLE:
this.buffName = "Invincibility!" // Tag to display when the player is invincible
setEffectTimeout(5 * SECONDS); // How long the player should stay invincible for after attaining buff
break;
case DT_NO_JUMP:
this.buffName = "No Jumping!"
setEffectTimeout(5 * SECONDS);
break;
}
And you're done!
If a new level is created, within GetYofSurface()
add the filename that was used to fill out the surface tiles:
String filename = null;
if (screenType == GdxGame.ScreenType.MAIN_GAME) {
filename = "level-floors/levelOne.txt";
} else if (screenType == GdxGame.ScreenType.LEVEL_TWO_GAME) {
filename = "level-floors/levelTwo.txt";
}
And then within spawnBuffDebuff
inside the game area, make sure the correct level screen is passed as a parameter through to GetYOfSurface
so that the correct file is read.
Using a separate class for every different buff / debuff was considered. But, since buffs / debuffs are something which should be easy to extend and add in to the game, it seemed easier to avoid having to manually create a new class every time a new buff or debuff was added. Most buffs / debuffs are similar in the way they alter the players state.
The benefit of classes is that information or attributes of particular buffs can be kept somewhere. This functionality is where the BuffInformation
class comes in; a single class which holds all of the information which an individual buff or debuff class would. This means that whenever a new buff is added, only pre-existing code needs to be appended, resulting in fewer redundant & repeated classes, and ultimately a cleaner workspace.
- Player UI
- Popup Menus
- Obstacles
- Boss Enemies
- Progress Tracker
- Checkpoint Design and Functionality
- Score System
- Lives System
- Game Background
- Multiple game-level
- Visual Improvements
- Tutorial Level
- Character Design and Animations
- Character Damage Animations
- Player Animation Functionalities
- Player and Serpent Portal Transition
- Pop-up Menus
- Obstacles
- Lives & Score User Testing
- Buffs & Debuffs
- Buffs & Debuffs redesign
- Obstacle Animation
- Background Design
- Level 2 Background Appearance
- Enemy Monster User Testing
- Level 1 Floor Terrain Testing
- Introduction Screens User Testing
- Character Movement Interviews & User Testing
- Sound user testing
- Level 2 Obstacles and enemy
- Story, Loading, Level 4 and Win Condition Sound Design User Testing
- Giant Bug and Purple Squid animation user testing
- General Gameplay and Tutorial Level User Testing
- Level 4 Terrain User Testing
- Game Outro User Testing