Reload weapon in textbased shooter (simulation) game

http://hastebin.com/xegaquciga.avrasm

I don't get it to work, sorry if the code is a mess.. the combination of tired, new to c++ and trying to find diffent kind of solutions, so it might be stuff that isn't used in the code.. ^^. The reloadtime should be 1.5f (based on deltatime). But i have no clue how to do it.

Any help would be much appreciated :).

Best Regards.
Last edited on
@Janbananberg
All I did was change McCree.reloadTime <= 0 to McCree.reloadTime >= 0 and reloading worked.

1
2
3
4
if (McCree.bulletCount <= 0 && McCree.reloadTime >= 0)
{
	McCree.bulletCount += 6;
}
Thanks @whitenite1 :). The problem is that i want him to "wait" while he reloads. And that he can't shoot or do anything else while he reloads. But i don't know how to do it :(
Last edited on
@Janbananberg

Add to your includes, to use Sleep mode
#include <Windows.h>

Then change McCree.bulletCount section, to..
1
2
3
4
5
6
7
8
9
10
if (McCree.bulletCount == 0)
{
	cout << "McCree is reloading!" << endl;
	do
	{
		McCree.reloadTime -= delta_time;// count down reload time
		Sleep(200); //Pause (change 200 to whatever needed. 1000 is 1 second)
	} while (McCree.reloadTime > 0);
	McCree.reloadTime = 1.5; // Reset reload time
}
Thanks for the answer @whitenite1!. But he doesn't reload his gun. Did it work when you tried it? :)
@whitenite1, I think you can achieve sleep without pulling in platform-specific headers:
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <chrono>
#include <thread>

int main(int argc, const char* argv[]) 
{
  std::cout << "Start..." << std::endl;
  std::this_thread::sleep_for(std::chrono::seconds(2));
  std::cout << "...End" << std::endl;

  return 0;
}


I'm not entirely sure that sleep is the right functionality here, given it'll halt the entire thread. I think a more likely solution is an update loop that takes a delta time parameter.
Last edited on
@Janbananberg

Here's your code, with all additions/corrections, I made. Yes, the gun is reloaded.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <iostream>
#include <Windows.h>

using namespace std;

// Character Class (Character Name, HP, DMGFar and DMGNear, Mag Size, Bullet Count, and ReloadTime)
class Character
{
public:
	string name;
	int hp;
	int dmgFar;
	int dmgNear;
	int magSize;
	int bulletCount;
	float reloadTime;

	Character(string char_Name, int max_HP, int damage_Far, int damage_Near, int mag_Capacity, float reload_Time)
	{
		name = char_Name;
		hp = max_HP;
		dmgFar = damage_Far;
		dmgNear = damage_Near;
		magSize = mag_Capacity;
		bulletCount = mag_Capacity;
		reloadTime = reload_Time;
	}
};

int main()
{
	Character McCree = Character("McCree", 200, 35, 70, 6, 1.5f);
	Character Roadhog = Character("Roadhog", 600, 20, 255, 4, 1.5f);

	const float delta_time = 0.1f;

	bool running = true;

	float time_left = 10.0f;

	while (running)
	{
		if (McCree.bulletCount > 0 && Roadhog.hp >= 0)
		{
			McCree.bulletCount--;
			cout << "McCree now has " << McCree.bulletCount << " bullets remaining" << endl;
			Roadhog.hp -= McCree.dmgFar;
			cout << "Roadhog now has " << Roadhog.hp << " HP left" << endl;

			if (McCree.bulletCount == 0)
			{
				cout << "McCree is reloading!" << endl;
				do
				{
					McCree.reloadTime -= delta_time;
					Sleep(200);
				} while (McCree.reloadTime > 0);
				McCree.reloadTime = 1.5;
			}

			if (McCree.bulletCount == 0)
                         // This part NOT needed && McCree.reloadTime >= 0)
			{
				McCree.bulletCount = 6;
			}

			if (time_left <= 0 || McCree.hp <= 0 || Roadhog.hp <= 0)
			{
				cout << "Game Over!" << endl;
				running = false;
			}
		}
		//cout << delta_time << endl;
	}
	//system("pause");
	return 0;
}
Thanks MrHutch, the problem is that i'm not really sure how delta time works, i don't get the character to reload, and if i get him to reload then he does it instantly.. and it doesn't seem to matter how i do with the delta time "/. I want him to be forced to wait while he reloads :p
Thanks alot @whitenite1! seems to work now :). You are the best, thanks alot for all the help.. i really appreciate it ^^.
Thanks to you aswell MrHutch! :)
Hmm.. is there any way of doing it without freezing the whole game with Sleep()?. Since i want to other character to be able to shoot while the other one is reloading :p
@Janbananberg

