1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: monster_imp.cpp
5 	Desc: implements all of the imp monster's code
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 "items.hpp"
17 #include "monster.hpp"
18 #include "sound.hpp"
19 #include "net.hpp"
20 #include "collision.hpp"
21 #include "player.hpp"
22 
initImp(Entity * my,Stat * myStats)23 void initImp(Entity* my, Stat* myStats)
24 {
25 	int c;
26 	node_t* node;
27 
28 	my->initMonster(289);
29 
30 	if ( multiplayer != CLIENT )
31 	{
32 		MONSTER_SPOTSND = 198;
33 		MONSTER_SPOTVAR = 3;
34 		MONSTER_IDLESND = 201;
35 		MONSTER_IDLEVAR = 3;
36 	}
37 	if ( multiplayer != CLIENT && !MONSTER_INIT )
38 	{
39 		if ( myStats != nullptr )
40 		{
41 			if ( !myStats->leader_uid )
42 			{
43 				myStats->leader_uid = 0;
44 			}
45 
46 			// apply random stat increases if set in stat_shared.cpp or editor
47 			setRandomMonsterStats(myStats);
48 
49 			// generate 6 items max, less if there are any forced items from boss variants
50 			int customItemsToGenerate = ITEM_CUSTOM_SLOT_LIMIT;
51 
52 			// boss variants
53 
54 			// random effects
55 			myStats->EFFECTS[EFF_LEVITATING] = true;
56 			myStats->EFFECTS_TIMERS[EFF_LEVITATING] = 0;
57 
58 			if ( rand() % 4 == 0 && strncmp(map.name, "Hell Boss", 9) )
59 			{
60 				myStats->EFFECTS[EFF_ASLEEP] = true;
61 				myStats->EFFECTS_TIMERS[EFF_ASLEEP] = 1800 + rand() % 3600;
62 			}
63 
64 			// generates equipment and weapons if available from editor
65 			createMonsterEquipment(myStats);
66 
67 			// create any custom inventory items from editor if available
68 			createCustomInventory(myStats, customItemsToGenerate);
69 
70 			// count if any custom inventory items from editor
71 			int customItems = countCustomItems(myStats); //max limit of 6 custom items per entity.
72 
73 														 // count any inventory items set to default in edtior
74 			int defaultItems = countDefaultItems(myStats);
75 
76 			my->setHardcoreStats(*myStats);
77 
78 			// generate the default inventory items for the monster, provided the editor sprite allowed enough default slots
79 			switch ( defaultItems )
80 			{
81 				case 6:
82 				case 5:
83 				case 4:
84 				case 3:
85 				case 2:
86 				case 1:
87 					if ( rand() % 4 == 0 )
88 					{
89 						newItem(static_cast<ItemType>(SPELLBOOK_FORCEBOLT + rand() % 21), static_cast<Status>(1 + rand() % 4), -1 + rand() % 3, 1, rand(), false, &myStats->inventory);
90 					}
91 					break;
92 				default:
93 					break;
94 			}
95 
96 			//give weapon
97 			if ( myStats->weapon == nullptr && myStats->EDITOR_ITEMS[ITEM_SLOT_WEAPON] == 1 )
98 			{
99 				myStats->weapon = newItem(SPELLBOOK_FIREBALL, EXCELLENT, 0, 1, 0, false, nullptr);
100 			}
101 		}
102 	}
103 
104 	// torso
105 	Entity* entity = newEntity(290, 0, map.entities, nullptr); //Limb entity.
106 	entity->sizex = 4;
107 	entity->sizey = 4;
108 	entity->focaly = 1;
109 	entity->skill[2] = my->getUID();
110 	entity->flags[PASSABLE] = true;
111 	entity->flags[NOUPDATE] = true;
112 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
113 	entity->focalx = limbs[CREATURE_IMP][1][0]; // 0
114 	entity->focaly = limbs[CREATURE_IMP][1][1]; // 1
115 	entity->focalz = limbs[CREATURE_IMP][1][2]; // 0
116 	entity->behavior = &actImpLimb;
117 	entity->parent = my->getUID();
118 	node = list_AddNodeLast(&my->children);
119 	node->element = entity;
120 	node->deconstructor = &emptyDeconstructor;
121 	node->size = sizeof(Entity*);
122 	my->bodyparts.push_back(entity);
123 
124 	// right leg
125 	entity = newEntity(292, 0, map.entities, nullptr); //Limb entity.
126 	entity->sizex = 4;
127 	entity->sizey = 4;
128 	entity->skill[2] = my->getUID();
129 	entity->flags[PASSABLE] = true;
130 	entity->flags[NOUPDATE] = true;
131 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
132 	entity->focalx = limbs[CREATURE_IMP][2][0]; // 0
133 	entity->focaly = limbs[CREATURE_IMP][2][1]; // 0
134 	entity->focalz = limbs[CREATURE_IMP][2][2]; // 2
135 	entity->behavior = &actImpLimb;
136 	entity->parent = my->getUID();
137 	node = list_AddNodeLast(&my->children);
138 	node->element = entity;
139 	node->deconstructor = &emptyDeconstructor;
140 	node->size = sizeof(Entity*);
141 	my->bodyparts.push_back(entity);
142 
143 	// left leg
144 	entity = newEntity(291, 0, map.entities, nullptr); //Limb entity.
145 	entity->sizex = 4;
146 	entity->sizey = 4;
147 	entity->skill[2] = my->getUID();
148 	entity->flags[PASSABLE] = true;
149 	entity->flags[NOUPDATE] = true;
150 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
151 	entity->focalx = limbs[CREATURE_IMP][3][0]; // 0
152 	entity->focaly = limbs[CREATURE_IMP][3][1]; // 0
153 	entity->focalz = limbs[CREATURE_IMP][3][2]; // 2
154 	entity->behavior = &actImpLimb;
155 	entity->parent = my->getUID();
156 	node = list_AddNodeLast(&my->children);
157 	node->element = entity;
158 	node->deconstructor = &emptyDeconstructor;
159 	node->size = sizeof(Entity*);
160 	my->bodyparts.push_back(entity);
161 
162 	// right arm
163 	entity = newEntity(294, 0, map.entities, nullptr); //Limb entity.
164 	entity->sizex = 4;
165 	entity->sizey = 4;
166 	entity->skill[2] = my->getUID();
167 	entity->flags[PASSABLE] = true;
168 	entity->flags[NOUPDATE] = true;
169 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
170 	entity->focalx = limbs[CREATURE_IMP][4][0]; // 0
171 	entity->focaly = limbs[CREATURE_IMP][4][1]; // 0
172 	entity->focalz = limbs[CREATURE_IMP][4][2]; // 3
173 	entity->behavior = &actImpLimb;
174 	entity->parent = my->getUID();
175 	node = list_AddNodeLast(&my->children);
176 	node->element = entity;
177 	node->deconstructor = &emptyDeconstructor;
178 	node->size = sizeof(Entity*);
179 	my->bodyparts.push_back(entity);
180 
181 	// left arm
182 	entity = newEntity(293, 0, map.entities, nullptr); //Limb entity.
183 	entity->sizex = 4;
184 	entity->sizey = 4;
185 	entity->skill[2] = my->getUID();
186 	entity->flags[PASSABLE] = true;
187 	entity->flags[NOUPDATE] = true;
188 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
189 	entity->focalx = limbs[CREATURE_IMP][5][0]; // 0
190 	entity->focaly = limbs[CREATURE_IMP][5][1]; // 0
191 	entity->focalz = limbs[CREATURE_IMP][5][2]; // 3
192 	entity->behavior = &actImpLimb;
193 	entity->parent = my->getUID();
194 	node = list_AddNodeLast(&my->children);
195 	node->element = entity;
196 	node->deconstructor = &emptyDeconstructor;
197 	node->size = sizeof(Entity*);
198 	my->bodyparts.push_back(entity);
199 
200 	// right wing
201 	entity = newEntity(310, 0, map.entities, nullptr); //Limb entity.
202 	entity->sizex = 4;
203 	entity->sizey = 4;
204 	entity->skill[2] = my->getUID();
205 	entity->flags[PASSABLE] = true;
206 	entity->flags[NOUPDATE] = true;
207 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
208 	entity->focalx = limbs[CREATURE_IMP][6][0]; // 0
209 	entity->focaly = limbs[CREATURE_IMP][6][1]; // 4
210 	entity->focalz = limbs[CREATURE_IMP][6][2]; // 0
211 	entity->behavior = &actImpLimb;
212 	entity->parent = my->getUID();
213 	node = list_AddNodeLast(&my->children);
214 	node->element = entity;
215 	node->deconstructor = &emptyDeconstructor;
216 	node->size = sizeof(Entity*);
217 	my->bodyparts.push_back(entity);
218 
219 	// left wing
220 	entity = newEntity(309, 0, map.entities, nullptr); //Limb entity.
221 	entity->sizex = 4;
222 	entity->sizey = 4;
223 	entity->skill[2] = my->getUID();
224 	entity->flags[PASSABLE] = true;
225 	entity->flags[NOUPDATE] = true;
226 	entity->flags[USERFLAG2] = my->flags[USERFLAG2];
227 	entity->focalx = limbs[CREATURE_IMP][7][0]; // 0
228 	entity->focaly = limbs[CREATURE_IMP][7][1]; // -4
229 	entity->focalz = limbs[CREATURE_IMP][7][2]; // 0
230 	entity->behavior = &actImpLimb;
231 	entity->parent = my->getUID();
232 	node = list_AddNodeLast(&my->children);
233 	node->element = entity;
234 	node->deconstructor = &emptyDeconstructor;
235 	node->size = sizeof(Entity*);
236 	my->bodyparts.push_back(entity);
237 }
238 
actImpLimb(Entity * my)239 void actImpLimb(Entity* my)
240 {
241 	my->actMonsterLimb();
242 }
243 
impDie(Entity * my)244 void impDie(Entity* my)
245 {
246 	int c;
247 	for ( c = 0; c < 5; c++ )
248 	{
249 		Entity* gib = spawnGib(my);
250 		serverSpawnGibForClient(gib);
251 	}
252 
253 	my->spawnBlood();
254 
255 	playSoundEntity(my, 28, 128);
256 
257 	my->removeMonsterDeathNodes();
258 
259 	list_RemoveNode(my->mynode);
260 	return;
261 }
262 
263 #define IMPWALKSPEED .01
264 
impMoveBodyparts(Entity * my,Stat * myStats,double dist)265 void impMoveBodyparts(Entity* my, Stat* myStats, double dist)
266 {
267 	node_t* node;
268 	Entity* entity = nullptr;
269 	Entity* rightbody = nullptr;
270 	int bodypart;
271 
272 	// set invisibility //TODO: isInvisible()?
273 	if ( multiplayer != CLIENT )
274 	{
275 		if ( myStats->EFFECTS[EFF_INVISIBLE] == true )
276 		{
277 			my->flags[INVISIBLE] = true;
278 			my->flags[BLOCKSIGHT] = false;
279 			bodypart = 0;
280 			for (node = my->children.first; node != nullptr; node = node->next)
281 			{
282 				if ( bodypart < LIMB_HUMANOID_TORSO )
283 				{
284 					bodypart++;
285 					continue;
286 				}
287 				if ( bodypart >= 7 )
288 				{
289 					break;
290 				}
291 				entity = (Entity*)node->element;
292 				if ( !entity->flags[INVISIBLE] )
293 				{
294 					entity->flags[INVISIBLE] = true;
295 					serverUpdateEntityBodypart(my, bodypart);
296 				}
297 				bodypart++;
298 			}
299 		}
300 		else
301 		{
302 			my->flags[INVISIBLE] = false;
303 			my->flags[BLOCKSIGHT] = true;
304 			bodypart = 0;
305 			for (node = my->children.first; node != nullptr; node = node->next)
306 			{
307 				if ( bodypart < LIMB_HUMANOID_TORSO )
308 				{
309 					bodypart++;
310 					continue;
311 				}
312 				if ( bodypart >= 7 )
313 				{
314 					break;
315 				}
316 				entity = (Entity*)node->element;
317 				if ( entity->flags[INVISIBLE] )
318 				{
319 					entity->flags[INVISIBLE] = false;
320 					serverUpdateEntityBodypart(my, bodypart);
321 					serverUpdateEntityFlag(my, INVISIBLE);
322 				}
323 				bodypart++;
324 			}
325 		}
326 
327 		// sleeping
328 		if ( myStats->EFFECTS[EFF_ASLEEP] )
329 		{
330 			my->pitch = PI / 4;
331 		}
332 		else
333 		{
334 			my->pitch = 0;
335 		}
336 
337 		// imps are always flying
338 		myStats->EFFECTS[EFF_LEVITATING] = true;
339 		myStats->EFFECTS_TIMERS[EFF_LEVITATING] = 0;
340 	}
341 
342 	//Move bodyparts
343 	for (bodypart = 0, node = my->children.first; node != nullptr; node = node->next, bodypart++)
344 	{
345 		if ( bodypart < LIMB_HUMANOID_TORSO )
346 		{
347 			continue;
348 		}
349 		entity = (Entity*)node->element;
350 		entity->x = my->x;
351 		entity->y = my->y;
352 		entity->z = my->z;
353 		entity->yaw = my->yaw;
354 		if ( bodypart == LIMB_HUMANOID_RIGHTLEG || bodypart == LIMB_HUMANOID_LEFTARM )
355 		{
356 			if ( bodypart == LIMB_HUMANOID_RIGHTLEG )
357 			{
358 				rightbody = (Entity*)node->next->element;
359 			}
360 			if ( bodypart == LIMB_HUMANOID_RIGHTLEG || !my->monsterAttack )
361 			{
362 				if ( !rightbody->skill[0] )
363 				{
364 					entity->pitch -= IMPWALKSPEED;
365 					if ( entity->pitch < -PI / 8.0 )
366 					{
367 						entity->pitch = -PI / 8.0;
368 						if (bodypart == 3)
369 						{
370 							entity->skill[0] = 1;
371 						}
372 					}
373 				}
374 				else
375 				{
376 					entity->pitch += IMPWALKSPEED;
377 					if ( entity->pitch > PI / 8.0 )
378 					{
379 						entity->pitch = PI / 8.0;
380 						if (bodypart == 3)
381 						{
382 							entity->skill[0] = 0;
383 						}
384 					}
385 				}
386 			}
387 			else
388 			{
389 				// vertical chop windup
390 				if ( my->monsterAttack == MONSTER_POSE_MELEE_WINDUP1 )
391 				{
392 					if ( my->monsterAttackTime == 0 )
393 					{
394 						// init rotations
395 						entity->pitch = 0;
396 						entity->roll = 0;
397 					}
398 
399 					limbAnimateToLimit(entity, ANIMATE_PITCH, -0.25, 6 * PI / 4, false, 0);
400 					entity->skill[0] = 0;
401 
402 					if ( my->monsterAttackTime >= ANIMATE_DURATION_WINDUP )
403 					{
404 						if ( multiplayer != CLIENT )
405 						{
406 							my->attack(1, 0, nullptr);
407 						}
408 					}
409 				}
410 				// vertical chop attack
411 				else if ( my->monsterAttack == 1 )
412 				{
413 					if ( my->monsterAttackTime > 0 )
414 					{
415 						if ( limbAnimateToLimit(entity, ANIMATE_PITCH, 0.3, PI / 3, false, 0.0) == 1 )
416 						{
417 							entity->skill[0] = rightbody->skill[0];
418 							entity->pitch = rightbody->pitch;
419 							MONSTER_ATTACK = 0;
420 						}
421 					}
422 				}
423 			}
424 		}
425 		else if ( bodypart == LIMB_HUMANOID_LEFTLEG || bodypart == LIMB_HUMANOID_RIGHTARM )
426 		{
427 			if ( bodypart == LIMB_HUMANOID_RIGHTARM )
428 			{
429 				if ( my->monsterAttack > 0 )
430 				{
431 					// vertical chop
432 					// get leftarm from bodypart 6 element if ready to attack
433 					Entity* leftarm = (Entity*)node->next->element;
434 
435 					if ( my->monsterAttack == 1 || my->monsterAttack == MONSTER_POSE_MELEE_WINDUP1 )
436 					{
437 						if ( leftarm != nullptr )
438 						{
439 							// follow the right arm animation.
440 							entity->pitch = leftarm->pitch;
441 							entity->roll = -leftarm->roll;
442 						}
443 					}
444 				}
445 			}
446 
447 			if ( bodypart != LIMB_HUMANOID_RIGHTARM || (my->monsterAttack == 0 && my->monsterAttackTime == 0) )
448 			{
449 				if ( entity->skill[0] )
450 				{
451 					entity->pitch -= IMPWALKSPEED;
452 					if ( entity->pitch < -PI / 8.0 )
453 					{
454 						entity->skill[0] = 0;
455 						entity->pitch = -PI / 8.0;
456 					}
457 				}
458 				else
459 				{
460 					entity->pitch += IMPWALKSPEED;
461 					if ( entity->pitch > PI / 8.0 )
462 					{
463 						entity->skill[0] = 1;
464 						entity->pitch = PI / 8.0;
465 					}
466 				}
467 			}
468 		}
469 		else if ( bodypart == 7 || bodypart == 8 )
470 		{
471 			if ( my->monsterAttack == MONSTER_POSE_MELEE_WINDUP1 )
472 			{
473 				// flap wings faster during windup
474 				entity->fskill[1] += .4;
475 			}
476 			else
477 			{
478 				entity->fskill[1] += .1;
479 			}
480 			if ( entity->fskill[1] >= PI * 2 )
481 			{
482 				entity->fskill[1] -= PI * 2;
483 			}
484 		}
485 		switch ( bodypart )
486 		{
487 			// torso
488 			case LIMB_HUMANOID_TORSO:
489 				entity->x -= 2 * cos(my->yaw);
490 				entity->y -= 2 * sin(my->yaw);
491 				entity->z += 2.75;
492 				break;
493 			// right leg
494 			case LIMB_HUMANOID_RIGHTLEG:
495 				entity->x += 1 * cos(my->yaw + PI / 2);
496 				entity->y += 1 * sin(my->yaw + PI / 2);
497 				entity->z += 6;
498 				break;
499 			// left leg
500 			case LIMB_HUMANOID_LEFTLEG:
501 				entity->x -= 1 * cos(my->yaw + PI / 2);
502 				entity->y -= 1 * sin(my->yaw + PI / 2);
503 				entity->z += 6;
504 				break;
505 			// right arm
506 			case LIMB_HUMANOID_RIGHTARM:
507 				entity->x += 3 * cos(my->yaw + PI / 2) - 1 * cos(my->yaw);
508 				entity->y += 3 * sin(my->yaw + PI / 2) - 1 * sin(my->yaw);
509 				entity->z += 1;
510 				entity->yaw += MONSTER_WEAPONYAW;
511 				break;
512 			// left arm
513 			case LIMB_HUMANOID_LEFTARM:
514 				entity->x -= 3 * cos(my->yaw + PI / 2) + 1 * cos(my->yaw);
515 				entity->y -= 3 * sin(my->yaw + PI / 2) + 1 * sin(my->yaw);
516 				entity->z += 1;
517 				break;
518 			// right wing
519 			case 7:
520 				entity->x += 1 * cos(my->yaw + PI / 2) - 2.5 * cos(my->yaw);
521 				entity->y += 1 * sin(my->yaw + PI / 2) - 2.5 * sin(my->yaw);
522 				entity->z += 1;
523 				entity->yaw += cos(entity->fskill[1]) * PI / 6 + PI / 6;
524 				break;
525 			// left wing
526 			case 8:
527 				entity->x -= 1 * cos(my->yaw + PI / 2) + 2.5 * cos(my->yaw);
528 				entity->y -= 1 * sin(my->yaw + PI / 2) + 2.5 * sin(my->yaw);
529 				entity->z += 1;
530 				entity->yaw -= cos(entity->fskill[1]) * PI / 6 + PI / 6;
531 				break;
532 		}
533 	}
534 	if ( my->monsterAttack > 0 && my->monsterAttack <= MONSTER_POSE_MAGIC_CAST3 )
535 	{
536 		my->monsterAttackTime++;
537 	}
538 	else if ( my->monsterAttack == 0 )
539 	{
540 		my->monsterAttackTime = 0;
541 	}
542 	else
543 	{
544 		// do nothing, don't reset attacktime or increment it.
545 	}
546 }
547