1 ///////////////////////////////////////////////////////////////////////////////
2 //            Copyright (C) 2004-2010 by The Allacrost Project
3 //                         All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software
6 // and you may modify it and/or redistribute it under the terms of this license.
7 // See http://www.gnu.org/copyleft/gpl.html for details.
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 /** ****************************************************************************
11 *** \file    map_zones.cpp
12 *** \author  Guillaume Anctil, drakkoon@allacrost.org
13 *** \brief   Source file for map mode zones.
14 *** ***************************************************************************/
15 
16 // Allacrost engines
17 #include "system.h"
18 
19 // Local map mode headers
20 #include "map.h"
21 #include "map_objects.h"
22 #include "map_sprites.h"
23 #include "map_zones.h"
24 
25 using namespace std;
26 using namespace hoa_utils;
27 
28 namespace hoa_map {
29 
30 namespace private_map {
31 
32 // *****************************************************************************
33 // ********** MapZone Class Functions
34 // *****************************************************************************
35 
AddSection(ZoneSection * section)36 void MapZone::AddSection(ZoneSection* section) {
37 	if (section == NULL) {
38 		IF_PRINT_WARNING(MAP_DEBUG) << "function argument was NULL" << endl;
39 		return;
40 	}
41 
42 	_sections.push_back(*section);
43 	delete section;
44 }
45 
46 
47 
IsInsideZone(uint16 pos_x,uint16 pos_y)48 bool MapZone::IsInsideZone(uint16 pos_x, uint16 pos_y) {
49 	// Verify each section of the zone to make sure the position is in bounds.
50 	for (vector<ZoneSection>::const_iterator i = _sections.begin(); i != _sections.end(); ++i) {
51 		if (pos_x >= i->left_col && pos_x <= i->right_col &&
52 			pos_y >= i->top_row && pos_y <= i->bottom_row)
53 		{
54 			return true;
55 		}
56 	}
57 	return false;
58 }
59 
60 
61 
_RandomPosition(uint16 & x,uint16 & y)62 void MapZone::_RandomPosition(uint16& x, uint16& y) {
63 	// Select a random ZoneSection
64 	uint16 i = RandomBoundedInteger(0, _sections.size() - 1);
65 
66 	// Select a random x and y position inside that section
67 	x = RandomBoundedInteger(_sections[i].left_col, _sections[i].right_col);
68 	y = RandomBoundedInteger(_sections[i].top_row, _sections[i].bottom_row);
69 }
70 
71 // *****************************************************************************
72 // ********** EnemyZone Class Functions
73 // *****************************************************************************
74 
EnemyZone(uint32 regen_time,bool restrained)75 EnemyZone::EnemyZone(uint32 regen_time, bool restrained) :
76 	_regen_time(regen_time),
77 	_spawn_timer(0),
78 	_active_enemies(0),
79 	_restrained(restrained)
80 {}
81 
82 
83 
AddEnemy(EnemySprite * enemy,MapMode * map,uint8 count)84 void EnemyZone::AddEnemy(EnemySprite* enemy, MapMode* map, uint8 count) {
85 	if (count == 0) {
86 		IF_PRINT_WARNING(MAP_DEBUG) << "function called with a count argument equal to zero" << endl;
87 		return;
88 	}
89 
90 	// Prepare the first enemy
91 	enemy->SetZone(this);
92 	map->AddGroundObject(enemy);
93 	_enemies.push_back(enemy);
94 
95 	// Create any additional copies of the enemy and add them as well
96 	for (uint8 i = 1; i < count; i++) {
97 		EnemySprite* copy = new EnemySprite(*enemy);
98 		copy->SetObjectID(map->GetObjectSupervisor()->GenerateObjectID());
99 		// Add a 10% random margin of error to make enemies look less synchronized
100 		copy->SetTimeToChange(static_cast<uint32>(copy->GetTimeToChange() * (1 + RandomFloat() * 10)));
101 		copy->Reset();
102 		map->AddGroundObject(copy);
103 		_enemies.push_back(copy);
104 	}
105 }
106 
107 
108 
EnemyDead()109 void EnemyZone::EnemyDead() {
110 	if (_active_enemies == 0) {
111 		IF_PRINT_WARNING(MAP_DEBUG) << "function called when no enemies were active" << endl;
112 	}
113 	else {
114 		--_active_enemies;
115 	}
116 }
117 
118 
119 
Update()120 void EnemyZone::Update() {
121 	// When spawning an enemy in a random zone location, sometimes it is occupied by another
122 	// object or that section is unwalkable. We try only a few different spawn locations before
123 	// giving up and waiting for the next call to Update(). Otherwise this function could
124 	// potentially take a noticable amount of time to complete
125 	const int8 SPAWN_RETRIES = 5;
126 
127 	if (_enemies.empty() == true)
128 		return;
129 
130 	// Spawn new enemies only if there is at least one enemy that is not active
131 	if (_active_enemies == _enemies.size())
132 		return;
133 
134 	// Update the regeneration timer and return if the spawn time has not yet been reached
135 	_spawn_timer += hoa_system::SystemManager->GetUpdateTime();
136 	if (_spawn_timer < _regen_time) {
137 		return;
138 	}
139 
140 	// Otherwise, select a DEAD enemy to spawn
141 	uint32 index = 0;
142 	for (uint32 i = 0; i < _enemies.size(); i++) {
143 		if (_enemies[i]->IsDead() == true) {
144 			index = i;
145 			break;
146 		}
147 	}
148 
149 
150 	uint16 x, y; // Used to retain random position coordinates in the zone
151 	int8 retries = SPAWN_RETRIES; // Number of times to try finding a valid spawning location
152 	bool collision; // Holds the result of a collision detection check
153 
154 	// Select a random position inside the zone to place the spawning enemy
155 	// If there is a collision, retry a different location
156 	_enemies[index]->no_collision = false;
157 	do {
158 		_RandomPosition(x, y);
159 		_enemies[index]->SetXPosition(x, 0.0f);
160 		_enemies[index]->SetYPosition(y, 0.0f);
161 		collision = MapMode::CurrentInstance()->GetObjectSupervisor()->DetectCollision(_enemies[index], NULL);
162 	} while (collision && --retries > 0);
163 
164 	// If we didn't find a suitable spawning location, reset the collision info
165 	// on the enemy sprite and we will retry on the next call to this function
166 	if (collision) {
167 		_enemies[index]->no_collision = true;
168 	}
169 	// Otherwise, spawn the enemy and reset the spawn timer
170 	else {
171 		_spawn_timer = 0;
172 		_enemies[index]->ChangeStateSpawning();
173 		_active_enemies++;
174 	}
175 } // void EnemyZone::Update()
176 
177 // *****************************************************************************
178 // ********** ContextZone Class Functions
179 // *****************************************************************************
180 
ContextZone(MAP_CONTEXT one,MAP_CONTEXT two)181 ContextZone::ContextZone(MAP_CONTEXT one, MAP_CONTEXT two) :
182 	_context_one(one),
183 	_context_two(two)
184 {
185 	if (_context_one == _context_two) {
186 		PRINT_ERROR << "tried to create a ContextZone with two equal context values: " << _context_one << endl;
187 		exit(1);
188 	}
189 }
190 
191 
192 
AddSection(ZoneSection * section,bool context)193 void ContextZone::AddSection(ZoneSection* section, bool context) {
194 	if (section == NULL) {
195 		IF_PRINT_WARNING(MAP_DEBUG) << "function argument was NULL" << endl;
196 		return;
197 	}
198 
199 	_sections.push_back(*section);
200 	_section_contexts.push_back(context);
201 	delete section;
202 }
203 
204 
205 
Update()206 void ContextZone::Update() {
207 	int16 index;
208 
209 	// Check every ground object and determine if its context should be changed by this zone
210 	for (std::vector<MapObject*>::iterator i = MapMode::CurrentInstance()->GetObjectSupervisor()->_ground_objects.begin();
211 		i != MapMode::CurrentInstance()->GetObjectSupervisor()->_ground_objects.end(); i++)
212 	{
213 		// If the object does not have a context equal to one of the two switching contexts, do not examine it further
214 		if ((*i)->GetContext() != _context_one && (*i)->GetContext() != _context_two) {
215 			continue;
216 		}
217 
218 		// If the object is inside the zone, set their context to that zone's context
219 		// (This may result in no change from the object's current context depending on the zone section)
220 		index = _IsInsideZone(*i);
221 		if (index >= 0) {
222 			(*i)->SetContext(_section_contexts[index] ? _context_one : _context_two);
223 		}
224 	}
225 }
226 
227 
228 
_IsInsideZone(MapObject * object)229 int16 ContextZone::_IsInsideZone(MapObject* object) {
230 	// NOTE: argument is not checked here for performance reasons
231 
232 	// Check each section of the zone to see if the object is located within
233 	for (uint16 i = 0; i < _sections.size(); i++) {
234 		if (object->x_position >= _sections[i].left_col && object->x_position <= _sections[i].right_col &&
235 			object->y_position >= _sections[i].top_row && object->y_position <= _sections[i].bottom_row)
236 		{
237 			return i;
238 		}
239 	}
240 	return -1;
241 }
242 
243 } // namespace private_map
244 
245 } // namespace hoa_map
246