Here's the code, with chance of Roadhog shooting, while McCree is re-loading.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <iostream>
#include <Windows.h>

using namespace std;

// Character Class (Character Name, HP, DMGFar and DMGNear, Mag Size, Bullet Count, and ReloadTime)
class Character
{
public:
	string name;
	int hp;
	int dmgFar;
	int dmgNear;
	int magSize;
	int bulletCount;
	float reloadTime;

	Character(string char_Name, int max_HP, int damage_Far, int damage_Near, int mag_Capacity, float reload_Time)
	{
		name = char_Name;
		hp = max_HP;
		dmgFar = damage_Far;
		dmgNear = damage_Near;
		magSize = mag_Capacity;
		bulletCount = mag_Capacity;
		reloadTime = reload_Time;
	}
};

int main()
{
Character McCree = Character("McCree", 200, 35, 70, 6, 1.5f);
Character Roadhog = Character("Roadhog", 600, 20, 255, 4, 1.5f);

const float delta_time = 0.1f;

bool running = true;

float time_left = 10.0f;

while (running)
{
	if (McCree.bulletCount > 0 && Roadhog.hp >= 0)
	{
		McCree.bulletCount--;
		cout << "McCree now has " << McCree.bulletCount << " bullets remaining" << endl;
		Roadhog.hp -= McCree.dmgFar;
		cout << "Roadhog now has " << Roadhog.hp << " HP left" << endl;

		if (McCree.bulletCount == 0)
		{
			cout << "McCree is reloading!" << endl;
			do
			{
				McCree.reloadTime -= delta_time;
				Sleep(200);
				if (rand() % 100 < 25) // Change to what percent chance of Roadhog shooting, you want
				//Here, it's a 25% chance
				{
					cout << "Roadhog shoots, while McCree is reloading!" << endl;
					McCree.hp -= Roadhog.dmgFar;
					cout << "McCree now has " << McCree.hp << " HP left" << endl<<endl;
				}
			} while (McCree.reloadTime > 0 && (McCree.hp >= 0 && Roadhog.hp >=0));
			McCree.reloadTime = 1.5;
		}

		if (McCree.bulletCount == 0)
		{
			McCree.bulletCount = 6;
		}

		if (time_left <= 0 || McCree.hp <= 0 || Roadhog.hp <= 0)
		{
			if (McCree.hp <= 0)
				cout <<endl << "McCree died.." << endl;
			if (Roadhog.hp <= 0)
				cout << endl << "Roadhog died.." << endl;
			cout << "Game Over!" << endl;
				running = false;
		}
	}
	//cout << delta_time << endl;
 }
//system("pause");
return 0;
}
The best way to solve this is by implementing a game loop and having some state modelling for your characters, in my opinion. Sleep is absolutely not the way to go.

Say your character had an Update method. In that method, you could add logic for handling a variety of states such as shooting, reloading etc. The simplest way to do this would be to have an enum for each state and handle each case, like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>

class Character
{
public:
    enum State 
    {
        Idle,
        Reloading,
        Shooting
    };
    
    Character() : m_currentState(Idle){}
    void Update(float dt)
    {
        switch (m_currentState) 
        {
            case Idle:
                std::cout << "Yawn!\n";
                break;
            case Reloading:
                std::cout << "Click!\n";
                break;
            case Shooting:
                std::cout << "Bang!\n";
                break;
            default:
                throw "Unexpected state";
        };
    }
    void SetState(State newState)
    {
        m_currentState = newState;
    }
private:
    State m_currentState;
};

