1 /*-------------------------------------------------------------------------------
2
3 BARONY
4 File: actplayer.cpp
5 Desc: behavior function(s) for player
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 "messages.hpp"
16 #include "entity.hpp"
17 #include "interface/interface.hpp"
18 #include "sound.hpp"
19 #include "items.hpp"
20 #include "magic/magic.hpp"
21 #include "menu.hpp"
22 #include "scores.hpp"
23 #include "monster.hpp"
24 #include "net.hpp"
25 #include "collision.hpp"
26 #include "player.hpp"
27 #include "colors.hpp"
28 #include "draw.hpp"
29 #include "mod_tools.hpp"
30
31 bool smoothmouse = false;
32 bool settings_smoothmouse = false;
33 bool usecamerasmoothing = false;
34 bool disablemouserotationlimit = false;
35 bool settings_disablemouserotationlimit = false;
36 bool swimDebuffMessageHasPlayed = false;
37 int monsterEmoteGimpTimer = 0;
38 int selectedEntityGimpTimer = 0;
39 bool insectoidLevitating[MAXPLAYERS] = { false, false, false, false };
40
41 /*-------------------------------------------------------------------------------
42
43 act*
44
45 The following functions describe various entity behaviors. All functions
46 take a pointer to the entities that use them as an argument.
47
48 -------------------------------------------------------------------------------*/
49
50 #define DEATHCAM_TIME my->skill[0]
51 #define DEATHCAM_PLAYERTARGET my->skill[1]
52 #define DEATHCAM_PLAYERNUM my->skill[2]
53 #define DEATHCAM_ROTX my->fskill[0]
54 #define DEATHCAM_ROTY my->fskill[1]
55
actDeathCam(Entity * my)56 void actDeathCam(Entity* my)
57 {
58 /*if ( keystatus[SDL_SCANCODE_F4] )
59 {
60 buttonStartSingleplayer(nullptr);
61 keystatus[SDL_SCANCODE_F4] = 0;
62 }*/
63 DEATHCAM_TIME++;
64
65 Uint32 deathcamGameoverPromptTicks = TICKS_PER_SECOND * 6;
66 if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL )
67 {
68 deathcamGameoverPromptTicks = TICKS_PER_SECOND * 3;
69 }
70
71 if ( DEATHCAM_TIME == 1 )
72 {
73 DEATHCAM_PLAYERTARGET = -1;
74 }
75 else if ( DEATHCAM_TIME == deathcamGameoverPromptTicks )
76 {
77 if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL )
78 {
79 gameModeManager.Tutorial.openGameoverWindow();
80 }
81 else
82 {
83 openGameoverWindow();
84 }
85 }
86 if ( shootmode && !gamePaused )
87 {
88 if ( smoothmouse )
89 {
90 DEATHCAM_ROTX += mousexrel * .006 * (mousespeed / 128.f);
91 DEATHCAM_ROTX = fmin(fmax(-0.35, DEATHCAM_ROTX), 0.35);
92 }
93 else
94 {
95 DEATHCAM_ROTX = std::min<float>(std::max<float>(-0.35f, mousexrel * .01f * (mousespeed / 128.f)), 0.35f);
96 }
97 my->yaw += DEATHCAM_ROTX;
98 if ( my->yaw >= PI * 2 )
99 {
100 my->yaw -= PI * 2;
101 }
102 else if ( my->yaw < 0 )
103 {
104 my->yaw += PI * 2;
105 }
106
107 if ( smoothmouse )
108 {
109 DEATHCAM_ROTY += mouseyrel * .006 * (mousespeed / 128.f) * (reversemouse * 2 - 1);
110 DEATHCAM_ROTY = fmin(fmax(-0.35, DEATHCAM_ROTY), 0.35);
111 }
112 else
113 {
114 DEATHCAM_ROTY = std::min<float>(std::max<float>(-0.35f, mouseyrel * .01f * (mousespeed / 128.f) * (reversemouse * 2 - 1)), 0.35f);
115 }
116 my->pitch -= DEATHCAM_ROTY;
117 if ( my->pitch > PI / 2 )
118 {
119 my->pitch = PI / 2;
120 }
121 else if ( my->pitch < -PI / 2 )
122 {
123 my->pitch = -PI / 2;
124 }
125 }
126 if ( smoothmouse )
127 {
128 DEATHCAM_ROTX *= .5;
129 DEATHCAM_ROTY *= .5;
130 }
131 else
132 {
133 DEATHCAM_ROTX = 0;
134 DEATHCAM_ROTY = 0;
135 }
136
137 if ( players[DEATHCAM_PLAYERNUM] && players[DEATHCAM_PLAYERNUM]->entity )
138 {
139 // do nothing if still alive
140 }
141 else if ((*inputPressed(impulses[IN_ATTACK]) || (shootmode && *inputPressed(joyimpulses[INJOY_GAME_ATTACK]))) && shootmode)
142 {
143 *inputPressed(impulses[IN_ATTACK]) = 0;
144 if ( shootmode )
145 {
146 *inputPressed(joyimpulses[INJOY_GAME_ATTACK]) = 0;
147 }
148 DEATHCAM_PLAYERTARGET++;
149 if (DEATHCAM_PLAYERTARGET >= MAXPLAYERS)
150 {
151 DEATHCAM_PLAYERTARGET = 0;
152 }
153 int c = 0;
154 while (!players[DEATHCAM_PLAYERTARGET] || !players[DEATHCAM_PLAYERTARGET]->entity) //TODO: PLAYERSWAP VERIFY. I'm not sure if the loop's condition should look like this. I think it should be fine...
155 {
156 if (c > MAXPLAYERS)
157 {
158 break;
159 }
160 DEATHCAM_PLAYERTARGET++;
161 if (DEATHCAM_PLAYERTARGET >= MAXPLAYERS)
162 {
163 DEATHCAM_PLAYERTARGET = 0;
164 }
165 c++;
166 }
167 }
168
169 if (DEATHCAM_PLAYERTARGET >= 0)
170 {
171 if (players[DEATHCAM_PLAYERTARGET] && players[DEATHCAM_PLAYERTARGET]->entity)
172 {
173 my->x = players[DEATHCAM_PLAYERTARGET]->entity->x;
174 my->y = players[DEATHCAM_PLAYERTARGET]->entity->y;
175 }
176 }
177
178 my->removeLightField();
179 my->light = lightSphereShadow(my->x / 16, my->y / 16, 3, 128);
180
181 cameras[DEATHCAM_PLAYERNUM].x = my->x / 16.f;
182 cameras[DEATHCAM_PLAYERNUM].y = my->y / 16.f;
183 cameras[DEATHCAM_PLAYERNUM].z = my->z * 2.f;
184 cameras[DEATHCAM_PLAYERNUM].ang = my->yaw;
185 cameras[DEATHCAM_PLAYERNUM].vang = my->pitch;
186
187 cameras[DEATHCAM_PLAYERNUM].x -= cos(my->yaw) * cos(my->pitch) * 1.5;
188 cameras[DEATHCAM_PLAYERNUM].y -= sin(my->yaw) * cos(my->pitch) * 1.5;
189 cameras[DEATHCAM_PLAYERNUM].z -= sin(my->pitch) * 16;
190 }
191
192 #define PLAYER_INIT my->skill[0]
193 #define PLAYER_TORCH my->skill[1]
194 #define PLAYER_NUM my->skill[2]
195 #define PLAYER_DEBUGCAM my->skill[3]
196 #define PLAYER_BOBMODE my->skill[4]
197 #define PLAYER_ATTACK my->skill[9]
198 #define PLAYER_ATTACKTIME my->skill[10]
199 #define PLAYER_ARMBENDED my->skill[11]
200 #define PLAYER_ALIVETIME my->skill[12]
201 #define PLAYER_INWATER my->skill[13]
202 #define PLAYER_CLICKED my->skill[14]
203 #define PLAYER_DEATH_AUTOMATON my->skill[15]
204 #define PLAYER_VELX my->vel_x
205 #define PLAYER_VELY my->vel_y
206 #define PLAYER_VELZ my->vel_z
207 #define PLAYER_BOB my->fskill[0]
208 #define PLAYER_BOBMOVE my->fskill[1]
209 #define PLAYER_WEAPONYAW my->fskill[2]
210 #define PLAYER_DX my->fskill[3]
211 #define PLAYER_DY my->fskill[4]
212 #define PLAYER_DYAW my->fskill[5]
213 #define PLAYER_ROTX my->fskill[6]
214 #define PLAYER_ROTY my->fskill[7]
215 #define PLAYER_SHIELDYAW my->fskill[8]
216 #define PLAYER_SIDEBOB my->fskill[10]
217 #define PLAYER_CAMERAZ_ACCEL my->fskill[14]
218 #define PLAYERWALKSPEED .12
219
isPlayerSwimming(Entity * my)220 bool isPlayerSwimming(Entity* my)
221 {
222 // swimming
223 bool waterwalkingboots = false;
224 if ( stats[PLAYER_NUM]->shoes != NULL )
225 {
226 if ( stats[PLAYER_NUM]->shoes->type == IRON_BOOTS_WATERWALKING )
227 {
228 waterwalkingboots = true;
229 }
230 }
231 bool swimming = false;
232 bool levitating = isLevitating(stats[PLAYER_NUM]);
233 if ( !levitating && !waterwalkingboots && !noclip && !skillCapstoneUnlocked(PLAYER_NUM, PRO_SWIMMING) )
234 {
235 int x = std::min(std::max<unsigned int>(0, floor(my->x / 16)), map.width - 1);
236 int y = std::min(std::max<unsigned int>(0, floor(my->y / 16)), map.height - 1);
237 if ( swimmingtiles[map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height]]
238 || lavatiles[map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height]] )
239 {
240 // can swim in lavatiles or swimmingtiles only.
241 swimming = true;
242 }
243 }
244 return swimming;
245 }
246
handlePlayerCameraUpdate(Entity * my,int playernum,bool useRefreshRateDelta)247 void handlePlayerCameraUpdate(Entity* my, int playernum, bool useRefreshRateDelta)
248 {
249 if ( !my )
250 {
251 return;
252 }
253
254 double refreshRateDelta = 1.0;
255 if ( useRefreshRateDelta && fps > 0.0 )
256 {
257 refreshRateDelta *= TICKS_PER_SECOND / fps;
258 }
259
260 // rotate
261 if ( !command && my->isMobile() )
262 {
263 if ( !stats[PLAYER_NUM]->EFFECTS[EFF_CONFUSED] )
264 {
265 if ( noclip )
266 {
267 my->z -= (*inputPressed(impulses[IN_TURNR]) - *inputPressed(impulses[IN_TURNL])) * .25 * refreshRateDelta;
268 }
269 else
270 {
271 my->yaw += (*inputPressed(impulses[IN_TURNR]) - *inputPressed(impulses[IN_TURNL])) * .05 * refreshRateDelta;
272 }
273 }
274 else
275 {
276 my->yaw += (*inputPressed(impulses[IN_TURNL]) - *inputPressed(impulses[IN_TURNR])) * .05 * refreshRateDelta;
277 }
278 }
279 if ( shootmode && !gamePaused )
280 {
281 if ( !stats[PLAYER_NUM]->EFFECTS[EFF_CONFUSED] )
282 {
283 if ( smoothmouse )
284 {
285 if ( my->isMobile() )
286 {
287 PLAYER_ROTX += mousexrel * .006 * (mousespeed / 128.f);
288 }
289 if ( !disablemouserotationlimit )
290 {
291 PLAYER_ROTX = fmin(fmax(-0.35, PLAYER_ROTX), 0.35);
292 }
293 PLAYER_ROTX *= pow(0.5, refreshRateDelta);
294 }
295 else
296 {
297 if ( my->isMobile() )
298 {
299 if ( disablemouserotationlimit )
300 {
301 PLAYER_ROTX = mousexrel * .01f * (mousespeed / 128.f);
302 }
303 else
304 {
305 PLAYER_ROTX = std::min<float>(std::max<float>(-0.35f, mousexrel * .01f * (mousespeed / 128.f)), 0.35f);
306 }
307 }
308 else
309 {
310 PLAYER_ROTX = 0;
311 }
312 }
313 }
314 else
315 {
316 if ( smoothmouse )
317 {
318 if ( my->isMobile() )
319 {
320 PLAYER_ROTX -= mousexrel * .006f * (mousespeed / 128.f);
321 }
322 if ( !disablemouserotationlimit )
323 {
324 PLAYER_ROTX = fmin(fmax(-0.35f, PLAYER_ROTX), 0.35f);
325 }
326 PLAYER_ROTX *= pow(0.5, refreshRateDelta);
327 }
328 else
329 {
330 if ( my->isMobile() )
331 {
332 if ( disablemouserotationlimit )
333 {
334 PLAYER_ROTX = -mousexrel * .01f * (mousespeed / 128.f);
335 }
336 else
337 {
338 PLAYER_ROTX = -std::min<float>(std::max<float>(-0.35f, mousexrel * .01f * (mousespeed / 128.f)), 0.35f);
339 }
340 }
341 else
342 {
343 PLAYER_ROTX = 0;
344 }
345 }
346 }
347 }
348 my->yaw += PLAYER_ROTX * refreshRateDelta;
349 while ( my->yaw >= PI * 2 )
350 {
351 my->yaw -= PI * 2;
352 }
353 while ( my->yaw < 0 )
354 {
355 my->yaw += PI * 2;
356 }
357 if ( smoothmouse )
358 {
359 PLAYER_ROTX *= pow(0.5, refreshRateDelta);
360 }
361 else
362 {
363 PLAYER_ROTX = 0;
364 }
365
366 // look up and down
367 if ( !command && my->isMobile() )
368 {
369 if ( !stats[PLAYER_NUM]->EFFECTS[EFF_CONFUSED] )
370 {
371 my->pitch += (*inputPressed(impulses[IN_DOWN]) - *inputPressed(impulses[IN_UP])) * .05 * refreshRateDelta;
372 }
373 else
374 {
375 my->pitch += (*inputPressed(impulses[IN_UP]) - *inputPressed(impulses[IN_DOWN])) * .05 * refreshRateDelta;
376 }
377 }
378 if ( shootmode && !gamePaused )
379 {
380 if ( !stats[PLAYER_NUM]->EFFECTS[EFF_CONFUSED] )
381 {
382 if ( smoothmouse )
383 {
384 if ( my->isMobile() )
385 {
386 PLAYER_ROTY += mouseyrel * .006 * (mousespeed / 128.f) * (reversemouse * 2 - 1);
387 }
388 PLAYER_ROTY = fmin(fmax(-0.35, PLAYER_ROTY), 0.35);
389 PLAYER_ROTY *= pow(0.5, refreshRateDelta);
390 }
391 else
392 {
393 if ( my->isMobile() )
394 {
395 PLAYER_ROTY = std::min<float>(std::max<float>(-0.35f,
396 mouseyrel * .01f * (mousespeed / 128.f) * (reversemouse * 2 - 1)), 0.35f);
397 }
398 else
399 {
400 PLAYER_ROTY = 0;
401 }
402 }
403 }
404 else
405 {
406 if ( smoothmouse )
407 {
408 if ( my->isMobile() )
409 {
410 PLAYER_ROTY -= mouseyrel * .006f * (mousespeed / 128.f) * (reversemouse * 2 - 1);
411 }
412 PLAYER_ROTY = fmin(fmax(-0.35f, PLAYER_ROTY), 0.35f);
413 PLAYER_ROTY *= pow(0.5, refreshRateDelta);
414 }
415 else
416 {
417 if ( my->isMobile() )
418 {
419 PLAYER_ROTY = std::min<float>(std::max<float>(-0.35f,
420 mouseyrel * .01f * (mousespeed / 128.f) * (reversemouse * 2 - 1)), 0.35f);
421 }
422 else
423 {
424 PLAYER_ROTY = 0;
425 }
426 }
427 }
428 }
429 my->pitch -= PLAYER_ROTY * refreshRateDelta;
430
431 if ( softwaremode )
432 {
433 if ( my->pitch > PI / 6 )
434 {
435 my->pitch = PI / 6;
436 }
437 if ( my->pitch < -PI / 6 )
438 {
439 my->pitch = -PI / 6;
440 }
441 }
442 else
443 {
444 if ( my->pitch > PI / 3 )
445 {
446 my->pitch = PI / 3;
447 }
448 if ( my->pitch < -PI / 3 )
449 {
450 my->pitch = -PI / 3;
451 }
452 }
453 if ( !smoothmouse )
454 {
455 PLAYER_ROTY = 0;
456 }
457 else if ( !shootmode )
458 {
459 PLAYER_ROTY *= .5;
460 }
461 }
462
handlePlayerCameraBobbing(Entity * my,int playernum,bool useRefreshRateDelta)463 void handlePlayerCameraBobbing(Entity* my, int playernum, bool useRefreshRateDelta)
464 {
465 if ( !my )
466 {
467 return;
468 }
469 bool swimming = isPlayerSwimming(my);
470
471 double refreshRateDelta = 1.0;
472 if ( useRefreshRateDelta && fps > 0.0 )
473 {
474 refreshRateDelta *= TICKS_PER_SECOND / fps;
475 }
476
477 // camera bobbing
478 if ( bobbing )
479 {
480 if ( swimming )
481 {
482 if ( PLAYER_BOBMODE )
483 {
484 PLAYER_BOBMOVE += .03 * refreshRateDelta;
485 }
486 else
487 {
488 PLAYER_BOBMOVE -= .03 * refreshRateDelta;
489 }
490 }
491 else if ( ((*inputPressed(impulses[IN_FORWARD])
492 || *inputPressed(impulses[IN_BACK]))
493 || (*inputPressed(impulses[IN_RIGHT]) - *inputPressed(impulses[IN_LEFT]))
494 || (game_controller && (game_controller->getLeftXPercent() || game_controller->getLeftYPercent())))
495 && !command && !swimming )
496 {
497 if ( !(stats[PLAYER_NUM]->defending || stats[PLAYER_NUM]->sneaking == 0) )
498 {
499 if ( PLAYER_BOBMODE )
500 {
501 PLAYER_BOBMOVE += .0125 * refreshRateDelta;
502 }
503 else
504 {
505 PLAYER_BOBMOVE -= .0125 * refreshRateDelta;
506 }
507 }
508 else
509 {
510 if ( PLAYER_BOBMODE )
511 {
512 PLAYER_BOBMOVE += .025 * refreshRateDelta;
513 }
514 else
515 {
516 PLAYER_BOBMOVE -= .025 * refreshRateDelta;
517 }
518 }
519 }
520 else if ( !swimming )
521 {
522 PLAYER_BOBMOVE = 0;
523 PLAYER_BOB = 0;
524 PLAYER_BOBMODE = 0;
525 }
526
527 if ( !command && !swimming && (*inputPressed(impulses[IN_RIGHT]) - *inputPressed(impulses[IN_LEFT])) )
528 {
529 if ( (*inputPressed(impulses[IN_RIGHT]) && !(*inputPressed(impulses[IN_BACK])))
530 || (*inputPressed(impulses[IN_LEFT]) && (*inputPressed(impulses[IN_BACK]))) )
531 {
532 PLAYER_SIDEBOB += 0.01 * refreshRateDelta;
533 real_t angle = PI / 32;
534 if ( *inputPressed(impulses[IN_BACK]) )
535 {
536 angle = PI / 64;
537 }
538 if ( PLAYER_SIDEBOB > angle )
539 {
540 PLAYER_SIDEBOB = angle;
541 }
542 }
543 else if ( (*inputPressed(impulses[IN_LEFT]) && !(*inputPressed(impulses[IN_BACK])))
544 || (*inputPressed(impulses[IN_RIGHT]) && (*inputPressed(impulses[IN_BACK]))) )
545 {
546 PLAYER_SIDEBOB -= 0.01 * refreshRateDelta;
547 real_t angle = -PI / 32;
548 if ( *inputPressed(impulses[IN_BACK]) )
549 {
550 angle = -PI / 64;
551 }
552 if ( PLAYER_SIDEBOB < angle )
553 {
554 PLAYER_SIDEBOB = angle;
555 }
556 }
557 }
558 else
559 {
560 if ( PLAYER_SIDEBOB > 0.001 )
561 {
562 PLAYER_SIDEBOB -= 0.02 * refreshRateDelta;
563 if ( PLAYER_SIDEBOB < 0.f )
564 {
565 PLAYER_SIDEBOB = 0.f;
566 }
567 }
568 else
569 {
570 PLAYER_SIDEBOB += 0.02 * refreshRateDelta;
571 if ( PLAYER_SIDEBOB > 0.f )
572 {
573 PLAYER_SIDEBOB = 0.f;
574 }
575 }
576 }
577
578 if ( !swimming && !(stats[PLAYER_NUM]->defending || stats[PLAYER_NUM]->sneaking == 0) )
579 {
580 if ( PLAYER_BOBMOVE > .1 )
581 {
582 PLAYER_BOBMOVE = .1;
583 PLAYER_BOBMODE = 0;
584 }
585 else if ( PLAYER_BOBMOVE < -.1 )
586 {
587 PLAYER_BOBMOVE = -.1;
588 PLAYER_BOBMODE = 1;
589 }
590 }
591 else if ( swimming )
592 {
593 if ( PLAYER_BOBMOVE > .3 )
594 {
595 PLAYER_BOBMOVE = .3;
596 PLAYER_BOBMODE = 0;
597 }
598 else if ( PLAYER_BOBMOVE < -.3 )
599 {
600 PLAYER_BOBMOVE = -.3;
601 PLAYER_BOBMODE = 1;
602 }
603 }
604 else
605 {
606 if ( PLAYER_BOBMOVE > .1 )
607 {
608 PLAYER_BOBMOVE = .1;
609 PLAYER_BOBMODE = 0;
610 }
611 else if ( PLAYER_BOBMOVE < -.1 )
612 {
613 PLAYER_BOBMOVE = -.1;
614 PLAYER_BOBMODE = 1;
615 }
616 }
617 PLAYER_BOB += PLAYER_BOBMOVE * refreshRateDelta;
618 if ( static_cast<Monster>(my->effectShapeshift) == SPIDER || static_cast<Monster>(my->effectShapeshift) == RAT )
619 {
620 PLAYER_BOB = std::min(static_cast<real_t>(1), PLAYER_BOB);
621 }
622 }
623 else
624 {
625 PLAYER_BOBMOVE = 0;
626 PLAYER_BOB = 0;
627 PLAYER_BOBMODE = 0;
628 }
629 }
630
handlePlayerMovement(Entity * my,int playernum,bool useRefreshRateDelta)631 void handlePlayerMovement(Entity* my, int playernum, bool useRefreshRateDelta)
632 {
633 if ( !my )
634 {
635 return;
636 }
637
638 double refreshRateDelta = 1.0;
639 if ( useRefreshRateDelta && fps > 0.0 )
640 {
641 refreshRateDelta *= TICKS_PER_SECOND / fps;
642 }
643
644 // calculate weight
645 double weightratio = 0.0;
646 int weight = 0;
647 for ( node_t* node = stats[PLAYER_NUM]->inventory.first; node != NULL; node = node->next )
648 {
649 Item* item = (Item*)node->element;
650 if ( item != NULL )
651 {
652 if ( item->type >= 0 && item->type < NUMITEMS )
653 {
654 if ( itemTypeIsQuiver(item->type) )
655 {
656 weight += std::max(1, items[item->type].weight * item->count / 5);
657 }
658 else
659 {
660 weight += items[item->type].weight * item->count;
661 }
662 }
663 }
664 }
665 weight += stats[PLAYER_NUM]->GOLD / 100;
666 if ( gameplayCustomManager.inUse() )
667 {
668 weight = weight * (gameplayCustomManager.playerWeightPercent / 100.f);
669 }
670 if ( stats[PLAYER_NUM]->EFFECTS[EFF_FAST] && !stats[PLAYER_NUM]->EFFECTS[EFF_SLOW] )
671 {
672 weight = weight * 0.5;
673 }
674 weightratio = (1000 + my->getSTR() * 100 - weight) / (double)(1000 + my->getSTR() * 100);
675 weightratio = fmin(fmax(0, weightratio), 1);
676
677 // calculate movement forces
678
679 bool allowMovement = my->isMobile();
680 bool pacified = stats[PLAYER_NUM]->EFFECTS[EFF_PACIFY];
681 if ( !allowMovement && pacified )
682 {
683 if ( !stats[PLAYER_NUM]->EFFECTS[EFF_PARALYZED] && !stats[PLAYER_NUM]->EFFECTS[EFF_STUNNED]
684 && !stats[PLAYER_NUM]->EFFECTS[EFF_ASLEEP] )
685 {
686 allowMovement = true;
687 }
688 }
689
690 if ( (!command || pacified) && allowMovement )
691 {
692 //x_force and y_force represent the amount of percentage pushed on that respective axis. Given a keyboard, it's binary; either you're pushing "move left" or you aren't. On an analog stick, it can range from whatever value to whatever.
693 float x_force = 0;
694 float y_force = 0;
695
696 if ( pacified )
697 {
698 x_force = 0.f;
699 y_force = -0.1;
700 if ( stats[PLAYER_NUM]->EFFECTS[EFF_CONFUSED] )
701 {
702 y_force *= -1;
703 }
704 }
705 else
706 {
707 double backpedalMultiplier = 0.25;
708 if ( stats[PLAYER_NUM]->EFFECTS[EFF_DASH] )
709 {
710 backpedalMultiplier = 1.25;
711 }
712
713 if ( !stats[PLAYER_NUM]->EFFECTS[EFF_CONFUSED] )
714 {
715 //Normal controls.
716 x_force = (*inputPressed(impulses[IN_RIGHT]) - *inputPressed(impulses[IN_LEFT]));
717 y_force = (*inputPressed(impulses[IN_FORWARD]) - (double)* inputPressed(impulses[IN_BACK]) * backpedalMultiplier);
718 if ( noclip )
719 {
720 if ( keystatus[SDL_SCANCODE_LSHIFT] )
721 {
722 x_force = x_force * 0.5;
723 y_force = y_force * 0.5;
724 }
725 }
726 }
727 else
728 {
729 //Confused controls.
730 x_force = (*inputPressed(impulses[IN_LEFT]) - *inputPressed(impulses[IN_RIGHT]));
731 y_force = (*inputPressed(impulses[IN_BACK]) - (double)* inputPressed(impulses[IN_FORWARD]) * backpedalMultiplier);
732 }
733
734 if ( game_controller && !*inputPressed(impulses[IN_LEFT]) && !*inputPressed(impulses[IN_RIGHT]) )
735 {
736 x_force = game_controller->getLeftXPercent();
737
738 if ( stats[PLAYER_NUM]->EFFECTS[EFF_CONFUSED] )
739 {
740 x_force *= -1;
741 }
742 }
743 if ( game_controller && !*inputPressed(impulses[IN_FORWARD]) && !*inputPressed(impulses[IN_BACK]) )
744 {
745 y_force = game_controller->getLeftYPercent();
746
747 if ( stats[PLAYER_NUM]->EFFECTS[EFF_CONFUSED] )
748 {
749 y_force *= -1;
750 }
751
752 if ( y_force < 0 )
753 {
754 y_force *= backpedalMultiplier; //Move backwards more slowly.
755 }
756 }
757 }
758
759 int DEX = my->getDEX();
760 real_t slowSpeedPenalty = 0.0;
761 if ( !stats[PLAYER_NUM]->EFFECTS[EFF_FAST] && stats[PLAYER_NUM]->EFFECTS[EFF_SLOW] )
762 {
763 DEX = std::min(DEX - 3, -2);
764 slowSpeedPenalty = 2.0;
765 }
766
767 double maxSpeed = 18.0;
768 if ( gameplayCustomManager.inUse() )
769 {
770 maxSpeed = gameplayCustomManager.playerSpeedMax;
771 }
772
773 real_t speedFactor = std::min((DEX * 0.1 + 15.5 - slowSpeedPenalty) * weightratio, maxSpeed);
774 if ( DEX <= 5 )
775 {
776 speedFactor = std::min((DEX + 10) * weightratio, maxSpeed);
777 }
778 else if ( DEX <= 15 )
779 {
780 speedFactor = std::min((DEX * 0.2 + 14 - slowSpeedPenalty) * weightratio, maxSpeed);
781 }
782 /*if ( ticks % 50 == 0 )
783 {
784 messagePlayer(0, "%f", speedFactor);
785 }*/
786 if ( stats[PLAYER_NUM]->EFFECTS[EFF_DASH] )
787 {
788 PLAYER_VELX += my->monsterKnockbackVelocity * cos(my->monsterKnockbackTangentDir) * refreshRateDelta;
789 PLAYER_VELY += my->monsterKnockbackVelocity * sin(my->monsterKnockbackTangentDir) * refreshRateDelta;
790 my->monsterKnockbackVelocity *= pow(0.95, refreshRateDelta);
791 }
792 else if ( stats[PLAYER_NUM]->EFFECTS[EFF_KNOCKBACK] )
793 {
794 speedFactor = std::min(speedFactor, 5.0);
795 PLAYER_VELX += my->monsterKnockbackVelocity * cos(my->monsterKnockbackTangentDir) * refreshRateDelta;
796 PLAYER_VELY += my->monsterKnockbackVelocity * sin(my->monsterKnockbackTangentDir) * refreshRateDelta;
797 my->monsterKnockbackVelocity *= pow(0.95, refreshRateDelta);
798 }
799 else
800 {
801 my->monsterKnockbackVelocity = 0.f;
802 my->monsterKnockbackTangentDir = 0.f;
803 }
804
805 if ( fabs(my->playerStrafeVelocity) > 0.1 )
806 {
807 //speedFactor = std::min(speedFactor, 5.0);
808 PLAYER_VELX += my->playerStrafeVelocity * cos(my->playerStrafeDir) * refreshRateDelta;
809 PLAYER_VELY += my->playerStrafeVelocity * sin(my->playerStrafeDir) * refreshRateDelta;
810 my->playerStrafeVelocity *= pow(0.95, refreshRateDelta);
811 }
812 else
813 {
814 my->playerStrafeDir = 0.0f;
815 my->playerStrafeVelocity = 0.0f;
816 }
817
818 speedFactor *= refreshRateDelta;
819 PLAYER_VELX += y_force * cos(my->yaw) * .045 * speedFactor / (1 + (stats[PLAYER_NUM]->defending || stats[PLAYER_NUM]->sneaking == 1));
820 PLAYER_VELY += y_force * sin(my->yaw) * .045 * speedFactor / (1 + (stats[PLAYER_NUM]->defending || stats[PLAYER_NUM]->sneaking == 1));
821 PLAYER_VELX += x_force * cos(my->yaw + PI / 2) * .0225 * speedFactor / (1 + (stats[PLAYER_NUM]->defending || stats[PLAYER_NUM]->sneaking == 1));
822 PLAYER_VELY += x_force * sin(my->yaw + PI / 2) * .0225 * speedFactor / (1 + (stats[PLAYER_NUM]->defending || stats[PLAYER_NUM]->sneaking == 1));
823
824 }
825 PLAYER_VELX *= pow(0.75, refreshRateDelta);
826 PLAYER_VELY *= pow(0.75, refreshRateDelta);
827
828 /*if ( keystatus[SDL_SCANCODE_G] )
829 {
830 messagePlayer(0, "X: %5.5f, Y: %5.5f", PLAYER_VELX, PLAYER_VELY);
831 }*/
832
833 for ( node_t* node = map.creatures->first; node != nullptr; node = node->next ) //Since looking for players only, don't search full entity list. Best idea would be to directly example players[] though.
834 {
835 Entity* entity = (Entity*)node->element;
836 if ( entity == my )
837 {
838 continue;
839 }
840 if ( entity->behavior == &actPlayer )
841 {
842 if ( entityInsideEntity(my, entity) )
843 {
844 double tangent = atan2(my->y - entity->y, my->x - entity->x);
845 PLAYER_VELX += cos(tangent) * 0.075 * refreshRateDelta;
846 PLAYER_VELY += sin(tangent) * 0.075 * refreshRateDelta;
847 }
848 }
849 }
850
851 // swimming slows you down
852 bool amuletwaterbreathing = false;
853 if ( stats[PLAYER_NUM]->amulet != NULL )
854 {
855 if ( stats[PLAYER_NUM]->amulet->type == AMULET_WATERBREATHING )
856 {
857 amuletwaterbreathing = true;
858 }
859 }
860 bool swimming = isPlayerSwimming(my);
861 if ( swimming && !amuletwaterbreathing )
862 {
863 PLAYER_VELX *= (((stats[PLAYER_NUM]->PROFICIENCIES[PRO_SWIMMING] / 100.f) * 50.f) + 50) / 100.f;
864 PLAYER_VELY *= (((stats[PLAYER_NUM]->PROFICIENCIES[PRO_SWIMMING] / 100.f) * 50.f) + 50) / 100.f;
865
866 if ( stats[PLAYER_NUM]->type == SKELETON )
867 {
868 if ( !swimDebuffMessageHasPlayed )
869 {
870 messagePlayer(PLAYER_NUM, language[3182]);
871 swimDebuffMessageHasPlayed = true;
872 }
873 // no swim good
874 PLAYER_VELX *= 0.5;
875 PLAYER_VELY *= 0.5;
876 }
877 }
878 }
879
handlePlayerCameraPosition(Entity * my,int playernum,bool useRefreshRateDelta)880 void handlePlayerCameraPosition(Entity* my, int playernum, bool useRefreshRateDelta)
881 {
882 if ( !my )
883 {
884 return;
885 }
886 int playerRace = my->getMonsterTypeFromSprite();
887 bool swimming = isPlayerSwimming(my);
888
889 double refreshRateDelta = 1.0;
890 if ( useRefreshRateDelta && fps > 0.0 )
891 {
892 refreshRateDelta *= TICKS_PER_SECOND / fps;
893 }
894
895 // camera
896 if ( !PLAYER_DEBUGCAM && stats[PLAYER_NUM] && stats[PLAYER_NUM]->HP > 0 )
897 {
898 cameras[PLAYER_NUM].x = my->x / 16.0;
899 cameras[PLAYER_NUM].y = my->y / 16.0;
900 real_t cameraSetpointZ = (my->z * 2) - 2.5 + (swimming ? 1 : 0);
901 if ( swimming && (playerRace == RAT || playerRace == SPIDER) )
902 {
903 cameraSetpointZ -= 0.5; // float a little higher.
904 }
905
906 if ( playerRace == CREATURE_IMP && my->z == -4.5 )
907 {
908 cameraSetpointZ += 1;
909 }
910 else if ( playerRace == TROLL && my->z <= -1.5 )
911 {
912 cameraSetpointZ -= 2;
913 }
914
915 real_t diff = abs(PLAYER_CAMERAZ_ACCEL - cameraSetpointZ);
916 if ( diff > 0.01 )
917 {
918 real_t rateChange = std::min(2.0, std::max(0.3, diff * 0.5)) * refreshRateDelta;
919
920 if ( cameraSetpointZ >= 0.f )
921 {
922 PLAYER_CAMERAZ_ACCEL += rateChange;
923 PLAYER_CAMERAZ_ACCEL = std::min(cameraSetpointZ, PLAYER_CAMERAZ_ACCEL);
924 }
925 else if ( cameraSetpointZ < 0.f )
926 {
927 PLAYER_CAMERAZ_ACCEL += -rateChange;
928 PLAYER_CAMERAZ_ACCEL = std::max(cameraSetpointZ, PLAYER_CAMERAZ_ACCEL);
929 }
930 cameras[PLAYER_NUM].z = PLAYER_CAMERAZ_ACCEL;
931
932 // check updated value.
933 if ( abs(PLAYER_CAMERAZ_ACCEL - cameraSetpointZ) <= 0.01 )
934 {
935 PLAYER_BOBMOVE = 0;
936 PLAYER_BOB = 0;
937 PLAYER_BOBMODE = 0;
938 }
939 }
940 else
941 {
942 PLAYER_CAMERAZ_ACCEL = cameraSetpointZ;
943 cameras[PLAYER_NUM].z = PLAYER_CAMERAZ_ACCEL + PLAYER_BOB;
944 }
945
946 //messagePlayer(0, "Z: %.2f | %.2f | %.2f", my->z, PLAYER_CAMERAZ_ACCEL, cameraSetpointZ);
947
948 cameras[PLAYER_NUM].ang = my->yaw;
949 if ( softwaremode )
950 {
951 cameras[PLAYER_NUM].vang = (my->pitch / (PI / 4)) * cameras[PLAYER_NUM].winh;
952 }
953 else
954 {
955 cameras[PLAYER_NUM].vang = my->pitch;
956 }
957 }
958 }
959
actPlayer(Entity * my)960 void actPlayer(Entity* my)
961 {
962 if (!my)
963 {
964 return;
965 }
966 if ( logCheckObstacle )
967 {
968 if ( ticks % 50 == 0 )
969 {
970 messagePlayer(0, "checkObstacle() calls/sec: %d", logCheckObstacleCount);
971 logCheckObstacleCount = 0;
972 }
973 }
974 if ( spamming && my->ticks % 2 == 0 )
975 {
976 for (int i = 0; i < 1; ++i)
977 {
978 char s[64] = "";
979 char alphanum[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
980
981 for ( int j = 0; j < 63; ++j ) {
982 s[j] = alphanum[rand() % (sizeof(alphanum) - 1)];
983 }
984 Uint32 totalSize = 0;
985 for ( size_t c = 0; c < HASH_SIZE; ++c ) {
986 totalSize += list_Size(&ttfTextHash[c]);
987 }
988 messagePlayer(0, "IMGREF: %d, total size: %d", imgref, totalSize);
989 s[63] = '\0';
990 messagePlayer(0, "%s", s);
991 //messagePlayer(0, "Lorem ipsum dolor sit amet, dico accusam reprehendunt ne mea, ea est illum tincidunt voluptatibus. Ne labore voluptua eos, nostro fierent mnesarchum an mei, cu mea dolor verear epicuri. Est id iriure principes, unum cotidieque qui te. An sit tractatos complectitur.");
992 }
993 }
994 if ( autoLimbReload && ticks % 20 == 0 && (PLAYER_NUM == clientnum || splitscreen) )
995 {
996 consoleCommand("/reloadlimbs");
997 }
998
999 Entity* entity;
1000 Entity* entity2 = nullptr;
1001 Entity* rightbody = nullptr;
1002 Entity* weaponarm = nullptr;
1003 Entity* shieldarm = nullptr;
1004 Entity* additionalLimb = nullptr;
1005 Entity* torso = nullptr;
1006 node_t* node;
1007 Item* item;
1008 int i, bodypart;
1009 double dist = 0;
1010 bool wearingring = false;
1011 bool levitating = false;
1012 bool isHumanoid = true;
1013 bool showEquipment = true;
1014 if ( PLAYER_NUM < 0 || PLAYER_NUM >= MAXPLAYERS )
1015 {
1016 return;
1017 }
1018
1019 Monster playerRace = HUMAN;
1020 int spriteTorso = 106 + 12 * stats[PLAYER_NUM]->sex;
1021 int spriteLegRight = 107 + 12 * stats[PLAYER_NUM]->sex;
1022 int spriteLegLeft = 108 + 12 * stats[PLAYER_NUM]->sex;
1023 int spriteArmRight = 109 + 12 * stats[PLAYER_NUM]->sex;
1024 int spriteArmLeft = 110 + 12 * stats[PLAYER_NUM]->sex;
1025 int playerAppearance = stats[PLAYER_NUM]->appearance;
1026
1027 if ( my->effectShapeshift != NOTHING )
1028 {
1029 playerRace = static_cast<Monster>(my->effectShapeshift);
1030 stats[PLAYER_NUM]->type = playerRace;
1031 }
1032 else if ( stats[PLAYER_NUM]->playerRace > 0 || stats[PLAYER_NUM]->EFFECTS[EFF_POLYMORPH] || my->effectPolymorph != NOTHING )
1033 {
1034 playerRace = my->getMonsterFromPlayerRace(stats[PLAYER_NUM]->playerRace);
1035 if ( my->effectPolymorph != NOTHING )
1036 {
1037 if ( my->effectPolymorph > NUMMONSTERS )
1038 {
1039 playerRace = HUMAN;
1040 playerAppearance = my->effectPolymorph - 100;
1041 }
1042 else
1043 {
1044 playerRace = static_cast<Monster>(my->effectPolymorph);
1045 }
1046 }
1047 if ( stats[PLAYER_NUM]->appearance == 0 || my->effectPolymorph != NOTHING )
1048 {
1049 stats[PLAYER_NUM]->type = playerRace;
1050 }
1051 else
1052 {
1053 stats[PLAYER_NUM]->type = HUMAN; // appearance of 1 is aesthetic only
1054 }
1055 }
1056 else
1057 {
1058 stats[PLAYER_NUM]->type = HUMAN;
1059 }
1060
1061 if ( stats[PLAYER_NUM]->type == RAT || stats[PLAYER_NUM]->type == SPIDER )
1062 {
1063 isHumanoid = false;
1064 }
1065 else if ( stats[PLAYER_NUM]->type == TROLL || stats[PLAYER_NUM]->type == CREATURE_IMP )
1066 {
1067 showEquipment = false;
1068 }
1069
1070 if ( multiplayer != CLIENT )
1071 {
1072 if ( stats[PLAYER_NUM]->EFFECTS[EFF_SHAPESHIFT] )
1073 {
1074 stats[PLAYER_NUM]->playerShapeshiftStorage = my->effectShapeshift; // keep track of player shapeshift effects
1075 }
1076 else
1077 {
1078 if ( my->effectShapeshift != NOTHING ) // just in case this was cleared other than normal progression ticking down
1079 {
1080 my->effectShapeshift = NOTHING;
1081 serverUpdateEntitySkill(my, 53);
1082 }
1083 }
1084
1085 if ( stats[PLAYER_NUM]->EFFECTS[EFF_POLYMORPH] )
1086 {
1087 stats[PLAYER_NUM]->playerPolymorphStorage = my->effectPolymorph; // keep track of player polymorph effects
1088 }
1089 else
1090 {
1091 if ( my->effectPolymorph != NOTHING ) // just in case this was cleared other than normal progression ticking down
1092 {
1093 my->effectPolymorph = NOTHING;
1094 serverUpdateEntitySkill(my, 50);
1095 }
1096 }
1097 }
1098
1099 if ( PLAYER_NUM == clientnum || splitscreen )
1100 {
1101 if ( stats[PLAYER_NUM]->type != HUMAN && stats[PLAYER_NUM]->EFFECTS[EFF_SHAPESHIFT] )
1102 {
1103 if ( swapHotbarOnShapeshift == 0 )
1104 {
1105 initShapeshiftHotbar();
1106 }
1107 else if ( swapHotbarOnShapeshift != stats[PLAYER_NUM]->type )
1108 {
1109 // we likely transformed while still shapeshifted, fully init the hotbar code again.
1110 deinitShapeshiftHotbar();
1111 initShapeshiftHotbar();
1112 }
1113 }
1114 else if ( !stats[PLAYER_NUM]->EFFECTS[EFF_SHAPESHIFT] && swapHotbarOnShapeshift > 0 )
1115 {
1116 deinitShapeshiftHotbar();
1117 }
1118 }
1119
1120 my->focalx = limbs[playerRace][0][0];
1121 my->focaly = limbs[playerRace][0][1];
1122 my->focalz = limbs[playerRace][0][2];
1123
1124 if ( playerRace == GOATMAN && my->sprite == 768 )
1125 {
1126 my->focalz = limbs[playerRace][0][2] - 0.25; // minor head position fix to match male variant.
1127 }
1128
1129 if ( playerRace == TROLL )
1130 {
1131 my->focalz += 1.25;
1132 my->scalex = 1.01;
1133 my->scaley = 1.01;
1134 my->scalez = 1.01;
1135 }
1136 else
1137 {
1138 my->scalex = 1.f;
1139 my->scaley = 1.f;
1140 my->scalez = 1.f;
1141 }
1142
1143 if ( multiplayer == CLIENT )
1144 {
1145 if ( PLAYER_NUM != clientnum )
1146 {
1147 my->flags[UPDATENEEDED] = true;
1148 }
1149
1150 my->handleEffectsClient();
1151
1152 // request entity update (check if I've been deleted)
1153 if ( ticks % (TICKS_PER_SECOND * 5) == my->getUID() % (TICKS_PER_SECOND * 5) )
1154 {
1155 strcpy((char*)net_packet->data, "ENTE");
1156 net_packet->data[4] = clientnum;
1157 SDLNet_Write32(my->getUID(), &net_packet->data[5]);
1158 net_packet->address.host = net_server.host;
1159 net_packet->address.port = net_server.port;
1160 net_packet->len = 9;
1161 sendPacketSafe(net_sock, -1, net_packet, 0);
1162 }
1163 }
1164
1165 if ( !PLAYER_INIT )
1166 {
1167 PLAYER_INIT = 1;
1168 my->flags[BURNABLE] = true;
1169
1170 Entity* nametag = newEntity(-1, 1, map.entities, nullptr);
1171 nametag->x = my->x;
1172 nametag->y = my->y;
1173 nametag->z = my->z - 6;
1174 nametag->sizex = 1;
1175 nametag->sizey = 1;
1176 nametag->flags[NOUPDATE] = true;
1177 nametag->flags[PASSABLE] = true;
1178 nametag->flags[SPRITE] = true;
1179 nametag->flags[BRIGHT] = true;
1180 nametag->flags[UNCLICKABLE] = true;
1181 nametag->behavior = &actSpriteNametag;
1182 nametag->parent = my->getUID();
1183 nametag->scalex = 0.2;
1184 nametag->scaley = 0.2;
1185 nametag->scalez = 0.2;
1186 if ( multiplayer != CLIENT )
1187 {
1188 entity_uids--;
1189 }
1190 nametag->setUID(-3);
1191
1192 // hud weapon
1193 if ( PLAYER_NUM == clientnum || splitscreen )
1194 {
1195 if ( multiplayer == CLIENT )
1196 {
1197 my->flags[UPDATENEEDED] = false;
1198 }
1199
1200 entity = newEntity(-1, 1, map.entities, nullptr); //HUD entity.
1201 entity->flags[PASSABLE] = true;
1202 entity->flags[OVERDRAW] = true;
1203 entity->flags[NOUPDATE] = true;
1204 entity->flags[INVISIBLE] = true;
1205 entity->skill[11] = PLAYER_NUM;
1206 entity->behavior = &actHudWeapon;
1207 entity->focalz = -4;
1208 node = list_AddNodeLast(&my->children);
1209 node->element = entity;
1210 node->deconstructor = &emptyDeconstructor;
1211 node->size = sizeof(Entity*);
1212 my->bodyparts.push_back(entity);
1213
1214 // magic hands
1215
1216 //Left hand.
1217 entity = newEntity(-1, 1, map.entities, nullptr); //HUD entity.
1218 entity->flags[PASSABLE] = true;
1219 entity->flags[OVERDRAW] = true;
1220 entity->flags[NOUPDATE] = true;
1221 entity->flags[INVISIBLE] = true;
1222 entity->skill[2] = PLAYER_NUM;
1223 entity->behavior = &actLeftHandMagic;
1224 entity->focalz = -4;
1225 magicLeftHand = entity;
1226 //Right hand.
1227 entity = newEntity(-1, 1, map.entities, nullptr); //HUD entity.
1228 entity->flags[PASSABLE] = true;
1229 entity->flags[OVERDRAW] = true;
1230 entity->flags[NOUPDATE] = true;
1231 entity->flags[INVISIBLE] = true;
1232 entity->skill[2] = PLAYER_NUM;
1233 entity->behavior = &actRightHandMagic;
1234 entity->focalz = -4;
1235 magicRightHand = entity;
1236 my->bodyparts.push_back(entity);
1237
1238 // hud shield
1239 entity = newEntity(-1, 1, map.entities, nullptr); //HUD entity.
1240 entity->flags[PASSABLE] = true;
1241 entity->flags[OVERDRAW] = true;
1242 entity->flags[NOUPDATE] = true;
1243 entity->flags[INVISIBLE] = true;
1244 entity->skill[2] = PLAYER_NUM;
1245 entity->behavior = &actHudShield;
1246 my->bodyparts.push_back(entity);
1247
1248 // hud additional limb
1249 entity = newEntity(-1, 1, map.entities, nullptr); //HUD entity.
1250 entity->flags[PASSABLE] = true;
1251 entity->flags[OVERDRAW] = true;
1252 entity->flags[NOUPDATE] = true;
1253 entity->flags[INVISIBLE] = true;
1254 entity->skill[2] = PLAYER_NUM;
1255 entity->behavior = &actHudAdditional;
1256 my->bodyparts.push_back(entity);
1257
1258 // hud additional limb 2
1259 entity = newEntity(-1, 1, map.entities, nullptr); //HUD entity.
1260 entity->flags[PASSABLE] = true;
1261 entity->flags[OVERDRAW] = true;
1262 entity->flags[NOUPDATE] = true;
1263 entity->flags[INVISIBLE] = true;
1264 entity->skill[2] = PLAYER_NUM;
1265 entity->behavior = &actHudArrowModel;
1266 my->bodyparts.push_back(entity);
1267 }
1268 else
1269 {
1270 node = list_AddNodeLast(&my->children);
1271 node->element = NULL;
1272 node->deconstructor = &emptyDeconstructor;
1273 node->size = 0;
1274 if ( multiplayer == CLIENT )
1275 {
1276 PLAYER_TORCH = 0;
1277 }
1278 }
1279
1280 // torso
1281 entity = newEntity(spriteTorso, 1, map.entities, nullptr); //Limb entity.
1282 entity->sizex = 4;
1283 entity->sizey = 4;
1284 entity->skill[2] = PLAYER_NUM;
1285 entity->flags[PASSABLE] = true;
1286 entity->flags[NOUPDATE] = true;
1287 entity->flags[GENIUS] = true;
1288 entity->focalx = limbs[playerRace][1][0];
1289 entity->focaly = limbs[playerRace][1][1];
1290 entity->focalz = limbs[playerRace][1][2];
1291 entity->behavior = &actPlayerLimb;
1292 entity->parent = my->getUID();
1293 node = list_AddNodeLast(&my->children);
1294 node->element = entity;
1295 node->deconstructor = &emptyDeconstructor;
1296 node->size = sizeof(Entity*);
1297 my->bodyparts.push_back(entity);
1298 entity->setDefaultPlayerModel(PLAYER_NUM, playerRace, LIMB_HUMANOID_TORSO);
1299
1300 // right leg
1301 entity = newEntity(spriteLegRight, 1, map.entities, nullptr); //Limb entity.
1302 entity->sizex = 4;
1303 entity->sizey = 4;
1304 entity->skill[2] = PLAYER_NUM;
1305 entity->flags[PASSABLE] = true;
1306 entity->flags[NOUPDATE] = true;
1307 entity->flags[GENIUS] = true;
1308 entity->focalx = limbs[playerRace][2][0];
1309 entity->focaly = limbs[playerRace][2][1];
1310 entity->focalz = limbs[playerRace][2][2];
1311 entity->behavior = &actPlayerLimb;
1312 entity->parent = my->getUID();
1313 node = list_AddNodeLast(&my->children);
1314 node->element = entity;
1315 node->deconstructor = &emptyDeconstructor;
1316 node->size = sizeof(Entity*);
1317 my->bodyparts.push_back(entity);
1318 entity->setDefaultPlayerModel(PLAYER_NUM, playerRace, LIMB_HUMANOID_RIGHTLEG);
1319
1320 // left leg
1321 entity = newEntity(spriteLegLeft, 1, map.entities, nullptr); //Limb entity.
1322 entity->sizex = 4;
1323 entity->sizey = 4;
1324 entity->skill[2] = PLAYER_NUM;
1325 entity->flags[PASSABLE] = true;
1326 entity->flags[NOUPDATE] = true;
1327 entity->flags[GENIUS] = true;
1328 entity->focalx = limbs[playerRace][3][0];
1329 entity->focaly = limbs[playerRace][3][1];
1330 entity->focalz = limbs[playerRace][3][2];
1331 entity->behavior = &actPlayerLimb;
1332 entity->parent = my->getUID();
1333 node = list_AddNodeLast(&my->children);
1334 node->element = entity;
1335 node->deconstructor = &emptyDeconstructor;
1336 node->size = sizeof(Entity*);
1337 my->bodyparts.push_back(entity);
1338 entity->setDefaultPlayerModel(PLAYER_NUM, playerRace, LIMB_HUMANOID_LEFTLEG);
1339
1340 // right arm
1341 entity = newEntity(spriteArmRight, 1, map.entities, nullptr); //Limb entity.
1342 entity->sizex = 4;
1343 entity->sizey = 4;
1344 entity->skill[2] = PLAYER_NUM;
1345 entity->flags[PASSABLE] = true;
1346 entity->flags[NOUPDATE] = true;
1347 entity->flags[GENIUS] = true;
1348 entity->focalx = limbs[playerRace][4][0];
1349 entity->focaly = limbs[playerRace][4][1];
1350 entity->focalz = limbs[playerRace][4][2];
1351 entity->behavior = &actPlayerLimb;
1352 entity->parent = my->getUID();
1353 node = list_AddNodeLast(&my->children);
1354 node->element = entity;
1355 node->deconstructor = &emptyDeconstructor;
1356 node->size = sizeof(Entity*);
1357 my->bodyparts.push_back(entity);
1358 entity->setDefaultPlayerModel(PLAYER_NUM, playerRace, LIMB_HUMANOID_RIGHTARM);
1359
1360 // left arm
1361 entity = newEntity(spriteArmLeft, 1, map.entities, nullptr); //Limb entity.
1362 entity->sizex = 4;
1363 entity->sizey = 4;
1364 entity->skill[2] = PLAYER_NUM;
1365 entity->flags[PASSABLE] = true;
1366 entity->flags[NOUPDATE] = true;
1367 entity->flags[GENIUS] = true;
1368 entity->focalx = limbs[playerRace][5][0];
1369 entity->focaly = limbs[playerRace][5][1];
1370 entity->focalz = limbs[playerRace][5][2];
1371 entity->behavior = &actPlayerLimb;
1372 entity->parent = my->getUID();
1373 node = list_AddNodeLast(&my->children);
1374 node->element = entity;
1375 node->deconstructor = &emptyDeconstructor;
1376 node->size = sizeof(Entity*);
1377 my->bodyparts.push_back(entity);
1378 entity->setDefaultPlayerModel(PLAYER_NUM, playerRace, LIMB_HUMANOID_LEFTARM);
1379
1380 // world weapon
1381 entity = newEntity(-1, 1, map.entities, nullptr); //Limb entity.
1382 entity->sizex = 4;
1383 entity->sizey = 4;
1384 entity->skill[2] = PLAYER_NUM;
1385 entity->flags[PASSABLE] = true;
1386 entity->flags[NOUPDATE] = true;
1387 entity->flags[GENIUS] = true;
1388 entity->flags[INVISIBLE] = true;
1389 entity->focalx = limbs[playerRace][6][0];
1390 entity->focaly = limbs[playerRace][6][1];
1391 entity->focalz = limbs[playerRace][6][2];
1392 entity->behavior = &actPlayerLimb;
1393 entity->parent = my->getUID();
1394 node = list_AddNodeLast(&my->children);
1395 node->element = entity;
1396 node->deconstructor = &emptyDeconstructor;
1397 node->size = sizeof(Entity*);
1398 my->bodyparts.push_back(entity);
1399
1400 // shield
1401 entity = newEntity(-1, 1, map.entities, nullptr); //Limb entity.
1402 entity->sizex = 4;
1403 entity->sizey = 4;
1404 entity->skill[2] = PLAYER_NUM;
1405 entity->flags[PASSABLE] = true;
1406 entity->flags[NOUPDATE] = true;
1407 entity->flags[GENIUS] = true;
1408 entity->flags[INVISIBLE] = true;
1409 entity->focalx = limbs[playerRace][7][0];
1410 entity->focaly = limbs[playerRace][7][1];
1411 entity->focalz = limbs[playerRace][7][2];
1412 entity->behavior = &actPlayerLimb;
1413 entity->parent = my->getUID();
1414 entity->focalx = 2;
1415 node = list_AddNodeLast(&my->children);
1416 node->element = entity;
1417 node->deconstructor = &emptyDeconstructor;
1418 node->size = sizeof(Entity*);
1419 my->bodyparts.push_back(entity);
1420
1421 // cloak
1422 entity = newEntity(-1, 1, map.entities, nullptr); //Limb entity.
1423 entity->sizex = 4;
1424 entity->sizey = 4;
1425 entity->scalex = 1.01;
1426 entity->scaley = 1.01;
1427 entity->scalez = 1.01;
1428 entity->skill[2] = PLAYER_NUM;
1429 entity->flags[PASSABLE] = true;
1430 entity->flags[NOUPDATE] = true;
1431 entity->flags[GENIUS] = true;
1432 entity->flags[INVISIBLE] = true;
1433 entity->focalx = limbs[playerRace][8][0];
1434 entity->focaly = limbs[playerRace][8][1];
1435 entity->focalz = limbs[playerRace][8][2];
1436 entity->behavior = &actPlayerLimb;
1437 entity->parent = my->getUID();
1438 node = list_AddNodeLast(&my->children);
1439 node->element = entity;
1440 node->deconstructor = &emptyDeconstructor;
1441 node->size = sizeof(Entity*);
1442 my->bodyparts.push_back(entity);
1443
1444 // helmet
1445 entity = newEntity(-1, 1, map.entities, nullptr); //Limb entity.
1446 entity->sizex = 4;
1447 entity->sizey = 4;
1448 entity->scalex = 1.01;
1449 entity->scaley = 1.01;
1450 entity->scalez = 1.01;
1451 entity->skill[2] = PLAYER_NUM;
1452 entity->flags[PASSABLE] = true;
1453 entity->flags[NOUPDATE] = true;
1454 entity->flags[GENIUS] = true;
1455 entity->flags[INVISIBLE] = true;
1456 entity->focalx = limbs[playerRace][9][0];
1457 entity->focaly = limbs[playerRace][9][1];
1458 entity->focalz = limbs[playerRace][9][2];
1459 entity->behavior = &actPlayerLimb;
1460 entity->parent = my->getUID();
1461 node = list_AddNodeLast(&my->children);
1462 node->element = entity;
1463 node->deconstructor = &emptyDeconstructor;
1464 node->size = sizeof(Entity*);
1465 my->bodyparts.push_back(entity);
1466
1467 // mask
1468 entity = newEntity(-1, 1, map.entities, nullptr); //Limb entity.
1469 entity->sizex = 4;
1470 entity->sizey = 4;
1471 entity->scalex = .99;
1472 entity->scaley = .99;
1473 entity->scalez = .99;
1474 entity->skill[2] = PLAYER_NUM;
1475 entity->flags[PASSABLE] = true;
1476 entity->flags[NOUPDATE] = true;
1477 entity->flags[GENIUS] = true;
1478 entity->flags[INVISIBLE] = true;
1479 entity->focalx = limbs[playerRace][10][0];
1480 entity->focaly = limbs[playerRace][10][1];
1481 entity->focalz = limbs[playerRace][10][2];
1482 entity->behavior = &actPlayerLimb;
1483 entity->parent = my->getUID();
1484 node = list_AddNodeLast(&my->children);
1485 node->element = entity;
1486 node->deconstructor = &emptyDeconstructor;
1487 node->size = sizeof(Entity*);
1488 my->bodyparts.push_back(entity);
1489
1490 // additional limb 1
1491 entity = newEntity(-1, 1, map.entities, nullptr); //Limb entity.
1492 entity->sizex = 4;
1493 entity->sizey = 4;
1494 entity->skill[2] = PLAYER_NUM;
1495 entity->flags[PASSABLE] = true;
1496 entity->flags[NOUPDATE] = true;
1497 entity->flags[GENIUS] = true;
1498 entity->flags[INVISIBLE] = true;
1499 entity->focalx = limbs[playerRace][11][0];
1500 entity->focaly = limbs[playerRace][11][1];
1501 entity->focalz = limbs[playerRace][11][2];
1502 entity->behavior = &actPlayerLimb;
1503 entity->parent = my->getUID();
1504 node = list_AddNodeLast(&my->children);
1505 node->element = entity;
1506 node->deconstructor = &emptyDeconstructor;
1507 node->size = sizeof(Entity*);
1508 my->bodyparts.push_back(entity);
1509
1510 // additional limb 2
1511 entity = newEntity(-1, 1, map.entities, nullptr); //Limb entity.
1512 entity->sizex = 4;
1513 entity->sizey = 4;
1514 entity->skill[2] = PLAYER_NUM;
1515 entity->flags[PASSABLE] = true;
1516 entity->flags[NOUPDATE] = true;
1517 entity->flags[GENIUS] = true;
1518 entity->flags[INVISIBLE] = true;
1519 entity->focalx = limbs[playerRace][12][0];
1520 entity->focaly = limbs[playerRace][12][1];
1521 entity->focalz = limbs[playerRace][12][2];
1522 entity->behavior = &actPlayerLimb;
1523 entity->parent = my->getUID();
1524 node = list_AddNodeLast(&my->children);
1525 node->element = entity;
1526 node->deconstructor = &emptyDeconstructor;
1527 node->size = sizeof(Entity*);
1528 my->bodyparts.push_back(entity);
1529
1530 // additional limb 3 - 18.
1531 for ( int c = 0; c < 8; ++c )
1532 {
1533 entity = newEntity(-1, 1, map.entities, nullptr); //Limb entity.
1534 entity->sizex = 1;
1535 entity->sizey = 1;
1536 entity->skill[2] = PLAYER_NUM;
1537 entity->fskill[10] = (c / 8.f);
1538 entity->flags[PASSABLE] = true;
1539 entity->flags[NOUPDATE] = true;
1540 entity->flags[GENIUS] = true;
1541 entity->flags[INVISIBLE] = true;
1542 entity->focalx = limbs[playerRace][11][0];
1543 entity->focaly = limbs[playerRace][11][1];
1544 entity->focalz = limbs[playerRace][11][2];
1545 entity->behavior = &actPlayerLimb;
1546 entity->parent = my->getUID();
1547 node = list_AddNodeLast(&my->children);
1548 node->element = entity;
1549 node->deconstructor = &emptyDeconstructor;
1550 node->size = sizeof(Entity*);
1551 my->bodyparts.push_back(entity);
1552
1553 entity = newEntity(-1, 1, map.entities, nullptr); //Limb entity.
1554 entity->sizex = 1;
1555 entity->sizey = 1;
1556 entity->skill[2] = PLAYER_NUM;
1557 entity->flags[PASSABLE] = true;
1558 entity->flags[NOUPDATE] = true;
1559 entity->flags[GENIUS] = true;
1560 entity->flags[INVISIBLE] = true;
1561 entity->focalx = limbs[playerRace][12][0];
1562 entity->focaly = limbs[playerRace][12][1];
1563 entity->focalz = limbs[playerRace][12][2];
1564 entity->behavior = &actPlayerLimb;
1565 entity->parent = my->getUID();
1566 node = list_AddNodeLast(&my->children);
1567 node->element = entity;
1568 node->deconstructor = &emptyDeconstructor;
1569 node->size = sizeof(Entity*);
1570 my->bodyparts.push_back(entity);
1571 }
1572 }
1573 Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 255);
1574 int blueSpeechVolume = 100;
1575 int orangeSpeechVolume = 128;
1576
1577 if ( !intro )
1578 {
1579 PLAYER_ALIVETIME++;
1580 if ( PLAYER_NUM == clientnum || splitscreen )
1581 {
1582 clientplayer = my->getUID();
1583 if ( !strcmp(map.name, "Boss") && !my->skill[29] )
1584 {
1585 bool foundherx = false;
1586 for ( node = map.creatures->first; node != nullptr; node = node->next ) //Herx is in the creature list, so only search that.
1587 {
1588 Entity* entity = (Entity*)node->element;
1589 if ( entity->sprite == 274 )
1590 {
1591 foundherx = true;
1592 break;
1593 }
1594 }
1595 if ( !foundherx )
1596 {
1597 // ding, dong, the witch is dead
1598 my->skill[29] = PLAYER_ALIVETIME;
1599 }
1600 else
1601 {
1602 if ( PLAYER_ALIVETIME == 300 )
1603 {
1604 playSound(185, 128);
1605 messagePlayerColor(clientnum, color, language[537]);
1606 messagePlayerColor(clientnum, color, language[89]);
1607 }
1608 }
1609 }
1610 else
1611 {
1612 if ( PLAYER_ALIVETIME == 300 && !MFLAG_DISABLEMESSAGES )
1613 {
1614 // five seconds in, herx chimes in (maybe)
1615 my->playerLevelEntrySpeech = 0;
1616 if ( currentlevel == 0 && !secretlevel )
1617 {
1618 int speech = rand() % 3;
1619 playSound(126 + speech, 128);
1620 messagePlayerColor(clientnum, color, language[537]);
1621 messagePlayerColor(clientnum, color, language[77 + speech]);
1622 }
1623 else if ( currentlevel == 1 && !secretlevel )
1624 {
1625 int speech = rand() % 3;
1626 playSound(117 + speech, 128);
1627 messagePlayerColor(clientnum, color, language[537]);
1628 messagePlayerColor(clientnum, color, language[70 + speech]);
1629 }
1630 else if ( currentlevel == 5 && !secretlevel )
1631 {
1632 int speech = rand() % 2;
1633 playSound(156 + speech, 128);
1634 messagePlayerColor(clientnum, color, language[537]);
1635 messagePlayerColor(clientnum, color, language[83 + speech]);
1636 }
1637 else if ( currentlevel == 10 && !secretlevel )
1638 {
1639 int speech = rand() % 2;
1640 playSound(158 + speech, 128);
1641 messagePlayerColor(clientnum, color, language[537]);
1642 messagePlayerColor(clientnum, color, language[85 + speech]);
1643 }
1644 else if ( currentlevel == 15 && !secretlevel )
1645 {
1646 int speech = rand() % 2;
1647 playSound(160 + speech, 128);
1648 messagePlayerColor(clientnum, color, language[537]);
1649 messagePlayerColor(clientnum, color, language[87 + speech]);
1650 }
1651 else if ( currentlevel == 26 && !secretlevel )
1652 {
1653 int speech = 1 + rand() % 3;
1654 switch ( speech )
1655 {
1656 case 1:
1657 playSound(341, blueSpeechVolume);
1658 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[537]);
1659 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2615]);
1660 break;
1661 case 2:
1662 playSound(343, orangeSpeechVolume);
1663 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[537]);
1664 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2617]);
1665 break;
1666 case 3:
1667 playSound(346, orangeSpeechVolume);
1668 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[537]);
1669 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2620]);
1670 break;
1671 }
1672 my->playerLevelEntrySpeech = speech;
1673 }
1674 else if ( currentlevel == 28 && !secretlevel )
1675 {
1676 int speech = 1 + rand() % 3;
1677 switch ( speech )
1678 {
1679 case 1:
1680 playSound(349, blueSpeechVolume);
1681 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[537]);
1682 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2629]);
1683 break;
1684 case 2:
1685 playSound(352, orangeSpeechVolume);
1686 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[537]);
1687 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2632]);
1688 break;
1689 case 3:
1690 playSound(354, blueSpeechVolume);
1691 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[537]);
1692 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2634]);
1693 break;
1694 }
1695 my->playerLevelEntrySpeech = speech;
1696 }
1697 else if ( currentlevel == 30 && !secretlevel )
1698 {
1699 int speech = 1;
1700 switch ( speech )
1701 {
1702 case 1:
1703 playSound(356, blueSpeechVolume - 16);
1704 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[537]);
1705 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2636]);
1706 break;
1707 }
1708 my->playerLevelEntrySpeech = speech;
1709 }
1710 else if ( currentlevel == 31 && !secretlevel )
1711 {
1712 int speech = 1;
1713 switch ( speech )
1714 {
1715 case 1:
1716 playSound(358, blueSpeechVolume);
1717 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[537]);
1718 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2638]);
1719 break;
1720 }
1721 my->playerLevelEntrySpeech = speech;
1722 }
1723 else if ( currentlevel == 33 && !secretlevel )
1724 {
1725 int speech = 1 + rand() % 2;
1726 switch ( speech )
1727 {
1728 case 1:
1729 playSound(360, orangeSpeechVolume);
1730 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[537]);
1731 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2640]);
1732 break;
1733 case 2:
1734 playSound(362, blueSpeechVolume);
1735 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[537]);
1736 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2642]);
1737 break;
1738 }
1739 my->playerLevelEntrySpeech = speech;
1740 }
1741 else if ( currentlevel == 35 && !secretlevel )
1742 {
1743 int speech = 1;
1744 switch ( speech )
1745 {
1746 case 1:
1747 playSound(364, orangeSpeechVolume);
1748 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[537]);
1749 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2644]);
1750 break;
1751 }
1752 my->playerLevelEntrySpeech = speech;
1753 }
1754 else if ( minotaurlevel )
1755 {
1756 if ( currentlevel < 25 )
1757 {
1758 int speech = rand() % 3;
1759 playSound(123 + speech, 128);
1760 messagePlayerColor(clientnum, color, language[537]);
1761 messagePlayerColor(clientnum, color, language[74 + speech]);
1762 }
1763 else
1764 {
1765 int speech = 1 + rand() % 3;
1766 switch ( speech )
1767 {
1768 case 1:
1769 playSound(366, blueSpeechVolume);
1770 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[537]);
1771 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2623]);
1772 break;
1773 case 2:
1774 playSound(368, orangeSpeechVolume);
1775 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[537]);
1776 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2625]);
1777 break;
1778 case 3:
1779 playSound(370, blueSpeechVolume);
1780 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[537]);
1781 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2627]);
1782 break;
1783 }
1784 my->playerLevelEntrySpeech = speech;
1785 }
1786 }
1787 }
1788 else if ( PLAYER_ALIVETIME == 480 && !MFLAG_DISABLEMESSAGES )
1789 {
1790 // 8 seconds in, herx chimes in again (maybe)
1791 if ( currentlevel == 1 && !secretlevel )
1792 {
1793 playSound(120 + rand() % 3, 128);
1794 messagePlayerColor(clientnum, color, language[73]);
1795 }
1796 else if ( minotaurlevel && currentlevel < 25 )
1797 {
1798 int speech = rand() % 3;
1799 playSound(129 + speech, 128);
1800 messagePlayerColor(clientnum, color, language[80 + speech]);
1801 }
1802 }
1803 else if ( my->playerLevelEntrySpeech > 0 )
1804 {
1805 my->playerLevelEntrySpeechSecond();
1806 }
1807 }
1808
1809 // shurar the talking mace
1810 if ( stats[PLAYER_NUM]->weapon )
1811 {
1812 if ( stats[PLAYER_NUM]->weapon->type == ARTIFACT_MACE )
1813 {
1814 if ( PLAYER_ALIVETIME % 420 == 0 )
1815 {
1816 messagePlayerColor(clientnum, color, language[538 + rand() % 32]);
1817 }
1818 }
1819 }
1820
1821 if ( monsterEmoteGimpTimer > 0 )
1822 {
1823 --monsterEmoteGimpTimer;
1824 }
1825 }
1826 if ( multiplayer == SERVER )
1827 {
1828 if ( my->getUID() % (TICKS_PER_SECOND * 3) == ticks % (TICKS_PER_SECOND * 3) )
1829 {
1830 serverUpdateBodypartIDs(my);
1831
1832 int i;
1833 for ( i = 1; i < 11; i++ )
1834 {
1835 serverUpdateEntityBodypart(my, i);
1836 }
1837 }
1838 }
1839 if ( multiplayer != CLIENT )
1840 {
1841 if ( PLAYER_ALIVETIME == 50 && currentlevel == 0 )
1842 {
1843 int monsterSquad = 0;
1844 for ( int c = 0; c < MAXPLAYERS; ++c )
1845 {
1846 if ( players[c] && players[c]->entity && stats[c]->playerRace > 0 )
1847 {
1848 ++monsterSquad;
1849 }
1850 }
1851 if ( monsterSquad >= 3 )
1852 {
1853 for ( int c = 0; c < MAXPLAYERS; ++c )
1854 {
1855 steamAchievementClient(c, "BARONY_ACH_MONSTER_SQUAD");
1856 }
1857 }
1858 if ( client_classes[PLAYER_NUM] == CLASS_ACCURSED )
1859 {
1860 my->setEffect(EFF_VAMPIRICAURA, true, -2, true);
1861 my->playerVampireCurse = 1;
1862 serverUpdateEntitySkill(my, 51);
1863 Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
1864 messagePlayerColor(PLAYER_NUM, color, language[2477]);
1865 color = SDL_MapRGB(mainsurface->format, 255, 255, 0);
1866 messagePlayerColor(PLAYER_NUM, color, language[3202]);
1867
1868 playSoundEntity(my, 167, 128);
1869 playSoundEntity(my, 403, 128);
1870 createParticleDropRising(my, 600, 0.7);
1871 serverSpawnMiscParticles(my, PARTICLE_EFFECT_VAMPIRIC_AURA, 600);
1872 }
1873 }
1874 if ( currentlevel == 0 && stats[PLAYER_NUM]->playerRace == RACE_GOATMAN && stats[PLAYER_NUM]->appearance == 0 )
1875 {
1876 if ( PLAYER_ALIVETIME == 1 )
1877 {
1878 my->setEffect(EFF_WITHDRAWAL, true, -2, true);
1879 }
1880 if ( PLAYER_ALIVETIME == 330 )
1881 {
1882 my->setEffect(EFF_ASLEEP, false, 0, true);
1883 if ( svFlags & SV_FLAG_HUNGER )
1884 {
1885 if ( stats[PLAYER_NUM]->HUNGER <= 1000 ) // just in case you ate before scripted sequence
1886 {
1887 playSoundPlayer(PLAYER_NUM, 32, 128);
1888 stats[PLAYER_NUM]->HUNGER = 150;
1889 serverUpdateHunger(PLAYER_NUM);
1890 }
1891 else
1892 {
1893 stats[PLAYER_NUM]->HUNGER -= 850;
1894 serverUpdateHunger(PLAYER_NUM);
1895 }
1896 }
1897 }
1898 if ( stats[PLAYER_NUM]->EFFECTS[EFF_WITHDRAWAL] )
1899 {
1900 if ( PLAYER_ALIVETIME == 500 )
1901 {
1902 color = SDL_MapRGB(mainsurface->format, 255, 255, 255);
1903 messagePlayerColor(PLAYER_NUM, color, language[3221]);
1904 }
1905 else if ( PLAYER_ALIVETIME == 700 )
1906 {
1907 color = SDL_MapRGB(mainsurface->format, 255, 255, 255);
1908 messagePlayerColor(PLAYER_NUM, color, language[3222]);
1909 }
1910 }
1911 }
1912 }
1913 if ( multiplayer == CLIENT && client_classes[PLAYER_NUM] == CLASS_ACCURSED )
1914 {
1915 if ( PLAYER_NUM == clientnum && my->playerVampireCurse == 1 )
1916 {
1917 stats[PLAYER_NUM]->EFFECTS_TIMERS[EFF_VAMPIRICAURA] = -2;
1918 }
1919 else
1920 {
1921 stats[PLAYER_NUM]->EFFECTS_TIMERS[EFF_VAMPIRICAURA] = 0;
1922 }
1923 }
1924 }
1925
1926 //if ( keystatus[SDL_SCANCODE_F1] )
1927 //{
1928 // //gameloopFreezeEntities = !gameloopFreezeEntities;
1929 // cpp_SteamMatchmaking_RequestAppTicket();
1930 // keystatus[SDL_SCANCODE_F1] = 0;
1931 //}
1932 /*if ( my->ticks % 50 == 0 )
1933 {
1934 messagePlayer(clientnum, "%d", stats[clientnum]->HUNGER);
1935 }*/
1936 /*
1937 if ( keystatus[SDL_SCANCODE_F2] )
1938 {
1939 if ( players[clientnum] != nullptr && players[clientnum]->entity != nullptr )
1940 {
1941 lightSphereShadow(players[clientnum]->entity->x / 16, players[clientnum]->entity->y / 16, 8, 150);
1942 }
1943 keystatus[SDL_SCANCODE_F2] = 0;
1944 }
1945 if ( keystatus[SDL_SCANCODE_F3] )
1946 {
1947 if ( players[clientnum] != nullptr && players[clientnum]->entity != nullptr )
1948 {
1949 players[clientnum]->entity->skill[3] = (players[clientnum]->entity->skill[3] == 0);
1950 }
1951 keystatus[SDL_SCANCODE_F3] = 0;
1952 }
1953 if ( keystatus[SDL_SCANCODE_F4] )
1954 {
1955 buttonStartSingleplayer(nullptr);
1956 keystatus[SDL_SCANCODE_F4] = 0;
1957 }
1958 if ( keystatus[SDL_SCANCODE_F4] )
1959 {
1960 keystatus[SDL_SCANCODE_F4] = 0;
1961
1962 SteamUserStats()->SetAchievement("BARONY_ACH_BONY_BARON");
1963 SteamUserStats()->SetAchievement("BARONY_ACH_BUCKTOOTH_BARON");
1964 SteamUserStats()->SetAchievement("BARONY_ACH_BOMBSHELL_BARON");
1965 SteamUserStats()->SetAchievement("BARONY_ACH_BLEATING_BARON");
1966 SteamUserStats()->SetAchievement("BARONY_ACH_BOILERPLATE_BARON");
1967 SteamUserStats()->SetAchievement("BARONY_ACH_BAD_BOY_BARON");
1968 SteamUserStats()->SetAchievement("BARONY_ACH_BAYOU_BARON");
1969 SteamUserStats()->SetAchievement("BARONY_ACH_BUGGAR_BARON");
1970 SteamUserStats()->StoreStats();
1971 }
1972 if ( keystatus[SDL_SCANCODE_F5] )
1973 {
1974 keystatus[SDL_SCANCODE_F5] = 0;
1975 SteamUserStats()->ClearAchievement("BARONY_ACH_BONY_BARON");
1976 SteamUserStats()->ClearAchievement("BARONY_ACH_BUCKTOOTH_BARON");
1977 SteamUserStats()->ClearAchievement("BARONY_ACH_BOMBSHELL_BARON");
1978 SteamUserStats()->ClearAchievement("BARONY_ACH_BLEATING_BARON");
1979 SteamUserStats()->ClearAchievement("BARONY_ACH_BOILERPLATE_BARON");
1980 SteamUserStats()->ClearAchievement("BARONY_ACH_BAD_BOY_BARON");
1981 SteamUserStats()->ClearAchievement("BARONY_ACH_BAYOU_BARON");
1982 SteamUserStats()->ClearAchievement("BARONY_ACH_BUGGAR_BARON");
1983 SteamUserStats()->StoreStats();
1984 }
1985 if ( keystatus[SDL_SCANCODE_KP_1] )
1986 {
1987 keystatus[SDL_SCANCODE_KP_1] = 0;
1988 SteamUserStats()->SetAchievement("BARONY_ACH_BONY_BARON");
1989 SteamUserStats()->StoreStats();
1990 }
1991 if ( keystatus[SDL_SCANCODE_KP_2] )
1992 {
1993 keystatus[SDL_SCANCODE_KP_2] = 0;
1994 SteamUserStats()->SetAchievement("BARONY_ACH_BUCKTOOTH_BARON");
1995 SteamUserStats()->StoreStats();
1996 }
1997 if ( keystatus[SDL_SCANCODE_KP_3] )
1998 {
1999 keystatus[SDL_SCANCODE_KP_3] = 0;
2000 SteamUserStats()->SetAchievement("BARONY_ACH_BOMBSHELL_BARON");
2001 SteamUserStats()->StoreStats();
2002 }
2003 if ( keystatus[SDL_SCANCODE_KP_4] )
2004 {
2005 keystatus[SDL_SCANCODE_KP_4] = 0;
2006 SteamUserStats()->SetAchievement("BARONY_ACH_BLEATING_BARON");
2007 SteamUserStats()->StoreStats();
2008 }
2009 if ( keystatus[SDL_SCANCODE_KP_5] )
2010 {
2011 keystatus[SDL_SCANCODE_KP_5] = 0;
2012 SteamUserStats()->SetAchievement("BARONY_ACH_BOILERPLATE_BARON");
2013 SteamUserStats()->StoreStats();
2014 }
2015 if ( keystatus[SDL_SCANCODE_KP_6] )
2016 {
2017 keystatus[SDL_SCANCODE_KP_6] = 0;
2018 SteamUserStats()->SetAchievement("BARONY_ACH_BAD_BOY_BARON");
2019 SteamUserStats()->StoreStats();
2020 }
2021 if ( keystatus[SDL_SCANCODE_KP_7] )
2022 {
2023 keystatus[SDL_SCANCODE_KP_7] = 0;
2024 SteamUserStats()->SetAchievement("BARONY_ACH_BAYOU_BARON");
2025 SteamUserStats()->StoreStats();
2026 }
2027 if ( keystatus[SDL_SCANCODE_KP_8] )
2028 {
2029 keystatus[SDL_SCANCODE_KP_8] = 0;
2030 SteamUserStats()->SetAchievement("BARONY_ACH_BUGGAR_BARON");
2031 SteamUserStats()->StoreStats();
2032 }*/
2033
2034 // SPLITSCREEN TODO
2035 if (PLAYER_NUM == clientnum && appraisal_timer > 0)
2036 {
2037 Item* tempItem = uidToItem(appraisal_item);
2038 if ( tempItem )
2039 {
2040 if ( tempItem->identified )
2041 {
2042 appraisal_timer = 0;
2043 appraisal_item = 0;
2044 }
2045 else if ( tempItem->type == GEM_ROCK )
2046 {
2047 //Auto-succeed on rocks.
2048 tempItem->identified = true;
2049 messagePlayer(clientnum, language[570], tempItem->description());
2050 appraisal_item = 0;
2051 appraisal_timer = 0;
2052
2053 // update inventory by trying to stack the newly identified item.
2054 std::unordered_set<Uint32> appearancesOfSimilarItems;
2055 for ( node = stats[PLAYER_NUM]->inventory.first; node != NULL; node = node->next )
2056 {
2057 Item* item2 = (Item*)node->element;
2058 if ( item2 == tempItem )
2059 {
2060 continue;
2061 }
2062 if ( !item2 )
2063 {
2064 continue;
2065 }
2066 if ( !itemCompare(tempItem, item2, false) )
2067 {
2068 // if items are the same, check to see if they should stack
2069 if ( item2->shouldItemStack(PLAYER_NUM) )
2070 {
2071 item2->count += tempItem->count;
2072 if ( multiplayer == CLIENT && itemIsEquipped(item2, clientnum) )
2073 {
2074 // if incrementing qty and holding item, then send "equip" for server to update their count of your held item.
2075 clientSendEquipUpdateToServer(EQUIP_ITEM_SLOT_WEAPON, EQUIP_ITEM_SUCCESS_UPDATE_QTY, PLAYER_NUM,
2076 item2->type, item2->status, item2->beatitude, item2->count, item2->appearance, item2->identified);
2077 }
2078 if ( tempItem->node )
2079 {
2080 list_RemoveNode(tempItem->node);
2081 tempItem = nullptr;
2082 }
2083 else
2084 {
2085 free(tempItem);
2086 tempItem = nullptr;
2087 }
2088 break;
2089 }
2090 else if ( !itemCompare(tempItem, item2, true) )
2091 {
2092 // items are the same (incl. appearance!)
2093 // if they shouldn't stack, we need to change appearance of the new item.
2094 appearancesOfSimilarItems.insert(item2->appearance);
2095 }
2096 }
2097 }
2098 if ( tempItem && !appearancesOfSimilarItems.empty() )
2099 {
2100 int tries = 100;
2101 // we need to find a unique appearance within the list.
2102 tempItem->appearance = rand();
2103 auto it = appearancesOfSimilarItems.find(tempItem->appearance);
2104 while ( it != appearancesOfSimilarItems.end() && tries > 0 )
2105 {
2106 tempItem->appearance = rand();
2107 it = appearancesOfSimilarItems.find(tempItem->appearance);
2108 --tries;
2109 }
2110 }
2111 }
2112 else
2113 {
2114 appraisal_timer -= 1; //De-increment appraisal timer.
2115 if (appraisal_timer <= 0)
2116 {
2117 appraisal_timer = 0;
2118
2119 //Cool. Time to identify the item.
2120 bool success = false;
2121 if ( stats[PLAYER_NUM]->PROFICIENCIES[PRO_APPRAISAL] < 100 )
2122 {
2123 if ( tempItem->type != GEM_GLASS )
2124 {
2125 success = (stats[PLAYER_NUM]->PROFICIENCIES[PRO_APPRAISAL] + my->getPER() * 5 >= items[tempItem->type].value / 10);
2126 }
2127 else
2128 {
2129 success = (stats[PLAYER_NUM]->PROFICIENCIES[PRO_APPRAISAL] + my->getPER() * 5 >= 100);
2130 }
2131 }
2132 else
2133 {
2134 success = true; // always succeed when appraisal is maxed out
2135 }
2136 if ( success )
2137 {
2138 tempItem->identified = true;
2139 messagePlayer(clientnum, language[570], tempItem->description());
2140 if ( tempItem->type == GEM_GLASS )
2141 {
2142 steamStatisticUpdate(STEAM_STAT_RHINESTONE_COWBOY, STEAM_STAT_INT, 1);
2143 }
2144 }
2145 else
2146 {
2147 if ( itemCategory(tempItem) == GEM )
2148 {
2149 messagePlayer(clientnum, language[3240], tempItem->description());
2150 }
2151 }
2152
2153 //Attempt a level up.
2154 if ( tempItem && items[tempItem->type].value > 0 && stats[PLAYER_NUM] )
2155 {
2156 if ( tempItem->identified )
2157 {
2158 int appraisalEaseOfDifficulty = 0;
2159 if ( items[tempItem->type].value < 100 )
2160 {
2161 // easy junk items
2162 appraisalEaseOfDifficulty = 2;
2163 }
2164 else if ( items[tempItem->type].value < 200 )
2165 {
2166 // medium
2167 appraisalEaseOfDifficulty = 1;
2168 }
2169 else if ( items[tempItem->type].value < 300 )
2170 {
2171 // medium
2172 appraisalEaseOfDifficulty = 0;
2173 }
2174 else if ( items[tempItem->type].value < 400 )
2175 {
2176 // hardest
2177 appraisalEaseOfDifficulty = -1;
2178 }
2179 else
2180 {
2181 // hardest
2182 appraisalEaseOfDifficulty = -1;
2183 }
2184 appraisalEaseOfDifficulty += stats[PLAYER_NUM]->PROFICIENCIES[PRO_APPRAISAL] / 20;
2185 // difficulty ranges from 1-in-1 to 1-in-6
2186 appraisalEaseOfDifficulty = std::max(appraisalEaseOfDifficulty, 1);
2187 //messagePlayer(0, "Appraisal level up chance: 1 in %d", appraisalEaseOfDifficulty);
2188 if ( rand() % appraisalEaseOfDifficulty == 0 )
2189 {
2190 my->increaseSkill(PRO_APPRAISAL);
2191 }
2192 }
2193 else if ( rand() % 7 == 0 )
2194 {
2195 my->increaseSkill(PRO_APPRAISAL);
2196 }
2197 }
2198
2199 if ( success )
2200 {
2201 // update inventory by trying to stack the newly identified item.
2202 std::unordered_set<Uint32> appearancesOfSimilarItems;
2203 for ( node = stats[PLAYER_NUM]->inventory.first; node != NULL; node = node->next )
2204 {
2205 Item* item2 = (Item*)node->element;
2206 if ( item2 && item2 != tempItem && !itemCompare(tempItem, item2, false) )
2207 {
2208 if ( itemTypeIsQuiver(item2->type) && (tempItem->count + item2->count) >= QUIVER_MAX_AMMO_QTY )
2209 {
2210 int maxStack = QUIVER_MAX_AMMO_QTY;
2211 // too many, split off into a new stack with reduced qty.
2212 if ( tempItem->count >= (maxStack - 1) )
2213 {
2214 // identified item is at max count so don't stack, abort.
2215 break;
2216 }
2217 if ( item2->count >= (maxStack - 1) )
2218 {
2219 // if we're at max count then skip this check.
2220
2221 if ( tempItem->appearance == item2->appearance )
2222 {
2223 // items are the same (incl. appearance!)
2224 // if they shouldn't stack, we need to change appearance of the new item.
2225 appearancesOfSimilarItems.insert(item2->appearance);
2226 }
2227 continue;
2228 }
2229 int total = tempItem->count + item2->count;
2230 item2->count = maxStack - 1;
2231 tempItem->count = total - item2->count;
2232 if ( multiplayer == CLIENT && itemIsEquipped(item2, clientnum) )
2233 {
2234 // if incrementing qty and holding item, then send "equip" for server to update their count of your held item.
2235 strcpy((char*)net_packet->data, "EQUS");
2236 SDLNet_Write32((Uint32)item2->type, &net_packet->data[4]);
2237 SDLNet_Write32((Uint32)item2->status, &net_packet->data[8]);
2238 SDLNet_Write32((Uint32)item2->beatitude, &net_packet->data[12]);
2239 SDLNet_Write32((Uint32)item2->count, &net_packet->data[16]);
2240 SDLNet_Write32((Uint32)item2->appearance, &net_packet->data[20]);
2241 net_packet->data[24] = item2->identified;
2242 net_packet->data[25] = clientnum;
2243 net_packet->address.host = net_server.host;
2244 net_packet->address.port = net_server.port;
2245 net_packet->len = 27;
2246 sendPacketSafe(net_sock, -1, net_packet, 0);
2247 }
2248 if ( tempItem->count <= 0 )
2249 {
2250 if ( tempItem->node )
2251 {
2252 list_RemoveNode(tempItem->node);
2253 tempItem = nullptr;
2254 }
2255 else
2256 {
2257 free(tempItem);
2258 tempItem = nullptr;
2259 }
2260 break;
2261 }
2262 else
2263 {
2264 // we have to search other items to stack with, otherwise this search ends after 1 full stack.
2265 if ( tempItem->appearance == item2->appearance )
2266 {
2267 // items are the same (incl. appearance!)
2268 // if they shouldn't stack, we need to change appearance of the new item.
2269 appearancesOfSimilarItems.insert(item2->appearance);
2270 }
2271 continue;
2272 }
2273 }
2274 // if items are the same, check to see if they should stack
2275 else if ( item2->shouldItemStack(PLAYER_NUM) )
2276 {
2277 item2->count += tempItem->count;
2278 if ( multiplayer == CLIENT && itemIsEquipped(item2, clientnum) )
2279 {
2280 // if incrementing qty and holding item, then send "equip" for server to update their count of your held item.
2281 Item** slot = itemSlot(stats[PLAYER_NUM], item2);
2282 if ( slot )
2283 {
2284 if ( slot == &stats[clientnum]->weapon )
2285 {
2286 clientSendEquipUpdateToServer(EQUIP_ITEM_SLOT_WEAPON, EQUIP_ITEM_SUCCESS_UPDATE_QTY, PLAYER_NUM,
2287 item2->type, item2->status, item2->beatitude, item2->count, item2->appearance, item2->identified);
2288 }
2289 else if ( slot == &stats[clientnum]->shield )
2290 {
2291 clientSendEquipUpdateToServer(EQUIP_ITEM_SLOT_SHIELD, EQUIP_ITEM_SUCCESS_UPDATE_QTY, PLAYER_NUM,
2292 item2->type, item2->status, item2->beatitude, item2->count, item2->appearance, item2->identified);
2293 }
2294 }
2295 }
2296 if ( tempItem->node )
2297 {
2298 list_RemoveNode(tempItem->node);
2299 tempItem = nullptr;
2300 }
2301 else
2302 {
2303 free(tempItem);
2304 tempItem = nullptr;
2305 }
2306 break;
2307 }
2308 else if ( !itemCompare(tempItem, item2, true) )
2309 {
2310 // items are the same (incl. appearance!)
2311 // if they shouldn't stack, we need to change appearance of the new item.
2312 appearancesOfSimilarItems.insert(item2->appearance);
2313 }
2314 }
2315 }
2316 if ( tempItem )
2317 {
2318 if ( !appearancesOfSimilarItems.empty() )
2319 {
2320 int tries = 100;
2321 bool robot = false;
2322 // we need to find a unique appearance within the list.
2323 if ( tempItem->type == TOOL_SENTRYBOT || tempItem->type == TOOL_SPELLBOT || tempItem->type == TOOL_GYROBOT
2324 || tempItem->type == TOOL_DUMMYBOT )
2325 {
2326 robot = true;
2327 tempItem->appearance += (rand() % 100000) * 10;
2328 }
2329 else
2330 {
2331 tempItem->appearance = rand();
2332 }
2333 auto it = appearancesOfSimilarItems.find(tempItem->appearance);
2334 while ( it != appearancesOfSimilarItems.end() && tries > 0 )
2335 {
2336 if ( robot )
2337 {
2338 tempItem->appearance += (rand() % 100000) * 10;
2339 }
2340 else
2341 {
2342 tempItem->appearance = rand();
2343 }
2344 it = appearancesOfSimilarItems.find(tempItem->appearance);
2345 --tries;
2346 }
2347 }
2348 }
2349 }
2350
2351 appraisal_item = 0;
2352 }
2353 }
2354 }
2355 else
2356 {
2357 appraisal_timer = 0;
2358 appraisal_item = 0;
2359 }
2360 }
2361
2362 // remove broken equipment
2363 if ( stats[PLAYER_NUM]->helmet != NULL )
2364 if ( stats[PLAYER_NUM]->helmet->status == BROKEN )
2365 {
2366 stats[PLAYER_NUM]->helmet = NULL;
2367 }
2368 if ( stats[PLAYER_NUM]->breastplate != NULL )
2369 if ( stats[PLAYER_NUM]->breastplate->status == BROKEN )
2370 {
2371 stats[PLAYER_NUM]->breastplate = NULL;
2372 }
2373 if ( stats[PLAYER_NUM]->gloves != NULL )
2374 if ( stats[PLAYER_NUM]->gloves->status == BROKEN )
2375 {
2376 stats[PLAYER_NUM]->gloves = NULL;
2377 }
2378 if ( stats[PLAYER_NUM]->shoes != NULL )
2379 if ( stats[PLAYER_NUM]->shoes->status == BROKEN )
2380 {
2381 stats[PLAYER_NUM]->shoes = NULL;
2382 }
2383 if ( stats[PLAYER_NUM]->shield != NULL )
2384 if ( stats[PLAYER_NUM]->shield->status == BROKEN )
2385 {
2386 stats[PLAYER_NUM]->shield = NULL;
2387 }
2388 if ( stats[PLAYER_NUM]->weapon != NULL )
2389 if ( stats[PLAYER_NUM]->weapon->status == BROKEN )
2390 {
2391 stats[PLAYER_NUM]->weapon = NULL;
2392 }
2393 if ( stats[PLAYER_NUM]->cloak != NULL )
2394 if ( stats[PLAYER_NUM]->cloak->status == BROKEN )
2395 {
2396 stats[PLAYER_NUM]->cloak = NULL;
2397 }
2398 if ( stats[PLAYER_NUM]->amulet != NULL )
2399 if ( stats[PLAYER_NUM]->amulet->status == BROKEN )
2400 {
2401 stats[PLAYER_NUM]->amulet = NULL;
2402 }
2403 if ( stats[PLAYER_NUM]->ring != NULL )
2404 if ( stats[PLAYER_NUM]->ring->status == BROKEN )
2405 {
2406 stats[PLAYER_NUM]->ring = NULL;
2407 }
2408 if ( stats[PLAYER_NUM]->mask != NULL )
2409 if ( stats[PLAYER_NUM]->mask->status == BROKEN )
2410 {
2411 stats[PLAYER_NUM]->mask = NULL;
2412 }
2413
2414 if ( multiplayer != CLIENT )
2415 {
2416 my->effectTimes();
2417 }
2418
2419 // invisibility
2420 if ( !intro )
2421 {
2422 if ( PLAYER_NUM == clientnum || multiplayer == SERVER || splitscreen )
2423 {
2424 if ( stats[PLAYER_NUM]->ring != NULL )
2425 if ( stats[PLAYER_NUM]->ring->type == RING_INVISIBILITY )
2426 {
2427 wearingring = true;
2428 }
2429 if ( stats[PLAYER_NUM]->cloak != NULL )
2430 if ( stats[PLAYER_NUM]->cloak->type == CLOAK_INVISIBILITY )
2431 {
2432 wearingring = true;
2433 }
2434 //if ( stats[PLAYER_NUM]->EFFECTS[EFF_INVISIBLE] == true || wearingring == true )
2435 if ( my->isInvisible() )
2436 {
2437 if ( !my->flags[INVISIBLE] )
2438 {
2439 my->flags[INVISIBLE] = true;
2440 serverUpdateEntityFlag(my, INVISIBLE);
2441 }
2442 my->flags[BLOCKSIGHT] = false;
2443 if ( multiplayer != CLIENT )
2444 {
2445 for ( i = 0, node = my->children.first; node != NULL; node = node->next, ++i )
2446 {
2447 if ( i == 0 )
2448 {
2449 continue;
2450 }
2451 if ( i >= 6 )
2452 {
2453 break;
2454 }
2455 entity = (Entity*)node->element;
2456 if ( !entity->flags[INVISIBLE] )
2457 {
2458 entity->flags[INVISIBLE] = true;
2459 serverUpdateEntityBodypart(my, i);
2460 }
2461 }
2462 }
2463 }
2464 else
2465 {
2466 if ( my->flags[INVISIBLE] )
2467 {
2468 my->flags[INVISIBLE] = false;
2469 serverUpdateEntityFlag(my, INVISIBLE);
2470 }
2471 my->flags[BLOCKSIGHT] = true;
2472 if ( multiplayer != CLIENT )
2473 {
2474 for (i = 0, node = my->children.first; node != NULL; node = node->next, i++)
2475 {
2476 if ( i == 0 )
2477 {
2478 continue;
2479 }
2480 if ( i >= 6 )
2481 {
2482 break;
2483 }
2484 entity = (Entity*)node->element;
2485 if ( entity->flags[INVISIBLE] )
2486 {
2487 if ( stats[PLAYER_NUM]->type == RAT )
2488 {
2489 if ( i == 1 )
2490 {
2491 // only unhide the body.
2492 if ( entity->flags[INVISIBLE] )
2493 {
2494 entity->flags[INVISIBLE] = false;
2495 serverUpdateEntityBodypart(my, i);
2496 }
2497 }
2498 }
2499 else if ( stats[PLAYER_NUM]->type == SPIDER )
2500 {
2501 // do nothing, these limbs are invisible for the spider so don't unhide.
2502 }
2503 else
2504 {
2505 if ( entity->flags[INVISIBLE] )
2506 {
2507 entity->flags[INVISIBLE] = false;
2508 serverUpdateEntityBodypart(my, i);
2509 }
2510 }
2511 }
2512 }
2513 }
2514 }
2515 }
2516 }
2517
2518 real_t zOffset = 0;
2519 bool oldInsectoidLevitate = insectoidLevitating[PLAYER_NUM];
2520 insectoidLevitating[PLAYER_NUM] = false;
2521
2522 if ( PLAYER_NUM == clientnum || multiplayer == SERVER || splitscreen )
2523 {
2524 switch ( stats[PLAYER_NUM]->type )
2525 {
2526 case RAT:
2527 zOffset = 6;
2528 break;
2529 case TROLL:
2530 zOffset = -1.5;
2531 break;
2532 case SPIDER:
2533 zOffset = 4.5;
2534 break;
2535 case CREATURE_IMP:
2536 zOffset = -3.5;
2537 break;
2538 default:
2539 break;
2540 }
2541 bool prevlevitating = false;
2542 if ( multiplayer != CLIENT )
2543 {
2544 if ( abs(zOffset) <= 0.05 )
2545 {
2546 if ( (my->z >= -2.05 && my->z <= -1.95 ) || (my->z >= -1.55 && my->z <= -1.45) )
2547 {
2548 prevlevitating = true;
2549 }
2550 }
2551 else
2552 {
2553 if( (my->z >= (zOffset - 1.05)) && (my->z <= (zOffset - 0.95))
2554 || (my->z >= (zOffset - .55)) && (my->z <= (zOffset - .45)) )
2555 {
2556 prevlevitating = true;
2557 }
2558 }
2559 }
2560
2561 // sleeping
2562 if ( stats[PLAYER_NUM]->EFFECTS[EFF_ASLEEP] )
2563 {
2564 switch ( playerRace )
2565 {
2566 case GOBLIN:
2567 case GOATMAN:
2568 case INSECTOID:
2569 my->z = 2.5;
2570 break;
2571 case SKELETON:
2572 case AUTOMATON:
2573 my->z = 2.f;
2574 break;
2575 case HUMAN:
2576 case VAMPIRE:
2577 case INCUBUS:
2578 case SUCCUBUS:
2579 case TROLL:
2580 my->z = 1.5;
2581 break;
2582 default:
2583 my->z = 1.5;
2584 break;
2585 }
2586 my->pitch = PI / 4;
2587 }
2588 else if ( !noclip || (noclip && intro) )
2589 {
2590 my->z = -1;
2591 if ( abs(zOffset) >= 0.05 )
2592 {
2593 my->z = zOffset;
2594 }
2595 if ( intro )
2596 {
2597 my->pitch = 0;
2598 }
2599 }
2600
2601 // levitation
2602 levitating = isLevitating(stats[PLAYER_NUM]);
2603
2604 if ( levitating )
2605 {
2606 my->z -= 1; // floating
2607 insectoidLevitating[PLAYER_NUM] = (playerRace == INSECTOID && stats[PLAYER_NUM]->EFFECTS[EFF_FLUTTER]);
2608 }
2609
2610 if ( !levitating && prevlevitating )
2611 {
2612 int x, y, u, v;
2613 x = std::min(std::max<unsigned int>(1, my->x / 16), map.width - 2);
2614 y = std::min(std::max<unsigned int>(1, my->y / 16), map.height - 2);
2615 std::vector<std::pair<std::pair<int, int>, real_t>> safeTiles; // pair of coordinates, with a distance to player.
2616 std::vector<std::pair<int, int>> deathTiles;
2617 bool checkSafeTiles = true;
2618 for ( u = x - 1; u <= x + 1; u++ )
2619 {
2620 for ( v = y - 1; v <= y + 1; v++ )
2621 {
2622 if ( entityInsideTile(my, u, v, 0, !checkSafeTiles) )
2623 {
2624 // intersecting with no floor, we might die.
2625 deathTiles.push_back(std::make_pair(u, v));
2626 }
2627 else if ( entityInsideTile(my, u, v, 0, checkSafeTiles) )
2628 {
2629 // intersecting with a safe floor, we might live.
2630 if ( barony_clear(u * 16 + 8, v * 16 + 8, my) )
2631 {
2632 // tile is clear from obstacles.
2633 real_t dx = my->x - u * 16 + 8;
2634 real_t dy = my->y - v * 16 + 8;
2635 real_t dist = sqrt(dx * dx + dy * dy);
2636 safeTiles.push_back(std::make_pair(std::make_pair(u, v), dist));
2637 }
2638 }
2639 }
2640 }
2641
2642 if ( !deathTiles.empty() )
2643 {
2644 if ( !safeTiles.empty() )
2645 {
2646 // we might be able to warp ourselves to a safe spot.
2647 real_t lowestDist = 1000.0;
2648 int lowestIndex = -1;
2649 for ( auto it = safeTiles.begin(); it != safeTiles.end(); ++it )
2650 {
2651 if ( (*it).second < lowestDist )
2652 {
2653 lowestIndex = it - safeTiles.begin();
2654 }
2655 }
2656 if ( lowestIndex >= 0 )
2657 {
2658 int newx = safeTiles.at(lowestIndex).first.first;
2659 int newy = safeTiles.at(lowestIndex).first.second;
2660 real_t velx = newx * 16 + 8 - my->x;
2661 real_t vely = newy * 16 + 8 - my->y;
2662 // try moving in one direction only.
2663 if ( newy == y ) // keep y the same
2664 {
2665 if ( newx != x )
2666 {
2667 if ( barony_clear(newx * 16 + 8, y, my) )
2668 {
2669 vely = 0.0;
2670 }
2671 }
2672 }
2673 else if ( newx == x ) // keep x the same
2674 {
2675 if ( newy != y )
2676 {
2677 if ( barony_clear(x, newy * 16 + 8, my) )
2678 {
2679 velx = 0.0;
2680 }
2681 }
2682 }
2683 clipMove(&my->x, &my->y, velx, vely, my);
2684 messagePlayer(PLAYER_NUM, language[3869]);
2685 if ( PLAYER_NUM == clientnum || splitscreen )
2686 {
2687 cameravars[PLAYER_NUM].shakex += .1;
2688 cameravars[PLAYER_NUM].shakey += 10;
2689 }
2690 else if ( PLAYER_NUM != clientnum && multiplayer == SERVER )
2691 {
2692 if ( PLAYER_NUM > 0 )
2693 {
2694 strcpy((char*)net_packet->data, "SHAK");
2695 net_packet->data[4] = 10; // turns into .1
2696 net_packet->data[5] = 10;
2697 net_packet->address.host = net_clients[PLAYER_NUM - 1].host;
2698 net_packet->address.port = net_clients[PLAYER_NUM - 1].port;
2699 net_packet->len = 6;
2700 sendPacketSafe(net_sock, -1, net_packet, PLAYER_NUM - 1);
2701 }
2702 }
2703 }
2704 else
2705 {
2706 my->setObituary(language[3010]); // fell to their death.
2707 stats[PLAYER_NUM]->HP = 0; // kill me instantly
2708 if (stats[PLAYER_NUM]->type == AUTOMATON)
2709 {
2710 my->playerAutomatonDeathCounter = TICKS_PER_SECOND * 5; // set the death timer to immediately pop for players.
2711 }
2712 }
2713 }
2714 else if ( safeTiles.empty() )
2715 {
2716 my->setObituary(language[3010]); // fell to their death.
2717 stats[PLAYER_NUM]->HP = 0; // kill me instantly
2718 if (stats[PLAYER_NUM]->type == AUTOMATON)
2719 {
2720 my->playerAutomatonDeathCounter = TICKS_PER_SECOND * 5; // set the death timer to immediately pop for players.
2721 }
2722 }
2723 }
2724
2725 }
2726 }
2727 else
2728 {
2729 if ( playerRace == INSECTOID && stats[PLAYER_NUM]->EFFECTS[EFF_FLUTTER] && (my->z >= -2.05 && my->z <= -1.95) )
2730 {
2731 insectoidLevitating[PLAYER_NUM] = true;
2732 }
2733 }
2734
2735 // swimming
2736 bool waterwalkingboots = false;
2737 if ( stats[PLAYER_NUM]->shoes != NULL )
2738 if ( stats[PLAYER_NUM]->shoes->type == IRON_BOOTS_WATERWALKING )
2739 {
2740 waterwalkingboots = true;
2741 }
2742 bool swimming = isPlayerSwimming(my);
2743 if ( PLAYER_NUM == clientnum || multiplayer == SERVER || splitscreen )
2744 {
2745 if ( swimming )
2746 {
2747 int x = std::min(std::max<unsigned int>(0, floor(my->x / 16)), map.width - 1);
2748 int y = std::min(std::max<unsigned int>(0, floor(my->y / 16)), map.height - 1);
2749 if ( rand() % 400 == 0 && multiplayer != CLIENT )
2750 {
2751 my->increaseSkill(PRO_SWIMMING);
2752 }
2753 my->z = 7;
2754 if ( playerRace == SPIDER || playerRace == RAT )
2755 {
2756 my->z += 1;
2757 }
2758 if ( !PLAYER_INWATER && (PLAYER_NUM == clientnum || splitscreen) )
2759 {
2760 PLAYER_INWATER = 1;
2761 if ( lavatiles[map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height]] )
2762 {
2763 messagePlayer(PLAYER_NUM, language[573]);
2764 if ( stats[PLAYER_NUM]->type == AUTOMATON )
2765 {
2766 messagePlayer(PLAYER_NUM, language[3703]);
2767 }
2768 cameravars[PLAYER_NUM].shakex += .1;
2769 cameravars[PLAYER_NUM].shakey += 10;
2770 }
2771 else if ( swimmingtiles[map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height]] && stats[PLAYER_NUM]->type == VAMPIRE )
2772 {
2773 messagePlayerColor(PLAYER_NUM, SDL_MapRGB(mainsurface->format, 255, 0, 0), language[3183]);
2774 playSoundPlayer(PLAYER_NUM, 28, 128);
2775 playSoundPlayer(PLAYER_NUM, 249, 128);
2776 cameravars[PLAYER_NUM].shakex += .1;
2777 cameravars[PLAYER_NUM].shakey += 10;
2778 }
2779 else if ( swimmingtiles[map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height]] && stats[PLAYER_NUM]->type == AUTOMATON )
2780 {
2781 messagePlayer(PLAYER_NUM, language[3702]);
2782 playSound(136, 128);
2783 }
2784 else if ( swimmingtiles[map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height]] )
2785 {
2786 playSound(136, 128);
2787 }
2788 }
2789 if ( multiplayer != CLIENT )
2790 {
2791 // Check if the Player is in Water or Lava
2792 if ( swimmingtiles[map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height]] )
2793 {
2794 if ( my->flags[BURNING] )
2795 {
2796 my->flags[BURNING] = false;
2797 messagePlayer(PLAYER_NUM, language[574]); // "The water extinguishes the flames!"
2798 serverUpdateEntityFlag(my, BURNING);
2799 }
2800 if ( stats[PLAYER_NUM]->EFFECTS[EFF_POLYMORPH] )
2801 {
2802 if ( stats[PLAYER_NUM]->EFFECTS[EFF_POLYMORPH] )
2803 {
2804 my->setEffect(EFF_POLYMORPH, false, 0, true);
2805 my->effectPolymorph = 0;
2806 serverUpdateEntitySkill(my, 50);
2807
2808 messagePlayer(PLAYER_NUM, language[3192]);
2809 messagePlayer(PLAYER_NUM, language[3185]);
2810 }
2811 /*if ( stats[PLAYER_NUM]->EFFECTS[EFF_SHAPESHIFT] )
2812 {
2813 my->setEffect(EFF_SHAPESHIFT, false, 0, true);
2814 my->effectShapeshift = 0;
2815 serverUpdateEntitySkill(my, 53);
2816
2817 messagePlayer(PLAYER_NUM, language[3418]);
2818 messagePlayer(PLAYER_NUM, language[3417]);
2819 }*/
2820 playSoundEntity(my, 400, 92);
2821 createParticleDropRising(my, 593, 1.f);
2822 serverSpawnMiscParticles(my, PARTICLE_EFFECT_RISING_DROP, 593);
2823 }
2824 if ( stats[PLAYER_NUM]->type == VAMPIRE )
2825 {
2826 if ( ticks % 10 == 0 ) // Water deals damage every 10 ticks
2827 {
2828 my->modHP(-2 - rand() % 2);
2829 if ( ticks % 20 == 0 )
2830 {
2831 playSoundPlayer(PLAYER_NUM, 28, 92);
2832 }
2833 my->setObituary(language[3254]); // "goes for a swim in some water."
2834 steamAchievementClient(PLAYER_NUM, "BARONY_ACH_BLOOD_BOIL");
2835 }
2836 }
2837 else if ( stats[PLAYER_NUM]->type == AUTOMATON )
2838 {
2839 if ( ticks % 10 == 0 ) // Water deals heat damage every 10 ticks
2840 {
2841 my->safeConsumeMP(2);
2842 }
2843 if ( ticks % 50 == 0 )
2844 {
2845 messagePlayer(PLAYER_NUM, language[3702]);
2846 stats[PLAYER_NUM]->HUNGER -= 25;
2847 serverUpdateHunger(PLAYER_NUM);
2848 }
2849 }
2850 }
2851 else if ( ticks % 10 == 0 ) // Lava deals damage every 10 ticks
2852 {
2853 my->modHP(-2 - rand() % 2);
2854 if ( stats[PLAYER_NUM]->type == AUTOMATON )
2855 {
2856 my->modMP(2);
2857 if ( ticks % 50 == 0 )
2858 {
2859 messagePlayer(PLAYER_NUM, language[3703]);
2860 stats[PLAYER_NUM]->HUNGER += 50;
2861 serverUpdateHunger(PLAYER_NUM);
2862 }
2863 }
2864 my->setObituary(language[1506]); // "goes for a swim in some lava."
2865 if ( !my->flags[BURNING] )
2866 {
2867 // Attempt to set the Entity on fire
2868 my->SetEntityOnFire();
2869 }
2870 if ( stats[PLAYER_NUM]->type == AUTOMATON || stats[PLAYER_NUM]->type == SKELETON )
2871 {
2872 if ( ticks % 20 == 0 )
2873 {
2874 playSoundPlayer(PLAYER_NUM, 28, 92);
2875 }
2876 }
2877 }
2878 }
2879 }
2880 }
2881
2882 if ( !swimming )
2883 {
2884 if ( PLAYER_INWATER != 0 )
2885 {
2886 PLAYER_INWATER = 0;
2887 PLAYER_BOBMOVE = 0;
2888 PLAYER_BOB = 0;
2889 PLAYER_BOBMODE = 0;
2890 }
2891 }
2892
2893 if (PLAYER_NUM == clientnum || splitscreen)
2894 {
2895 players[PLAYER_NUM]->entity = my;
2896
2897 if ( !usecamerasmoothing )
2898 {
2899 handlePlayerCameraBobbing(my, PLAYER_NUM, false);
2900 }
2901
2902 // object interaction
2903 if ( intro == false )
2904 {
2905 clickDescription(PLAYER_NUM, NULL); // inspecting objects
2906
2907 if ( FollowerMenu.optionSelected == ALLY_CMD_ATTACK_SELECT )
2908 {
2909 Entity* underMouse = nullptr;
2910 if ( FollowerMenu.optionSelected == ALLY_CMD_ATTACK_SELECT && ticks % 10 == 0 )
2911 {
2912 if ( !shootmode )
2913 {
2914 Uint32 uidnum = GO_GetPixelU32(omousex, yres - omousey, cameras[PLAYER_NUM]);
2915 if ( uidnum > 0 )
2916 {
2917 underMouse = uidToEntity(uidnum);
2918 }
2919 }
2920 else
2921 {
2922 Uint32 uidnum = GO_GetPixelU32(xres / 2, yres / 2, cameras[PLAYER_NUM]);
2923 if ( uidnum > 0 )
2924 {
2925 underMouse = uidToEntity(uidnum);
2926 }
2927 }
2928
2929 if ( underMouse && FollowerMenu.followerToCommand )
2930 {
2931 Entity* parent = uidToEntity(underMouse->skill[2]);
2932 if ( underMouse->behavior == &actMonster || (parent && parent->behavior == &actMonster) )
2933 {
2934 // see if we selected a limb
2935 if ( parent )
2936 {
2937 underMouse = parent;
2938 }
2939 }
2940 FollowerMenu.allowedInteractEntity(*underMouse);
2941 }
2942 else
2943 {
2944 strcpy(FollowerMenu.interactText, "");
2945 }
2946 }
2947 }
2948
2949 if ( FollowerMenu.followerToCommand == nullptr && FollowerMenu.selectMoveTo == false )
2950 {
2951 bool clickedOnGUI = false;
2952 selectedEntity = entityClicked(&clickedOnGUI, false, PLAYER_NUM); // using objects
2953 if ( !selectedEntity && !clickedOnGUI )
2954 {
2955 // otherwise if we hold right click we'll keep trying this function, FPS will drop.
2956 ++selectedEntityGimpTimer;
2957 }
2958 }
2959 else
2960 {
2961 selectedEntity = NULL;
2962
2963 if ( !command && (*inputPressed(impulses[IN_USE]) || *inputPressed(joyimpulses[INJOY_GAME_USE])) )
2964 {
2965 if ( !FollowerMenu.menuToggleClick && FollowerMenu.selectMoveTo )
2966 {
2967 if ( FollowerMenu.optionSelected == ALLY_CMD_MOVETO_SELECT )
2968 {
2969 // we're selecting a point for the ally to move to.
2970 *inputPressed(impulses[IN_USE]) = 0;
2971 *inputPressed(joyimpulses[INJOY_GAME_USE]) = 0;
2972
2973 int minimapTotalScale = minimapScaleQuickToggle + minimapScale;
2974 if ( map.height > 64 || map.width > 64 )
2975 {
2976 int maxDimension = std::max(map.height, map.width);
2977 maxDimension -= 64;
2978 int numMinimapSizesToReduce = 0;
2979 while ( maxDimension > 0 )
2980 {
2981 maxDimension -= 32;
2982 ++numMinimapSizesToReduce;
2983 }
2984 minimapTotalScale = std::max(1, minimapScale - numMinimapSizesToReduce) + minimapScaleQuickToggle;
2985 }
2986 if ( !shootmode && mouseInBounds(xres - map.width * minimapTotalScale, xres, yres - map.height * minimapTotalScale, yres) ) // mouse within minimap pixels (each map tile is 4 pixels)
2987 {
2988 MinimapPing newPing(ticks, -1, (omousex - (xres - map.width * minimapTotalScale)) / minimapTotalScale, (omousey - (yres - map.height * minimapTotalScale)) / minimapTotalScale);
2989 minimapPingAdd(newPing);
2990 createParticleFollowerCommand(newPing.x, newPing.y, 0, 174);
2991 FollowerMenu.optionSelected = ALLY_CMD_MOVETO_CONFIRM;
2992 FollowerMenu.selectMoveTo = false;
2993 FollowerMenu.moveToX = static_cast<int>(newPing.x);
2994 FollowerMenu.moveToY = static_cast<int>(newPing.y);
2995 }
2996 else if ( players[PLAYER_NUM] && players[PLAYER_NUM]->entity )
2997 {
2998 real_t startx = players[PLAYER_NUM]->entity->x;
2999 real_t starty = players[PLAYER_NUM]->entity->y;
3000 real_t startz = -4;
3001 real_t pitch = players[PLAYER_NUM]->entity->pitch;
3002 if ( pitch < 0 )
3003 {
3004 pitch = 0;
3005 }
3006 // draw line from the players height and direction until we hit the ground.
3007 real_t previousx = startx;
3008 real_t previousy = starty;
3009 int index = 0;
3010 for ( ; startz < 0.f; startz += abs(0.05 * tan(pitch)) )
3011 {
3012 startx += 0.1 * cos(players[PLAYER_NUM]->entity->yaw);
3013 starty += 0.1 * sin(players[PLAYER_NUM]->entity->yaw);
3014 index = (static_cast<int>(starty + 16 * sin(players[PLAYER_NUM]->entity->yaw)) >> 4) * MAPLAYERS + (static_cast<int>(startx + 16 * cos(players[PLAYER_NUM]->entity->yaw)) >> 4) * MAPLAYERS * map.height;
3015 if ( map.tiles[index] && !map.tiles[OBSTACLELAYER + index] )
3016 {
3017 // store the last known good coordinate
3018 previousx = startx;
3019 previousy = starty;
3020 }
3021 if ( map.tiles[OBSTACLELAYER + index] )
3022 {
3023 break;
3024 }
3025 }
3026
3027 createParticleFollowerCommand(previousx, previousy, 0, 174);
3028 FollowerMenu.optionSelected = ALLY_CMD_MOVETO_CONFIRM;
3029 FollowerMenu.selectMoveTo = false;
3030 FollowerMenu.moveToX = static_cast<int>(previousx) / 16;
3031 FollowerMenu.moveToY = static_cast<int>(previousy) / 16;
3032 //messagePlayer(PLAYER_NUM, "%d, %d, pitch: %f", followerMoveToX, followerMoveToY, players[PLAYER_NUM]->entity->pitch);
3033 }
3034 }
3035 else if ( FollowerMenu.optionSelected == ALLY_CMD_ATTACK_SELECT )
3036 {
3037 // we're selecting a target for the ally.
3038 Entity* target = entityClicked(nullptr, false, PLAYER_NUM);
3039 *inputPressed(impulses[IN_USE]) = 0;
3040 *inputPressed(joyimpulses[INJOY_GAME_USE]) = 0;
3041 if ( target )
3042 {
3043 Entity* parent = uidToEntity(target->skill[2]);
3044 if ( target->behavior == &actMonster || (parent && parent->behavior == &actMonster) )
3045 {
3046 // see if we selected a limb
3047 if ( parent )
3048 {
3049 target = parent;
3050 }
3051 }
3052 else if ( target->sprite == 184 ) // switch base.
3053 {
3054 parent = uidToEntity(target->parent);
3055 if ( parent )
3056 {
3057 target = parent;
3058 }
3059 }
3060 if ( FollowerMenu.allowedInteractEntity(*target) )
3061 {
3062 createParticleFollowerCommand(target->x, target->y, 0, 174);
3063 FollowerMenu.optionSelected = ALLY_CMD_ATTACK_CONFIRM;
3064 FollowerMenu.followerToCommand->monsterAllyInteractTarget = target->getUID();
3065 }
3066 else
3067 {
3068 messagePlayer(clientnum, language[3094]);
3069 FollowerMenu.optionSelected = ALLY_CMD_CANCEL;
3070 FollowerMenu.optionPrevious = ALLY_CMD_ATTACK_CONFIRM;
3071 FollowerMenu.followerToCommand->monsterAllyInteractTarget = 0;
3072 }
3073 }
3074 else
3075 {
3076 FollowerMenu.optionSelected = ALLY_CMD_CANCEL;
3077 FollowerMenu.optionPrevious = ALLY_CMD_ATTACK_CONFIRM;
3078 FollowerMenu.followerToCommand->monsterAllyInteractTarget = 0;
3079 }
3080 FollowerMenu.selectMoveTo = false;
3081 strcpy(FollowerMenu.interactText, "");
3082 }
3083 }
3084 }
3085 }
3086
3087 if ( !command && !FollowerMenu.followerToCommand && FollowerMenu.recentEntity )
3088 {
3089 if ( *inputPressed(impulses[IN_FOLLOWERMENU]) || *inputPressed(joyimpulses[INJOY_GAME_FOLLOWERMENU]) )
3090 {
3091 if ( players[PLAYER_NUM] && players[PLAYER_NUM]->entity
3092 && FollowerMenu.recentEntity->monsterTarget == players[PLAYER_NUM]->entity->getUID() )
3093 {
3094 // your ally is angry at you!
3095 }
3096 else
3097 {
3098 selectedEntity = FollowerMenu.recentEntity;
3099 FollowerMenu.holdWheel = true;
3100 }
3101 }
3102 else if ( *inputPressed(impulses[IN_FOLLOWERMENU_LASTCMD]) || *inputPressed(joyimpulses[INJOY_GAME_FOLLOWERMENU_LASTCMD]) )
3103 {
3104 if ( players[PLAYER_NUM] && players[PLAYER_NUM]->entity
3105 && FollowerMenu.recentEntity->monsterTarget == players[PLAYER_NUM]->entity->getUID() )
3106 {
3107 // your ally is angry at you!
3108 *inputPressed(impulses[IN_FOLLOWERMENU_LASTCMD]) = 0;
3109 *inputPressed(joyimpulses[INJOY_GAME_FOLLOWERMENU_LASTCMD]) = 0;
3110 }
3111 else if ( FollowerMenu.optionPrevious != -1 )
3112 {
3113 FollowerMenu.followerToCommand = FollowerMenu.recentEntity;
3114 }
3115 else
3116 {
3117 *inputPressed(impulses[IN_FOLLOWERMENU_LASTCMD]) = 0;
3118 *inputPressed(joyimpulses[INJOY_GAME_FOLLOWERMENU_LASTCMD]) = 0;
3119 }
3120 }
3121 }
3122 if ( selectedEntity != NULL )
3123 {
3124 FollowerMenu.followerToCommand = nullptr;
3125 Entity* parent = uidToEntity(selectedEntity->skill[2]);
3126 if ( selectedEntity->behavior == &actMonster || (parent && parent->behavior == &actMonster) )
3127 {
3128 // see if we selected a follower to process right click menu.
3129 if ( parent && parent->monsterAllyIndex == PLAYER_NUM )
3130 {
3131 FollowerMenu.followerToCommand = parent;
3132 //messagePlayer(0, "limb");
3133 }
3134 else if ( selectedEntity->monsterAllyIndex == PLAYER_NUM )
3135 {
3136 FollowerMenu.followerToCommand = selectedEntity;
3137 //messagePlayer(0, "head");
3138 }
3139
3140 if ( FollowerMenu.followerToCommand )
3141 {
3142 if ( players[PLAYER_NUM] && players[PLAYER_NUM]->entity
3143 && FollowerMenu.followerToCommand->monsterTarget == players[PLAYER_NUM]->entity->getUID() )
3144 {
3145 // your ally is angry at you!
3146 FollowerMenu.followerToCommand = nullptr;
3147 FollowerMenu.optionPrevious = -1;
3148 }
3149 else
3150 {
3151 FollowerMenu.recentEntity = FollowerMenu.followerToCommand;
3152 FollowerMenu.initFollowerMenuGUICursor(true);
3153 FollowerMenu.updateScrollPartySheet();
3154 selectedEntity = NULL;
3155 }
3156 }
3157 }
3158 if ( selectedEntity )
3159 {
3160 *inputPressed(impulses[IN_USE]) = 0;
3161 *inputPressed(joyimpulses[INJOY_GAME_USE]) = 0;
3162 bool foundTinkeringKit = false;
3163 if ( entityDist(my, selectedEntity) <= TOUCHRANGE )
3164 {
3165 inrange[PLAYER_NUM] = true;
3166
3167 if ( (selectedEntity->behavior == &actItem
3168 || selectedEntity->behavior == &actTorch
3169 || selectedEntity->behavior == &actCrystalShard)
3170 && stats[clientnum] && stats[clientnum]->shield && stats[clientnum]->defending
3171 && stats[clientnum]->shield->type == TOOL_TINKERING_KIT )
3172 {
3173 foundTinkeringKit = true;
3174 }
3175 if ( foundTinkeringKit && (clientnum == 0 || splitscreen) )
3176 {
3177 selectedEntity->itemAutoSalvageByPlayer = static_cast<Sint32>(players[PLAYER_NUM]->entity->getUID());
3178 }
3179 }
3180 else
3181 {
3182 inrange[PLAYER_NUM] = false;
3183 }
3184 if ( multiplayer == CLIENT )
3185 {
3186 if ( inrange[PLAYER_NUM] )
3187 {
3188 if ( foundTinkeringKit )
3189 {
3190 strcpy((char*)net_packet->data, "SALV");
3191 }
3192 else
3193 {
3194 strcpy((char*)net_packet->data, "CKIR");
3195 if ( stats[PLAYER_NUM]->type == RAT
3196 && selectedEntity->behavior == &actItem
3197 && selectedEntity->itemShowOnMap == 1 )
3198 {
3199 strcpy((char*)net_packet->data, "RATF");
3200 }
3201 }
3202 }
3203 else
3204 {
3205 strcpy((char*)net_packet->data, "CKOR");
3206 }
3207 net_packet->data[4] = PLAYER_NUM;
3208 if (selectedEntity->behavior == &actPlayerLimb)
3209 {
3210 SDLNet_Write32((Uint32)players[selectedEntity->skill[2]]->entity->getUID(), &net_packet->data[5]);
3211 }
3212 else
3213 {
3214 Entity* tempEntity = uidToEntity(selectedEntity->skill[2]);
3215 if (tempEntity)
3216 {
3217 if (tempEntity->behavior == &actMonster)
3218 {
3219 SDLNet_Write32((Uint32)tempEntity->getUID(), &net_packet->data[5]);
3220 }
3221 else
3222 {
3223 SDLNet_Write32((Uint32)selectedEntity->getUID(), &net_packet->data[5]);
3224 }
3225 }
3226 else
3227 {
3228 SDLNet_Write32((Uint32)selectedEntity->getUID(), &net_packet->data[5]);
3229 }
3230 }
3231 net_packet->address.host = net_server.host;
3232 net_packet->address.port = net_server.port;
3233 net_packet->len = 9;
3234 sendPacketSafe(net_sock, -1, net_packet, 0);
3235 }
3236 }
3237 }
3238 }
3239 }
3240
3241 if (multiplayer != CLIENT)
3242 {
3243 for (i = 0; i < MAXPLAYERS; i++)
3244 {
3245 if ((i == 0 && selectedEntity == my) || (client_selected[i] == my) || i == PLAYER_CLICKED - 1)
3246 {
3247 PLAYER_CLICKED = 0;
3248 if (inrange[i] && i != PLAYER_NUM)
3249 {
3250 messagePlayer(i, language[575], stats[PLAYER_NUM]->name, stats[PLAYER_NUM]->HP, stats[PLAYER_NUM]->MAXHP, stats[PLAYER_NUM]->MP, stats[PLAYER_NUM]->MAXMP);
3251 messagePlayer(PLAYER_NUM, language[576], stats[i]->name);
3252 if (PLAYER_NUM == clientnum && players[i] && players[i]->entity)
3253 {
3254 double tangent = atan2(my->y - players[i]->entity->y, my->x - players[i]->entity->x);
3255 PLAYER_VELX += cos(tangent);
3256 PLAYER_VELY += sin(tangent);
3257 }
3258 }
3259 }
3260 }
3261 }
3262
3263 // torch light
3264 if ( !intro )
3265 {
3266 if ( multiplayer == SERVER || PLAYER_NUM == clientnum || splitscreen )
3267 {
3268 if ( stats[PLAYER_NUM]->shield != NULL && (showEquipment && isHumanoid) && !itemTypeIsQuiver(stats[PLAYER_NUM]->shield->type) )
3269 {
3270 if ( PLAYER_NUM == clientnum || splitscreen)
3271 {
3272 if ( stats[PLAYER_NUM]->shield->type == TOOL_TORCH )
3273 {
3274 PLAYER_TORCH = 7 + my->getPER() / 3 + (stats[PLAYER_NUM]->defending) * 1;
3275 }
3276 else if ( stats[PLAYER_NUM]->shield->type == TOOL_LANTERN )
3277 {
3278 PLAYER_TORCH = 10 + my->getPER() / 3 + (stats[PLAYER_NUM]->defending) * 1;
3279 }
3280 else if ( stats[PLAYER_NUM]->shield->type == TOOL_CRYSTALSHARD )
3281 {
3282 PLAYER_TORCH = 5 + my->getPER() / 3 + (stats[PLAYER_NUM]->defending) * 2;
3283 }
3284 else if ( !PLAYER_DEBUGCAM )
3285 {
3286 PLAYER_TORCH = 3 + my->getPER() / 3;
3287 }
3288 else
3289 {
3290 PLAYER_TORCH = 0;
3291 }
3292 }
3293 else
3294 {
3295 if ( stats[PLAYER_NUM]->shield->type == TOOL_TORCH )
3296 {
3297 PLAYER_TORCH = 7;
3298 }
3299 else if ( stats[PLAYER_NUM]->shield->type == TOOL_LANTERN )
3300 {
3301 PLAYER_TORCH = 10;
3302 }
3303 else if ( stats[PLAYER_NUM]->shield->type == TOOL_CRYSTALSHARD )
3304 {
3305 PLAYER_TORCH = 5;
3306 }
3307 else
3308 {
3309 PLAYER_TORCH = 0;
3310 }
3311 }
3312 }
3313 else
3314 {
3315 if ( (PLAYER_NUM == clientnum || splitscreen) && !PLAYER_DEBUGCAM )
3316 {
3317 PLAYER_TORCH = 3 + (my->getPER() / 3);
3318 if ( playerRace == RAT )
3319 {
3320 PLAYER_TORCH += 3;
3321 }
3322 else if ( playerRace == SPIDER )
3323 {
3324 PLAYER_TORCH += 2;
3325 }
3326 // more visible world if defending/sneaking with no shield
3327 PLAYER_TORCH += ((stats[PLAYER_NUM]->sneaking == 1) * (2 + (stats[PLAYER_NUM]->PROFICIENCIES[PRO_STEALTH] / 40)));
3328 }
3329 else
3330 {
3331 PLAYER_TORCH = 0;
3332 }
3333 }
3334 }
3335 }
3336 else
3337 {
3338 PLAYER_TORCH = 0;
3339 }
3340
3341 my->removeLightField();
3342
3343 if ( PLAYER_TORCH && my->light == NULL )
3344 {
3345 my->light = lightSphereShadow(my->x / 16, my->y / 16, PLAYER_TORCH, 50 + 15 * PLAYER_TORCH);
3346 }
3347
3348 // server controls players primarily
3349 if ( PLAYER_NUM == clientnum || multiplayer == SERVER || splitscreen )
3350 {
3351 // set head model
3352 if ( playerRace != HUMAN )
3353 {
3354 if ( playerRace == SKELETON )
3355 {
3356 my->sprite = 686;
3357 }
3358 else if ( playerRace == RAT )
3359 {
3360 my->sprite = 814;
3361 }
3362 else if ( playerRace == TROLL )
3363 {
3364 my->sprite = 817;
3365 }
3366 else if ( playerRace == SPIDER )
3367 {
3368 my->sprite = 823;
3369 }
3370 else if ( playerRace == CREATURE_IMP )
3371 {
3372 my->sprite = 827;
3373 }
3374 else if ( playerRace == GOBLIN )
3375 {
3376 if ( stats[PLAYER_NUM]->sex == FEMALE )
3377 {
3378 my->sprite = 752;
3379 }
3380 else
3381 {
3382 my->sprite = 694;
3383 }
3384 }
3385 else if ( playerRace == INCUBUS )
3386 {
3387 my->sprite = 702;
3388 }
3389 else if ( playerRace == SUCCUBUS )
3390 {
3391 my->sprite = 710;
3392 }
3393 else if ( playerRace == VAMPIRE )
3394 {
3395 if ( stats[PLAYER_NUM]->sex == FEMALE )
3396 {
3397 my->sprite = 756;
3398 }
3399 else
3400 {
3401 my->sprite = 718;
3402 }
3403 }
3404 else if ( playerRace == INSECTOID )
3405 {
3406 if ( stats[PLAYER_NUM]->sex == FEMALE )
3407 {
3408 my->sprite = 760;
3409 }
3410 else
3411 {
3412 my->sprite = 726;
3413 }
3414 }
3415 else if ( playerRace == GOATMAN )
3416 {
3417 if ( stats[PLAYER_NUM]->sex == FEMALE )
3418 {
3419 my->sprite = 768;
3420 }
3421 else
3422 {
3423 my->sprite = 734;
3424 }
3425 }
3426 else if ( playerRace == AUTOMATON )
3427 {
3428 if ( stats[PLAYER_NUM]->sex == FEMALE )
3429 {
3430 my->sprite = 770;
3431 }
3432 else
3433 {
3434 my->sprite = 742;
3435 }
3436 }
3437 }
3438 else if ( playerAppearance < 5 )
3439 {
3440 my->sprite = 113 + 12 * stats[PLAYER_NUM]->sex + playerAppearance;
3441 }
3442 else if ( playerAppearance == 5 )
3443 {
3444 my->sprite = 332 + stats[PLAYER_NUM]->sex;
3445 }
3446 else if ( playerAppearance >= 6 && playerAppearance < 12 )
3447 {
3448 my->sprite = 341 + stats[PLAYER_NUM]->sex * 13 + playerAppearance - 6;
3449 }
3450 else if ( playerAppearance >= 12 )
3451 {
3452 my->sprite = 367 + stats[PLAYER_NUM]->sex * 13 + playerAppearance - 12;
3453 }
3454 else
3455 {
3456 my->sprite = 113; // default
3457 }
3458 }
3459 if ( multiplayer != CLIENT )
3460 {
3461 // remove client entities that should no longer exist
3462 if ( !intro )
3463 {
3464 my->handleEffects(stats[PLAYER_NUM]); // hunger, regaining hp/mp, poison, etc.
3465
3466 if ( client_disconnected[PLAYER_NUM] || stats[PLAYER_NUM]->HP <= 0 )
3467 {
3468 bool doDeathProcedure = true;
3469 if ( client_disconnected[PLAYER_NUM] )
3470 {
3471 doDeathProcedure = true;
3472 }
3473 else if ( stats[PLAYER_NUM]->type == AUTOMATON && !client_disconnected[PLAYER_NUM] && stats[PLAYER_NUM]->HP <= 0 )
3474 {
3475 // delay death. (not via disconnection).
3476 if ( PLAYER_DEATH_AUTOMATON == 0 )
3477 {
3478 my->flags[PASSABLE] = true;
3479 serverUpdateEntityFlag(my, PASSABLE);
3480 if ( clientnum == PLAYER_NUM || splitscreen )
3481 {
3482 // deathcam
3483 entity = newEntity(-1, 1, map.entities, nullptr); //Deathcam entity.
3484 entity->x = my->x;
3485 entity->y = my->y;
3486 entity->z = -2;
3487 entity->flags[NOUPDATE] = true;
3488 entity->flags[PASSABLE] = true;
3489 entity->flags[INVISIBLE] = true;
3490 entity->behavior = &actDeathCam;
3491 entity->skill[2] = PLAYER_NUM;
3492 entity->yaw = my->yaw;
3493 entity->pitch = PI / 8;
3494 my->playerCreatedDeathCam = 1;
3495 }
3496 createParticleExplosionCharge(my, 174, 100, 0.25);
3497 serverSpawnMiscParticles(my, PARTICLE_EFFECT_PLAYER_AUTOMATON_DEATH, 174);
3498 playSoundEntity(my, 263, 128);
3499 playSoundEntity(my, 321, 128);
3500 }
3501 ++PLAYER_DEATH_AUTOMATON;
3502 if ( PLAYER_DEATH_AUTOMATON >= TICKS_PER_SECOND * 2 )
3503 {
3504 doDeathProcedure = true;
3505 spawnExplosion(my->x, my->y, my->z);
3506 playSoundEntity(my, 260 + rand() % 2, 128);
3507 my->attack(MONSTER_POSE_AUTOMATON_MALFUNCTION, 0, my);
3508 }
3509 else
3510 {
3511 doDeathProcedure = false;
3512 }
3513 }
3514 if ( doDeathProcedure )
3515 {
3516 // remove body parts
3517 node_t* nextnode;
3518 for ( node = my->children.first, i = 0; node != NULL; node = nextnode, i++ )
3519 {
3520 nextnode = node->next;
3521 if ( i == 0 )
3522 {
3523 continue;
3524 }
3525 if ( node->element )
3526 {
3527 Entity* tempEntity = (Entity*)node->element;
3528 if ( tempEntity )
3529 {
3530 list_RemoveNode(tempEntity->mynode);
3531 }
3532 if ( i > 28 )
3533 {
3534 break;
3535 }
3536 }
3537 }
3538 if ( stats[PLAYER_NUM]->HP <= 0 )
3539 {
3540 // die //TODO: Refactor.
3541 playSoundEntity(my, 28, 128);
3542 for ( i = 0; i < 5; i++ )
3543 {
3544 Entity* gib = spawnGib(my);
3545 serverSpawnGibForClient(gib);
3546 }
3547 if ( spawn_blood )
3548 {
3549 if ( !checkObstacle(my->x, my->y, my, NULL) )
3550 {
3551 int x, y;
3552 x = std::min(std::max<unsigned int>(0, my->x / 16), map.width - 1);
3553 y = std::min(std::max<unsigned int>(0, my->y / 16), map.height - 1);
3554 if ( map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height] )
3555 {
3556 entity = newEntity(160, 1, map.entities, nullptr); //Limb entity.
3557 entity->x = my->x;
3558 entity->y = my->y;
3559 entity->z = 8.0 + (rand() % 20) / 100.0;
3560 entity->parent = my->getUID();
3561 entity->sizex = 2;
3562 entity->sizey = 2;
3563 entity->yaw = (rand() % 360) * PI / 180.0;
3564 entity->flags[UPDATENEEDED] = true;
3565 entity->flags[PASSABLE] = true;
3566 }
3567 }
3568 }
3569 node_t* spellnode;
3570 spellnode = stats[PLAYER_NUM]->magic_effects.first;
3571 while ( spellnode )
3572 {
3573 node_t* oldnode = spellnode;
3574 spellnode = spellnode->next;
3575 spell_t* spell = (spell_t*)oldnode->element;
3576 spell->magic_effects_node = NULL;
3577 list_RemoveNode(oldnode);
3578 }
3579 int c;
3580 for ( c = 0; c < MAXPLAYERS; c++ )
3581 {
3582 if ( client_disconnected[c] )
3583 {
3584 continue;
3585 }
3586 char whatever[256];
3587 snprintf(whatever, 255, "%s %s", stats[PLAYER_NUM]->name, stats[PLAYER_NUM]->obituary); //Potential snprintf of 256 bytes into 255 byte destination
3588 messagePlayer(c, whatever);
3589 }
3590 Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
3591 messagePlayerColor(PLAYER_NUM, color, language[577]);
3592
3593 for ( node_t* node = stats[PLAYER_NUM]->FOLLOWERS.first; node != nullptr; node = nextnode )
3594 {
3595 nextnode = node->next;
3596 Uint32* c = (Uint32*)node->element;
3597 Entity* myFollower = nullptr;
3598 if ( c )
3599 {
3600 myFollower = uidToEntity(*c);
3601 }
3602 if ( myFollower )
3603 {
3604 if ( myFollower->monsterAllySummonRank != 0 )
3605 {
3606 myFollower->setMP(0);
3607 myFollower->setHP(0); // rip
3608 }
3609 else if ( myFollower->flags[USERFLAG2] )
3610 {
3611 // our leader died, let's undo the color change since we're now rabid.
3612 myFollower->flags[USERFLAG2] = false;
3613 serverUpdateEntityFlag(myFollower, USERFLAG2);
3614
3615 int bodypart = 0;
3616 for ( node_t* node = myFollower->children.first; node != nullptr; node = node->next )
3617 {
3618 if ( bodypart >= LIMB_HUMANOID_TORSO )
3619 {
3620 Entity* tmp = (Entity*)node->element;
3621 if ( tmp )
3622 {
3623 tmp->flags[USERFLAG2] = false;
3624 }
3625 }
3626 ++bodypart;
3627 }
3628
3629 Stat* followerStats = myFollower->getStats();
3630 if ( followerStats )
3631 {
3632 followerStats->leader_uid = 0;
3633 }
3634 list_RemoveNode(node);
3635 if ( PLAYER_NUM != clientnum && !splitscreen )
3636 {
3637 serverRemoveClientFollower(PLAYER_NUM, myFollower->getUID());
3638 }
3639 }
3640 }
3641 }
3642
3643 /* //TODO: Eventually.
3644 {
3645 strcpy((char *)net_packet->data,"UDIE");
3646 net_packet->address.host = net_clients[player-1].host;
3647 net_packet->address.port = net_clients[player-1].port;
3648 net_packet->len = 4;
3649 sendPacketSafe(net_sock, -1, net_packet, player-1);
3650 }
3651 */
3652 if ( clientnum == PLAYER_NUM || splitscreen )
3653 {
3654 if ( (stats[PLAYER_NUM]->type != AUTOMATON)
3655 || (stats[PLAYER_NUM]->type == AUTOMATON && my->playerCreatedDeathCam == 0) )
3656 {
3657 // deathcam
3658 entity = newEntity(-1, 1, map.entities, nullptr); //Deathcam entity.
3659 entity->x = my->x;
3660 entity->y = my->y;
3661 entity->z = -2;
3662 entity->flags[NOUPDATE] = true;
3663 entity->flags[PASSABLE] = true;
3664 entity->flags[INVISIBLE] = true;
3665 entity->behavior = &actDeathCam;
3666 entity->skill[2] = PLAYER_NUM;
3667 entity->yaw = my->yaw;
3668 entity->pitch = PI / 8;
3669 }
3670 node_t* nextnode;
3671
3672 if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_DEFAULT )
3673 {
3674 if ( multiplayer == SINGLE )
3675 {
3676 deleteSaveGame(multiplayer); // stops save scumming c:
3677 }
3678 else
3679 {
3680 deleteMultiplayerSaveGames(); //Will only delete save games if was last player alive.
3681 }
3682 }
3683
3684 closeBookGUI();
3685
3686 #ifdef SOUND
3687 levelmusicplaying = true;
3688 combatmusicplaying = false;
3689 fadein_increment = default_fadein_increment * 4;
3690 fadeout_increment = default_fadeout_increment * 4;
3691 if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL )
3692 {
3693 playmusic(tutorialmusic, true, true, true);
3694 }
3695 playmusic(sounds[209], false, true, false);
3696 #endif
3697 combat = false;
3698
3699 if ( PLAYER_NUM == clientnum
3700 && gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL )
3701 {
3702 if ( !strncmp(map.name, "Tutorial Hub", 12) )
3703 {
3704 steamAchievement("BARONY_ACH_EXPELLED");
3705 }
3706 else
3707 {
3708 steamAchievement("BARONY_ACH_TEACHABLE_MOMENT");
3709 }
3710 }
3711
3712 if ( multiplayer == SINGLE || !(svFlags & SV_FLAG_KEEPINVENTORY) )
3713 {
3714 for ( node = stats[PLAYER_NUM]->inventory.first; node != nullptr; node = nextnode )
3715 {
3716 nextnode = node->next;
3717 Item* item = (Item*)node->element;
3718 if ( itemCategory(item) == SPELL_CAT )
3719 {
3720 continue; // don't drop spells on death, stupid!
3721 }
3722 if ( item->type >= ARTIFACT_SWORD && item->type <= ARTIFACT_GLOVES )
3723 {
3724 if ( itemIsEquipped(item, clientnum) )
3725 {
3726 steamAchievement("BARONY_ACH_CHOSEN_ONE");
3727 }
3728 }
3729 int c = item->count;
3730 for ( c = item->count; c > 0; c-- )
3731 {
3732 entity = newEntity(-1, 1, map.entities, nullptr); //Item entity.
3733 entity->flags[INVISIBLE] = true;
3734 entity->flags[UPDATENEEDED] = true;
3735 entity->x = my->x;
3736 entity->y = my->y;
3737 entity->sizex = 4;
3738 entity->sizey = 4;
3739 entity->yaw = (rand() % 360) * (PI / 180.f);
3740 entity->vel_x = (rand() % 20 - 10) / 10.0;
3741 entity->vel_y = (rand() % 20 - 10) / 10.0;
3742 entity->vel_z = -.5;
3743 entity->flags[PASSABLE] = true;
3744 entity->flags[USERFLAG1] = true;
3745 entity->behavior = &actItem;
3746 entity->skill[10] = item->type;
3747 entity->skill[11] = item->status;
3748 entity->skill[12] = item->beatitude;
3749 int qtyToDrop = 1;
3750 if ( c >= 10 && (item->type == TOOL_METAL_SCRAP || item->type == TOOL_MAGIC_SCRAP) )
3751 {
3752 qtyToDrop = 10;
3753 c -= 9;
3754 }
3755 else if ( itemTypeIsQuiver(item->type) )
3756 {
3757 qtyToDrop = item->count;
3758 c -= item->count;
3759 }
3760 entity->skill[13] = qtyToDrop;
3761 entity->skill[14] = item->appearance;
3762 entity->skill[15] = item->identified;
3763 }
3764 }
3765 if ( multiplayer != SINGLE )
3766 {
3767 for ( node = stats[PLAYER_NUM]->inventory.first; node != nullptr; node = nextnode )
3768 {
3769 nextnode = node->next;
3770 Item* item = (Item*)node->element;
3771 if ( itemCategory(item) == SPELL_CAT )
3772 {
3773 continue; // don't drop spells on death, stupid!
3774 }
3775 list_RemoveNode(node);
3776 }
3777 stats[0]->helmet = NULL;
3778 stats[0]->breastplate = NULL;
3779 stats[0]->gloves = NULL;
3780 stats[0]->shoes = NULL;
3781 stats[0]->shield = NULL;
3782 stats[0]->weapon = NULL;
3783 stats[0]->cloak = NULL;
3784 stats[0]->amulet = NULL;
3785 stats[0]->ring = NULL;
3786 stats[0]->mask = NULL;
3787 }
3788 }
3789 else
3790 {
3791 // to not soft lock at Herx
3792 for ( node = stats[PLAYER_NUM]->inventory.first; node != nullptr; node = nextnode )
3793 {
3794 nextnode = node->next;
3795 Item* item = (Item*)node->element;
3796 if ( item->type == ARTIFACT_ORB_PURPLE )
3797 {
3798 int c = item->count;
3799 for ( c = item->count; c > 0; c-- )
3800 {
3801 entity = newEntity(-1, 1, map.entities, nullptr); //Item entity.
3802 entity->flags[INVISIBLE] = true;
3803 entity->flags[UPDATENEEDED] = true;
3804 entity->x = my->x;
3805 entity->y = my->y;
3806 entity->sizex = 4;
3807 entity->sizey = 4;
3808 entity->yaw = (rand() % 360) * (PI / 180.f);
3809 entity->vel_x = (rand() % 20 - 10) / 10.0;
3810 entity->vel_y = (rand() % 20 - 10) / 10.0;
3811 entity->vel_z = -.5;
3812 entity->flags[PASSABLE] = true;
3813 entity->flags[USERFLAG1] = true;
3814 entity->behavior = &actItem;
3815 entity->skill[10] = item->type;
3816 entity->skill[11] = item->status;
3817 entity->skill[12] = item->beatitude;
3818 entity->skill[13] = 1;
3819 entity->skill[14] = item->appearance;
3820 entity->skill[15] = item->identified;
3821 }
3822 break;
3823 }
3824 }
3825 }
3826 for ( node_t* mapNode = map.creatures->first; mapNode != nullptr; mapNode = mapNode->next )
3827 {
3828 Entity* mapCreature = (Entity*)mapNode->element;
3829 if ( mapCreature )
3830 {
3831 mapCreature->monsterEntityRenderAsTelepath = 0; // do a final pass to undo any telepath rendering.
3832 }
3833 }
3834 }
3835 else
3836 {
3837 if ( !(svFlags & SV_FLAG_KEEPINVENTORY) )
3838 {
3839 my->x = ((int)(my->x / 16)) * 16 + 8;
3840 my->y = ((int)(my->y / 16)) * 16 + 8;
3841 item = stats[PLAYER_NUM]->helmet;
3842 if ( item )
3843 {
3844 int c = item->count;
3845 for ( c = item->count; c > 0; c-- )
3846 {
3847 dropItemMonster(item, my, stats[PLAYER_NUM]);
3848 }
3849 }
3850 item = stats[PLAYER_NUM]->breastplate;
3851 if ( item )
3852 {
3853 int c = item->count;
3854 for ( c = item->count; c > 0; c-- )
3855 {
3856 dropItemMonster(item, my, stats[PLAYER_NUM]);
3857 }
3858 }
3859 item = stats[PLAYER_NUM]->gloves;
3860 if ( item )
3861 {
3862 int c = item->count;
3863 for ( c = item->count; c > 0; c-- )
3864 {
3865 dropItemMonster(item, my, stats[PLAYER_NUM]);
3866 }
3867 }
3868 item = stats[PLAYER_NUM]->shoes;
3869 if ( item )
3870 {
3871 int c = item->count;
3872 for ( c = item->count; c > 0; c-- )
3873 {
3874 dropItemMonster(item, my, stats[PLAYER_NUM]);
3875 }
3876 }
3877 item = stats[PLAYER_NUM]->shield;
3878 if ( item )
3879 {
3880 if ( itemTypeIsQuiver(item->type) )
3881 {
3882 dropItemMonster(item, my, stats[PLAYER_NUM], item->count);
3883 }
3884 else
3885 {
3886 int c = item->count;
3887 for ( c = item->count; c > 0; c-- )
3888 {
3889 dropItemMonster(item, my, stats[PLAYER_NUM]);
3890 }
3891 }
3892 }
3893 item = stats[PLAYER_NUM]->weapon;
3894 if ( item )
3895 {
3896 int c = item->count;
3897 for ( c = item->count; c > 0; c-- )
3898 {
3899 dropItemMonster(item, my, stats[PLAYER_NUM]);
3900 }
3901 }
3902 item = stats[PLAYER_NUM]->cloak;
3903 if ( item )
3904 {
3905 int c = item->count;
3906 for ( c = item->count; c > 0; c-- )
3907 {
3908 dropItemMonster(item, my, stats[PLAYER_NUM]);
3909 }
3910 }
3911 item = stats[PLAYER_NUM]->amulet;
3912 if ( item )
3913 {
3914 int c = item->count;
3915 for ( c = item->count; c > 0; c-- )
3916 {
3917 dropItemMonster(item, my, stats[PLAYER_NUM]);
3918 }
3919 }
3920 item = stats[PLAYER_NUM]->ring;
3921 if ( item )
3922 {
3923 int c = item->count;
3924 for ( c = item->count; c > 0; c-- )
3925 {
3926 dropItemMonster(item, my, stats[PLAYER_NUM]);
3927 }
3928 }
3929 item = stats[PLAYER_NUM]->mask;
3930 if ( item )
3931 {
3932 int c = item->count;
3933 for ( c = item->count; c > 0; c-- )
3934 {
3935 dropItemMonster(item, my, stats[PLAYER_NUM]);
3936 }
3937 }
3938 list_FreeAll(&stats[PLAYER_NUM]->inventory);
3939 }
3940
3941 deleteMultiplayerSaveGames(); //Will only delete save games if was last player alive.
3942 }
3943
3944 assailant[PLAYER_NUM] = false;
3945 assailantTimer[PLAYER_NUM] = 0;
3946
3947 if ( multiplayer != SINGLE )
3948 {
3949 messagePlayer(PLAYER_NUM, language[578]);
3950 }
3951 }
3952 my->removeLightField();
3953 list_RemoveNode(my->mynode);
3954 return;
3955 }
3956 }
3957 else
3958 {
3959 my->playerCreatedDeathCam = 0;
3960 PLAYER_DEATH_AUTOMATON = 0;
3961 }
3962 }
3963 }
3964
3965 if ( (PLAYER_NUM == clientnum || splitscreen) && intro == false )
3966 {
3967 // effects of drunkenness
3968 if ( (stats[PLAYER_NUM]->EFFECTS[EFF_DRUNK] && (stats[PLAYER_NUM]->type != GOATMAN))
3969 || stats[PLAYER_NUM]->EFFECTS[EFF_WITHDRAWAL] )
3970 {
3971 CHAR_DRUNK++;
3972 int drunkInterval = 180;
3973 if ( stats[PLAYER_NUM]->EFFECTS[EFF_WITHDRAWAL] )
3974 {
3975 if ( PLAYER_ALIVETIME < 800 )
3976 {
3977 drunkInterval = 300;
3978 }
3979 else
3980 {
3981 drunkInterval = TICKS_PER_SECOND * 30;
3982 }
3983 }
3984
3985 if ( CHAR_DRUNK >= drunkInterval )
3986 {
3987 CHAR_DRUNK = 0;
3988 messagePlayer(PLAYER_NUM, language[579]);
3989 cameravars[PLAYER_NUM].shakex -= .04;
3990 cameravars[PLAYER_NUM].shakey -= 5;
3991 }
3992 }
3993
3994 if ( !my->isMobile() )
3995 {
3996 if ( (clientnum == PLAYER_NUM || splitscreen) && openedChest[PLAYER_NUM] )
3997 {
3998 openedChest[PLAYER_NUM]->closeChest();
3999 }
4000 }
4001
4002 if ( PLAYER_DEATH_AUTOMATON > 0 )
4003 {
4004 if ( my->pitch < PI / 3 || my->pitch > 5 * PI / 3 )
4005 {
4006 limbAnimateToLimit(my, ANIMATE_PITCH, 0.01, PI / 3, true, 0.005);
4007 }
4008 }
4009
4010 if ( !usecamerasmoothing )
4011 {
4012 handlePlayerMovement(my, PLAYER_NUM, false);
4013 handlePlayerCameraUpdate(my, PLAYER_NUM, false);
4014 }
4015
4016 // send movement updates to server
4017 if ( multiplayer == CLIENT )
4018 {
4019 strcpy((char*)net_packet->data, "PMOV");
4020 net_packet->data[4] = clientnum;
4021 net_packet->data[5] = currentlevel;
4022 SDLNet_Write16((Sint16)(my->x * 32), &net_packet->data[6]);
4023 SDLNet_Write16((Sint16)(my->y * 32), &net_packet->data[8]);
4024 SDLNet_Write16((Sint16)(PLAYER_VELX * 128), &net_packet->data[10]);
4025 SDLNet_Write16((Sint16)(PLAYER_VELY * 128), &net_packet->data[12]);
4026 SDLNet_Write16((Sint16)(my->yaw * 128), &net_packet->data[14]);
4027 SDLNet_Write16((Sint16)(my->pitch * 128), &net_packet->data[16]);
4028 net_packet->data[18] = secretlevel;
4029 net_packet->address.host = net_server.host;
4030 net_packet->address.port = net_server.port;
4031 net_packet->len = 19;
4032 sendPacket(net_sock, -1, net_packet, 0);
4033 }
4034
4035 // move
4036 if ( noclip == false )
4037 {
4038 // perform collision detection
4039 dist = clipMove(&my->x, &my->y, PLAYER_VELX, PLAYER_VELY, my);
4040
4041 // bumping into monsters disturbs them
4042 if ( hit.entity && !everybodyfriendly && multiplayer != CLIENT )
4043 {
4044 if ( hit.entity->behavior == &actMonster )
4045 {
4046 bool enemy = my->checkEnemy(hit.entity);
4047 if ( enemy )
4048 {
4049 if ( hit.entity->monsterState == MONSTER_STATE_WAIT || (hit.entity->monsterState == MONSTER_STATE_HUNT && hit.entity->monsterTarget == 0) )
4050 {
4051 double tangent = atan2( my->y - hit.entity->y, my->x - hit.entity->x );
4052 hit.entity->skill[4] = 1;
4053 hit.entity->skill[6] = rand() % 10 + 1;
4054 hit.entity->fskill[4] = tangent;
4055 }
4056 }
4057 }
4058 else if ( stats[PLAYER_NUM]->EFFECTS[EFF_DASH] && hit.entity->behavior == &actDoor )
4059 {
4060 hit.entity->doorHealth = 0;
4061 }
4062 }
4063 }
4064 else
4065 {
4066 // no collision detection
4067 my->x += PLAYER_VELX;
4068 my->y += PLAYER_VELY;
4069 dist = sqrt(PLAYER_VELX * PLAYER_VELX + PLAYER_VELY * PLAYER_VELY);
4070 }
4071 }
4072
4073 if ( PLAYER_NUM != clientnum && multiplayer == SERVER )
4074 {
4075 // PLAYER_VEL* skills updated by messages sent to server from client
4076
4077 // move (dead reckoning)
4078 if ( noclip == false )
4079 {
4080 // from PMOV in serverHandlePacket - new_x and new_y are accumulated positions
4081 if ( my->new_x > 0.001 )
4082 {
4083 my->x = my->new_x;
4084 }
4085 if ( my->new_y > 0.001 )
4086 {
4087 my->y = my->new_y;
4088 }
4089
4090 dist = clipMove(&my->x, &my->y, PLAYER_VELX, PLAYER_VELY, my);
4091
4092 // bumping into monsters disturbs them
4093 if ( hit.entity )
4094 {
4095 if ( hit.entity->behavior == &actMonster )
4096 {
4097 bool enemy = my->checkEnemy(hit.entity);
4098 if ( enemy )
4099 {
4100 if ( hit.entity->monsterState == MONSTER_STATE_WAIT || (hit.entity->monsterState == MONSTER_STATE_HUNT && hit.entity->monsterTarget == 0) )
4101 {
4102 double tangent = atan2( my->y - hit.entity->y, my->x - hit.entity->x );
4103 hit.entity->skill[4] = 1;
4104 hit.entity->skill[6] = rand() % 10 + 1;
4105 hit.entity->fskill[4] = tangent;
4106 }
4107 }
4108 }
4109 else if ( stats[PLAYER_NUM]->EFFECTS[EFF_DASH] && hit.entity->behavior == &actDoor )
4110 {
4111 hit.entity->doorHealth = 0;
4112 }
4113 }
4114 }
4115 else
4116 {
4117 my->x += PLAYER_VELX;
4118 my->y += PLAYER_VELY;
4119 dist = sqrt(PLAYER_VELX * PLAYER_VELX + PLAYER_VELY * PLAYER_VELY);
4120 }
4121 }
4122
4123 if ( PLAYER_NUM != clientnum && multiplayer == CLIENT )
4124 {
4125 dist = sqrt(PLAYER_VELX * PLAYER_VELX + PLAYER_VELY * PLAYER_VELY);
4126 }
4127
4128 if ( (PLAYER_NUM == clientnum || splitscreen) && ticks % 65 == 0 )
4129 {
4130 for ( node_t* mapNode = map.creatures->first; mapNode != nullptr; mapNode = mapNode->next )
4131 {
4132 Entity* mapCreature = (Entity*)mapNode->element;
4133 if ( mapCreature )
4134 {
4135 if ( stats[PLAYER_NUM]->EFFECTS[EFF_TELEPATH] )
4136 {
4137 // periodically set the telepath rendering flag.
4138 mapCreature->monsterEntityRenderAsTelepath = 1;
4139 }
4140 else
4141 {
4142 mapCreature->monsterEntityRenderAsTelepath = 0;
4143 }
4144 }
4145 }
4146 }
4147
4148 Entity* helmet = nullptr;
4149
4150 // move bodyparts
4151 if ( isHumanoid )
4152 {
4153 for ( bodypart = 0, node = my->children.first; node != NULL; node = node->next, bodypart++ )
4154 {
4155 if ( bodypart == 0 )
4156 {
4157 // hudweapon case
4158 continue;
4159 }
4160 entity = (Entity*)node->element;
4161 entity->x = my->x;
4162 entity->y = my->y;
4163 entity->z = my->z;
4164
4165 if ( bodypart < 9 ) // don't shift helm/mask.
4166 {
4167 // these monsters are shorter than humans so extend the limbs down to floor, gives longer neck.
4168 if ( playerRace == GOBLIN || playerRace == INSECTOID || playerRace == GOATMAN )
4169 {
4170 entity->z += 0.5;
4171 }
4172 else if ( playerRace == SKELETON || playerRace == AUTOMATON )
4173 {
4174 entity->z += 0.25;
4175 }
4176 }
4177
4178 if ( bodypart > 12 )
4179 {
4180 entity->flags[INVISIBLE] = true;
4181 continue;
4182 }
4183
4184 entity->yaw = my->yaw;
4185 if ( bodypart == 2 || bodypart == 5 ) // right leg, left arm
4186 {
4187 if ( bodypart == 2 )
4188 {
4189 rightbody = (Entity*)node->next->element;
4190 }
4191 if ( bodypart == 5 )
4192 {
4193 shieldarm = entity;
4194 }
4195 double limbSpeed = dist;
4196 double pitchLimit = PI / 4.f;
4197 if ( playerRace == CREATURE_IMP )
4198 {
4199 limbSpeed = 1 / 12.f;
4200 pitchLimit = PI / 8.f;
4201 }
4202 if ( stats[PLAYER_NUM]->EFFECTS[EFF_DASH] )
4203 {
4204 limbSpeed = 1 / 12.f;
4205 }
4206 node_t* shieldNode = list_Node(&my->children, 7);
4207 if ( shieldNode )
4208 {
4209 Entity* shield = (Entity*)shieldNode->element;
4210 bool bendArm = true;
4211 if ( shield->flags[INVISIBLE] )
4212 {
4213 bendArm = false;
4214 }
4215 else if ( shield->sprite >= items[SPELLBOOK_LIGHT].index
4216 && shield->sprite < (items[SPELLBOOK_LIGHT].index + items[SPELLBOOK_LIGHT].variations) )
4217 {
4218 bendArm = false;
4219 }
4220
4221 if ( insectoidLevitating[PLAYER_NUM] )
4222 {
4223 // hands stationary, legs pitched back and little swing.
4224 limbSpeed = 0.03;
4225 if ( bodypart == 5 ) // left arm
4226 {
4227 if ( entity->pitch < 0 )
4228 {
4229 entity->pitch += 1 / fmax(limbSpeed * .1, 10.0);
4230 if ( entity->pitch > 0 )
4231 {
4232 entity->pitch = 0;
4233 }
4234 }
4235 else if ( entity->pitch > 0 )
4236 {
4237 entity->pitch -= 1 / fmax(limbSpeed * .1, 10.0);
4238 if ( entity->pitch < 0 )
4239 {
4240 entity->pitch = 0;
4241 }
4242 }
4243 }
4244 else if ( bodypart == 2 )
4245 {
4246 if ( entity->pitch < 0 )
4247 {
4248 entity->pitch += 5 * limbSpeed * PLAYERWALKSPEED; // speed up to reach target.
4249 }
4250 if ( !rightbody->skill[3] )
4251 {
4252 entity->pitch -= limbSpeed * PLAYERWALKSPEED;
4253 if ( entity->pitch < PI / 6.f )
4254 {
4255 entity->pitch = PI / 6.f;
4256 }
4257 }
4258 else
4259 {
4260 entity->pitch += limbSpeed * PLAYERWALKSPEED;
4261 if ( entity->pitch > PI / 3.f )
4262 {
4263 entity->pitch = PI / 3.f;
4264 }
4265 }
4266 }
4267 }
4268 else if ( (fabs(PLAYER_VELX) > 0.1 || fabs(PLAYER_VELY) > 0.1) && (bodypart != 5 || !bendArm)
4269 || playerRace == CREATURE_IMP )
4270 {
4271 if ( oldInsectoidLevitate )
4272 {
4273 entity->pitch = 0;
4274 }
4275
4276 if ( !rightbody->skill[0] )
4277 {
4278 entity->pitch -= limbSpeed * PLAYERWALKSPEED;
4279 if ( entity->pitch < -pitchLimit )
4280 {
4281 entity->pitch = -pitchLimit;
4282 if ( bodypart == 2 && dist > .4 && !levitating && !swimming )
4283 {
4284 node_t* tempNode = list_Node(&my->children, 2);
4285 if ( tempNode )
4286 {
4287 Entity* foot = (Entity*)tempNode->element;
4288 if ( playerRace == TROLL )
4289 {
4290 playSoundEntityLocal(my, my->getMonsterFootstepSound(MONSTER_FOOTSTEP_STOMP, foot->sprite), 32);
4291 }
4292 else if ( playerRace == SPIDER || playerRace == RAT || playerRace == CREATURE_IMP )
4293 {
4294 // no sound.
4295 }
4296 else
4297 {
4298 playSoundEntityLocal(my, my->getMonsterFootstepSound(MONSTER_FOOTSTEP_USE_BOOTS, foot->sprite), 32);
4299 }
4300 }
4301 }
4302 }
4303 }
4304 else
4305 {
4306 entity->pitch += limbSpeed * PLAYERWALKSPEED;
4307 if ( entity->pitch > pitchLimit )
4308 {
4309 entity->pitch = pitchLimit;
4310 if ( bodypart == 2 && dist > .4 && !levitating && !swimming )
4311 {
4312 node_t* tempNode = list_Node(&my->children, 2);
4313 if ( tempNode )
4314 {
4315 Entity* foot = (Entity*)tempNode->element;
4316 if ( playerRace == TROLL )
4317 {
4318 playSoundEntityLocal(my, my->getMonsterFootstepSound(MONSTER_FOOTSTEP_STOMP, foot->sprite), 32);
4319 }
4320 else if ( playerRace == SPIDER || playerRace == RAT || playerRace == CREATURE_IMP )
4321 {
4322 // no sound.
4323 }
4324 else
4325 {
4326 playSoundEntityLocal(my, my->getMonsterFootstepSound(MONSTER_FOOTSTEP_USE_BOOTS, foot->sprite), 32);
4327 }
4328 }
4329 }
4330 }
4331 }
4332 }
4333 else
4334 {
4335 if ( entity->pitch < 0 )
4336 {
4337 entity->pitch += 1 / fmax(limbSpeed * .1, 10.0);
4338 if ( entity->pitch > 0 )
4339 {
4340 entity->pitch = 0;
4341 }
4342 }
4343 else if ( entity->pitch > 0 )
4344 {
4345 entity->pitch -= 1 / fmax(limbSpeed * .1, 10.0);
4346 if ( entity->pitch < 0 )
4347 {
4348 entity->pitch = 0;
4349 }
4350 }
4351 }
4352 }
4353 }
4354 else if ( bodypart == 3 || bodypart == 4 || bodypart == 8 ) // left leg, right arm, cloak
4355 {
4356 if ( bodypart == 4 )
4357 {
4358 weaponarm = entity;
4359 if ( PLAYER_ATTACK == 1 || PLAYER_ATTACK == PLAYER_POSE_GOLEM_SMASH )
4360 {
4361 // vertical chop
4362 if ( PLAYER_ATTACKTIME == 0 )
4363 {
4364 PLAYER_ARMBENDED = 0;
4365 PLAYER_WEAPONYAW = 0;
4366 entity->pitch = 0;
4367 entity->roll = 0;
4368 entity->skill[1] = 0;
4369 }
4370 else
4371 {
4372 if ( entity->skill[1] == 0 )
4373 {
4374 // upswing
4375 if ( limbAnimateToLimit(entity, ANIMATE_PITCH, -0.5, 5 * PI / 4, false, 0.0) )
4376 {
4377 entity->skill[1] = 1;
4378 }
4379 }
4380 else
4381 {
4382 if ( entity->pitch >= 3 * PI / 2 )
4383 {
4384 PLAYER_ARMBENDED = 1;
4385 }
4386 if ( limbAnimateToLimit(entity, ANIMATE_PITCH, 0.3, PI / 4, false, 0.0) )
4387 {
4388 entity->skill[0] = rightbody->skill[0];
4389 entity->skill[1] = 0;
4390 PLAYER_WEAPONYAW = 0;
4391 entity->pitch = rightbody->pitch;
4392 entity->roll = 0;
4393 PLAYER_ARMBENDED = 0;
4394 PLAYER_ATTACK = 0;
4395 }
4396 }
4397 }
4398
4399 if ( PLAYER_ATTACK == PLAYER_POSE_GOLEM_SMASH && (PLAYER_NUM == clientnum || splitscreen) )
4400 {
4401 if ( my->pitch < PI / 32 )
4402 {
4403 // rotate head upwards
4404 if ( limbAngleWithinRange(my->pitch, 0.1, PI / 32) )
4405 {
4406 my->pitch = PI / 32;
4407 }
4408 else
4409 {
4410 my->pitch += 0.1;
4411 }
4412 }
4413 else
4414 {
4415 // rotate head downwards
4416 if ( limbAngleWithinRange(my->pitch, -0.1, PI / 32) )
4417 {
4418 my->pitch = PI / 32;
4419 }
4420 else
4421 {
4422 my->pitch -= 0.1;
4423 }
4424 }
4425 }
4426 }
4427 else if ( PLAYER_ATTACK == 2 )
4428 {
4429 // horizontal chop
4430 if ( PLAYER_ATTACKTIME == 0 )
4431 {
4432 PLAYER_ARMBENDED = 1;
4433 PLAYER_WEAPONYAW = -3 * PI / 4;
4434 entity->pitch = 0;
4435 entity->roll = -PI / 2;
4436 }
4437 else
4438 {
4439 if ( PLAYER_WEAPONYAW >= PI / 8 )
4440 {
4441 entity->skill[0] = rightbody->skill[0];
4442 PLAYER_WEAPONYAW = 0;
4443 entity->pitch = rightbody->pitch;
4444 entity->roll = 0;
4445 PLAYER_ARMBENDED = 0;
4446 PLAYER_ATTACK = 0;
4447 }
4448 else
4449 {
4450 PLAYER_WEAPONYAW += .25;
4451 }
4452 }
4453 }
4454 else if ( PLAYER_ATTACK == 3 )
4455 {
4456 // stab
4457 if ( PLAYER_ATTACKTIME == 0 )
4458 {
4459 PLAYER_ARMBENDED = 0;
4460 PLAYER_WEAPONYAW = 0;
4461 entity->pitch = 0;
4462 entity->roll = 0;
4463 }
4464 else
4465 {
4466 if ( PLAYER_ATTACKTIME >= 5 )
4467 {
4468 if ( playerRace != CREATURE_IMP )
4469 {
4470 PLAYER_ARMBENDED = 1;
4471 }
4472 limbAnimateToLimit(entity, ANIMATE_PITCH, -0.5, 11 * PI / 6, false, 0.0);
4473 }
4474 else
4475 {
4476 limbAnimateToLimit(entity, ANIMATE_PITCH, 0.4, 2 * PI / 3, false, 0.0);
4477 }
4478 if ( PLAYER_ATTACKTIME >= 10 )
4479 {
4480 entity->skill[0] = rightbody->skill[0];
4481 PLAYER_WEAPONYAW = 0;
4482 entity->pitch = rightbody->pitch;
4483 entity->roll = 0;
4484 PLAYER_ARMBENDED = 0;
4485 PLAYER_ATTACK = 0;
4486 }
4487 }
4488 }
4489 // special double vertical chop
4490 else if ( PLAYER_ATTACK == MONSTER_POSE_SPECIAL_WINDUP1
4491 || PLAYER_ATTACK == MONSTER_POSE_SPECIAL_WINDUP2 )
4492 {
4493 if ( PLAYER_ATTACKTIME == 0 )
4494 {
4495 // init rotations
4496 PLAYER_ARMBENDED = 0;
4497 PLAYER_WEAPONYAW = 0;
4498 entity->pitch = 0;
4499 entity->roll = 0;
4500 entity->skill[1] = 0;
4501 if ( PLAYER_ATTACK == MONSTER_POSE_SPECIAL_WINDUP1 )
4502 {
4503 createParticleDot(my);
4504 }
4505 }
4506 else
4507 {
4508 // move the head.
4509 //limbAnimateToLimit(my, ANIMATE_PITCH, -0.1, 11 * PI / 6, true, 0.1);
4510 if ( (PLAYER_NUM == clientnum || splitscreen) && PLAYER_ATTACK == MONSTER_POSE_SPECIAL_WINDUP1 )
4511 {
4512 if ( my->pitch > -PI / 12 )
4513 {
4514 // rotate head upwards
4515 if ( limbAngleWithinRange(my->pitch, -0.03, -PI / 12) )
4516 {
4517 my->pitch = -PI / 12;
4518 }
4519 else
4520 {
4521 my->pitch += -0.02;
4522 }
4523 }
4524 else
4525 {
4526 // slowly rotate head downwards
4527 if ( limbAngleWithinRange(my->pitch, 0.03, -PI / 12) )
4528 {
4529 my->pitch = -PI / 12;
4530 }
4531 else
4532 {
4533 my->pitch -= -0.01;
4534 }
4535 }
4536 }
4537
4538 if ( PLAYER_ATTACK == MONSTER_POSE_SPECIAL_WINDUP2 )
4539 {
4540 // lower right arm.
4541 if ( entity->skill[1] == 0 )
4542 {
4543 // upswing
4544 if ( limbAnimateToLimit(entity, ANIMATE_PITCH, -0.5, 5 * PI / 4, false, 0.0) )
4545 {
4546 entity->skill[1] = 1;
4547 }
4548 }
4549 else
4550 {
4551 if ( entity->pitch >= 3 * PI / 2 )
4552 {
4553 PLAYER_ARMBENDED = 1;
4554 }
4555 if ( limbAnimateToLimit(entity, ANIMATE_PITCH, 0.3, PI / 4, false, 0.1) )
4556 {
4557 entity->skill[0] = rightbody->skill[0];
4558 entity->skill[1] = 0;
4559 PLAYER_WEAPONYAW = 0;
4560 entity->pitch = rightbody->pitch;
4561 entity->roll = 0;
4562 PLAYER_ARMBENDED = 0;
4563 PLAYER_ATTACK = 0;
4564 }
4565 }
4566 }
4567 else
4568 {
4569 // raise right arm and tilt.
4570 limbAnimateToLimit(entity, ANIMATE_PITCH, -0.1, 9 * PI / 8, true, 0.1);
4571 limbAnimateToLimit(entity, ANIMATE_ROLL, -0.2, PI / 32, false, 0);
4572 if ( PLAYER_ATTACKTIME == 5 )
4573 {
4574 if ( playerRace == TROLL && rand() % 4 == 0 )
4575 {
4576 playSoundEntityLocal(players[PLAYER_NUM]->entity, 79, 128);
4577 }
4578 }
4579 else if ( PLAYER_ATTACKTIME == 35 )
4580 {
4581 playSoundEntityLocal(players[PLAYER_NUM]->entity, 164, 128);
4582 }
4583 }
4584 }
4585 }
4586 else if ( PLAYER_ATTACK == MONSTER_POSE_RANGED_SHOOT1 || PLAYER_ATTACK == MONSTER_POSE_RANGED_SHOOT2 )
4587 {
4588 // init rotations
4589 PLAYER_ARMBENDED = 0;
4590 PLAYER_WEAPONYAW = 0;
4591 weaponarm->roll = 0;
4592 weaponarm->skill[1] = 0;
4593 weaponarm->pitch = 0;
4594 PLAYER_ATTACK = MONSTER_POSE_RANGED_SHOOT3;
4595 }
4596 else if ( PLAYER_ATTACK == MONSTER_POSE_RANGED_SHOOT3 )
4597 {
4598 // recoil upwards
4599 if ( weaponarm->skill[1] == 0 )
4600 {
4601 real_t targetPitch = 14 * PI / 8;
4602 if ( weaponarm->sprite == items[CROSSBOW].index || weaponarm->sprite == items[HEAVY_CROSSBOW].index )
4603 {
4604 targetPitch = 15 * PI / 8;
4605 }
4606 if ( limbAnimateToLimit(weaponarm, ANIMATE_PITCH, -0.2, 14 * PI / 8, false, 0.0) )
4607 {
4608 weaponarm->skill[1] = 1;
4609 }
4610 }
4611 // recoil downwards
4612 else if ( weaponarm->skill[1] == 1 )
4613 {
4614 if ( limbAnimateToLimit(weaponarm, ANIMATE_PITCH, 0.1, 1 * PI / 3, false, 0.0) )
4615 {
4616 weaponarm->skill[1] = 2;
4617 }
4618 }
4619 else if ( weaponarm->skill[1] >= 2 )
4620 {
4621 // limbAngleWithinRange cuts off animation early so it doesn't snap too far back to position.
4622 real_t targetPitch = rightbody->pitch;
4623 while ( targetPitch < 0 )
4624 {
4625 targetPitch += 2 * PI;
4626 }
4627 while ( targetPitch >= 2 * PI )
4628 {
4629 targetPitch -= 2 * PI;
4630 }
4631 if ( limbAnimateToLimit(weaponarm, ANIMATE_PITCH, -0.2, targetPitch, false, 0.0)
4632 || limbAngleWithinRange(weaponarm->pitch, -0.4, targetPitch)
4633 || weaponarm->pitch < -PI / 8)
4634 {
4635 weaponarm->skill[0] = rightbody->skill[0];
4636 PLAYER_WEAPONYAW = 0;
4637 weaponarm->pitch = rightbody->pitch;
4638 weaponarm->roll = 0;
4639 PLAYER_ARMBENDED = 0;
4640 PLAYER_ATTACK = 0;
4641 }
4642 }
4643 }
4644 }
4645 else if ( bodypart == 8 )
4646 {
4647 entity->pitch = entity->fskill[0];
4648 }
4649
4650 if ( insectoidLevitating[PLAYER_NUM] )
4651 {
4652 // hands stationary, legs pitched back and little swing.
4653 double limbSpeed = 0.03;
4654 if ( bodypart == 4 && (PLAYER_ATTACK == 0 && PLAYER_ATTACKTIME == 0) ) // right arm relaxed, not attacking.
4655 {
4656 entity->skill[0] = rightbody->skill[0];
4657 if ( entity->pitch < 0 )
4658 {
4659 entity->pitch += 1 / fmax(limbSpeed * .1, 10.0);
4660 if ( entity->pitch > 0 )
4661 {
4662 entity->pitch = 0;
4663 }
4664 }
4665 else if ( entity->pitch > 0 )
4666 {
4667 entity->pitch -= 1 / fmax(limbSpeed * .1, 10.0);
4668 if ( entity->pitch < 0 )
4669 {
4670 entity->pitch = 0;
4671 }
4672 }
4673 }
4674 else if ( bodypart == 3 ) // leftleg
4675 {
4676 if ( entity->pitch < 0 )
4677 {
4678 entity->pitch += 5 * limbSpeed * PLAYERWALKSPEED; // speed up to reach target.
4679 }
4680 entity->skill[0] = 1;
4681 if ( entity->skill[3] == 1 ) // throwaway skill.
4682 {
4683 entity->pitch -= limbSpeed * PLAYERWALKSPEED;
4684 if ( entity->pitch < PI / 6.f )
4685 {
4686 entity->skill[3] = 0;
4687 entity->pitch = PI / 6.f;
4688 }
4689 }
4690 else
4691 {
4692 entity->pitch += limbSpeed * PLAYERWALKSPEED;
4693 if ( entity->pitch > PI / 3.f )
4694 {
4695 entity->skill[3] = 1;
4696 entity->pitch = PI / 3.f;
4697 }
4698 }
4699 }
4700 }
4701 else if ( bodypart != 4 || (PLAYER_ATTACK == 0 && PLAYER_ATTACKTIME == 0) )
4702 {
4703 if ( bodypart != 8 )
4704 {
4705 if ( oldInsectoidLevitate )
4706 {
4707 entity->pitch = 0;
4708 }
4709 }
4710
4711 double limbSpeed = dist;
4712 double pitchLimit = PI / 4.f;
4713 if ( playerRace == CREATURE_IMP )
4714 {
4715 limbSpeed = 1 / 12.f;
4716 pitchLimit = PI / 8.f;
4717 }
4718 if ( stats[PLAYER_NUM]->EFFECTS[EFF_DASH] )
4719 {
4720 limbSpeed = 1 / 12.f;
4721 }
4722 if ( (fabs(PLAYER_VELX) > 0.1 || fabs(PLAYER_VELY) > 0.1 || playerRace == CREATURE_IMP) )
4723 {
4724 if ( entity->skill[0] )
4725 {
4726 entity->pitch -= limbSpeed * PLAYERWALKSPEED;
4727 if ( entity->pitch < -pitchLimit )
4728 {
4729 entity->skill[0] = 0;
4730 entity->pitch = -pitchLimit;
4731 }
4732 }
4733 else
4734 {
4735 entity->pitch += limbSpeed * PLAYERWALKSPEED;
4736 if ( entity->pitch > pitchLimit )
4737 {
4738 entity->skill[0] = 1;
4739 entity->pitch = pitchLimit;
4740 }
4741 }
4742 }
4743 else
4744 {
4745 if ( entity->pitch < 0 )
4746 {
4747 entity->pitch += 1 / fmax(limbSpeed * .1, 10.0);
4748 if ( entity->pitch > 0 )
4749 {
4750 entity->pitch = 0;
4751 }
4752 }
4753 else if ( entity->pitch > 0 )
4754 {
4755 entity->pitch -= 1 / fmax(limbSpeed * .1, 10.0);
4756 if ( entity->pitch < 0 )
4757 {
4758 entity->pitch = 0;
4759 }
4760 }
4761 }
4762 }
4763 if ( bodypart == 8 )
4764 {
4765 entity->fskill[0] = entity->pitch;
4766 entity->roll = my->roll - fabs(entity->pitch) / 2;
4767 entity->pitch = 0;
4768 }
4769 }
4770
4771 switch ( bodypart )
4772 {
4773 // torso
4774 case 1:
4775 torso = entity;
4776 entity->focalx = limbs[playerRace][1][0];
4777 entity->focaly = limbs[playerRace][1][1];
4778 entity->focalz = limbs[playerRace][1][2];
4779 if ( multiplayer != CLIENT )
4780 {
4781 if ( stats[PLAYER_NUM]->breastplate == NULL || !showEquipment )
4782 {
4783 entity->setDefaultPlayerModel(PLAYER_NUM, playerRace, LIMB_HUMANOID_TORSO);
4784 }
4785 else
4786 {
4787 entity->sprite = itemModel(stats[PLAYER_NUM]->breastplate);
4788 }
4789 if ( multiplayer == SERVER )
4790 {
4791 // update sprites for clients
4792 if ( entity->skill[10] != entity->sprite )
4793 {
4794 entity->skill[10] = entity->sprite;
4795 serverUpdateEntityBodypart(my, bodypart);
4796 }
4797 if ( PLAYER_ALIVETIME == TICKS_PER_SECOND + bodypart )
4798 {
4799 serverUpdateEntityBodypart(my, bodypart);
4800 }
4801 }
4802 }
4803 my->setHumanoidLimbOffset(entity, playerRace, LIMB_HUMANOID_TORSO);
4804 break;
4805 // right leg
4806 case 2:
4807 entity->focalx = limbs[playerRace][2][0];
4808 entity->focaly = limbs[playerRace][2][1];
4809 entity->focalz = limbs[playerRace][2][2];
4810 if ( multiplayer != CLIENT )
4811 {
4812 if ( stats[PLAYER_NUM]->shoes == NULL || !showEquipment )
4813 {
4814 entity->setDefaultPlayerModel(PLAYER_NUM, playerRace, LIMB_HUMANOID_RIGHTLEG);
4815 }
4816 else
4817 {
4818 my->setBootSprite(entity, SPRITE_BOOT_RIGHT_OFFSET);
4819 }
4820 if ( multiplayer == SERVER )
4821 {
4822 // update sprites for clients
4823 if ( entity->skill[10] != entity->sprite )
4824 {
4825 entity->skill[10] = entity->sprite;
4826 serverUpdateEntityBodypart(my, bodypart);
4827 }
4828 if ( PLAYER_ALIVETIME == TICKS_PER_SECOND + bodypart )
4829 {
4830 serverUpdateEntityBodypart(my, bodypart);
4831 }
4832 }
4833 }
4834 my->setHumanoidLimbOffset(entity, playerRace, LIMB_HUMANOID_RIGHTLEG);
4835 break;
4836 // left leg
4837 case 3:
4838 entity->focalx = limbs[playerRace][3][0];
4839 entity->focaly = limbs[playerRace][3][1];
4840 entity->focalz = limbs[playerRace][3][2];
4841 if ( multiplayer != CLIENT )
4842 {
4843 if ( stats[PLAYER_NUM]->shoes == NULL || !showEquipment )
4844 {
4845 entity->setDefaultPlayerModel(PLAYER_NUM, playerRace, LIMB_HUMANOID_LEFTLEG);
4846 }
4847 else
4848 {
4849 my->setBootSprite(entity, SPRITE_BOOT_LEFT_OFFSET);
4850 }
4851 if ( multiplayer == SERVER )
4852 {
4853 // update sprites for clients
4854 if ( entity->skill[10] != entity->sprite )
4855 {
4856 entity->skill[10] = entity->sprite;
4857 serverUpdateEntityBodypart(my, bodypart);
4858 }
4859 if ( PLAYER_ALIVETIME == TICKS_PER_SECOND + bodypart )
4860 {
4861 serverUpdateEntityBodypart(my, bodypart);
4862 }
4863 }
4864 }
4865 my->setHumanoidLimbOffset(entity, playerRace, LIMB_HUMANOID_LEFTLEG);
4866 break;
4867 // right arm
4868 case 4:
4869 {
4870 if ( multiplayer != CLIENT )
4871 {
4872 if ( stats[PLAYER_NUM]->gloves == NULL || !showEquipment )
4873 {
4874 entity->setDefaultPlayerModel(PLAYER_NUM, playerRace, LIMB_HUMANOID_RIGHTARM);
4875 }
4876 else
4877 {
4878 if ( setGloveSprite(stats[PLAYER_NUM], entity, SPRITE_GLOVE_RIGHT_OFFSET) != 0 )
4879 {
4880 // successfully set sprite for the human model
4881 }
4882 }
4883 if ( (!PLAYER_ARMBENDED && showEquipment) || (insectoidLevitating[PLAYER_NUM] && PLAYER_ATTACK == 0 && PLAYER_ATTACKTIME == 0) )
4884 {
4885 entity->sprite += 2 * (stats[PLAYER_NUM]->weapon != NULL);
4886
4887 if ( stats[PLAYER_NUM]->weapon == nullptr
4888 && insectoidLevitating[PLAYER_NUM] )
4889 {
4890 entity->sprite += 2;
4891 }
4892 }
4893 if ( multiplayer == SERVER )
4894 {
4895 // update sprites for clients
4896 if ( entity->skill[10] != entity->sprite )
4897 {
4898 entity->skill[10] = entity->sprite;
4899 serverUpdateEntityBodypart(my, bodypart);
4900 }
4901 if ( PLAYER_ALIVETIME == TICKS_PER_SECOND + bodypart )
4902 {
4903 serverUpdateEntityBodypart(my, bodypart);
4904 }
4905 }
4906 }
4907 my->setHumanoidLimbOffset(entity, playerRace, LIMB_HUMANOID_RIGHTARM);
4908 node_t* tempNode = list_Node(&my->children, 6);
4909 if ( tempNode )
4910 {
4911 Entity* weapon = (Entity*)tempNode->element;
4912 if ( weapon->flags[INVISIBLE] || PLAYER_ARMBENDED || playerRace == CREATURE_IMP )
4913 {
4914 if ( playerRace == INCUBUS || playerRace == SUCCUBUS )
4915 {
4916 entity->focalx = limbs[playerRace][4][0] - 0.25;
4917 entity->focaly = limbs[playerRace][4][1] - 0.25;
4918 entity->focalz = limbs[playerRace][4][2];
4919 }
4920 else
4921 {
4922 entity->focalx = limbs[playerRace][4][0]; // 0
4923 entity->focaly = limbs[playerRace][4][1]; // 0
4924 entity->focalz = limbs[playerRace][4][2]; // 1.5
4925 }
4926 }
4927 else
4928 {
4929 if ( playerRace == INCUBUS || playerRace == SUCCUBUS )
4930 {
4931 entity->focalx = limbs[playerRace][4][0];
4932 entity->focaly = limbs[playerRace][4][1];
4933 entity->focalz = limbs[playerRace][4][2];
4934 }
4935 else if ( playerRace == AUTOMATON )
4936 {
4937 entity->focalx = limbs[playerRace][4][0] + 1.5; // 1
4938 entity->focaly = limbs[playerRace][4][1] + 0.25; // 0
4939 entity->focalz = limbs[playerRace][4][2] - 1; // 1
4940 }
4941 else
4942 {
4943 entity->focalx = limbs[playerRace][4][0] + 0.75;
4944 entity->focaly = limbs[playerRace][4][1];
4945 entity->focalz = limbs[playerRace][4][2] - 0.75;
4946 }
4947 }
4948 }
4949 entity->yaw += PLAYER_WEAPONYAW;
4950 break;
4951 }
4952 // left arm
4953 case 5:
4954 {
4955 if ( multiplayer != CLIENT )
4956 {
4957 if ( stats[PLAYER_NUM]->gloves == NULL || !showEquipment )
4958 {
4959 entity->setDefaultPlayerModel(PLAYER_NUM, playerRace, LIMB_HUMANOID_LEFTARM);
4960 }
4961 else
4962 {
4963 if ( setGloveSprite(stats[PLAYER_NUM], entity, SPRITE_GLOVE_LEFT_OFFSET) != 0 )
4964 {
4965 // successfully set sprite for the human model
4966 }
4967 }
4968 if ( showEquipment )
4969 {
4970 bool bendArm = false;
4971 if ( insectoidLevitating[PLAYER_NUM] )
4972 {
4973 bendArm = true;
4974 }
4975 if ( stats[PLAYER_NUM]->shield != NULL )
4976 {
4977 if ( itemCategory(stats[PLAYER_NUM]->shield) == SPELLBOOK )
4978 {
4979 bendArm = false;
4980 }
4981 else
4982 {
4983 bendArm = true;
4984 }
4985 }
4986 entity->sprite += 2 * (bendArm);
4987 }
4988 if ( multiplayer == SERVER )
4989 {
4990 // update sprites for clients
4991 if ( entity->skill[10] != entity->sprite )
4992 {
4993 entity->skill[10] = entity->sprite;
4994 serverUpdateEntityBodypart(my, bodypart);
4995 }
4996 if ( PLAYER_ALIVETIME == TICKS_PER_SECOND + bodypart )
4997 {
4998 serverUpdateEntityBodypart(my, bodypart);
4999 }
5000 }
5001 }
5002 my->setHumanoidLimbOffset(entity, playerRace, LIMB_HUMANOID_LEFTARM);
5003
5004 if ( weaponarm && !showEquipment &&
5005 (PLAYER_ATTACK == MONSTER_POSE_SPECIAL_WINDUP1
5006 || PLAYER_ATTACK == MONSTER_POSE_SPECIAL_WINDUP2
5007 || PLAYER_ATTACK == PLAYER_POSE_GOLEM_SMASH))
5008 {
5009 // special swing - copy the right arm movements.
5010 entity->pitch = weaponarm->pitch;
5011 entity->roll = -weaponarm->roll;
5012 }
5013 else
5014 {
5015 entity->roll = 0.f;
5016 }
5017
5018 node_t* tempNode = list_Node(&my->children, 7);
5019 if ( tempNode )
5020 {
5021 Entity* shield = (Entity*)tempNode->element;
5022 bool bendArm = true;
5023 if ( shield->flags[INVISIBLE] )
5024 {
5025 bendArm = false;
5026 if ( insectoidLevitating[PLAYER_NUM] )
5027 {
5028 bendArm = true;
5029 }
5030 }
5031 else if ( shield->sprite >= items[SPELLBOOK_LIGHT].index
5032 && shield->sprite < (items[SPELLBOOK_LIGHT].index + items[SPELLBOOK_LIGHT].variations) )
5033 {
5034 bendArm = false;
5035 }
5036 else if ( playerRace == CREATURE_IMP )
5037 {
5038 bendArm = false;
5039 }
5040
5041 if ( !bendArm )
5042 {
5043 if ( playerRace == INCUBUS || playerRace == SUCCUBUS )
5044 {
5045 entity->focalx = limbs[playerRace][5][0] - 0.25;
5046 entity->focaly = limbs[playerRace][5][1] + 0.25;
5047 entity->focalz = limbs[playerRace][5][2];
5048 }
5049 else
5050 {
5051 entity->focalx = limbs[playerRace][5][0]; // 0
5052 entity->focaly = limbs[playerRace][5][1]; // 0
5053 entity->focalz = limbs[playerRace][5][2]; // 1.5
5054 }
5055 }
5056 else
5057 {
5058 if ( playerRace == INCUBUS || playerRace == SUCCUBUS )
5059 {
5060 entity->focalx = limbs[playerRace][5][0];
5061 entity->focaly = limbs[playerRace][5][1];
5062 entity->focalz = limbs[playerRace][5][2];
5063 }
5064 else if ( playerRace == AUTOMATON )
5065 {
5066 entity->focalx = limbs[playerRace][5][0] + 1.5; // 1
5067 entity->focaly = limbs[playerRace][5][1] - 0.25; // 0
5068 entity->focalz = limbs[playerRace][5][2] - 1; // 1
5069 }
5070 else
5071 {
5072 entity->focalx = limbs[playerRace][5][0] + 0.75;
5073 entity->focaly = limbs[playerRace][5][1];
5074 entity->focalz = limbs[playerRace][5][2] - 0.75;
5075 }
5076 }
5077 }
5078 if ( multiplayer != CLIENT )
5079 {
5080 real_t prevYaw = PLAYER_SHIELDYAW;
5081 if ( stats[PLAYER_NUM]->defending )
5082 {
5083 PLAYER_SHIELDYAW = PI / 5;
5084 }
5085 else
5086 {
5087 PLAYER_SHIELDYAW = 0;
5088 }
5089 if ( prevYaw != PLAYER_SHIELDYAW || ticks % 200 == 0 )
5090 {
5091 serverUpdateEntityFSkill(players[PLAYER_NUM]->entity, 8);
5092 }
5093 }
5094 entity->yaw += PLAYER_SHIELDYAW;
5095 break;
5096 }
5097 // weapon
5098 case 6:
5099 if ( multiplayer != CLIENT )
5100 {
5101 if ( swimming )
5102 {
5103 entity->flags[INVISIBLE] = true;
5104 }
5105 else
5106 {
5107 if ( stats[PLAYER_NUM]->weapon == NULL || my->isInvisible() )
5108 {
5109 entity->flags[INVISIBLE] = true;
5110 }
5111 else
5112 {
5113 entity->sprite = itemModel(stats[PLAYER_NUM]->weapon);
5114 if ( itemCategory(stats[PLAYER_NUM]->weapon) == SPELLBOOK )
5115 {
5116 entity->flags[INVISIBLE] = true;
5117 }
5118 else
5119 {
5120 entity->flags[INVISIBLE] = false;
5121 }
5122 }
5123 }
5124 if ( multiplayer == SERVER )
5125 {
5126 // update sprites for clients
5127 if ( entity->skill[10] != entity->sprite )
5128 {
5129 entity->skill[10] = entity->sprite;
5130 serverUpdateEntityBodypart(my, bodypart);
5131 }
5132 if ( entity->skill[11] != entity->flags[INVISIBLE] )
5133 {
5134 entity->skill[11] = entity->flags[INVISIBLE];
5135 serverUpdateEntityBodypart(my, bodypart);
5136 }
5137 if ( PLAYER_ALIVETIME == TICKS_PER_SECOND + bodypart )
5138 {
5139 serverUpdateEntityBodypart(my, bodypart);
5140 }
5141 }
5142 }
5143 else
5144 {
5145 if ( entity->sprite <= 0 )
5146 {
5147 entity->flags[INVISIBLE] = true;
5148 }
5149 }
5150 my->handleHumanoidWeaponLimb(entity, weaponarm);
5151 break;
5152 // shield
5153 case 7:
5154 if ( multiplayer != CLIENT )
5155 {
5156 if ( swimming )
5157 {
5158 entity->flags[INVISIBLE] = true;
5159 }
5160 else
5161 {
5162 if ( stats[PLAYER_NUM]->shield == NULL )
5163 {
5164 entity->flags[INVISIBLE] = true;
5165 entity->sprite = 0;
5166 }
5167 else
5168 {
5169 entity->flags[INVISIBLE] = false;
5170 entity->sprite = itemModel(stats[PLAYER_NUM]->shield);
5171 if ( itemTypeIsQuiver(stats[PLAYER_NUM]->shield->type) )
5172 {
5173 if ( itemTypeIsQuiver(stats[PLAYER_NUM]->shield->type) )
5174 {
5175 entity->handleQuiverThirdPersonModel(*stats[PLAYER_NUM]);
5176 }
5177 }
5178 }
5179 if ( my->isInvisible() )
5180 {
5181 entity->flags[INVISIBLE] = true;
5182 }
5183 }
5184 if ( multiplayer == SERVER )
5185 {
5186 // update sprites for clients
5187 if ( entity->skill[10] != entity->sprite )
5188 {
5189 entity->skill[10] = entity->sprite;
5190 serverUpdateEntityBodypart(my, bodypart);
5191 }
5192 if ( entity->skill[11] != entity->flags[INVISIBLE] )
5193 {
5194 entity->skill[11] = entity->flags[INVISIBLE];
5195 serverUpdateEntityBodypart(my, bodypart);
5196 }
5197 if ( PLAYER_ALIVETIME == TICKS_PER_SECOND + bodypart )
5198 {
5199 serverUpdateEntityBodypart(my, bodypart);
5200 }
5201 }
5202 }
5203 else
5204 {
5205 if ( entity->sprite <= 0 )
5206 {
5207 entity->flags[INVISIBLE] = true;
5208 }
5209 }
5210 my->handleHumanoidShieldLimb(entity, shieldarm);
5211 break;
5212 // cloak
5213 case 8:
5214 entity->focalx = limbs[playerRace][8][0];
5215 entity->focaly = limbs[playerRace][8][1];
5216 entity->focalz = limbs[playerRace][8][2];
5217 entity->scalex = 1.01;
5218 entity->scaley = 1.01;
5219 if ( multiplayer != CLIENT )
5220 {
5221 if ( stats[PLAYER_NUM]->cloak == NULL || my->isInvisible() )
5222 {
5223 entity->flags[INVISIBLE] = true;
5224 }
5225 else
5226 {
5227 entity->flags[INVISIBLE] = false;
5228 entity->sprite = itemModel(stats[PLAYER_NUM]->cloak);
5229 }
5230 if ( multiplayer == SERVER )
5231 {
5232 // update sprites for clients
5233 if ( entity->skill[10] != entity->sprite )
5234 {
5235 entity->skill[10] = entity->sprite;
5236 serverUpdateEntityBodypart(my, bodypart);
5237 }
5238 if ( entity->skill[11] != entity->flags[INVISIBLE] )
5239 {
5240 entity->skill[11] = entity->flags[INVISIBLE];
5241 serverUpdateEntityBodypart(my, bodypart);
5242 }
5243 if ( PLAYER_ALIVETIME == TICKS_PER_SECOND + bodypart )
5244 {
5245 serverUpdateEntityBodypart(my, bodypart);
5246 }
5247 }
5248 }
5249 else
5250 {
5251 if ( entity->sprite <= 0 )
5252 {
5253 entity->flags[INVISIBLE] = true;
5254 }
5255 }
5256
5257 if ( entity->sprite == items[CLOAK_BACKPACK].index )
5258 {
5259 // human
5260 if ( playerRace == HUMAN || playerRace == VAMPIRE )
5261 {
5262 entity->focaly = limbs[playerRace][8][1] + 0.25;
5263 entity->focalz = limbs[playerRace][8][2] - 0.3;
5264 }
5265 else if ( playerRace == SUCCUBUS || playerRace == INCUBUS )
5266 {
5267 // succubus/incubus
5268 entity->focaly = limbs[playerRace][8][1] + 0.25;
5269 entity->focalz = limbs[playerRace][8][2] - 0.7;
5270 }
5271 else if ( playerRace == SKELETON )
5272 {
5273 entity->focaly = limbs[playerRace][8][1] + 0.25;
5274 entity->focalz = limbs[playerRace][8][2] - 0.5;
5275 }
5276 else if ( playerRace == AUTOMATON )
5277 {
5278 entity->focaly = limbs[playerRace][8][1] - 0.25;
5279 entity->focalz = limbs[playerRace][8][2] - 0.5;
5280 }
5281 else if ( playerRace == GOATMAN || playerRace == INSECTOID || playerRace == GOBLIN )
5282 {
5283 entity->focaly = limbs[playerRace][8][1] - 0.25;
5284 entity->focalz = limbs[playerRace][8][2] - 0.5;
5285 }
5286
5287 entity->scalex = 0.99;
5288 entity->scaley = 0.99;
5289 }
5290 entity->x -= cos(my->yaw);
5291 entity->y -= sin(my->yaw);
5292 entity->yaw += PI / 2;
5293 break;
5294 // helm
5295 case 9:
5296 helmet = entity;
5297 entity->focalx = limbs[playerRace][9][0]; // 0
5298 entity->focaly = limbs[playerRace][9][1]; // 0
5299 entity->focalz = limbs[playerRace][9][2]; // -1.75
5300 entity->pitch = my->pitch;
5301 entity->roll = 0;
5302 if ( multiplayer != CLIENT )
5303 {
5304 entity->sprite = itemModel(stats[PLAYER_NUM]->helmet);
5305 if ( stats[PLAYER_NUM]->helmet == NULL || my->isInvisible() )
5306 {
5307 entity->flags[INVISIBLE] = true;
5308 }
5309 else
5310 {
5311 entity->flags[INVISIBLE] = false;
5312 }
5313 if ( multiplayer == SERVER )
5314 {
5315 // update sprites for clients
5316 if ( entity->skill[10] != entity->sprite )
5317 {
5318 entity->skill[10] = entity->sprite;
5319 serverUpdateEntityBodypart(my, bodypart);
5320 }
5321 if ( entity->skill[11] != entity->flags[INVISIBLE] )
5322 {
5323 entity->skill[11] = entity->flags[INVISIBLE];
5324 serverUpdateEntityBodypart(my, bodypart);
5325 }
5326 if ( PLAYER_ALIVETIME == TICKS_PER_SECOND + bodypart )
5327 {
5328 serverUpdateEntityBodypart(my, bodypart);
5329 }
5330 }
5331 }
5332 else
5333 {
5334 if ( entity->sprite <= 0 )
5335 {
5336 entity->flags[INVISIBLE] = true;
5337 }
5338 }
5339 my->setHelmetLimbOffset(entity);
5340 break;
5341 // mask
5342 case 10:
5343 entity->focalx = limbs[playerRace][10][0]; // 0
5344 entity->focaly = limbs[playerRace][10][1]; // 0
5345 entity->focalz = limbs[playerRace][10][2]; // .5
5346 entity->pitch = my->pitch;
5347 entity->roll = PI / 2;
5348 if ( multiplayer != CLIENT )
5349 {
5350 bool hasSteelHelm = false;
5351 if ( stats[PLAYER_NUM]->helmet )
5352 {
5353 if ( stats[PLAYER_NUM]->helmet->type == STEEL_HELM
5354 || stats[PLAYER_NUM]->helmet->type == CRYSTAL_HELM
5355 || stats[PLAYER_NUM]->helmet->type == ARTIFACT_HELM )
5356 {
5357 hasSteelHelm = true;
5358 }
5359 }
5360 if ( stats[PLAYER_NUM]->mask == NULL || my->isInvisible() || hasSteelHelm )
5361 {
5362 entity->flags[INVISIBLE] = true;
5363 }
5364 else
5365 {
5366 entity->flags[INVISIBLE] = false;
5367 }
5368 if ( stats[PLAYER_NUM]->mask != NULL )
5369 {
5370 if ( stats[PLAYER_NUM]->mask->type == TOOL_GLASSES )
5371 {
5372 entity->sprite = 165; // GlassesWorn.vox
5373 }
5374 else
5375 {
5376 entity->sprite = itemModel(stats[PLAYER_NUM]->mask);
5377 }
5378 }
5379 if ( multiplayer == SERVER )
5380 {
5381 // update sprites for clients
5382 if ( entity->skill[10] != entity->sprite )
5383 {
5384 entity->skill[10] = entity->sprite;
5385 serverUpdateEntityBodypart(my, bodypart);
5386 }
5387 if ( entity->skill[11] != entity->flags[INVISIBLE] )
5388 {
5389 entity->skill[11] = entity->flags[INVISIBLE];
5390 serverUpdateEntityBodypart(my, bodypart);
5391 }
5392 if ( PLAYER_ALIVETIME == TICKS_PER_SECOND + bodypart )
5393 {
5394 serverUpdateEntityBodypart(my, bodypart);
5395 }
5396 }
5397 }
5398 else
5399 {
5400 if ( entity->sprite <= 0 )
5401 {
5402 entity->flags[INVISIBLE] = true;
5403 }
5404 }
5405 entity->scalex = 0.99;
5406 entity->scaley = 0.99;
5407 entity->scalez = 0.99;
5408 if ( entity->sprite == 165 )
5409 {
5410 entity->focalx = limbs[playerRace][10][0] + .25; // .25
5411 entity->focaly = limbs[playerRace][10][1] - 2.25; // -2.25
5412 entity->focalz = limbs[playerRace][10][2]; // .5
5413 if ( helmet && !helmet->flags[INVISIBLE] && helmet->sprite == items[PUNISHER_HOOD].index )
5414 {
5415 switch ( playerRace )
5416 {
5417 case HUMAN:
5418 case VAMPIRE:
5419 case SHOPKEEPER:
5420 case INSECTOID:
5421 entity->focaly += 0.25; // lower glasses a bit.
5422 break;
5423 case INCUBUS:
5424 case SUCCUBUS:
5425 case AUTOMATON:
5426 case GOBLIN:
5427 case GOATMAN:
5428 case SKELETON:
5429 // no change.
5430 break;
5431 default:
5432 break;
5433 }
5434 }
5435 }
5436 else if ( entity->sprite == items[MASK_SHAMAN].index )
5437 {
5438 entity->roll = 0;
5439 my->setHelmetLimbOffset(entity);
5440 my->setHelmetLimbOffsetWithMask(helmet, entity);
5441 }
5442 else
5443 {
5444 entity->focalx = limbs[playerRace][10][0] + .35; // .35
5445 entity->focaly = limbs[playerRace][10][1] - 2; // -2
5446 entity->focalz = limbs[playerRace][10][2]; // .5
5447 }
5448
5449 break;
5450 case 11:
5451 additionalLimb = entity;
5452 entity->focalx = limbs[playerRace][11][0];
5453 entity->focaly = limbs[playerRace][11][1];
5454 entity->focalz = limbs[playerRace][11][2];
5455 entity->flags[INVISIBLE] = true;
5456 if ( playerRace == INSECTOID || playerRace == CREATURE_IMP )
5457 {
5458 entity->flags[INVISIBLE] = my->flags[INVISIBLE];
5459 if ( playerRace == INSECTOID )
5460 {
5461 if ( stats[PLAYER_NUM]->sex == FEMALE )
5462 {
5463 entity->sprite = 771;
5464 }
5465 else
5466 {
5467 entity->sprite = 750;
5468 }
5469 if ( torso && torso->sprite != 727 && torso->sprite != 761 && torso->sprite != 458 )
5470 {
5471 // wearing armor, offset more.
5472 entity->x -= 2.25 * cos(my->yaw);
5473 entity->y -= 2.25 * sin(my->yaw);
5474 }
5475 else
5476 {
5477 entity->x -= 1.5 * cos(my->yaw);
5478 entity->y -= 1.5 * sin(my->yaw);
5479 }
5480 }
5481 else if ( playerRace == CREATURE_IMP )
5482 {
5483 entity->sprite = 833;
5484 entity->focalx = limbs[playerRace][7][0];
5485 entity->focaly = limbs[playerRace][7][1];
5486 entity->focalz = limbs[playerRace][7][2];
5487 entity->x -= 1 * cos(my->yaw + PI / 2) + 2.5 * cos(my->yaw);
5488 entity->y -= 1 * sin(my->yaw + PI / 2) + 2.5 * sin(my->yaw);
5489 entity->z += 1;
5490 }
5491 bool moving = false;
5492 if ( fabs(PLAYER_VELX) > 0.1 || fabs(PLAYER_VELY) > 0.1 || insectoidLevitating )
5493 {
5494 moving = true;
5495 }
5496
5497 if ( entity->skill[0] == 0 )
5498 {
5499 if ( moving )
5500 {
5501 if ( insectoidLevitating[PLAYER_NUM] )
5502 {
5503 entity->fskill[0] += std::min(std::max(0.2, dist * PLAYERWALKSPEED), 2.f * PLAYERWALKSPEED); // move proportional to move speed
5504 }
5505 else
5506 {
5507 entity->fskill[0] += std::min(dist * PLAYERWALKSPEED, 2.f * PLAYERWALKSPEED); // move proportional to move speed
5508 }
5509 }
5510 else if ( PLAYER_ATTACK != 0 )
5511 {
5512 entity->fskill[0] += PLAYERWALKSPEED; // move fixed speed when attacking if stationary
5513 }
5514 else
5515 {
5516 entity->fskill[0] += 0.01; // otherwise move slow idle
5517 }
5518
5519 if ( entity->fskill[0] > PI / 3
5520 || ((!moving || PLAYER_ATTACK != 0) && entity->fskill[0] > PI / 5)
5521 || (playerRace == CREATURE_IMP && entity->fskill[0] > PI / 8) )
5522 {
5523 // switch direction if angle too great, angle is shorter if attacking or stationary
5524 entity->skill[0] = 1;
5525 }
5526 }
5527 else // reverse of the above
5528 {
5529 if ( moving )
5530 {
5531 if ( insectoidLevitating[PLAYER_NUM] )
5532 {
5533 entity->fskill[0] -= std::min(std::max(0.15, dist * PLAYERWALKSPEED), 2.f * PLAYERWALKSPEED);
5534 }
5535 else
5536 {
5537 entity->fskill[0] -= std::min(dist * PLAYERWALKSPEED, 2.f * PLAYERWALKSPEED);
5538 }
5539 }
5540 else if ( PLAYER_ATTACK != 0 )
5541 {
5542 entity->fskill[0] -= PLAYERWALKSPEED;
5543 }
5544 else
5545 {
5546 entity->fskill[0] -= 0.007;
5547 }
5548
5549 if ( playerRace == INSECTOID && entity->fskill[0] < -PI / 32 )
5550 {
5551 entity->skill[0] = 0;
5552 }
5553 else if ( playerRace == CREATURE_IMP && entity->fskill[0] < -PI / 4 )
5554 {
5555 entity->skill[0] = 0;
5556 }
5557 }
5558 entity->yaw += entity->fskill[0];
5559 }
5560 break;
5561 case 12:
5562 entity->focalx = limbs[playerRace][12][0];
5563 entity->focaly = limbs[playerRace][12][1];
5564 entity->focalz = limbs[playerRace][12][2];
5565 entity->flags[INVISIBLE] = true;
5566 if ( playerRace == INSECTOID || playerRace == CREATURE_IMP )
5567 {
5568 entity->flags[INVISIBLE] = my->flags[INVISIBLE];
5569 if ( playerRace == INSECTOID )
5570 {
5571 if ( stats[PLAYER_NUM]->sex == FEMALE )
5572 {
5573 entity->sprite = 772;
5574 }
5575 else
5576 {
5577 entity->sprite = 751;
5578 }
5579 if ( torso && torso->sprite != 727 && torso->sprite != 761 && torso->sprite != 458 )
5580 {
5581 // wearing armor, offset more.
5582 entity->x -= 2.25 * cos(my->yaw);
5583 entity->y -= 2.25 * sin(my->yaw);
5584 }
5585 else
5586 {
5587 entity->x -= 1.5 * cos(my->yaw);
5588 entity->y -= 1.5 * sin(my->yaw);
5589 }
5590 }
5591 else if ( playerRace == CREATURE_IMP )
5592 {
5593 entity->sprite = 834;
5594 entity->focalx = limbs[playerRace][6][0];
5595 entity->focaly = limbs[playerRace][6][1];
5596 entity->focalz = limbs[playerRace][6][2];
5597 entity->x += 1 * cos(my->yaw + PI / 2) - 2.5 * cos(my->yaw);
5598 entity->y += 1 * sin(my->yaw + PI / 2) - 2.5 * sin(my->yaw);
5599 entity->z += 1;
5600 }
5601 if ( additionalLimb ) // follow the yaw of the previous limb.
5602 {
5603 entity->yaw -= additionalLimb->fskill[0];
5604 }
5605 }
5606 break;
5607 default:
5608 break;
5609 }
5610
5611
5612 if ( !showEquipment )
5613 {
5614 if ( bodypart >= 6 && bodypart <= 10 )
5615 {
5616 entity->flags[INVISIBLE] = true;
5617 if ( playerRace == CREATURE_IMP && bodypart == 7 )
5618 {
5619 if ( entity->sprite >= items[SPELLBOOK_LIGHT].index
5620 && entity->sprite < (items[SPELLBOOK_LIGHT].index + items[SPELLBOOK_LIGHT].variations) )
5621 {
5622 entity->flags[INVISIBLE] = false; // show spellbooks
5623 }
5624 }
5625 else if ( playerRace == CREATURE_IMP && bodypart == 6 )
5626 {
5627 if ( entity->sprite >= 59 && entity->sprite < 64 )
5628 {
5629 entity->flags[INVISIBLE] = false; // show magicstaffs
5630 }
5631 if ( multiplayer != CLIENT && !stats[PLAYER_NUM]->weapon )
5632 {
5633 entity->flags[INVISIBLE] = true; // show magicstaffs
5634 }
5635 }
5636 }
5637 }
5638 }
5639 // rotate shield a bit
5640 node_t* shieldNode = list_Node(&my->children, 7);
5641 if ( shieldNode )
5642 {
5643 Entity* shieldEntity = (Entity*)shieldNode->element;
5644 if ( shieldEntity->sprite != items[TOOL_TORCH].index && shieldEntity->sprite != items[TOOL_LANTERN].index && shieldEntity->sprite != items[TOOL_CRYSTALSHARD].index )
5645 {
5646 shieldEntity->yaw -= PI / 6;
5647 }
5648 }
5649 }
5650 else
5651 {
5652 if ( stats[PLAYER_NUM]->type == RAT )
5653 {
5654 playerAnimateRat(my);
5655 }
5656 else if ( stats[PLAYER_NUM]->type == SPIDER )
5657 {
5658 playerAnimateSpider(my);
5659 }
5660 }
5661 if ( PLAYER_ATTACK != 0 )
5662 {
5663 PLAYER_ATTACKTIME++;
5664 }
5665 else
5666 {
5667 PLAYER_ATTACKTIME = 0;
5668 }
5669
5670 handlePlayerCameraPosition(my, PLAYER_NUM, false);
5671 }
5672
5673 // client function
actPlayerLimb(Entity * my)5674 void actPlayerLimb(Entity* my)
5675 {
5676 int i;
5677
5678 Entity* parent = uidToEntity(my->parent);
5679
5680 if ( multiplayer == CLIENT )
5681 {
5682 if ( stats[PLAYER_NUM]->HP <= 0 )
5683 {
5684 if ( parent && parent->getMonsterTypeFromSprite() == AUTOMATON )
5685 {
5686 if ( parent->playerAutomatonDeathCounter != 0 )
5687 {
5688 my->flags[INVISIBLE] = false;
5689 }
5690 else
5691 {
5692 my->flags[INVISIBLE] = true;
5693 }
5694 }
5695 else
5696 {
5697 my->flags[INVISIBLE] = true;
5698 }
5699 return;
5700 }
5701 }
5702
5703 if ( parent && multiplayer != CLIENT )
5704 {
5705 for ( i = 0; i < MAXPLAYERS; i++ )
5706 {
5707 if ( inrange[i] )
5708 {
5709 if ( i == 0 && selectedEntity == my )
5710 {
5711 parent->skill[14] = i + 1;
5712 }
5713 else if ( client_selected[i] == my )
5714 {
5715 parent->skill[14] = i + 1;
5716 }
5717 }
5718 }
5719 }
5720
5721 if ( parent && parent->monsterEntityRenderAsTelepath == 1 )
5722 {
5723 my->monsterEntityRenderAsTelepath = 1;
5724 }
5725 else
5726 {
5727 my->monsterEntityRenderAsTelepath = 0;
5728 }
5729
5730 if (multiplayer != CLIENT)
5731 {
5732 return;
5733 }
5734
5735 if (my->skill[2] < 0 || my->skill[2] >= MAXPLAYERS )
5736 {
5737 return;
5738 }
5739 if (players[my->skill[2]] == nullptr || players[my->skill[2]]->entity == nullptr)
5740 {
5741 list_RemoveNode(my->mynode);
5742 return;
5743 }
5744
5745 //TODO: These three are _NOT_ PLAYERSWAP
5746 //my->vel_x = players[my->skill[2]]->vel_x;
5747 //my->vel_y = players[my->skill[2]]->vel_y;
5748 //my->vel_z = players[my->skill[2]]->vel_z;
5749
5750 if ( stats[PLAYER_NUM] )
5751 {
5752 if ( stats[PLAYER_NUM]->type == RAT
5753 || stats[PLAYER_NUM]->type == SPIDER
5754 || stats[PLAYER_NUM]->type == TROLL
5755 || stats[PLAYER_NUM]->type == CREATURE_IMP )
5756 {
5757 players[PLAYER_NUM]->entity->skill[1] = 0;
5758 return;
5759 }
5760 }
5761
5762 // set light size
5763 if (my->sprite == 93) // torch
5764 {
5765 my->skill[4] = 1;
5766 players[my->skill[2]]->entity->skill[1] = 6;
5767 }
5768 else if (my->sprite == 94) // lantern
5769 {
5770 my->skill[4] = 1;
5771 players[my->skill[2]]->entity->skill[1] = 9;
5772 }
5773 else if ( my->sprite == 529 ) // crystal shard
5774 {
5775 my->skill[4] = 1;
5776 players[my->skill[2]]->entity->skill[1] = 4;
5777 }
5778 else
5779 {
5780 if (my->skill[4] == 1)
5781 {
5782 players[my->skill[2]]->entity->skill[1] = 0;
5783 }
5784 }
5785
5786 }
5787
playerLevelEntrySpeechSecond()5788 void Entity::playerLevelEntrySpeechSecond()
5789 {
5790 int timeDiff = playerAliveTime - 300;
5791 int orangeSpeechVolume = 128;
5792 int blueSpeechVolume = 112;
5793 if ( timeDiff > 0 && playerLevelEntrySpeech > 0 && !secretlevel )
5794 {
5795 switch ( currentlevel )
5796 {
5797 case 26:
5798 switch ( playerLevelEntrySpeech )
5799 {
5800 case 1:
5801 if ( timeDiff == 200 )
5802 {
5803 playSound(342, orangeSpeechVolume);
5804 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2616]);
5805 playerLevelEntrySpeech = 0;
5806 }
5807 break;
5808 case 2:
5809 if ( timeDiff == 200 )
5810 {
5811 playSound(344, blueSpeechVolume);
5812 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2618]);
5813 }
5814 else if ( timeDiff == 350 )
5815 {
5816 playSound(345, orangeSpeechVolume);
5817 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2619]);
5818 playerLevelEntrySpeech = 0;
5819 }
5820 break;
5821 case 3:
5822 if ( timeDiff == 200 )
5823 {
5824 playSound(347, blueSpeechVolume);
5825 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2621]);
5826 }
5827 else if ( timeDiff == 350 )
5828 {
5829 playSound(348, orangeSpeechVolume);
5830 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2622]);
5831 playerLevelEntrySpeech = 0;
5832 }
5833 break;
5834 default:
5835 break;
5836 }
5837 break;
5838 case 28:
5839 switch ( playerLevelEntrySpeech )
5840 {
5841 case 1:
5842 if ( timeDiff == 200 )
5843 {
5844 playSound(350, orangeSpeechVolume);
5845 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2630]);
5846 }
5847 else if ( timeDiff == 350 )
5848 {
5849 playSound(351, blueSpeechVolume);
5850 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2631]);
5851 playerLevelEntrySpeech = 0;
5852 }
5853 break;
5854 case 2:
5855 if ( timeDiff == 350 )
5856 {
5857 playSound(353, blueSpeechVolume);
5858 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2633]);
5859 playerLevelEntrySpeech = 0;
5860 }
5861 break;
5862 case 3:
5863 if ( timeDiff == 200 )
5864 {
5865 playSound(355, orangeSpeechVolume);
5866 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2635]);
5867 playerLevelEntrySpeech = 0;
5868 }
5869 break;
5870 default:
5871 break;
5872 }
5873 break;
5874 case 30:
5875 switch ( playerLevelEntrySpeech )
5876 {
5877 case 1:
5878 if ( timeDiff == 350 )
5879 {
5880 playSound(357, orangeSpeechVolume);
5881 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2637]);
5882 }
5883 else if ( timeDiff == 500 )
5884 {
5885 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2652]);
5886 playerLevelEntrySpeech = 0;
5887 }
5888 break;
5889 default:
5890 break;
5891 }
5892 break;
5893 case 31:
5894 switch ( playerLevelEntrySpeech )
5895 {
5896 case 1:
5897 if ( timeDiff == 350 )
5898 {
5899 playSound(359, orangeSpeechVolume);
5900 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2639]);
5901 }
5902 else if ( timeDiff == 510 )
5903 {
5904 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2653]);
5905 playerLevelEntrySpeech = 0;
5906 }
5907 break;
5908 default:
5909 break;
5910 }
5911 break;
5912 case 33:
5913 switch ( playerLevelEntrySpeech )
5914 {
5915 case 1:
5916 if ( timeDiff == 200 )
5917 {
5918 playSound(361, blueSpeechVolume);
5919 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2641]);
5920 playerLevelEntrySpeech = 0;
5921 }
5922 break;
5923 case 2:
5924 if ( timeDiff == 350 )
5925 {
5926 playSound(363, orangeSpeechVolume);
5927 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2643]);
5928 playerLevelEntrySpeech = 0;
5929 }
5930 break;
5931 default:
5932 break;
5933 }
5934 break;
5935 case 35:
5936 switch ( playerLevelEntrySpeech )
5937 {
5938 case 1:
5939 if ( timeDiff == 310 )
5940 {
5941 playSound(365, blueSpeechVolume);
5942 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2645]);
5943 playerLevelEntrySpeech = 0;
5944 }
5945 break;
5946 default:
5947 break;
5948 }
5949 break;
5950 case 27:
5951 case 29:
5952 case 32:
5953 case 34:
5954 if ( minotaurlevel )
5955 {
5956 switch ( playerLevelEntrySpeech )
5957 {
5958 case 1:
5959 if ( timeDiff == 200 )
5960 {
5961 playSound(367, orangeSpeechVolume);
5962 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2624]);
5963 playerLevelEntrySpeech = 0;
5964 }
5965 break;
5966 case 2:
5967 if ( timeDiff == 200 )
5968 {
5969 playSound(369, blueSpeechVolume);
5970 messagePlayerColor(clientnum, uint32ColorBaronyBlue(*mainsurface), language[2626]);
5971 playerLevelEntrySpeech = 0;
5972 }
5973 break;
5974 case 3:
5975 if ( timeDiff == 200 )
5976 {
5977 playSound(371, orangeSpeechVolume);
5978 messagePlayerColor(clientnum, uint32ColorOrange(*mainsurface), language[2628]);
5979 playerLevelEntrySpeech = 0;
5980 }
5981 break;
5982 default:
5983 break;
5984 }
5985 break;
5986 }
5987 break;
5988 default:
5989 break;
5990 }
5991
5992 }
5993 }
5994
isPlayerHeadSprite()5995 bool Entity::isPlayerHeadSprite()
5996 {
5997 switch ( sprite )
5998 {
5999 case 113:
6000 case 114:
6001 case 115:
6002 case 116:
6003 case 117:
6004 case 125:
6005 case 126:
6006 case 127:
6007 case 128:
6008 case 129:
6009 case 332:
6010 case 333:
6011 case 341:
6012 case 342:
6013 case 343:
6014 case 344:
6015 case 345:
6016 case 346:
6017 case 354:
6018 case 355:
6019 case 356:
6020 case 357:
6021 case 358:
6022 case 359:
6023 case 367:
6024 case 368:
6025 case 369:
6026 case 370:
6027 case 371:
6028 case 372:
6029 case 380:
6030 case 381:
6031 case 382:
6032 case 383:
6033 case 384:
6034 case 385:
6035 case 686:
6036 case 694:
6037 case 702:
6038 case 710:
6039 case 718:
6040 case 726:
6041 case 734:
6042 case 742:
6043 case 752:
6044 case 756:
6045 case 760:
6046 case 768:
6047 case 770:
6048 case 814:
6049 case 817:
6050 case 823:
6051 case 827:
6052 // TODO
6053 return true;
6054 break;
6055 default:
6056 break;
6057 }
6058 return false;
6059 }
6060
getMonsterFromPlayerRace(int playerRace)6061 Monster Entity::getMonsterFromPlayerRace(int playerRace)
6062 {
6063 switch ( playerRace )
6064 {
6065 case RACE_HUMAN:
6066 return HUMAN;
6067 break;
6068 case RACE_SKELETON:
6069 return SKELETON;
6070 break;
6071 case RACE_INCUBUS:
6072 return INCUBUS;
6073 /*if ( stats[this->skill[2]]->sex == FEMALE )
6074 {
6075 return SUCCUBUS;
6076 }
6077 else
6078 {
6079 }*/
6080 break;
6081 case RACE_GOBLIN:
6082 return GOBLIN;
6083 break;
6084 case RACE_AUTOMATON:
6085 return AUTOMATON;
6086 break;
6087 case RACE_INSECTOID:
6088 return INSECTOID;
6089 break;
6090 case RACE_GOATMAN:
6091 return GOATMAN;
6092 break;
6093 case RACE_VAMPIRE:
6094 return VAMPIRE;
6095 break;
6096 case RACE_SUCCUBUS:
6097 return SUCCUBUS;
6098 break;
6099 case RACE_RAT:
6100 return RAT;
6101 break;
6102 case RACE_TROLL:
6103 return TROLL;
6104 break;
6105 case RACE_SPIDER:
6106 return SPIDER;
6107 break;
6108 case RACE_IMP:
6109 return CREATURE_IMP;
6110 break;
6111 default:
6112 return HUMAN;
6113 break;
6114 }
6115 return HUMAN;
6116 }
6117
setDefaultPlayerModel(int playernum,Monster playerRace,int limbType)6118 void Entity::setDefaultPlayerModel(int playernum, Monster playerRace, int limbType)
6119 {
6120 if ( !players[playernum] || !players[playernum]->entity )
6121 {
6122 return;
6123 }
6124
6125 int playerAppearance = stats[playernum]->appearance;
6126 if ( players[playernum] && players[playernum]->entity && players[playernum]->entity->effectPolymorph > NUMMONSTERS )
6127 {
6128 playerAppearance = players[playernum]->entity->effectPolymorph - 100;
6129 }
6130
6131 if ( limbType == LIMB_HUMANOID_TORSO )
6132 {
6133 if ( playerRace == HUMAN )
6134 {
6135 switch ( playerAppearance / 6 )
6136 {
6137 case 1:
6138 this->sprite = 334 + 13 * stats[playernum]->sex;
6139 break;
6140 case 2:
6141 this->sprite = 360 + 13 * stats[playernum]->sex;
6142 break;
6143 default:
6144 this->sprite = 106 + 12 * stats[playernum]->sex;
6145 break;
6146 }
6147 }
6148 else
6149 {
6150 switch ( playerRace )
6151 {
6152 case SKELETON:
6153 this->sprite = 687;
6154 break;
6155 case GOBLIN:
6156 if ( stats[playernum]->sex == FEMALE )
6157 {
6158 this->sprite = 753;
6159 }
6160 else
6161 {
6162 this->sprite = 695;
6163 }
6164 break;
6165 case CREATURE_IMP:
6166 this->sprite = 828;
6167 break;
6168 case INCUBUS:
6169 this->sprite = 703;
6170 break;
6171 case SUCCUBUS:
6172 this->sprite = 711;
6173 break;
6174 case VAMPIRE:
6175 if ( stats[playernum]->sex == FEMALE )
6176 {
6177 this->sprite = 757;
6178 }
6179 else
6180 {
6181 this->sprite = 719;
6182 }
6183 break;
6184 case INSECTOID:
6185 if ( stats[playernum]->sex == FEMALE )
6186 {
6187 this->sprite = 761;
6188 }
6189 else
6190 {
6191 this->sprite = 727;
6192 }
6193 break;
6194 case GOATMAN:
6195 if ( stats[playernum]->sex == FEMALE )
6196 {
6197 this->sprite = 769;
6198 }
6199 else
6200 {
6201 this->sprite = 735;
6202 }
6203 break;
6204 case AUTOMATON:
6205 this->sprite = 743;
6206 break;
6207 case TROLL:
6208 this->sprite = 818;
6209 break;
6210 default:
6211 break;
6212 }
6213 }
6214 }
6215 else if ( limbType == LIMB_HUMANOID_RIGHTLEG )
6216 {
6217 if ( playerRace == HUMAN )
6218 {
6219 switch ( playerAppearance / 6 )
6220 {
6221 case 1:
6222 this->sprite = 335 + 13 * stats[playernum]->sex;
6223 break;
6224 case 2:
6225 this->sprite = 361 + 13 * stats[playernum]->sex;
6226 break;
6227 default:
6228 this->sprite = 107 + 12 * stats[playernum]->sex;
6229 break;
6230 }
6231 }
6232 else
6233 {
6234 switch ( playerRace )
6235 {
6236 case SKELETON:
6237 this->sprite = 693;
6238 break;
6239 case GOBLIN:
6240 if ( stats[playernum]->sex == FEMALE )
6241 {
6242 this->sprite = 755;
6243 }
6244 else
6245 {
6246 this->sprite = 701;
6247 }
6248 break;
6249 case CREATURE_IMP:
6250 this->sprite = 830;
6251 break;
6252 case INCUBUS:
6253 this->sprite = 709;
6254 break;
6255 case SUCCUBUS:
6256 this->sprite = 717;
6257 break;
6258 case VAMPIRE:
6259 if ( stats[playernum]->sex == FEMALE )
6260 {
6261 this->sprite = 759;
6262 }
6263 else
6264 {
6265 this->sprite = 725;
6266 }
6267 break;
6268 case INSECTOID:
6269 if ( stats[playernum]->sex == FEMALE )
6270 {
6271 this->sprite = 767;
6272 }
6273 else
6274 {
6275 this->sprite = 733;
6276 }
6277 break;
6278 case GOATMAN:
6279 this->sprite = 741;
6280 break;
6281 case AUTOMATON:
6282 this->sprite = 749;
6283 break;
6284 case TROLL:
6285 this->sprite = 822;
6286 break;
6287 default:
6288 break;
6289 }
6290 }
6291 }
6292 else if ( limbType == LIMB_HUMANOID_LEFTLEG )
6293 {
6294 if ( playerRace == HUMAN )
6295 {
6296 switch ( playerAppearance / 6 )
6297 {
6298 case 1:
6299 this->sprite = 336 + 13 * stats[playernum]->sex;
6300 break;
6301 case 2:
6302 this->sprite = 362 + 13 * stats[playernum]->sex;
6303 break;
6304 default:
6305 this->sprite = 108 + 12 * stats[playernum]->sex;
6306 break;
6307 }
6308 }
6309 else
6310 {
6311 switch ( playerRace )
6312 {
6313 case SKELETON:
6314 this->sprite = 692;
6315 break;
6316 case GOBLIN:
6317 if ( stats[playernum]->sex == FEMALE )
6318 {
6319 this->sprite = 754;
6320 }
6321 else
6322 {
6323 this->sprite = 700;
6324 }
6325 break;
6326 case CREATURE_IMP:
6327 this->sprite = 829;
6328 break;
6329 case INCUBUS:
6330 this->sprite = 708;
6331 break;
6332 case SUCCUBUS:
6333 this->sprite = 716;
6334 break;
6335 case VAMPIRE:
6336 if ( stats[playernum]->sex == FEMALE )
6337 {
6338 this->sprite = 758;
6339 }
6340 else
6341 {
6342 this->sprite = 724;
6343 }
6344 break;
6345 case INSECTOID:
6346 if ( stats[playernum]->sex == FEMALE )
6347 {
6348 this->sprite = 766;
6349 }
6350 else
6351 {
6352 this->sprite = 732;
6353 }
6354 break;
6355 case GOATMAN:
6356 this->sprite = 740;
6357 break;
6358 case AUTOMATON:
6359 this->sprite = 748;
6360 break;
6361 case TROLL:
6362 this->sprite = 821;
6363 break;
6364 default:
6365 break;
6366 }
6367 }
6368 }
6369 else if ( limbType == LIMB_HUMANOID_RIGHTARM )
6370 {
6371 if ( playerRace == HUMAN )
6372 {
6373 switch ( playerAppearance / 6 )
6374 {
6375 case 1:
6376 this->sprite = 337 + 13 * stats[playernum]->sex;
6377 break;
6378 case 2:
6379 this->sprite = 363 + 13 * stats[playernum]->sex;
6380 break;
6381 default:
6382 this->sprite = 109 + 12 * stats[playernum]->sex;
6383 break;
6384 }
6385 }
6386 else
6387 {
6388 switch ( playerRace )
6389 {
6390 case SKELETON:
6391 this->sprite = 689;
6392 break;
6393 case GOBLIN:
6394 this->sprite = 697;
6395 break;
6396 case CREATURE_IMP:
6397 this->sprite = 832;
6398 break;
6399 case INCUBUS:
6400 this->sprite = 705;
6401 break;
6402 case SUCCUBUS:
6403 this->sprite = 713;
6404 break;
6405 case VAMPIRE:
6406 this->sprite = 721;
6407 break;
6408 case INSECTOID:
6409 if ( stats[playernum]->sex == FEMALE )
6410 {
6411 this->sprite = 763;
6412 }
6413 else
6414 {
6415 this->sprite = 729;
6416 }
6417 break;
6418 case GOATMAN:
6419 this->sprite = 737;
6420 break;
6421 case AUTOMATON:
6422 this->sprite = 745;
6423 break;
6424 case TROLL:
6425 this->sprite = 820;
6426 break;
6427 default:
6428 break;
6429 }
6430 }
6431 }
6432 else if ( limbType == LIMB_HUMANOID_LEFTARM )
6433 {
6434 if ( playerRace == HUMAN )
6435 {
6436 switch ( playerAppearance / 6 )
6437 {
6438 case 1:
6439 this->sprite = 338 + 13 * stats[playernum]->sex;
6440 break;
6441 case 2:
6442 this->sprite = 364 + 13 * stats[playernum]->sex;
6443 break;
6444 default:
6445 this->sprite = 110 + 12 * stats[playernum]->sex;
6446 break;
6447 }
6448 }
6449 else
6450 {
6451 switch ( playerRace )
6452 {
6453 case SKELETON:
6454 this->sprite = 688;
6455 break;
6456 case GOBLIN:
6457 this->sprite = 696;
6458 break;
6459 case CREATURE_IMP:
6460 this->sprite = 831;
6461 break;
6462 case INCUBUS:
6463 this->sprite = 704;
6464 break;
6465 case SUCCUBUS:
6466 this->sprite = 712;
6467 break;
6468 case VAMPIRE:
6469 this->sprite = 720;
6470 break;
6471 case INSECTOID:
6472 if ( stats[playernum]->sex == FEMALE )
6473 {
6474 this->sprite = 762;
6475 }
6476 else
6477 {
6478 this->sprite = 728;
6479 }
6480 break;
6481 case GOATMAN:
6482 this->sprite = 736;
6483 break;
6484 case AUTOMATON:
6485 this->sprite = 744;
6486 break;
6487 case TROLL:
6488 this->sprite = 819;
6489 break;
6490 default:
6491 break;
6492 }
6493 }
6494 }
6495 }
6496
playerRequiresBloodToSustain()6497 bool Entity::playerRequiresBloodToSustain()
6498 {
6499 if ( behavior != &actPlayer )
6500 {
6501 return false;
6502 }
6503 if ( !stats[skill[2]] )
6504 {
6505 return false;
6506 }
6507
6508 if ( stats[skill[2]]->type == VAMPIRE )
6509 {
6510 return true;
6511 }
6512 if ( stats[skill[2]]->EFFECTS[EFF_VAMPIRICAURA] || client_classes[skill[2]] == CLASS_ACCURSED )
6513 {
6514 return true;
6515 }
6516 if ( stats[skill[2]]->playerRace == VAMPIRE && stats[skill[2]]->appearance == 0 )
6517 {
6518 return true;
6519 }
6520
6521 return false;
6522 }
6523
playerAnimateRat(Entity * my)6524 void playerAnimateRat(Entity* my)
6525 {
6526 node_t* node = nullptr;
6527 int bodypart = 0;
6528 for ( bodypart = 0, node = my->children.first; node != NULL; node = node->next, bodypart++ )
6529 {
6530 if ( bodypart == 0 )
6531 {
6532 my->focalx = -2;
6533 continue;
6534 }
6535 Entity* entity = (Entity*)node->element;
6536 entity->x = my->x;
6537 entity->y = my->y;
6538 entity->z = my->z;
6539 if ( bodypart == 1 )
6540 {
6541 if ( entity->sprite != 815 && entity->sprite != 816 )
6542 {
6543 entity->sprite = 815;
6544 }
6545 entity->focalx = -2;
6546 if ( fabs(PLAYER_VELX) > 0.1 || fabs(PLAYER_VELY) > 0.1 )
6547 {
6548 if ( (ticks % 10 == 0) )
6549 {
6550 if ( entity->sprite == 815 )
6551 {
6552 entity->sprite = 816;
6553 }
6554 else
6555 {
6556 entity->sprite = 815;
6557 }
6558 }
6559 }
6560 }
6561 else if ( bodypart > 1 )
6562 {
6563 entity->flags[INVISIBLE] = true;
6564 }
6565 if ( multiplayer == SERVER )
6566 {
6567 // update sprites for clients
6568 if ( entity->skill[10] != entity->sprite )
6569 {
6570 entity->skill[10] = entity->sprite;
6571 serverUpdateEntityBodypart(my, bodypart);
6572 }
6573 if ( entity->skill[11] != entity->flags[INVISIBLE] )
6574 {
6575 entity->skill[11] = entity->flags[INVISIBLE];
6576 serverUpdateEntityBodypart(my, bodypart);
6577 }
6578 if ( PLAYER_ALIVETIME == TICKS_PER_SECOND + bodypart )
6579 {
6580 serverUpdateEntityBodypart(my, bodypart);
6581 }
6582 }
6583 entity->yaw = my->yaw;
6584 }
6585 }
6586
playerAnimateSpider(Entity * my)6587 void playerAnimateSpider(Entity* my)
6588 {
6589 node_t* node = nullptr;
6590 int bodypart = 0;
6591 for ( bodypart = 0, node = my->children.first; node != NULL; node = node->next, bodypart++ )
6592 {
6593 Entity* entity = (Entity*)node->element;
6594 if ( bodypart == 0 )
6595 {
6596 // hudweapon case
6597 continue;
6598 }
6599 if ( bodypart < 11 )
6600 {
6601 entity->flags[INVISIBLE] = true;
6602 if ( multiplayer == SERVER )
6603 {
6604 // update sprites for clients
6605 if ( entity->skill[10] != entity->sprite )
6606 {
6607 entity->skill[10] = entity->sprite;
6608 serverUpdateEntityBodypart(my, bodypart);
6609 }
6610 if ( entity->skill[11] != entity->flags[INVISIBLE] )
6611 {
6612 entity->skill[11] = entity->flags[INVISIBLE];
6613 serverUpdateEntityBodypart(my, bodypart);
6614 }
6615 if ( PLAYER_ALIVETIME == TICKS_PER_SECOND + bodypart )
6616 {
6617 serverUpdateEntityBodypart(my, bodypart);
6618 }
6619 }
6620 continue;
6621 }
6622 Entity* previous = NULL; // previous part
6623 if ( bodypart > 11 )
6624 {
6625 previous = (Entity*)node->prev->element;
6626 }
6627 entity->x = my->x;
6628 entity->y = my->y;
6629 entity->z = my->z;
6630 entity->yaw = my->yaw;
6631
6632 entity->roll = my->roll;
6633
6634 if ( bodypart > 12 )
6635 {
6636 entity->pitch = -my->pitch;
6637 entity->pitch = std::max(-PI / 32, std::min(PI / 32, entity->pitch));
6638 if ( bodypart % 2 == 0 )
6639 {
6640 entity->sprite = 826;
6641 entity->focalx = limbs[SPIDER][4][0]; // 3
6642 entity->focaly = limbs[SPIDER][4][1]; // 0
6643 entity->focalz = limbs[SPIDER][4][2]; // 0
6644 }
6645 else
6646 {
6647 entity->sprite = 825;
6648 entity->focalx = limbs[SPIDER][3][0]; // 1
6649 entity->focaly = limbs[SPIDER][3][1]; // 0
6650 entity->focalz = limbs[SPIDER][3][2]; // -1
6651 }
6652 }
6653
6654 if ( bodypart == 11 || bodypart == 12 )
6655 {
6656 if ( PLAYER_ATTACK >= 1 && PLAYER_ATTACK <= 3 )
6657 {
6658 // vertical chop
6659 if ( PLAYER_ATTACKTIME == 0 )
6660 {
6661 entity->pitch = 0;
6662 entity->roll = 0;
6663 entity->skill[1] = 0;
6664 }
6665 else
6666 {
6667 if ( entity->skill[1] == 0 )
6668 {
6669 // upswing
6670 if ( limbAnimateToLimit(entity, ANIMATE_PITCH, -0.5, 5 * PI / 4, false, 0.0) )
6671 {
6672 entity->skill[1] = 1;
6673 }
6674 }
6675 else
6676 {
6677 if ( entity->pitch >= 3 * PI / 2 )
6678 {
6679 PLAYER_ARMBENDED = 1;
6680 }
6681 if ( limbAnimateToLimit(entity, ANIMATE_PITCH, 0.3, 0, false, 0.0) )
6682 {
6683 entity->skill[1] = 0;
6684 entity->roll = 0;
6685 PLAYER_ARMBENDED = 0;
6686 PLAYER_ATTACK = 0;
6687 }
6688 }
6689 }
6690 }
6691 }
6692
6693 entity->flags[INVISIBLE] = my->flags[INVISIBLE];
6694
6695 switch ( bodypart )
6696 {
6697 // right pedipalp
6698 case 11:
6699 entity->sprite = 824;
6700 entity->focalx = limbs[SPIDER][1][0]; // 1
6701 entity->focaly = limbs[SPIDER][1][1]; // 0
6702 entity->focalz = limbs[SPIDER][1][2]; // 1
6703 entity->x += cos(my->yaw) * 2 + cos(my->yaw + PI / 2) * 2;
6704 entity->y += sin(my->yaw) * 2 + sin(my->yaw + PI / 2) * 2;
6705 entity->yaw += PI / 10;
6706 if ( PLAYER_ATTACK == 0 )
6707 {
6708 entity->pitch = my->pitch;
6709 entity->pitch -= PI / 8;
6710 }
6711 break;
6712 // left pedipalp
6713 case 12:
6714 entity->sprite = 824;
6715 entity->focalx = limbs[SPIDER][2][0]; // 1
6716 entity->focaly = limbs[SPIDER][2][1]; // 0
6717 entity->focalz = limbs[SPIDER][2][2]; // 1
6718 entity->x += cos(my->yaw) * 2 - cos(my->yaw + PI / 2) * 2;
6719 entity->y += sin(my->yaw) * 2 - sin(my->yaw + PI / 2) * 2;
6720 entity->yaw -= PI / 10;
6721 if ( PLAYER_ATTACK == 0 )
6722 {
6723 entity->pitch = my->pitch;
6724 entity->pitch -= PI / 8;
6725 }
6726 break;
6727
6728 // 1st/5th leg:
6729 // thigh
6730 case 13:
6731 case 21:
6732 entity->x += cos(my->yaw) * 1 + cos(my->yaw + PI / 2) * 2.5 * (1 - 2 * (bodypart > 20));
6733 entity->y += sin(my->yaw) * 1 + sin(my->yaw + PI / 2) * 2.5 * (1 - 2 * (bodypart > 20));
6734 if ( (fabs(PLAYER_VELX) > 0.1 || fabs(PLAYER_VELY) > 0.1) )
6735 {
6736 if ( !entity->skill[0] )
6737 {
6738 entity->fskill[0] += .1;
6739 if ( entity->fskill[0] >= 1 )
6740 {
6741 entity->fskill[0] = 1;
6742 entity->skill[0] = 1;
6743 }
6744 }
6745 else
6746 {
6747 entity->fskill[0] -= .1;
6748 if ( entity->fskill[0] <= 0 )
6749 {
6750 entity->fskill[0] = 0;
6751 entity->skill[0] = 0;
6752 }
6753 }
6754 }
6755 entity->z += entity->fskill[0];
6756 entity->yaw += PI / 6 * (1 - 2 * (bodypart > 20));
6757 entity->pitch += PI / 4;
6758 break;
6759 // shin
6760 case 14:
6761 case 22:
6762 entity->x = previous->x;
6763 entity->y = previous->y;
6764 entity->z = previous->z;
6765 entity->yaw = previous->yaw;
6766 entity->pitch = previous->pitch;
6767 entity->x += cos(my->yaw) * 3 + cos(my->yaw + PI / 2) * 2 * (1 - 2 * (bodypart > 20));
6768 entity->y += sin(my->yaw) * 3 + sin(my->yaw + PI / 2) * 2 * (1 - 2 * (bodypart > 20));
6769 entity->z += .5;
6770 entity->pitch += PI / 6 - PI / 4;
6771 entity->pitch -= (PI / 10) * (previous->z - my->z);
6772 break;
6773
6774 // 2nd/6th leg:
6775 // thigh
6776 case 15:
6777 case 23:
6778 entity->x += cos(my->yaw + PI / 2) * 3 * (1 - 2 * (bodypart > 20));
6779 entity->y += sin(my->yaw + PI / 2) * 3 * (1 - 2 * (bodypart > 20));
6780 if ( (fabs(PLAYER_VELX) > 0.1 || fabs(PLAYER_VELY) > 0.1) )
6781 {
6782 if ( !entity->skill[0] )
6783 {
6784 entity->fskill[0] += .1;
6785 if ( entity->fskill[0] >= 1 )
6786 {
6787 entity->fskill[0] = 1;
6788 entity->skill[0] = 1;
6789 }
6790 }
6791 else
6792 {
6793 entity->fskill[0] -= .1;
6794 if ( entity->fskill[0] <= 0 )
6795 {
6796 entity->fskill[0] = 0;
6797 entity->skill[0] = 0;
6798 }
6799 }
6800 }
6801 entity->z += entity->fskill[0];
6802 entity->yaw += PI / 3 * (1 - 2 * (bodypart > 20));
6803 entity->pitch += PI / 4;
6804 break;
6805 // shin
6806 case 16:
6807 case 24:
6808 entity->x = previous->x;
6809 entity->y = previous->y;
6810 entity->z = previous->z;
6811 entity->yaw = previous->yaw;
6812 entity->pitch = previous->pitch;
6813 entity->x += cos(my->yaw) * 1.75 + cos(my->yaw + PI / 2) * 3 * (1 - 2 * (bodypart > 20));
6814 entity->y += sin(my->yaw) * 1.75 + sin(my->yaw + PI / 2) * 3 * (1 - 2 * (bodypart > 20));
6815 entity->z += .5;
6816 entity->pitch += PI / 6 - PI / 4;
6817 entity->pitch -= (PI / 10) * (previous->z - my->z);
6818 break;
6819
6820 // 3rd/7th leg:
6821 // thigh
6822 case 17:
6823 case 25:
6824 entity->x += cos(my->yaw) * -.5 + cos(my->yaw + PI / 2) * 2 * (1 - 2 * (bodypart > 20));
6825 entity->y += sin(my->yaw) * -.5 + sin(my->yaw + PI / 2) * 2 * (1 - 2 * (bodypart > 20));
6826 if ( (fabs(PLAYER_VELX) > 0.1 || fabs(PLAYER_VELY) > 0.1) )
6827 {
6828 if ( !entity->skill[0] )
6829 {
6830 entity->fskill[0] += .1;
6831 if ( entity->fskill[0] >= 1 )
6832 {
6833 entity->fskill[0] = 1;
6834 entity->skill[0] = 1;
6835 }
6836 }
6837 else
6838 {
6839 entity->fskill[0] -= .1;
6840 if ( entity->fskill[0] <= 0 )
6841 {
6842 entity->fskill[0] = 0;
6843 entity->skill[0] = 0;
6844 }
6845 }
6846 }
6847 entity->z += entity->fskill[0];
6848 entity->yaw += (PI / 2 + PI / 8) * (1 - 2 * (bodypart > 20));
6849 entity->pitch += PI / 4;
6850 break;
6851 // shin
6852 case 18:
6853 case 26:
6854 entity->x = previous->x;
6855 entity->y = previous->y;
6856 entity->z = previous->z;
6857 entity->yaw = previous->yaw;
6858 entity->pitch = previous->pitch;
6859 entity->x += cos(my->yaw) * -1.25 + cos(my->yaw + PI / 2) * 3.25 * (1 - 2 * (bodypart > 20));
6860 entity->y += sin(my->yaw) * -1.25 + sin(my->yaw + PI / 2) * 3.25 * (1 - 2 * (bodypart > 20));
6861 entity->z += .5;
6862 entity->pitch += PI / 6 - PI / 4;
6863 entity->pitch -= (PI / 10) * (previous->z - my->z);
6864 break;
6865
6866 // 4th/8th leg:
6867 // thigh
6868 case 19:
6869 case 27:
6870 entity->x += cos(my->yaw) * -.5 + cos(my->yaw + PI / 2) * 2 * (1 - 2 * (bodypart > 20));
6871 entity->y += sin(my->yaw) * -.5 + sin(my->yaw + PI / 2) * 2 * (1 - 2 * (bodypart > 20));
6872 if ( (fabs(PLAYER_VELX) > 0.1 || fabs(PLAYER_VELY) > 0.1) )
6873 {
6874 if ( !entity->skill[0] )
6875 {
6876 entity->fskill[0] += .1;
6877 if ( entity->fskill[0] >= 1 )
6878 {
6879 entity->fskill[0] = 1;
6880 entity->skill[0] = 1;
6881 }
6882 }
6883 else
6884 {
6885 entity->fskill[0] -= .1;
6886 if ( entity->fskill[0] <= 0 )
6887 {
6888 entity->fskill[0] = 0;
6889 entity->skill[0] = 0;
6890 }
6891 }
6892 }
6893 entity->z += entity->fskill[0];
6894 entity->yaw += (PI / 2 + PI / 3) * (1 - 2 * (bodypart > 20));
6895 entity->pitch += PI / 4;
6896 break;
6897 // shin
6898 case 20:
6899 case 28:
6900 entity->x = previous->x;
6901 entity->y = previous->y;
6902 entity->z = previous->z;
6903 entity->yaw = previous->yaw;
6904 entity->pitch = previous->pitch;
6905 entity->x += cos(my->yaw) * -3 + cos(my->yaw + PI / 2) * 1.75 * (1 - 2 * (bodypart > 20));
6906 entity->y += sin(my->yaw) * -3 + sin(my->yaw + PI / 2) * 1.75 * (1 - 2 * (bodypart > 20));
6907 entity->z += .5;
6908 entity->pitch += PI / 6 - PI / 4;
6909 entity->pitch += (PI / 10) * (previous->z - my->z);
6910 break;
6911 default:
6912 //entity->flags[INVISIBLE] = true; // for debugging
6913 break;
6914 }
6915 }
6916 }