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