1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: actdoor.cpp
5 	Desc: behavior function for doors
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 "sound.hpp"
17 #include "net.hpp"
18 #include "collision.hpp"
19 #include "player.hpp"
20 #include "interface/interface.hpp"
21 #include "items.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 
actDoor(Entity * my)32 void actDoor(Entity* my)
33 {
34 	if (!my)
35 	{
36 		return;
37 	}
38 
39 	Entity* entity;
40 	int i, c;
41 
42 	if ( !my->doorInit )
43 	{
44 		my->doorInit = 1;
45 		my->doorStartAng = my->yaw;
46 		my->doorHealth = 15 + rand() % 5;
47 		my->doorMaxHealth = my->doorHealth;
48 		my->doorPreventLockpickExploit = 1;
49 		my->doorLockpickHealth = 20;
50 		if ( my->doorForceLockedUnlocked == 2 )
51 		{
52 			my->doorLocked = 0; // force unlocked.
53 		}
54 		else if ( rand() % 20 == 0 || (!strncmp(map.name, "The Great Castle", 16) && rand() % 2 == 0) || my->doorForceLockedUnlocked == 1 )   // 5% chance
55 		{
56 			my->doorLocked = 1;
57 			my->doorPreventLockpickExploit = 0;
58 		}
59 		my->doorOldStatus = my->doorStatus;
60 		my->scalex = 1.01;
61 		my->scaley = 1.01;
62 		my->scalez = 1.01;
63 		my->flags[BURNABLE] = true;
64 	}
65 	else
66 	{
67 		if ( multiplayer != CLIENT )
68 		{
69 			// burning
70 			if ( my->flags[BURNING] )
71 			{
72 				if ( ticks % 30 == 0 )
73 				{
74 					my->doorHealth--;
75 				}
76 			}
77 
78 			// door mortality :p
79 			if ( my->doorHealth <= 0 )
80 			{
81 				for ( c = 0; c < 5; c++ )
82 				{
83 					entity = spawnGib(my);
84 					entity->flags[INVISIBLE] = false;
85 					entity->sprite = 187; // Splinter.vox
86 					entity->x = floor(my->x / 16) * 16 + 8;
87 					entity->y = floor(my->y / 16) * 16 + 8;
88 					entity->z = 0;
89 					entity->z += -7 + rand() % 14;
90 					if ( !my->doorDir )
91 					{
92 						// horizontal door
93 						entity->y += -4 + rand() % 8;
94 						if ( my->doorSmacked )
95 						{
96 							entity->yaw = PI;
97 						}
98 						else
99 						{
100 							entity->yaw = 0;
101 						}
102 					}
103 					else
104 					{
105 						// vertical door
106 						entity->x += -4 + rand() % 8;
107 						if ( my->doorSmacked )
108 						{
109 							entity->yaw = PI / 2;
110 						}
111 						else
112 						{
113 							entity->yaw = 3 * PI / 2;
114 						}
115 					}
116 					entity->pitch = (rand() % 360) * PI / 180.0;
117 					entity->roll = (rand() % 360) * PI / 180.0;
118 					entity->vel_x = cos(entity->yaw) * (1.2 + (rand() % 10) / 50.0);
119 					entity->vel_y = sin(entity->yaw) * (1.2 + (rand() % 10) / 50.0);
120 					entity->vel_z = -.25;
121 					entity->fskill[3] = 0.04;
122 					serverSpawnGibForClient(entity);
123 				}
124 				playSoundEntity(my, 177, 64);
125 				list_RemoveNode(my->mynode);
126 				return;
127 			}
128 
129 			// using door
130 			for (i = 0; i < MAXPLAYERS; i++)
131 			{
132 				if ( (i == 0 && selectedEntity == my) || (client_selected[i] == my) )
133 				{
134 					if ( players[i]->entity && inrange[i])
135 					{
136 						if ( !my->doorLocked )   // door unlocked
137 						{
138 							if ( !my->doorDir && !my->doorStatus )
139 							{
140 								// open door
141 								my->doorStatus = 1 + (players[i]->entity->x > my->x);
142 								playSoundEntity(my, 21, 96);
143 								messagePlayer(i, language[464]);
144 							}
145 							else if ( my->doorDir && !my->doorStatus )
146 							{
147 								// open door
148 								my->doorStatus = 1 + (players[i]->entity->y < my->y);
149 								playSoundEntity(my, 21, 96);
150 								messagePlayer(i, language[464]);
151 							}
152 							else
153 							{
154 								// close door
155 								my->doorStatus = 0;
156 								playSoundEntity(my, 22, 96);
157 								messagePlayer(i, language[465]);
158 							}
159 						}
160 						else
161 						{
162 							// door locked
163 							messagePlayer(i, language[466]);
164 							playSoundEntity(my, 152, 64);
165 						}
166 					}
167 				}
168 			}
169 		}
170 
171 		// door swinging
172 		if ( !my->doorStatus )
173 		{
174 			// closing door
175 			if ( my->yaw > my->doorStartAng )
176 			{
177 				my->yaw = std::max(my->doorStartAng, my->yaw - 0.15);
178 			}
179 			else if ( my->yaw < my->doorStartAng )
180 			{
181 				my->yaw = std::min(my->doorStartAng, my->yaw + 0.15);
182 			}
183 		}
184 		else
185 		{
186 			// opening door
187 			if ( my->doorStatus == 1 )
188 			{
189 				if ( my->yaw > my->doorStartAng + PI / 2 )
190 				{
191 					my->yaw = std::max(my->doorStartAng + PI / 2, my->yaw - 0.15);
192 				}
193 				else if ( my->yaw < my->doorStartAng + PI / 2 )
194 				{
195 					my->yaw = std::min(my->doorStartAng + PI / 2, my->yaw + 0.15);
196 				}
197 			}
198 			else if ( my->doorStatus == 2 )
199 			{
200 				if ( my->yaw > my->doorStartAng - PI / 2 )
201 				{
202 					my->yaw = std::max(my->doorStartAng - PI / 2, my->yaw - 0.15);
203 				}
204 				else if ( my->yaw < my->doorStartAng - PI / 2 )
205 				{
206 					my->yaw = std::min(my->doorStartAng - PI / 2, my->yaw + 0.15);
207 				}
208 			}
209 		}
210 
211 		// setting collision
212 		if ( my->yaw == my->doorStartAng && my->flags[PASSABLE] )
213 		{
214 			// don't set impassable if someone's inside, otherwise do
215 			node_t* node;
216 			bool somebodyinside = false;
217 			std::vector<list_t*> entLists = TileEntityList.getEntitiesWithinRadiusAroundEntity(my, 2);
218 			for ( std::vector<list_t*>::iterator it = entLists.begin(); it != entLists.end() && !somebodyinside; ++it )
219 			{
220 				list_t* currentList = *it;
221 				for ( node = currentList->first; node != nullptr; node = node->next )
222 				{
223 					Entity* entity = (Entity*)node->element;
224 					if ( entity == my || entity->flags[PASSABLE] || entity->sprite == 1  )
225 					{
226 						continue;
227 					}
228 					if ( entityInsideEntity(my, entity) )
229 					{
230 						somebodyinside = true;
231 						break;
232 					}
233 				}
234 			}
235 			if ( !somebodyinside )
236 			{
237 				my->focaly = 0;
238 				if ( my->doorStartAng == 0 )
239 				{
240 					my->y -= 5;
241 				}
242 				else
243 				{
244 					my->x -= 5;
245 				}
246 				my->flags[PASSABLE] = false;
247 			}
248 		}
249 		else if ( my->yaw != my->doorStartAng && !my->flags[PASSABLE] )
250 		{
251 			my->focaly = -5;
252 			if ( my->doorStartAng == 0 )
253 			{
254 				my->y += 5;
255 			}
256 			else
257 			{
258 				my->x += 5;
259 			}
260 			my->flags[PASSABLE] = true;
261 		}
262 
263 		// update for clients
264 		if ( multiplayer == SERVER )
265 		{
266 			if ( my->doorOldStatus != my->doorStatus )
267 			{
268 				my->doorOldStatus = my->doorStatus;
269 				serverUpdateEntitySkill(my, 3);
270 			}
271 		}
272 	}
273 }
274 
actDoorFrame(Entity * my)275 void actDoorFrame(Entity* my)
276 {
277 	// dummy function
278 	// intended to make it easier
279 	// to determine whether an entity
280 	// is part of a door frame
281 	if ( my->sprite == 1 && my->flags[INVISIBLE] == false )
282 	{
283 		my->flags[PASSABLE] = true; // the actual frame should ALWAYS be passable
284 	}
285 }
286 
doorHandleDamageMagic(int damage,Entity & magicProjectile,Entity * caster)287 void Entity::doorHandleDamageMagic(int damage, Entity &magicProjectile, Entity *caster)
288 {
289 	doorHealth -= damage; //Decrease door health.
290 	if ( caster )
291 	{
292 		if ( caster->behavior == &actPlayer )
293 		{
294 			if ( doorHealth <= 0 )
295 			{
296 				if ( magicProjectile.behavior == &actBomb )
297 				{
298 					messagePlayer(caster->skill[2], language[3617], items[magicProjectile.skill[21]].name_identified, language[674]);
299 				}
300 				else
301 				{
302 					messagePlayer(caster->skill[2], language[387]);
303 				}
304 			}
305 			else
306 			{
307 				if ( magicProjectile.behavior == &actBomb )
308 				{
309 					messagePlayer(caster->skill[2], language[3618], items[magicProjectile.skill[21]].name_identified, language[674]);
310 				}
311 				else
312 				{
313 					messagePlayer(caster->skill[2], language[378], language[674]);
314 				}
315 			}
316 			updateEnemyBar(caster, this, language[674], doorHealth, doorMaxHealth);
317 		}
318 	}
319 	if ( !doorDir )
320 	{
321 		doorSmacked = (magicProjectile.x > this->x);
322 	}
323 	else
324 	{
325 		doorSmacked = (magicProjectile.y < this->y);
326 	}
327 
328 	playSoundEntity(this, 28, 128);
329 }
330