1 /*
2  * Seven Kingdoms: Ancient Adversaries
3  *
4  * Copyright 1997,1998 Enlight Software Ltd.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 // Filename   : OW_FIRE.CPP
22 // Description: World class function related to spreading of fire
23 // Ownership  : Gilbert
24 
25 
26 #include <OWORLD.h>
27 #include <OMATRIX.h>
28 #include <OWEATHER.h>
29 #include <OWORLDMT.h>
30 #include <OTERRAIN.h>
31 #include <OUNIT.h>
32 #include <OFIRM.h>
33 #include <OFIRMA.h>
34 #include <OCONFIG.h>
35 // #### begin Gilbert 29/5 #######//
36 #include <OSERES.h>
37 // #### end Gilbert 29/5 #######//
38 #include <math.h>
39 //### begin alex 6/8 ###//
40 #ifdef DEBUG
41 #include <OSYS.h>
42 #endif
43 //#### end alex 6/8 ####//
44 
45 
46 //------------ define constant ------------
47 #define SPREAD_RATE (config.fire_spread_rate)
48 // #define CONSUMPTION_RATE 1.0
49 #define WIND_SPREADFIRE (config.wind_spread_fire_rate)
50 #define FIRE_FADE_RATE (config.fire_fade_rate)
51 
52 // 0 - 100
53 #define RESTORE_RATE (config.fire_restore_prob)
54 
55 // 0 - 20
56 #define RAIN_SNOW_REDUCTION (config.rain_reduce_fire_rate)
57 
58 
59 // ----------- Define static function ----------//
bound_zero(char n)60 static char bound_zero(char n)
61 {
62 	if( n > 0)
63 		return n;
64 	return 0;
65 }
66 
67 // ---------- Level of Location::fire_str() ---------//
68 //   51 to  100	Highly flammable, restorable
69 //    1 to   50	flammable, restorable
70 //  -30 to    0   flammable but the fire cannot grow, or spreaded, restorable
71 //  -50 to  -31   flammable but the fire cannot grow, flammability unrestorable
72 // -100 to  -51   absolutely non-inflammable
73 
74 // ----------- begin of function World::init_fire ---------- //
init_fire()75 void World::init_fire()
76 {
77 	Location *locPtr = get_loc(0,0);
78 	for( int c = max_x_loc*max_y_loc; c >0; --c, ++locPtr)
79 	{
80 		if( locPtr->has_hill())
81 		{
82 			locPtr->set_fire_src(-100);
83 		}
84 		else if( locPtr->is_wall() )
85 		{
86 			locPtr->set_fire_src(-50);
87 		}
88 		else if( locPtr->is_firm() || locPtr->is_plant() || locPtr->is_town() )
89 		{
90 			locPtr->set_fire_src(100);
91 		}
92 		else
93 		{
94 			switch(terrain_res[locPtr->terrain_id]->average_type)
95 			{
96 			case TERRAIN_OCEAN:
97 				locPtr->set_fire_src(-100);
98 				break;
99 			case TERRAIN_DARK_GRASS:
100 				locPtr->set_fire_src(100);
101 				break;
102 			case TERRAIN_LIGHT_GRASS:
103 				locPtr->set_fire_src(50);
104 				break;
105 			case TERRAIN_DARK_DIRT:
106 				locPtr->set_fire_src(-50);
107 				break;
108 			default:
109 				err_now("Undefined terrain type");
110 			}
111 		}
112 		// --------- put off fire on the map ----------//
113 		locPtr->set_fire_str(-100);
114 	}
115 }
116 // ----------- end of function World::init_fire ---------- //
117 
118 
119 // ----------- begin of function World::spread_fire ---------- //
spread_fire(Weather & w)120 void World::spread_fire(Weather &w)
121 {
122 
123 	char fireValue;
124 	int x,y;
125 	Location *locPtr;
126 
127 	// -------- normalize wind_speed between -WIND_SPREADFIRE*SPREAD_RATE to +WIND_SPREADFIRE*SPREAD_RATE -------
128 	int windCos = int(w.wind_speed() * cos(w.wind_direct_rad()) / 100.0 * SPREAD_RATE * WIND_SPREADFIRE);
129 	int windSin = int(w.wind_speed() * sin(w.wind_direct_rad()) / 100.0 * SPREAD_RATE * WIND_SPREADFIRE);
130 	char rainSnowReduction = 0;
131 
132 	rainSnowReduction = (w.rain_scale() > 0 || w.snow_scale() > 0) ?
133 		RAIN_SNOW_REDUCTION + (w.rain_scale() + w.snow_scale()) / 4: 0;
134 
135 	float flameDamage = (float)config.fire_damage/ATTACK_SLOW_DOWN;
136 
137 	// -------------update fire_level-----------
138 	for( y = scan_fire_y; y < max_y_loc; y += SCAN_FIRE_DIST)
139 	{
140 		locPtr = get_loc(scan_fire_x,y);
141 		for( x = scan_fire_x; x < max_x_loc; x += SCAN_FIRE_DIST, locPtr+=SCAN_FIRE_DIST)
142 		{
143 			char oldFireValue = fireValue = locPtr->fire_str();
144 			char flammability = locPtr->fire_src();
145 
146 
147 			// ------- reduce fire_level on raining or snow
148 			fireValue -= rainSnowReduction;
149 			if(fireValue < -100)
150 				fireValue = -100;
151 
152 			if( fireValue > 0)
153 			{
154 				Unit *targetUnit;
155 
156 				// ------- burn wall -------- //
157 				if( locPtr->is_wall() )
158 				{
159 					if( !locPtr->attack_wall(int(4.0*flameDamage)))
160 						correct_wall(x, y, 2);
161 				}
162 				// ------- burn units ---------//
163 				else if( locPtr->has_unit(UNIT_LAND))
164 				{
165 					targetUnit = unit_array[locPtr->unit_recno(UNIT_LAND)];
166 					targetUnit->hit_points -= (float)2.0*flameDamage;
167 					if( targetUnit->hit_points <= 0 )
168 						targetUnit->hit_points = (float) 0;
169 				}
170 				else if( locPtr->has_unit(UNIT_SEA))
171 				{
172 					targetUnit = unit_array[locPtr->unit_recno(UNIT_SEA)];
173 					targetUnit->hit_points -= (float)2.0*flameDamage;
174 					if( targetUnit->hit_points <= 0 )
175 						targetUnit->hit_points = (float) 0;
176 				}
177 				else if( locPtr->is_firm() && firm_res[firm_array[locPtr->firm_recno()]->firm_id]->buildable)
178 				{
179 					Firm *targetFirm = firm_array[locPtr->firm_recno()];
180 					//### begin alex 6/8 ###//
181 					#ifdef DEBUG
182 					if(debug_sim_game_type!=2)
183 					#endif
184 					//#### end alex 6/8 ####//
185 					targetFirm->hit_points -= flameDamage;
186 					if( targetFirm->hit_points <= 0)
187 					{
188 						targetFirm->hit_points = (float) 0;
189 						// ###### begin Gilbert 29/5 ########//
190 						se_res.sound(targetFirm->center_x, targetFirm->center_y, 1,
191 							'F', targetFirm->firm_id, "DIE" );
192 						// ###### end Gilbert 29/5 ########//
193 						firm_array.del_firm(locPtr->firm_recno());
194 					}
195 				}
196 
197 				if(SPREAD_RATE > 0)
198 				{
199 
200 					Location *sidePtr;
201 					// spread of north square
202 					if( y>0 && (sidePtr = get_loc(x,y-1))->fire_src() >0
203 						&& sidePtr->fire_str() <= 0)
204 					{
205 						sidePtr->add_fire_str(bound_zero(char(SPREAD_RATE+windCos)));
206 					}
207 
208 					// spread of south square
209 					if( y<max_y_loc-1 && (sidePtr = get_loc(x,y+1))->fire_src() >0
210 						&& sidePtr->fire_str() <= 0)
211 					{
212 						sidePtr->add_fire_str(bound_zero(char(SPREAD_RATE-windCos)));
213 					}
214 
215 					// spread of east square
216 					if( x<max_x_loc-1 && (sidePtr = get_loc(x+1,y))->fire_src() >0
217 						&& sidePtr->fire_str() <= 0)
218 					{
219 						sidePtr->add_fire_str(bound_zero(char(SPREAD_RATE+windSin)));
220 					}
221 
222 					// spread of west square
223 					if( x>0 && (sidePtr = get_loc(x-1,y))->fire_src() >0
224 						&& sidePtr->fire_str() <= 0)
225 					{
226 						sidePtr->add_fire_str(bound_zero(char(SPREAD_RATE-windSin)));
227 					}
228 				}
229 
230 				if( flammability > 0)
231 				{
232 					// increase fire_level on its own
233 					if(++fireValue > 100)
234 						fireValue = 100;
235 
236 					flammability -= FIRE_FADE_RATE;
237 					// if a plant on it then remove the plant, if flammability <= 0
238 					if( locPtr->is_plant() && flammability <= 0)
239 					{
240 						locPtr->remove_plant();
241 						plant_count--;
242 					}
243 
244 				}
245 				else
246 				{
247 					// fireValue > 0, flammability < 0
248 					// putting of fire
249 					if( flammability >= -30)
250 					{
251 						fireValue-=2;
252 						flammability -= FIRE_FADE_RATE;
253 						if( flammability < -30)
254 							flammability = -30;
255 					}
256 					else if (flammability >= -50)
257 					{
258 						fireValue-=2;
259 						flammability -= FIRE_FADE_RATE;
260 						if( flammability < -50)
261 							flammability = -50;
262 					}
263 					else
264 					{
265 						fireValue = -100;
266 						flammability -= FIRE_FADE_RATE;
267 						if( flammability < -100)
268 							flammability = -100;
269 					}
270 
271 					// if a plant on it then remove the plant, if flammability <= 0
272 					if( locPtr->is_plant() && flammability <= 0)
273 					{
274 						locPtr->remove_plant();
275 						plant_count--;
276 					}
277 				}
278 			}
279 			else
280 			{
281 				// fireValue < 0
282 				// ---------- fire_level drop slightly ----------
283 				if( fireValue > -100)
284 					fireValue--;
285 
286 				// ---------- restore flammability ------------
287 				if( flammability >= -30 && flammability < 50 &&
288 					misc.random(100) < RESTORE_RATE)
289 					flammability++;
290 			}
291 
292 			// ---------- update new fire level -----------
293 			//-------- when fire is put off
294 			// so the fire will not light again very soon
295 			if(fireValue <= 0 && oldFireValue > 0)
296 			{
297 				fireValue -= 50;
298 			}
299 
300 			locPtr->set_fire_str(fireValue);
301 			locPtr->set_fire_src(flammability);
302 		}
303 	}
304 }
305 //----------- end of function World::spread_fire ---------- //
306 
307 
308 //----------- begin of function World::setup_fire ---------- //
309 //
310 // set a fire at location x, y
311 // fireStrength is between 1 - 100, (default: 30)
312 //
setup_fire(short x,short y,char fireStrength)313 void World::setup_fire(short x, short y, char fireStrength)
314 {
315 	err_when( x < 0 || y < 0 || x >= max_x_loc || y >= max_y_loc);
316 	err_when(fireStrength < 0 || fireStrength > 100);
317 
318 	Location *locPtr = get_loc(x, y);
319 	if( locPtr->fire_str() < fireStrength )
320 	{
321 		locPtr->set_fire_str(fireStrength);
322 	}
323 }
324 // ----------- end of function World::setup_fire ---------- //
325 
326