1 /*-------------------------------------------------------------------------------
2 
3 BARONY
4 File: actpowercrystal.cpp
5 Desc: behavior function for power crystals
6 
7 Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 See LICENSE for details.
9 
10 -------------------------------------------------------------------------------*/
11 
12 #include "main.hpp"
13 #include "game.hpp"
14 #include "stat.hpp"
15 #include "entity.hpp"
16 #include "monster.hpp"
17 #include "sound.hpp"
18 #include "items.hpp"
19 #include "net.hpp"
20 #include "collision.hpp"
21 #include "player.hpp"
22 
23 /*-------------------------------------------------------------------------------
24 
25 act*
26 
27 The following function describes an entity behavior. The function
28 takes a pointer to the entity that uses it as an argument.
29 
30 -------------------------------------------------------------------------------*/
31 
actPowerCrystalBase(Entity * my)32 void actPowerCrystalBase(Entity* my)
33 {
34 	if ( my->flags[PASSABLE] ) // stop the compiler optimising into a different entity.
35 	{
36 		my->flags[PASSABLE] = false;
37 	}
38 
39 	return;
40 }
41 
actPowerCrystal(Entity * my)42 void actPowerCrystal(Entity* my)
43 {
44 	if ( !my )
45 	{
46 		return;
47 	}
48 
49 	my->actPowerCrystal();
50 }
51 
actPowerCrystal()52 void Entity::actPowerCrystal()
53 {
54 	//Entity* entity;
55 	real_t upper_z = this->crystalStartZ - 0.4;
56 	real_t lower_z = crystalStartZ + 0.4;
57 	int i = 0;
58 
59 	real_t acceleration = 0.95;
60 
61 	//this->light = lightSphereShadow(this->x / 16, this->y / 16, 3, 64);
62 	//messagePlayer(0, "vel z: %f", this->vel_z);
63 
64 	if ( !crystalInitialised && !crystalSpellToActivate )
65 	{
66 		if ( this->z > crystalStartZ )
67 		{
68 			this->z -= this->vel_z * (1 / acceleration); // start levitating upwards.
69 		}
70 		else
71 		{
72 			this->z = crystalStartZ;
73 			this->powerCrystalCreateElectricityNodes();
74 			crystalInitialised = 1;
75 		}
76 	}
77 
78 	if ( crystalInitialised )
79 	{
80 		if ( crystalHoverDirection == CRYSTAL_HOVER_UP ) //rise state
81 		{
82 			this->z -= this->vel_z;
83 
84 			if ( this->z < upper_z )
85 			{
86 				this->z = upper_z;
87 				crystalHoverDirection = CRYSTAL_HOVER_UP_WAIT;
88 			}
89 
90 			if ( this->z < crystalStartZ ) //higher than mid point
91 			{
92 				this->vel_z = std::max(this->vel_z * acceleration, crystalMinZVelocity);
93 			}
94 			else if ( this->z > crystalStartZ ) //lower than midpoint
95 			{
96 				this->vel_z = std::min(this->vel_z * (1 / acceleration), crystalMaxZVelocity);
97 			}
98 		}
99 		else if ( crystalHoverDirection == CRYSTAL_HOVER_UP_WAIT ) // wait state
100 		{
101 			crystalHoverWaitTimer++;
102 			if ( crystalHoverWaitTimer >= 1 )
103 			{
104 				crystalHoverDirection = CRYSTAL_HOVER_DOWN; // advance state
105 				crystalHoverWaitTimer = 0; // reset timer
106 			}
107 		}
108 		else if ( crystalHoverDirection == CRYSTAL_HOVER_DOWN ) //fall state
109 		{
110 			this->z += this->vel_z;
111 
112 			if ( this->z > lower_z )
113 			{
114 				this->z = lower_z;
115 				crystalHoverDirection = CRYSTAL_HOVER_DOWN_WAIT;
116 			}
117 
118 			if ( this->z < crystalStartZ ) //higher than mid point, start accelerating
119 			{
120 				this->vel_z = std::min(this->vel_z * (1 / acceleration), crystalMaxZVelocity);
121 			}
122 			else if ( this->z > crystalStartZ ) //lower than midpoint, start decelerating
123 			{
124 				this->vel_z = std::max(this->vel_z * acceleration, crystalMinZVelocity);
125 			}
126 		}
127 		else if ( crystalHoverDirection == CRYSTAL_HOVER_DOWN_WAIT ) // wait state
128 		{
129 			crystalHoverWaitTimer++;
130 			if ( crystalHoverWaitTimer >= 1 )
131 			{
132 				crystalHoverDirection = CRYSTAL_HOVER_UP; // advance state
133 				crystalHoverWaitTimer = 0; // reset timer
134 			}
135 		}
136 
137 
138 		if ( this->z <= crystalStartZ + crystalMaxZVelocity && this->z >= crystalStartZ - crystalMaxZVelocity )
139 		{
140 			this->vel_z = this->fskill[1]; // reset velocity at the mid point of animation
141 		}
142 
143 		spawnAmbientParticles(80, 579, 10 + rand() % 40, 1.0, false);
144 
145 		if ( crystalTurning == 1 )
146 		{
147 			if ( !crystalTurnReverse )
148 			{
149 				this->yaw += crystalTurnVelocity; // reverse velocity if turnReverse is 1
150 
151 				if ( (this->yaw >= (crystalTurnStartDir * (PI / 2)) + (PI / 2)) )
152 				{
153 					this->yaw = crystalTurnStartDir * (PI / 2) + (PI / 2);
154 					crystalTurning = 0;
155 
156 					if ( this->yaw >= 2 * PI )
157 					{
158 						this->yaw = 0;
159 					}
160 					this->powerCrystalCreateElectricityNodes();
161 				}
162 			}
163 			else
164 			{
165 				this->yaw -= crystalTurnVelocity;// reverse velocity if turnReverse is 1
166 
167 				if ( (this->yaw <= (crystalTurnStartDir * (PI / 2)) - (PI / 2)) )
168 				{
169 					this->yaw = crystalTurnStartDir * (PI / 2) - (PI / 2);
170 					crystalTurning = 0;
171 
172 					if ( this->yaw < 0 )
173 					{
174 						this->yaw += 2 * PI;
175 					}
176 					this->powerCrystalCreateElectricityNodes();
177 				}
178 			}
179 		}
180 	}
181 
182 	if ( multiplayer == CLIENT )
183 	{
184 		return;
185 	}
186 
187 	// handle player turning the crystal
188 
189 	for ( i = 0; i < MAXPLAYERS; i++ )
190 	{
191 		if ( ((i == 0 && selectedEntity == this) || (client_selected[i] == this)) && crystalTurning == 0 )
192 		{
193 			if ( inrange[i] )
194 			{
195 				if ( players[i] && players[i]->entity && crystalInitialised )
196 				{
197 					playSoundEntity(this, 151, 128);
198 					crystalTurning = 1;
199 					crystalTurnStartDir = static_cast<Sint32>(this->yaw / (PI / 2));
200 					serverUpdateEntitySkill(this, 3);
201 					serverUpdateEntitySkill(this, 4);
202 					messagePlayer(i, language[2356]);
203 				}
204 				else if ( !crystalInitialised )
205 				{
206 					messagePlayer(i, language[2357]);
207 				}
208 			}
209 		}
210 	}
211 
212 	return;
213 }
214 
215 // ambient particle effects.
actPowerCrystalParticleIdle(Entity * my)216 void actPowerCrystalParticleIdle(Entity* my)
217 {
218 	if ( !my )
219 	{
220 		return;
221 	}
222 
223 	if ( my->skill[0] < 0 )
224 	{
225 		list_RemoveNode(my->mynode);
226 		return;
227 	}
228 	else
229 	{
230 		--my->skill[0];
231 		my->z += my->vel_z;
232 		//my->z -= 0.01;
233 	}
234 	return;
235 }
236 
powerCrystalCreateElectricityNodes()237 void Entity::powerCrystalCreateElectricityNodes()
238 {
239 	Entity* entity = nullptr;
240 	node_t* node = nullptr;
241 	node_t* nextnode = nullptr;
242 	real_t xtest = 0;
243 	real_t ytest = 0;
244 
245 	int i = 0;
246 
247 	if ( crystalGeneratedElectricityNodes )
248 	{
249 		this->mechanismPowerOff(); // turn off my signal
250 		this->updateCircuitNeighbors(); // update the old wires to depower
251 
252 		if ( multiplayer != CLIENT )
253 		{
254 			for ( node = this->children.first; node != nullptr; node = nextnode )
255 			{
256 				nextnode = node->next;
257 				if ( node->element != nullptr )
258 				{
259 					entity = (Entity*)node->element;
260 					if ( entity->light != nullptr )
261 					{
262 						list_RemoveNode(entity->light->node);
263 					}
264 					entity->light = nullptr;
265 					list_RemoveNode(entity->mynode);
266 				}
267 				list_RemoveNode(node); // delete all previously generated electricity nodes.
268 			}
269 		}
270 	}
271 
272 	for ( i = 1; i <= crystalNumElectricityNodes; i++ )
273 	{
274 		entity = newEntity(-1, 0, map.entities, nullptr); // electricity node
275 		xtest = this->x + i * 16 * ((this->yaw == 0) - (this->yaw == PI)); // add/subtract x depending on direction.
276 		ytest = this->y + i * 16 * ((this->yaw == PI / 2) - (this->yaw == 3 * PI / 2)); // add/subtract y depending on direction.
277 
278 		if ( (static_cast<int>(xtest) >> 4) < 0 || (static_cast<int>(xtest) >> 4) >= map.width ||
279 			(static_cast<int>(ytest) >> 4) < 0 || (static_cast<int>(ytest) >> 4) >= map.height )
280 		{
281 			//messagePlayer(0, "stopped at index %d, x: %d, y: %d", i, (static_cast<int>(xtest) >> 4), (static_cast<int>(ytest) >> 4));
282 			break; // stop generating more nodes as we are out of bounds
283 		}
284 
285 		//messagePlayer(0, "gen at index %d", i);
286 		entity->x = xtest;
287 		entity->y = ytest;
288 		entity->z = 5;
289 		entity->behavior = &actCircuit;
290 		entity->flags[PASSABLE] = true;
291 		entity->flags[INVISIBLE] = true;
292 		entity->flags[NOUPDATE] = true;
293 		entity->circuit_status = CIRCUIT_OFF; //It's a depowered powerable.
294 
295 		node = list_AddNodeLast(&this->children);
296 		node->element = entity; // add the node to the children list.
297 		node->deconstructor = &emptyDeconstructor;
298 		node->size = sizeof(Entity*);
299 
300 		TileEntityList.addEntity(*entity); // make sure new nodes are added to the tile list to properly update neighbors.
301 
302 		this->crystalGeneratedElectricityNodes = 1;
303 	}
304 
305 	this->mechanismPowerOn();
306 	this->updateCircuitNeighbors();
307 
308 	return;
309 }
310