PLS implement these functions! typedef enum type { ATTACK, DEFEND, HEAL } decisi
ID: 3765426 • Letter: P
Question
PLS implement these functions!
typedef enum type
{
ATTACK,
DEFEND,
HEAL
} decisionType;
typedef struct bossDecision_
{
decisionType type;
struct bossDecision_* next;
} bossDecision;
typedef struct bossDecisionNode_
{
struct bossDecisionNode_* leftNode;
struct bossDecisionNode_* rightNode;
int healthMin;
int healthMax;
bossDecision* topOfStack;
} bossDecisionNode;
typedef struct shield
{
char name[50];
int price;
int defense;
int weight;
} Shield;
typedef struct sword
{
char name[50];
int price;
int damage;
} Sword;
typedef struct hero
{
char name[30];
int health;
int maxHealth;
int defense;
int speed;
int attack;
int luck;
decisionType decision;
Shield* shield;
Sword* sword;
//can only heal fully once, so false means hero heals for a fraction
bool hasHealed;
} Hero;
typedef struct boss
{
char name[30];
int health;
int maxHealth;
int defense;
int speed;
int attack;
int luck;
bossDecisionNode* root;
decisionType nextAction;
} Boss;
typedef struct players
{
Hero* hero;
Boss* boss;
struct players* next;
} Player;
Explanation / Answer
Learning C With Game Concepts/Designing A Roleplaying Game
< Learning C With Game Concepts
Now that we've covered the basics of compilation and modularity (we'll refine that section later) lets move on to game design. Our game will be implemented using the terminal and will not rely on any third party libraries, save for the ones that come with C by default. We'll take a Bottom-Up approach to creating our Role-playing Game (hereafter referred to as an RPG).
Contents
Specifications
When undergoing a new project of some complexity, it is important to do some brainstorming about what your program will do and how you'll implement it. While that may sound painfully obvious, it is often tempting to jump right into coding and implement ideas as they pop into your head. It isn't until the code becomes hundreds of lines long does it become clear that organizing your thoughts is necessary. The initial brainstorming phase takes the main idea, an RPG in this case, and breaks it down into more elementary pieces. For each element, we either break it down into still smaller elements, or give a short summary of how we might implement it. In a commercial environment, this brainstorming session produces a Specification Document.
At the heart of every RPG, we have players.
Players should
Stats Easy. Just a variable telling us what the stat is for (like, health) and containing an integer value.
Talking To facilitate conversation, players need scripted dialog. This dialog could be stored with the main character, or the person with whom the main character will interact, and in the case of the latter, must be accessible by the main character.
Fighting A function, that when given a player (to attack) initiates a battle sequence, that persists until someone retreats or a player's health is reduced to 0.
Moving A player should contain a node for a linked list or binary tree. The first location node tells us where the player came from, where he is currently, and where he can go from there. "Where we can go" is a memory address (think, RAM) that tells the computer where another location node can be found. This new node will be structured the same as the first, but contain different information. Moving will involve changing the memory address of the current node (Swamp, let's say), to the memory address of another node (Forest). If that sounds confusing, don't worry, I'll make pictures to illustrate the concept. )
Inventory Inventory will start out as a doubly linked list. An item node contains an item (Health Potion), the number of that item, a description of the item, and two links. One link to the previous item in the list, and a second link to the next item in the list.
This preliminary specification acts as a blueprint for the next phase, the actual coding portion. Now that we've broken the main idea into smaller elements, we can focus on creating separate modules that enable these things. As an example, we will implement the player and player functions in the Main file for testing. Once we're positive that our code is working properly, we can migrate our datatypes and functions into a Header file, which we'll call whenever we want to create and manipulate players. Doing this will significantly reduce the amount of code to look at in the main file, and keeping player functions in the player header file will give us a logical place to look for, add, remove, and improve player functions. As we progress, we may think of new things to add to our specification.
Player Implementation
Because a player is too complex to represent with a single variable, we must create a Structure. Structures are complex datatypes that can hold several datatypes at once. Below, is a rudimentary example of a player structure.
Using the keyword struct, we declare a complex datatype called playerStructure. Within the curly braces, we define it with all the datatypes needs to hold for us. This structure can be used to create new structures just like it. Let's use it to make a Hero and display his stats.
player.c
Let's review what our code does. We've included a new standard library called <string.h> which contains functions that are helpful in working with strings. Next, we define the complex datatype playerStructure and immediately declare a playerStructure called Hero right after it. Be aware, the semicolon is always necessary after defining the struct. Unlike higher level languages, strings cannot be assigned in C using the assignment operator =, only the individual characters that make up the string can be assigned. Since name is 50 characters long, imagine that we have 50 blank spaces. To assign "Sir Leeroy" to our array, we must assign each character to a blank space, in order, like so:
name[0] = 'S'
name[1] = 'i'
name[2] = 'r'
name[3] = ' '
name[4] = 'L'
name[5] = 'e'
name[6] = 'e'
name[7] = 'r'
name[8] = 'o'
name[9] = 'y'
name[10] = '' // End of string marker
The function Strcpy() essentially loops through the array until it reaches the end of string marker for either arguments and assigns characters one at a time, filling the rest with blanks if the string is smaller than the size of the array we're storing it in.
The variables in our structure, Player, are called members, and they are accessed via the syntax struct.member.
Now our game would be boring, and tranquil, if it just had a Hero and no enemies. In order to do add more players, we would need to type "struct playerStructure variableName" to declare new players. That's tedious and prone to mistakes. Instead, it would be much better if we had a special name for our player datatype that we could call as wisfully as char or int or float. That's easily done using the keyword typedef Like before, we define the complex datatype playerstructure, but instead of declaring a playerStructure afterward, we create a keyword that can declare them whenever we want.
player2.c
There is still the problem of creating players. We could define every single player who will make an appearance in our game at the start of our program. As long as the list of players is short, that might be bearable, but each of those players occupies memory whether they are used or unused. Historically, this would be problematic due to the scarcity of memory on old computers. Nowadays, memory is relatively abundant, but for the sake of scalability, and because users will have other applications running in the background, we'll want to be efficient with our memory usage and use it dynamically.
Dynamically allocating memory is accomplished through the use of the malloc, a function included in <stdlib.h>. Given a number of bytes to return, malloc finds unused memory and hands us the address to it. To work with this memory address, we use a special datatype called a pointer, that is designed to hold memory addresses. Pointers are declared like any other datatype, except we put an asterisk (*) in front of the variable name. Consider this line of code
This is the standard way of declaring a pointer and assigning it a memory address. The asterisk tells us that instead of declaring a player, with a fixed, unchangeable address in memory, we want a variable that can point to any player's address. Uninitialized pointers have NULL as their value, meaning they don't point to an address. Since it would be difficult to memorize how many bytes are in a single datatype, let alone our player structure, we use the sizeof function to figure that out for us. Sizeof returns the number of bytes in player to malloc, which finds enough free memory for a player structure and returns the address to our pointer.
If malloc returns the memory address 502, Hero will now point to a player who exists at 502. Pointers to structures have a unique way of calling members. Instead of a period, we now use an arrow (->).
Remember, pointers don't contain values like integers and chars, they just tell the computer where to find those values. When we change a value our pointer points to, we're telling the computer "Hey, the value I want you to change lives at this address (502), I'm just directing traffic." So when you think of pointers, think "Directing Traffic". Here's a table to show what pointer declarations of various types mean
Declaration
What it means.
char *variable
Pointer to char
int *variable
Pointer to int
float *variable
Pointer to float
player *variable
Pointer to player
player **variable
Pointer to a pointer to player
Now that we're using pointers, we can write a function to dynamically allocate players. And while we're at it, let's add some new ideas to our specification.
Players should
dynamicPlayers.c
Before we move on to the next major development, you'll want to modularize what you've written. Start by making two header files, one named "gameProperties.h" and another called "players.h". In the game properties file, place your playerStructure and classEnum typedefs. The datatypes defined here will have the possibility of appearing in any other headers we may create. Therefore, this will always be the first header we call. Next, all functions related to creating and modifying players, as well as their prototypes, will go in our players header file.
Fight System
Rome wasn't built in a day and neither are good fight systems, but we'll try our best. Now that we have an enemy we are obligated to engage him in a friendly bout of fisticuffs. For our players to fight, we'll need to include two additional stats in our player structure, Attack and Defense. In our specification, all that our Fight function entailed was an argument of two players, but with further thought, lets do damage based on an EffectiveAttack, which is Attack minus Defense.
In the gameProperties header, modify playerStructure for two more integer variables, "attack" and "defense".
gameProperties.h
In the players header file, modify the case statements to assign values to the attack and defense attributes.
players.h
Finally, include your header files in your main program. Instead of sharp brackets <> we use quotation marks instead. If the headers are located in the same file as the executable, you only need to provide the name. If your header file is in a folder somewhere else, you'll need to provide the full path of the file location.
Let's also develop a rudimentary fight system to make use the attack and defense attributes.
player3.c
If we run compile and run this we get this output
TODO Adjust class stats to something more diverse.
Now that we've figured out how to deal damage, lets expand on our earlier specification
Fight() should
I'll refrain from posting the whole program when possible but I encourage you to continue making incremental changes and compiling/running the main program as we go along. For the battle sequence, we will modify the Fight function to loop until the Target's health reaches 0, whereupon a winner is named. A user interface will be provided by a "Fight Menu" which will pair a number with an action. It is our responsibility to modify this menu as new actions are added and make sure each individual action works when called.
When the User chooses an action, the associated number is handed to a Switch, which compares a given variable to a series of Cases. Each case has a number or character (strings aren't allowed) that is used for the aforementioned comparison. If Switch finds a match, it evaluates all the statements in that Case. We must use the keyword break to tell the switch to stop evaluating commands, or else it will move the to next case and execute those statements (sometimes that's useful, but not for our purpose). If Switch cannot match a variable to a case, then it looks for a special case called default and evalutes it instead. We'll always want to have a default present to handle unexpected input.
Testing the integrity of the program requires running it a few times after compilation. First we can see that if we enter random input like "123" or "Fish" we invoke the default case and are forced to pick another answer. Second, entering 2 will cause us to run away from the fight. Third, if we continue to enter 1, eventually Sir Leeroy will widdle down all of Sir Jenkin's health and be declared winner. Modifying the attack value on Sir Leeroy can help if you're impatient )
However, Sir Jenkins is still unable to defend himself, which makes for a very unsporting match. Even if Sir Jenkins was given a turn, the user would still be prompted to act on his behalf. The turn-based problem is solved by the idea proposed in the specification, that we swap the memory addresses of the Attacker and Target pointers on each loop. The solution to the problem of autonomy is to add a new property to our player structure, namely, a bool. A bool has a binary value, true or false, and, for us, it answers the simple question "To Autopilot or not to autopilot?". With the autopilot bool set to true, the Fight function, when modified by us to check for it, will know that they must automate the actions of these characters. To use bool datatypes, we need to include a new header called <stdbool.h>. Bools are declared with the bool keyword and can only be assigned true or false values.
Add the following line underneath int defense in your playerStructure from the "gameProperties.h".
Next, add this snippet of code to the NewPlayer function from the "Players.h" below the call to SetName.
The above code creates a persistent variable using the keyword static. Normally, once a function is called, the local variables disappear. By contrast, static variables maintain their value beyond the life of the function, and when a function starts again, it's value isn't reset. Autopilot is only turned on for players created after the first, main character.
That done, consider the following program. We've added our bools and IF statements to determine whether the player needs automating or prompting. The victory IF is moved inside the while loop, and declares victory if the condition is met, else, it swaps players for the next loop.
player4.c
Now that we've created a very rudimentary system for fighting, it is time once again to modularize. Take the Fight and DisplayFightMenu functions and put them in a new header file called "fightSys.h". This new header will contain all functions related to fighting and will be included in the next iteration of our Main program.
Inventory System
There is nothing quite so satisfying as equipping one's character. Most of the time, you start out at level 1, wearing rags and shackles. After being released from the local dungeon for one reason or another, you meet a Haberdasher with a heart of gold. He gives you some odd jobs so you can earn enough copper to eat. After weeks of punching things, fetching things, running from things, and still without two copper pieces to scratch together, you at last come across the "Unremarkable Sword of Rusty Might". Huzzah It nothing compared to what the City Guard is using, but now those sewer rats shall feel your wrath. So let's get to it
An Inventory Should
An Item Should
Putting the above specification into practice, we start by defining the kinds of items available, via an enumeration. In the future, we may want to import a list of items from a file, rather than keeping all of them with our source code. However, it's best to keep things small for testing purposes.
Each item that we create will be embedded in an item node. A node is a single unit which can be linked to other nodes in order to form a data structures such as lists and trees. In this case, the data structure is a doubly linked list. As you can see, the itemNodeStructure points forwards, backwards, and to its own item. When defining the itemNodeStructure, it is necessary to use "struct itemNodeStructure" to declare the pointers because the itemNode typedef is not yet in effect and the compiler will not understand.
Going back to ye olde playerStructure datatype, we add a new value to it.
Because the functions required for a basic inventory setup are somewhat involved, it cannot be broken up into more digestible pieces. My code is very likely not the most elegant solution to the problem, so there is room for improving the clarity. For now, if you want to understand how the functions for the inventory work, you'll need to read through the comments until you can identify what each piece of code is doing.
inventory.c
inventoryFinished.c
Declaration
What it means.
char *variable
Pointer to char
int *variable
Pointer to int
float *variable
Pointer to float
player *variable
Pointer to player
player **variable
Pointer to a pointer to player
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.