Reload weapon in textbased shooter (simulation) game

Aug 30, 2016 at 10:25am
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 Aug 30, 2016 at 10:26am
Aug 30, 2016 at 12:31pm
@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;
}
Aug 30, 2016 at 2:45pm
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 Aug 30, 2016 at 2:45pm
Aug 30, 2016 at 3:22pm
@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
}
Aug 30, 2016 at 3:34pm
Thanks for the answer @whitenite1!. But he doesn't reload his gun. Did it work when you tried it? :)
Aug 30, 2016 at 3:42pm
@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 Aug 30, 2016 at 3:42pm
Aug 30, 2016 at 3:46pm
@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;
}
Aug 30, 2016 at 3:49pm
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
Aug 30, 2016 at 3:56pm
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! :)
Aug 30, 2016 at 5:17pm
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
Aug 30, 2016 at 5:58pm
@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;
}
Aug 31, 2016 at 12:16am
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;
}
Aug 31, 2016 at 5:13pm
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.