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