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    : OTOWNRES.CPP
22 //Description : Town resource object
23 
24 #include <OSYS.h>
25 #include <OGAMESET.h>
26 #include <OWORLD.h>
27 #include <OIMGRES.h>
28 #include <ORACERES.h>
29 #include <OTOWNRES.h>
30 
31 //---------- define constant ------------//
32 
33 #define TOWN_LAYOUT_DB 		"TOWNLAY"
34 #define TOWN_SLOT_DB			"TOWNSLOT"
35 #define TOWN_BUILD_TYPE_DB "TOWNBTYP"
36 #define TOWN_BUILD_DB		"TOWNBULD"
37 #define TOWN_NAME_DB			"TOWNNAME"
38 
39 //------- Begin of function TownRes::TownRes -----------//
40 
TownRes()41 TownRes::TownRes()
42 {
43 	init_flag=0;
44 }
45 //--------- End of function TownRes::TownRes -----------//
46 
47 
48 //---------- Begin of function TownRes::init -----------//
49 //
50 // This function must be called after a map is generated.
51 //
init()52 void TownRes::init()
53 {
54 	deinit();
55 
56 	//----- open town material bitmap resource file -------//
57 
58 	String str;
59 
60 	str  = DIR_RES;
61 	str += "I_TOWN.RES";
62 
63 	res_bitmap.init_imported(str,1);	 // 1-read all into buffer
64 
65 	//------- load database information --------//
66 
67 	load_town_slot();			// load_town_slot() must be called first before load_town_layout(), as load_town_layout() accesses town_slot_array
68 	load_town_layout();
69 	load_town_build_type();
70 	load_town_build();
71    load_town_name();
72 
73 	init_flag=1;
74 }
75 //---------- End of function TownRes::init -----------//
76 
77 
78 //---------- Begin of function TownRes::deinit -----------//
79 
deinit()80 void TownRes::deinit()
81 {
82 	if( init_flag )
83 	{
84 		mem_del(town_layout_array);
85 		mem_del(town_slot_array);
86 		mem_del(town_build_array);
87 		mem_del(town_build_type_array);
88 		mem_del(town_name_array);
89 		mem_del(town_name_used_array);
90 
91 		res_bitmap.deinit();
92 
93 		init_flag=0;
94 	}
95 }
96 //---------- End of function TownRes::deinit -----------//
97 
98 
99 //------- Begin of function TownRes::load_town_layout -------//
100 //
101 // Read in information from TOWNLAY.DBF.
102 //
load_town_layout()103 void TownRes::load_town_layout()
104 {
105 	TownLayoutRec  *townLayoutRec;
106 	TownLayout     *townLayout;
107 	TownSlot			*townSlot;
108 	int      	  	i, j;
109 	Database 		*dbTownLayout = game_set.open_db(TOWN_LAYOUT_DB);
110 
111 	town_layout_count = (short) dbTownLayout->rec_count();
112 	town_layout_array = (TownLayout*) mem_add( sizeof(TownLayout)*town_layout_count );
113 
114 	//------ read in town layout info array -------//
115 
116 	memset( town_layout_array, 0, sizeof(TownLayout) * town_layout_count );
117 
118 	for( i=0 ; i<town_layout_count ; i++ )
119 	{
120 		townLayoutRec = (TownLayoutRec*) dbTownLayout->read(i+1);
121 		townLayout    = town_layout_array+i;
122 
123 		townLayout->first_slot_recno = misc.atoi(townLayoutRec->first_slot, TownLayoutRec::FIRST_SLOT_LEN);
124 		townLayout->slot_count = misc.atoi(townLayoutRec->slot_count, TownLayoutRec::SLOT_COUNT_LEN);
125 
126 		// ###### begin Gilbert 9/9 ########//
127 		// townLayout->ground_bitmap_ptr = image_spict.get_ptr( misc.nullify(townLayoutRec->ground_name, TownLayoutRec::GROUND_NAME_LEN) );
128 		townLayout->ground_bitmap_ptr = image_tpict.get_ptr( misc.nullify(townLayoutRec->ground_name, TownLayoutRec::GROUND_NAME_LEN) );
129 		// ###### end Gilbert 9/9 ########//
130 
131 		err_if( townLayout->slot_count > MAX_TOWN_LAYOUT_SLOT )
132 			err_now( "Error: MAX_TOWN_LAYOUT_SLOT limit exceeded." );
133 
134 		//----- calculate min_population & max_population -----//
135 
136 		townSlot = town_slot_array+townLayout->first_slot_recno-1;
137 
138 		for( j=0 ; j<townLayout->slot_count ; j++, townSlot++ )
139 		{
140 			if( townSlot->build_type==TOWN_OBJECT_HOUSE )		// if there is a building in this slot
141 				townLayout->build_count++;
142 		}
143 	}
144 }
145 //--------- End of function TownRes::load_town_layout ---------//
146 
147 
148 //------- Begin of function TownRes::load_town_slot -------//
149 //
150 // Read in information from TOWNSLOT.DBF.
151 //
load_town_slot()152 void TownRes::load_town_slot()
153 {
154 	TownSlotRec  	*townSlotRec;
155 	TownSlot     	*townSlot;
156 	int      	  	i;
157 	Database 		*dbTownSlot = game_set.open_db(TOWN_SLOT_DB);
158 
159 	town_slot_count = (short) dbTownSlot->rec_count();
160 	town_slot_array = (TownSlot*) mem_add( sizeof(TownSlot)*town_slot_count );
161 
162 	//------ read in town slot info array -------//
163 
164 	memset( town_slot_array, 0, sizeof(TownSlot) * town_slot_count );
165 
166 	for( i=0 ; i<town_slot_count ; i++ )
167 	{
168 		townSlotRec = (TownSlotRec*) dbTownSlot->read(i+1);
169 		townSlot    = town_slot_array+i;
170 
171 		townSlot->base_x = misc.atoi(townSlotRec->base_x, TownSlotRec::POS_LEN);
172 		townSlot->base_y = misc.atoi(townSlotRec->base_y, TownSlotRec::POS_LEN);
173 
174 		townSlot->build_type = misc.atoi(townSlotRec->type_id, TownSlotRec::TYPE_ID_LEN);
175 		townSlot->build_code = misc.atoi(townSlotRec->build_code, TownSlotRec::BUILD_CODE_LEN);
176 
177 		err_when( townSlot->build_type == TOWN_OBJECT_FARM &&
178 				  (townSlot->build_code < 1 || townSlot->build_code > 9) );
179 	}
180 }
181 //--------- End of function TownRes::load_town_slot ---------//
182 
183 
184 //------- Begin of function TownRes::load_town_build_type -------//
185 //
186 // Read in information from TOWNBTYP.DBF.
187 //
load_town_build_type()188 void TownRes::load_town_build_type()
189 {
190 	TownBuildTypeRec 	*buildTypeRec;
191 	TownBuildType    	*buildType;
192 	int      	  		i;
193 	Database 			*dbTownBuildType = game_set.open_db(TOWN_BUILD_TYPE_DB);
194 
195 	town_build_type_count = (short) dbTownBuildType->rec_count();
196 	town_build_type_array = (TownBuildType*) mem_add( sizeof(TownBuildType)*town_build_type_count );
197 
198 	//------ read in TownBuildType info array -------//
199 
200 	memset( town_build_type_array, 0, sizeof(TownBuildType) * town_build_type_count );
201 
202 	for( i=0 ; i<town_build_type_count ; i++ )
203 	{
204 		buildTypeRec = (TownBuildTypeRec*) dbTownBuildType->read(i+1);
205 		buildType    = town_build_type_array+i;
206 
207 		buildType->first_build_recno = misc.atoi(buildTypeRec->first_build, TownBuildTypeRec::FIRST_BUILD_LEN);
208 		buildType->build_count = misc.atoi(buildTypeRec->build_count, TownBuildTypeRec::BUILD_COUNT_LEN);
209 	}
210 }
211 //--------- End of function TownRes::load_town_build_type ---------//
212 
213 
214 //------- Begin of function TownRes::load_town_build -------//
215 //
216 // Read in information from TOWNBULD.DBF.
217 //
load_town_build()218 void TownRes::load_town_build()
219 {
220 	TownBuildRec  	*townBuildRec;
221 	TownBuild     	*townBuild;
222 	int      	  	i;
223 	uint32_t			bitmapOffset;
224 	Database 		*dbTownBuild = game_set.open_db(TOWN_BUILD_DB);
225 
226 	town_build_count = (short) dbTownBuild->rec_count();
227 	town_build_array = (TownBuild*) mem_add( sizeof(TownBuild)*town_build_count );
228 
229 	err_when( town_build_count > 255 );			// BYTE is used in TownZone::slot_array[]
230 
231 	//------ read in town build info array -------//
232 
233 	memset( town_build_array, 0, sizeof(TownBuild) * town_build_count );
234 
235 	for( i=0 ; i<town_build_count ; i++ )
236 	{
237 		townBuildRec = (TownBuildRec*) dbTownBuild->read(i+1);
238 		townBuild    = town_build_array+i;
239 
240 		townBuild->build_type = misc.atoi(townBuildRec->type_id, TownBuildRec::TYPE_ID_LEN);
241 		townBuild->build_code = misc.atoi(townBuildRec->build_code, TownBuildRec::BUILD_CODE_LEN);
242 		townBuild->race_id = misc.atoi(townBuildRec->race_id, TownBuildRec::RACE_ID_LEN);
243 
244 		memcpy( &bitmapOffset, townBuildRec->bitmap_ptr, sizeof(uint32_t) );
245 
246 		townBuild->bitmap_ptr    = res_bitmap.read_imported(bitmapOffset);
247 		townBuild->bitmap_width  = *((short*)townBuild->bitmap_ptr);
248 		townBuild->bitmap_height = *(((short*)townBuild->bitmap_ptr)+1);
249 	}
250 }
251 //--------- End of function TownRes::load_town_build ---------//
252 
253 
254 //------- Begin of function TownRes::load_town_name -------//
255 //
256 // Read in information from TOWNNAME.DBF.
257 //
258 // Note: race_res must be initialized before calling this function.
259 //
load_town_name()260 void TownRes::load_town_name()
261 {
262 	TownNameRec *townNameRec;
263 	TownName		*townName;
264 	int      	i;
265 	Database 	*dbTownName = game_set.open_db(TOWN_NAME_DB);
266 
267 	town_name_count 		= dbTownName->rec_count();
268 	town_name_array		= (TownName*) mem_add( sizeof(TownName)*town_name_count );
269 	town_name_used_array = (unsigned char*) mem_add( sizeof(town_name_used_array[0]) * town_name_count );		// store the used_count separately from town_name_array to faciliate file saving
270 
271 	memset( town_name_used_array, 0, sizeof(town_name_used_array[0]) * town_name_count );
272 
273 	//------ read in TownName info array -------//
274 
275 	int raceId=0;
276 
277 	for( i=1 ; i<=town_name_count ; i++ )
278 	{
279 		townNameRec = (TownNameRec*) dbTownName->read(i);
280 		townName    = town_name_array+i-1;
281 
282 		misc.rtrim_fld( townName->name, townNameRec->name, townNameRec->NAME_LEN );
283 
284 		if( townName->name[0]=='@' )		// next race
285 		{
286 			int j;
287 			for( j=1 ; j<=MAX_RACE ; j++ )
288 			{
289 				if( strcmp( race_res[j]->code, townName->name+1 ) == 0 )
290 					break;
291 			}
292 
293 			err_when( j > MAX_RACE );
294 
295 			if( raceId )
296 				race_res[raceId]->town_name_count = i-race_res[raceId]->first_town_name_recno;
297 
298 			raceId = j;
299 			race_res[raceId]->first_town_name_recno = i+1;
300 		}
301 	}
302 
303 	//-- set the town_name_count of the last  town in TOWNNAME.DBF --//
304 
305 	race_res[raceId]->town_name_count = i-race_res[raceId]->first_town_name_recno;
306 }
307 //--------- End of function TownRes::load_town_name ---------//
308 
309 
310 //---------- Begin of function TownRes::scan_build -----------//
311 //
312 // Set the given slot with a building that fits the given criteria
313 //
314 // <int> slotId 	  - the slot id. of the current town section to be set.
315 // <int> raceId     - one of the building selection criteria
316 //							 	    (0-any race)
317 //
318 // return : <int> townBuildId - the id. of the town building
319 //
scan_build(int slotId,int raceId)320 int TownRes::scan_build(int slotId, int raceId)
321 {
322 	enum { MAX_SCAN_ID = 100 };
323 
324 	TownSlot* 		townSlot = town_res.get_slot(slotId);
325 	TownBuildType* buildType;
326 	TownBuild*		townBuild;
327 	int				i, buildRecno, matchCount=0;
328 	int				scanIdArray[MAX_SCAN_ID];
329 
330 	//---- get the building type of the slot ------//
331 
332 	buildType = town_res.get_build_type(townSlot->build_type);
333 
334 	err_if( buildType->build_count==0 )
335 		err_here();
336 
337 	//------ scan_build buildings of the specified type ------//
338 
339 	buildRecno = buildType->first_build_recno;
340 	townBuild  = town_res.get_build(buildRecno);	   // the pointer to the first building of the specified type
341 
342 	for( i=buildType->build_count ; i>0 ; i--, townBuild++, buildRecno++ )
343 	{
344 		if( townBuild->build_code == townSlot->build_code )
345 		{
346 			if( !raceId || townBuild->race_id == raceId )
347 			{
348 				scanIdArray[matchCount] = buildRecno;
349 
350 				if( ++matchCount >= MAX_SCAN_ID )
351 					break;
352 			}
353 		}
354 	}
355 
356 	//--- pick one from those plants that match the criteria ---//
357 
358 	if( matchCount > 0 )
359 	{
360 		int buildId = scanIdArray[misc.random(matchCount)];
361 
362 		#ifdef DEBUG
363 			town_res.get_build( buildId );		// get_build() will error if buildId is not valid
364 		#endif
365 
366 		return buildId;
367 	}
368 	else
369 		return 0;
370 }
371 //---------- End of function TownRes::scan_build -----------//
372 
373 #ifdef DEBUG
374 
375 //---------- Begin of function TownRes::get_layout -----------//
376 
get_layout(int recNo)377 TownLayout* TownRes::get_layout(int recNo)
378 {
379 	err_if( recNo<1 || recNo>town_layout_count )
380 		err_now( "TownRes::get_layout()" );
381 
382 	return town_layout_array+recNo-1;
383 }
384 //------------ End of function TownRes::get_layout -----------//
385 
386 
387 //---------- Begin of function TownRes::get_slot -----------//
388 
get_slot(int recNo)389 TownSlot* TownRes::get_slot(int recNo)
390 {
391 	err_if( recNo<1 || recNo>town_slot_count )
392 		err_now( "TownRes::get_slot()" );
393 
394 	return town_slot_array+recNo-1;
395 }
396 //------------ End of function TownRes::get_slot -----------//
397 
398 
399 //---------- Begin of function TownRes::get_build_type -----------//
400 
get_build_type(int recNo)401 TownBuildType* TownRes::get_build_type(int recNo)
402 {
403 	err_if( recNo<1 || recNo>town_build_type_count )
404 		err_now( "TownRes::get_build_type()" );
405 
406 	return town_build_type_array+recNo-1;
407 }
408 //------------ End of function TownRes::get_build_type -----------//
409 
410 
411 //---------- Begin of function TownRes::get_build -----------//
412 
get_build(int recNo)413 TownBuild* TownRes::get_build(int recNo)
414 {
415 	err_if( recNo<1 || recNo>town_build_count )
416 		err_now( "TownRes::get_build()" );
417 
418 	return town_build_array+recNo-1;
419 }
420 //------------ End of function TownRes::get_build -----------//
421 
422 #endif
423 
424 //---------- Begin of function TownRes::get_name -----------//
425 
get_name(int recNo)426 char* TownRes::get_name(int recNo)
427 {
428 	err_if( recNo<1 || recNo>town_name_count )
429 		err_now( "TownRes::get_name()" );
430 
431 	return town_name_array[recNo-1].name;
432 }
433 //------------ End of function TownRes::get_name -----------//
434 
435 
436 //--------- Begin of function TownRes::get_new_name_id ----------//
437 //
get_new_name_id(int raceId)438 int TownRes::get_new_name_id(int raceId)
439 {
440 	RaceInfo* raceInfo = race_res[raceId];
441 
442 	err_when( raceInfo->town_name_used_count > raceInfo->town_name_count );
443 
444 	int townNameId;
445 
446 	//----- if all town names have been used already -----//
447 	//--- scan the town name one by one and pick an unused one ---//
448 
449 	if( raceInfo->town_name_used_count == raceInfo->town_name_count )
450 	{
451 		int nameId = misc.random(raceInfo->town_name_count)+1;		// this is the id. of one race only
452 
453 		for( int i=raceInfo->town_name_count ; i>0 ; i-- )
454 		{
455 			if( ++nameId > raceInfo->town_name_count )
456 				nameId = 1;
457 
458 			if( town_name_used_array[raceInfo->first_town_name_recno+nameId-2]==0 )		// -2 is the total of two -1, (one with first_town_name_recno, another with town_name_used_array[]
459 				break;
460 		}
461 
462 		townNameId = raceInfo->first_town_name_recno + nameId - 1;
463 	}
464 	else
465 	{
466 		raceInfo->town_name_used_count++;
467 
468 		townNameId = raceInfo->first_town_name_recno + raceInfo->town_name_used_count - 1;
469 	}
470 
471 	err_when( townNameId < 1 || townNameId > town_name_count );
472 
473 	town_name_used_array[townNameId-1]++;
474 
475 	return townNameId;
476 }
477 //--------- End of function TownRes::get_new_name_id ----------//
478 
479 
480 //--------- Begin of function TownRes::free_name_id ----------//
481 //
482 // Free an used name id.
483 //
free_name_id(int townNameId)484 void TownRes::free_name_id(int townNameId)
485 {
486 	town_name_used_array[townNameId-1]--;
487 }
488 //--------- End of function TownRes::free_name_id ----------//
489