int main(int argc, const char* argv[]) 
{
    Character myCharacter;
    
    // Simulating update loop...
    myCharacter.Update(0.f);
    myCharacter.SetState(Character::State::Shooting);
    myCharacter.Update(0.f);
    
    return 0;
}


Note, the logic is simplified in this example - you'd have more complex logic for reloading (probably some cooldown timer, which sets the state once it's done). Also, the double update call is in lieu of a functioning update loop, which would be expected in your application.

There are a couple of problems with this approach. That enum isn't all that flexible - what if certain characters don't have certain states (Torbjorn needs a 'Build' state, most others don't care for example). Your Character::State enum will get cluttered pretty quickly.

The other problem is how ugly the logic in your Update method will be. You're going to end up with a giant switch where each case has some potentially complex logic. Every time you need to change something, you'll need to change to innards of that method. Additionally, you'll need to do this for every character.

I did say it was the simplest way. I didn't say it was the best.

Personally, I'd have a small state interface and call into that in a characters Update method. That way, the logic for a state is isolated and multiple states could be used across multiple characters.

Bit of DRY fail and tidying needed (it's getting late), but here's an example - hopefully you get the gist. Again, simplified states, simulated updates.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <iostream>

class IStateOwner;
class ReloadState;

class IState
{
public:
    virtual void Enter(IStateOwner* pOwner) = 0; // Pre-state logic
    virtual void Exit() = 0;                     // Post-state logic
    virtual void Update(float dt) = 0;
    virtual ~IState(){}
};

class IStateOwner
{
public:
    virtual void ChangeState(IState* pNewState) = 0;    
};

class IdleState : public IState
{
public:
    void Enter(IStateOwner* pOwner)
    { 
        m_pOwner = pOwner; 
        std::cout << "Entering idle state...\n";
    }
    void Exit(){ std::cout << "Exiting idle state...\n"; }
    void Update(float dt) { std::cout << "Idle update...\n"; }
    
private:
    IStateOwner* m_pOwner;
};

class ReloadState: public IState
{
public:
    ReloadState(const float reloadCooldownTime)
    : m_reloadCooldownTime(reloadCooldownTime)
    , m_elapsedTimeInState(0.f)
    , m_pOwner(nullptr)
    {
        
    }
    
    void Enter(IStateOwner* pOwner)
    {
        m_pOwner = pOwner;
        std::cout << "Entering reload state...\n";
    }
    
    void Exit() { std::cout << "Exiting reload state...\n"; }
    
    void Update(float dt)
    {
        std::cout << "Reload update...\n";
        m_elapsedTimeInState += dt;
        if (m_elapsedTimeInState > m_reloadCooldownTime) {
            m_pOwner->ChangeState(new IdleState());
        }
    }
    
private:
    float m_reloadCooldownTime;
    float m_elapsedTimeInState;
    IStateOwner* m_pOwner;
};

class Character : public IStateOwner
{
public:
    void Update(float dt)
    {
        if (m_pCurrentState)
        {
            m_pCurrentState->Update(dt);   
        }
    }
    
    void ChangeState(IState* newState)
    {
        if (m_pCurrentState) m_pCurrentState->Exit();
        delete m_pCurrentState;
        m_pCurrentState= newState;
        m_pCurrentState->Enter(this);
    }

    Character() : m_pCurrentState(nullptr){}

    ~Character() 
    {
        delete m_pCurrentState;
    }
private:
    IState* m_pCurrentState;
};

int main(int argc, const char* argv[]) 
{
    Character myCharacter;
    myCharacter.Update(0.1f);
    myCharacter.ChangeState(new ReloadState(5));
    myCharacter.Update(4.0f);
    myCharacter.Update(1.01f);
    myCharacter.Update(0.1f);
    return 0;
}
Thanks alot for all the answers! I think i'm starting to get a hang of it now :).

Best regards
Topic archived. No new replies allowed.