summaryrefslogtreecommitdiffstats
path: root/source/cFireSimulator.cpp
blob: 180ac9744668fe9c24f0b823fe81f43bfadf0196 (plain) (blame)
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include "cFireSimulator.h"
#include "cWorld.h"
#include "Vector3i.h"
#include "BlockID.h"
#include "Defines.h"
#include <vector>

cFireSimulator::cFireSimulator( cWorld* a_World )
	: cSimulator(a_World)
	, m_Blocks(new std::vector <Vector3i *>)
	, m_Buffer(new std::vector <Vector3i *>)
	, m_BurningBlocks(new std::vector <Vector3i *>)
{

}

cFireSimulator::~cFireSimulator()
{
	delete m_Buffer;
	delete m_Blocks;
	delete m_BurningBlocks;
}

void cFireSimulator::Simulate( float a_Dt )
{
	m_Buffer->clear();
	std::swap( m_Blocks, m_Buffer );

	for( std::vector<Vector3i *>::iterator itr = m_Buffer->begin(); itr != m_Buffer->end(); ++itr )
	{
		Vector3i *Pos = *itr;

		if(!IsAllowedBlock(m_World->GetBlock(Pos->x, Pos->y, Pos->z)))	//Check wheather the block is still burning
			continue;

		if(BurnBlockAround(Pos->x, Pos->y, Pos->z))	//Burn single block and if there was one -> next time again
			_AddBlock(Pos->x, Pos->y, Pos->z);
		else
			if(!IsForeverBurnable(m_World->GetBlock(Pos->x, Pos->y - 1, Pos->z)))
				m_World->SetBlock(Pos->x, Pos->y, Pos->z, E_BLOCK_AIR, 0);

	}
		
}


bool cFireSimulator::IsAllowedBlock( char a_BlockID )
{
	return a_BlockID == E_BLOCK_FIRE
		|| IsBlockLava(a_BlockID);
}

void cFireSimulator::AddBlock(int a_X, int a_Y, int a_Z)
{
	char BlockID = m_World->GetBlock(a_X, a_Y, a_Z);
	if(!IsAllowedBlock(BlockID))		//This should save very much time because it doesn´t have to iterate through all blocks
		return;
	
	//check for duplicates
	for( std::vector<Vector3i *>::iterator itr = m_Blocks->begin(); itr != m_Blocks->end(); ++itr )
	{
		Vector3i *Pos = *itr;
		if( Pos->x == a_X && Pos->y == a_Y && Pos->z == a_Z )
			return;
	}

	_AddBlock(a_X, a_Y, a_Z);

}

void cFireSimulator::_AddBlock(int a_X, int a_Y, int a_Z)
{
	Vector3i *Block = new Vector3i(a_X, a_Y, a_Z);
	m_Blocks->push_back(Block);

}

bool cFireSimulator::IsForeverBurnable( char a_BlockID )
{
	return a_BlockID == E_BLOCK_BLOODSTONE;
}

bool cFireSimulator::IsBurnable( char a_BlockID )
{
	return a_BlockID == E_BLOCK_WOOD
		|| a_BlockID == E_BLOCK_LEAVES
		|| a_BlockID == E_BLOCK_LOG
		|| a_BlockID == E_BLOCK_WHITE_CLOTH
		|| a_BlockID == E_BLOCK_BOOKCASE
		|| a_BlockID == E_BLOCK_FENCE
		|| a_BlockID == E_BLOCK_TNT;
}

bool cFireSimulator::BurnBlockAround(int a_X, int a_Y, int a_Z)
{
	return BurnBlock(a_X + 1, a_Y, a_Z)
		|| BurnBlock(a_X - 1, a_Y, a_Z)
		|| BurnBlock(a_X, a_Y + 1, a_Z)
		|| BurnBlock(a_X, a_Y - 1, a_Z)
		|| BurnBlock(a_X, a_Y, a_Z + 1)
		|| BurnBlock(a_X, a_Y, a_Z - 1);
}

bool cFireSimulator::BurnBlock(int a_X, int a_Y, int a_Z)
{
	char BlockID = m_World->GetBlock(a_X, a_Y, a_Z);
	if(IsBurnable(BlockID))
	{
		m_World->SetBlock(a_X, a_Y, a_Z, E_BLOCK_FIRE, 0);
		return true;
	}
	if(IsForeverBurnable(BlockID))
	{
		char BlockAbove = m_World->GetBlock(a_X, a_Y + 1, a_Z);
		if(BlockAbove == E_BLOCK_AIR)
		{
			m_World->SetBlock(a_X, a_Y + 1, a_Z, E_BLOCK_FIRE, 0);	//Doesn´t notify the simulator so it won´t go off
			return true;
		}
		return false;
	}

	return false;
}