1 /*-------------------------------------------------------------------------------
2
3 BARONY
4 File: acthudweapon.cpp
5 Desc: behavior function for hud weapon (first person models)
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 "items.hpp"
16 #include "sound.hpp"
17 #include "magic/magic.hpp"
18 #include "interface/interface.hpp"
19 #include "net.hpp"
20 #include "collision.hpp"
21 #include "player.hpp"
22 #include "scores.hpp"
23
24 Entity* hudweapon = nullptr;
25 Entity* hudarm = nullptr;
26 bool weaponSwitch = false;
27 bool shieldSwitch = false;
28
29 Sint32 throwGimpTimer = 0; // player cannot throw objects unless zero
30 Sint32 pickaxeGimpTimer = 0; // player cannot swap weapons immediately after using pickaxe
31 // due to multiplayer weapon degrade lag... equipping new weapon before degrade
32 // message hits can degrade the wrong weapon.
33 Sint32 swapWeaponGimpTimer = 0; // player cannot swap weapons unless zero
34 Sint32 bowGimpTimer = 0; // can't draw bow unless zero.
35
36 /*-------------------------------------------------------------------------------
37
38 act*
39
40 The following function describes an entity behavior. The function
41 takes a pointer to the entity that uses it as an argument.
42
43 -------------------------------------------------------------------------------*/
44
45 #define HUD_SHAPESHIFT_HIDE my->skill[12]
46 #define HUD_LASTSHAPESHIFT_FORM my->skill[13]
47
actHudArm(Entity * my)48 void actHudArm(Entity* my)
49 {
50 hudarm = my;
51 Entity* parent = hudweapon;
52
53 if (players[clientnum] == nullptr || players[clientnum]->entity == nullptr)
54 {
55 hudarm = nullptr;
56 list_RemoveNode(my->mynode);
57 return;
58 }
59
60 if (stats[clientnum]->HP <= 0)
61 {
62 hudarm = nullptr;
63 list_RemoveNode(my->mynode);
64 return;
65 }
66
67 if (parent == nullptr)
68 {
69 hudarm = nullptr;
70 list_RemoveNode(my->mynode);
71 return;
72 }
73
74 // sprite
75 my->scalex = 1.f;
76 my->scaley = 1.f;
77 my->scalez = 1.f;
78 // position
79 my->x = parent->x;
80 my->y = parent->y;
81 my->z = parent->z - 2.5;
82
83 Monster playerRace = players[clientnum]->entity->getMonsterFromPlayerRace(stats[clientnum]->playerRace);
84 int playerAppearance = stats[clientnum]->appearance;
85 if ( players[clientnum]->entity->effectShapeshift != NOTHING )
86 {
87 playerRace = static_cast<Monster>(players[clientnum]->entity->effectShapeshift);
88 if ( playerRace == RAT || playerRace == SPIDER )
89 {
90 HUD_SHAPESHIFT_HIDE = 1;
91 }
92 }
93 else if ( players[clientnum]->entity->effectPolymorph != NOTHING )
94 {
95 if ( players[clientnum]->entity->effectPolymorph > NUMMONSTERS )
96 {
97 playerRace = HUMAN;
98 playerAppearance = players[clientnum]->entity->effectPolymorph - 100;
99 }
100 else
101 {
102 playerRace = static_cast<Monster>(players[clientnum]->entity->effectPolymorph);
103 }
104 }
105
106 // when reverting form, render main limb as invisible for 1 tick as it's position needs to settle.
107 if ( HUD_LASTSHAPESHIFT_FORM != playerRace )
108 {
109 if ( HUD_SHAPESHIFT_HIDE > 0 )
110 {
111 my->flags[INVISIBLE] = true;
112 --HUD_SHAPESHIFT_HIDE;
113 }
114 }
115 HUD_LASTSHAPESHIFT_FORM = playerRace;
116
117 bool noGloves = false;
118 bool hideWeapon = false;
119 if (stats[clientnum]->gloves == nullptr
120 || playerRace == SPIDER
121 || playerRace == RAT
122 || playerRace == CREATURE_IMP
123 || playerRace == TROLL )
124 {
125 noGloves = true;
126 hideWeapon = true;
127 }
128 else
129 {
130 if ( stats[clientnum]->gloves->type == GLOVES || stats[clientnum]->gloves->type == GLOVES_DEXTERITY )
131 {
132 my->sprite = 637;
133 }
134 else if ( stats[clientnum]->gloves->type == BRACERS || stats[clientnum]->gloves->type == BRACERS_CONSTITUTION )
135 {
136 my->sprite = 638;
137 }
138 else if ( stats[clientnum]->gloves->type == GAUNTLETS || stats[clientnum]->gloves->type == GAUNTLETS_STRENGTH )
139 {
140 my->sprite = 639;
141 }
142 else if ( stats[clientnum]->gloves->type == BRASS_KNUCKLES )
143 {
144 my->sprite = 640;
145 }
146 else if ( stats[clientnum]->gloves->type == IRON_KNUCKLES )
147 {
148 my->sprite = 641;
149 }
150 else if ( stats[clientnum]->gloves->type == SPIKED_GAUNTLETS )
151 {
152 my->sprite = 642;
153 }
154 else if ( stats[clientnum]->gloves->type == CRYSTAL_GLOVES )
155 {
156 my->sprite = 591;
157 }
158 else if ( stats[clientnum]->gloves->type == ARTIFACT_GLOVES )
159 {
160 my->sprite = 590;
161 }
162 else if ( stats[clientnum]->gloves->type == SUEDE_GLOVES )
163 {
164 my->sprite = 802;
165 }
166 if ( stats[clientnum]->weapon == nullptr )
167 {
168 my->scalex = 0.5f;
169 my->scaley = 0.5f;
170 my->scalez = 0.5f;
171 my->z -= 0.75;
172 //my->x += 0.5 * cos(parent->yaw);
173 //my->y += 0.5 * sin(parent->yaw);
174 }
175 }
176
177 if ( noGloves )
178 {
179 switch ( playerRace )
180 {
181 case SKELETON:
182 my->sprite = 774;
183 break;
184 case INCUBUS:
185 my->sprite = 776;
186 break;
187 case SUCCUBUS:
188 my->sprite = 778;
189 break;
190 case GOBLIN:
191 my->sprite = 780;
192 break;
193 case AUTOMATON:
194 my->sprite = 782;
195 break;
196 case INSECTOID:
197 if ( stats[clientnum]->sex == FEMALE )
198 {
199 my->sprite = 786;
200 }
201 else
202 {
203 my->sprite = 784;
204 }
205 break;
206 case GOATMAN:
207 my->sprite = 788;
208 break;
209 case VAMPIRE:
210 my->sprite = 790;
211 break;
212 case HUMAN:
213 if ( playerAppearance / 6 == 0 )
214 {
215 my->sprite = 634;
216 }
217 else if ( playerAppearance / 6 == 1 )
218 {
219 my->sprite = 635;
220 }
221 else
222 {
223 my->sprite = 636;
224 }
225 break;
226 case TROLL:
227 my->sprite = 855;
228 break;
229 case SPIDER:
230 my->sprite = 853;
231 break;
232 case CREATURE_IMP:
233 my->sprite = 857;
234 break;
235 case RAT:
236 my->sprite = 859;
237 break;
238 default:
239 my->sprite = 634;
240 break;
241 }
242
243 if ( stats[clientnum]->weapon == nullptr || hideWeapon )
244 {
245 my->scalex = 0.5f;
246 my->scaley = 0.5f;
247 my->scalez = 0.5f;
248 my->z -= 0.75;
249 //my->x += 0.5 * cos(parent->yaw);
250 //my->y += 0.5 * sin(parent->yaw);
251 }
252 }
253
254 // rotation
255 //my->yaw = atan2( my->y-camera.y*16, my->x-camera.x*16 );
256 //my->fskill[0] = sqrt( pow(my->x-camera.x*16,2) + pow(my->y-camera.y*16,2) );
257 //my->pitch = atan2( my->z-camera.z*.5, my->fskill[0] );
258 //messagePlayer(0, "my y: %f, my z: %f", my->y, my->z);
259 my->focalx = 0;
260 my->focaly = 0;
261 my->focalz = -1.5;
262 if ( playerRace == RAT )
263 {
264 my->pitch = 0.f;
265 my->yaw = parent->yaw + players[clientnum]->entity->fskill[10]; // PLAYER_SIDEBOB
266 my->scalex = 1.f;
267 my->scaley = 1.f;
268 my->scalez = 1.f;
269 my->x += -4.f;
270 my->y += -3.f;
271 my->z += 5.5;
272 }
273 else if ( playerRace == SPIDER )
274 {
275 my->pitch = parent->pitch;
276 my->yaw = parent->yaw + players[clientnum]->entity->fskill[10];
277 my->scalex = 1.f;
278 my->scaley = 1.f;
279 my->scalez = 1.f;
280 my->x += 1;
281 my->y += 1;
282 my->z += 2;
283 my->focalz = -1;
284 }
285 else
286 {
287 my->yaw = -2 * PI / 32;
288 my->pitch = -17 * PI / 32;
289 }
290 }
291
292 bool bowFire = false;
293 bool bowIsBeingDrawn = false;
294 Uint32 bowStartDrawingTick = 0;
295 Uint32 bowDrawBaseTicks = 50;
296 #ifdef USE_FMOD
297 FMOD_CHANNEL* bowDrawingSoundChannel = NULL;
298 FMOD_BOOL bowDrawingSoundPlaying = 0;
299 #elif defined USE_OPENAL
300 OPENAL_SOUND* bowDrawingSoundChannel = NULL;
301 ALboolean bowDrawingSoundPlaying = 0;
302 #endif
303
304 #define HUDWEAPON_CHOP my->skill[0]
305 #define HUDWEAPON_INIT my->skill[1]
306 #define HUDWEAPON_CHARGE my->skill[3]
307 #define HUDWEAPON_OVERCHARGE my->skill[4]
308 #define HUDWEAPON_WHIP_ANGLE my->skill[5]
309 #define HUDWEAPON_HIDEWEAPON my->skill[6]
310 #define HUDWEAPON_SHOOTING_RANGED_WEAPON my->skill[7]
311 #define HUDWEAPON_CROSSBOW_RELOAD_ANIMATION my->skill[8]
312 #define HUDWEAPON_BOW_HAS_QUIVER my->skill[9]
313 #define HUDWEAPON_BOW_FORCE_RELOAD my->skill[10]
314 #define HUDWEAPON_PLAYERNUM my->skill[11]
315 #define HUDWEAPON_MOVEX my->fskill[0]
316 #define HUDWEAPON_MOVEY my->fskill[1]
317 #define HUDWEAPON_MOVEZ my->fskill[2]
318 #define HUDWEAPON_YAW my->fskill[3]
319 #define HUDWEAPON_PITCH my->fskill[4]
320 #define HUDWEAPON_ROLL my->fskill[5]
321 #define HUDWEAPON_OLDVIBRATEX my->fskill[6]
322 #define HUDWEAPON_OLDVIBRATEY my->fskill[7]
323 #define HUDWEAPON_OLDVIBRATEZ my->fskill[8]
324
325 enum CrossbowAnimState : int
326 {
327 CROSSBOW_ANIM_NONE,
328 CROSSBOW_ANIM_SHOOT,
329 CROSSBOW_ANIM_RELOAD_START,
330 CROSSBOW_ANIM_RELOAD_END,
331 CROSSBOW_ANIM_SWAPPED_WEAPON,
332 CROSSBOW_ANIM_HEAVY_RELOAD_MIDPOINT
333 };
334
335 enum RangedWeaponAnimState : int
336 {
337 RANGED_ANIM_IDLE,
338 RANGED_ANIM_BEING_DRAWN,
339 RANGED_ANIM_FIRED
340 };
341
342 enum CrossbowHudweaponChop : int
343 {
344 CROSSBOW_CHOP_RELOAD_START = 16,
345 CROSSBOW_CHOP_RELOAD_ENDING = 17
346 };
347
348 Uint32 hudweaponuid = 0;
actHudWeapon(Entity * my)349 void actHudWeapon(Entity* my)
350 {
351 double result = 0;
352 ItemType type;
353 bool wearingring = false;
354 Entity* entity;
355 Entity* parent = hudarm;
356
357 auto& camera_shakex = cameravars[HUDWEAPON_PLAYERNUM].shakex;
358 auto& camera_shakey = cameravars[HUDWEAPON_PLAYERNUM].shakey;
359 auto& camera_shakex2 = cameravars[HUDWEAPON_PLAYERNUM].shakex2;
360 auto& camera_shakey2 = cameravars[HUDWEAPON_PLAYERNUM].shakey2;
361
362 // isn't active during intro/menu sequence
363 if ( intro == true )
364 {
365 my->flags[INVISIBLE] = true;
366 return;
367 }
368
369 if ( multiplayer == CLIENT )
370 {
371 if ( stats[clientnum]->HP <= 0 )
372 {
373 my->flags[INVISIBLE] = true;
374 return;
375 }
376 }
377
378 // initialize
379 if ( !HUDWEAPON_INIT )
380 {
381 HUDWEAPON_INIT = 1;
382 hudweapon = my;
383 hudweaponuid = my->getUID();
384 entity = newEntity(109, 1, map.entities, nullptr); // malearmright.vox
385 entity->focalz = -1.5;
386 entity->parent = my->getUID();
387 my->parent = entity->getUID(); // just an easy way to refer to eachother, doesn't mean much
388 hudarm = entity;
389 parent = hudarm;
390 entity->behavior = &actHudArm;
391 entity->flags[OVERDRAW] = true;
392 entity->flags[PASSABLE] = true;
393 entity->flags[NOUPDATE] = true;
394 }
395
396 if ( players[clientnum] == nullptr || players[clientnum]->entity == nullptr
397 || (players[clientnum]->entity && players[clientnum]->entity->playerCreatedDeathCam != 0) )
398 {
399 hudweapon = nullptr; //PLAYER DED. NULLIFY THIS.
400 list_RemoveNode(my->mynode);
401 return;
402 }
403
404 // reduce throwGimpTimer (allows player to throw items again)
405 if ( throwGimpTimer > 0 )
406 {
407 --throwGimpTimer;
408 }
409 if ( pickaxeGimpTimer > 0 )
410 {
411 //messagePlayer(clientnum, "%d", pickaxeGimpTimer);
412 --pickaxeGimpTimer;
413 }
414 if ( swapWeaponGimpTimer > 0 )
415 {
416 --swapWeaponGimpTimer;
417 }
418 if ( bowGimpTimer > 0 )
419 {
420 --bowGimpTimer;
421 }
422
423 // check levitating value
424 bool levitating = isLevitating(stats[clientnum]);
425
426 // swimming
427 if (players[clientnum] && players[clientnum]->entity)
428 {
429 if ( isPlayerSwimming(players[clientnum]->entity) || players[clientnum]->entity->skill[13] != 0 ) //skill[13] PLAYER_INWATER
430 {
431 my->flags[INVISIBLE] = true;
432 if (parent)
433 {
434 parent->flags[INVISIBLE] = true;
435 }
436 return;
437 }
438 }
439
440 Monster playerRace = players[clientnum]->entity->getMonsterFromPlayerRace(stats[clientnum]->playerRace);
441 int playerAppearance = stats[clientnum]->appearance;
442 if ( players[clientnum]->entity->effectShapeshift != NOTHING )
443 {
444 playerRace = static_cast<Monster>(players[clientnum]->entity->effectShapeshift);
445 }
446 else if ( players[clientnum]->entity->effectPolymorph != NOTHING )
447 {
448 if ( players[clientnum]->entity->effectPolymorph > NUMMONSTERS )
449 {
450 playerRace = HUMAN;
451 playerAppearance = players[clientnum]->entity->effectPolymorph - 100;
452 }
453 else
454 {
455 playerRace = static_cast<Monster>(players[clientnum]->entity->effectPolymorph);
456 }
457 }
458
459 if ( playerRace == RAT || playerRace == SPIDER )
460 {
461 HUD_SHAPESHIFT_HIDE = std::min(2, HUD_SHAPESHIFT_HIDE + 1);
462 }
463
464 bool hideWeapon = false;
465 if ( playerRace == SPIDER
466 || playerRace == RAT
467 || playerRace == CREATURE_IMP
468 || playerRace == TROLL )
469 {
470 hideWeapon = true;
471 if ( playerRace == CREATURE_IMP && stats[clientnum]->weapon && itemCategory(stats[clientnum]->weapon) == MAGICSTAFF )
472 {
473 hideWeapon = false;
474 }
475 }
476 else
477 {
478 if ( HUD_SHAPESHIFT_HIDE > 0 )
479 {
480 --HUD_SHAPESHIFT_HIDE;
481 }
482 }
483
484 HUDWEAPON_HIDEWEAPON = hideWeapon;
485 HUDWEAPON_SHOOTING_RANGED_WEAPON = RANGED_ANIM_IDLE;
486
487 bool rangedweapon = false;
488 if ( stats[clientnum]->weapon && !hideWeapon )
489 {
490 if ( isRangedWeapon(*stats[clientnum]->weapon) )
491 {
492 rangedweapon = true;
493 }
494 }
495
496 // select model
497 if ( stats[clientnum]->ring != nullptr )
498 {
499 if ( stats[clientnum]->ring->type == RING_INVISIBILITY )
500 {
501 wearingring = true;
502 }
503 }
504 if ( stats[clientnum]->cloak != nullptr )
505 {
506 if ( stats[clientnum]->cloak->type == CLOAK_INVISIBILITY )
507 {
508 wearingring = true;
509 }
510 }
511 if ( players[clientnum]->entity->skill[3] == 1 || players[clientnum]->entity->isInvisible() ) // debug cam or player invisible
512 {
513 my->flags[INVISIBLE] = true;
514 if (parent != nullptr)
515 {
516 parent->flags[INVISIBLE] = true;
517 }
518 }
519 else
520 {
521 if ( stats[clientnum]->weapon == nullptr || hideWeapon )
522 {
523 my->flags[INVISIBLE] = true;
524 if (parent != nullptr)
525 {
526 parent->flags[INVISIBLE] = false;
527 }
528 }
529 else
530 {
531 if ( stats[clientnum]->weapon )
532 {
533 if ( stats[clientnum]->weapon->type == TOOL_WHIP )
534 {
535 my->scalex = 0.6;
536 my->scaley = 0.6;
537 my->scalez = 0.6;
538 }
539 else if ( stats[clientnum]->weapon->type == TOOL_DECOY
540 || stats[clientnum]->weapon->type == TOOL_DUMMYBOT )
541 {
542 my->scalex = 0.8;
543 my->scaley = 0.8;
544 my->scalez = 0.8;
545 }
546 else if ( stats[clientnum]->weapon->type == TOOL_SENTRYBOT
547 || stats[clientnum]->weapon->type == TOOL_SPELLBOT )
548 {
549 my->scalex = 0.7;
550 my->scaley = 0.7;
551 my->scalez = 0.7;
552 }
553 else if ( (stats[clientnum]->weapon->type >= TOOL_BOMB && stats[clientnum]->weapon->type <= TOOL_TELEPORT_BOMB)
554 || stats[clientnum]->weapon->type == FOOD_CREAMPIE )
555 {
556 my->scalex = 1.f;
557 my->scaley = 1.f;
558 my->scalez = 1.f;
559 }
560 else if ( itemModelFirstperson(stats[clientnum]->weapon) != itemModel(stats[clientnum]->weapon) )
561 {
562 my->scalex = 0.5f;
563 my->scaley = 0.5f;
564 my->scalez = 0.5f;
565 }
566 else
567 {
568 my->scalex = 1.f;
569 my->scaley = 1.f;
570 my->scalez = 1.f;
571 }
572 }
573 my->sprite = itemModelFirstperson(stats[clientnum]->weapon);
574
575 if ( bowIsBeingDrawn && !(stats[clientnum]->weapon && stats[clientnum]->weapon->type == HEAVY_CROSSBOW) )
576 {
577 HUDWEAPON_SHOOTING_RANGED_WEAPON = RANGED_ANIM_BEING_DRAWN;
578 if ( (my->ticks - bowStartDrawingTick) >= bowDrawBaseTicks / 4 )
579 {
580 if ( rangedweapon )
581 {
582 if ( stats[clientnum]->weapon
583 && stats[clientnum]->weapon->type != CROSSBOW )
584 {
585 my->sprite++;
586 }
587 HUDWEAPON_SHOOTING_RANGED_WEAPON = RANGED_ANIM_FIRED;
588 }
589 }
590 }
591 else if ( stats[clientnum]->weapon && (stats[clientnum]->weapon->type == CROSSBOW || stats[clientnum]->weapon->type == HEAVY_CROSSBOW) )
592 {
593 HUDWEAPON_SHOOTING_RANGED_WEAPON = RANGED_ANIM_BEING_DRAWN;
594 if ( rangedWeaponUseQuiverOnAttack(stats[clientnum]) )
595 {
596 HUDWEAPON_BOW_HAS_QUIVER = 1;
597 }
598 else
599 {
600 if ( HUDWEAPON_BOW_HAS_QUIVER == 1 )
601 {
602 // force a reload animation.
603 HUDWEAPON_CROSSBOW_RELOAD_ANIMATION = CROSSBOW_ANIM_RELOAD_START;
604 HUDWEAPON_CHOP = CROSSBOW_CHOP_RELOAD_START;
605 if ( stats[clientnum]->weapon->type == HEAVY_CROSSBOW )
606 {
607 #ifdef SOUND
608 bowDrawingSoundChannel = playSound(410, 64);
609 #endif
610 }
611 HUDWEAPON_MOVEX = -1;
612 }
613 HUDWEAPON_BOW_HAS_QUIVER = 0;
614 }
615 if ( HUDWEAPON_CHOP != 0 )
616 {
617 if ( HUDWEAPON_CHOP == CROSSBOW_CHOP_RELOAD_START )
618 {
619 if ( stats[clientnum]->weapon->type == CROSSBOW )
620 {
621 my->sprite = 975; // model has been "fired" and the bowstring not pulled back
622 }
623 else if ( stats[clientnum]->weapon->type == HEAVY_CROSSBOW )
624 {
625 my->sprite = 987;
626 }
627 }
628 else if ( HUDWEAPON_CHOP == CROSSBOW_CHOP_RELOAD_ENDING )
629 {
630 if ( stats[clientnum]->weapon->type == HEAVY_CROSSBOW )
631 {
632 if ( HUDWEAPON_CROSSBOW_RELOAD_ANIMATION == CROSSBOW_ANIM_HEAVY_RELOAD_MIDPOINT )
633 {
634 my->sprite = 985;
635 }
636 }
637 }
638 HUDWEAPON_SHOOTING_RANGED_WEAPON = RANGED_ANIM_FIRED;
639 }
640 }
641
642 if ( itemCategory(stats[clientnum]->weapon) == SPELLBOOK )
643 {
644 my->flags[INVISIBLE] = true;
645 if ( parent != NULL )
646 {
647 parent->flags[INVISIBLE] = false;
648 }
649 }
650 else
651 {
652 my->flags[INVISIBLE] = false;
653 if ( parent != NULL )
654 {
655 parent->flags[INVISIBLE] = true;
656 }
657 }
658 }
659 }
660
661 // when transitioning from rat/spider to normal, need a tick to make invisible while camera/limb positions settle.
662 if ( HUD_SHAPESHIFT_HIDE > 0 && HUD_SHAPESHIFT_HIDE < 2 )
663 {
664 my->flags[INVISIBLE] = true;
665 if ( parent != NULL )
666 {
667 parent->flags[INVISIBLE] = true;
668 }
669 }
670
671 if ( cast_animation.active || cast_animation.active_spellbook )
672 {
673 if ( playerRace != RAT )
674 {
675 my->flags[INVISIBLE] = true;
676 if (parent != NULL)
677 {
678 parent->flags[INVISIBLE] = true;
679 }
680 }
681 }
682
683 bool swingweapon = false;
684 if ( players[clientnum]->entity
685 && (*inputPressed(impulses[IN_ATTACK]) || (shootmode && *inputPressed(joyimpulses[INJOY_GAME_ATTACK])))
686 && shootmode
687 && !gamePaused
688 && players[clientnum]->entity->isMobile()
689 && !(*inputPressed(impulses[IN_DEFEND]) && stats[clientnum]->defending || (shootmode && *inputPressed(joyimpulses[INJOY_GAME_DEFEND]) && stats[clientnum]->defending))
690 && HUDWEAPON_OVERCHARGE < MAXCHARGE )
691 {
692 swingweapon = true;
693 }
694 else if ( (*inputPressed(impulses[IN_ATTACK]) || (shootmode && *inputPressed(joyimpulses[INJOY_GAME_ATTACK]))) &&
695 (*inputPressed(impulses[IN_DEFEND]) && stats[clientnum]->defending || (shootmode && *inputPressed(joyimpulses[INJOY_GAME_DEFEND]) && stats[clientnum]->defending)) )
696 {
697 if ( stats[clientnum]->shield && stats[clientnum]->shield->type == TOOL_TINKERING_KIT )
698 {
699 if ( !GenericGUI.isGUIOpen() )
700 {
701 *inputPressed(impulses[IN_ATTACK]) = 0;
702 *inputPressed(joyimpulses[INJOY_GAME_ATTACK]) = 0;
703 GenericGUI.openGUI(GUI_TYPE_TINKERING, stats[clientnum]->shield);
704 swapWeaponGimpTimer = 20;
705 return;
706 }
707 }
708 }
709
710 bool thrownWeapon = stats[clientnum]->weapon
711 && (itemCategory(stats[clientnum]->weapon) == THROWN || itemCategory(stats[clientnum]->weapon) == GEM
712 || stats[clientnum]->weapon->type == FOOD_CREAMPIE);
713 bool castStrikeAnimation = (players[clientnum]->entity->skill[9] == MONSTER_POSE_SPECIAL_WINDUP1);
714
715 // weapon switch animation
716 if ( weaponSwitch )
717 {
718 weaponSwitch = false;
719 if ( !hideWeapon )
720 {
721 if ( stats[clientnum]->weapon && (stats[clientnum]->weapon->type == CROSSBOW || stats[clientnum]->weapon->type == HEAVY_CROSSBOW) )
722 {
723 swingweapon = false;
724 HUDWEAPON_CHARGE = 0;
725 HUDWEAPON_OVERCHARGE = 0;
726 HUDWEAPON_CHOP = 0;
727 throwGimpTimer = std::max(throwGimpTimer, 20);
728
729 HUDWEAPON_MOVEY = 0;
730 HUDWEAPON_PITCH = 0;
731 HUDWEAPON_YAW = -0.1;
732 HUDWEAPON_MOVEZ = 0;
733
734 if ( rangedWeaponUseQuiverOnAttack(stats[clientnum]) )
735 {
736 if ( stats[clientnum]->weapon->type == HEAVY_CROSSBOW )
737 {
738 #ifdef SOUND
739 bowDrawingSoundChannel = playSound(410, 64);
740 #endif
741 }
742 HUDWEAPON_CROSSBOW_RELOAD_ANIMATION = CROSSBOW_ANIM_SWAPPED_WEAPON;
743 if ( swingweapon )
744 {
745 swapWeaponGimpTimer = 20; // gimp timer for quivers and ranged weapons.
746 }
747 else
748 {
749 // let go of attack button, if the animation is the post-attack portion, then quickly fade the timer.
750 if ( swapWeaponGimpTimer > 5 )
751 {
752 swapWeaponGimpTimer = 5;
753 }
754 }
755 swingweapon = false;
756 }
757 else
758 {
759 HUDWEAPON_CROSSBOW_RELOAD_ANIMATION = CROSSBOW_ANIM_RELOAD_START;
760 HUDWEAPON_CHOP = CROSSBOW_CHOP_RELOAD_START;
761 HUDWEAPON_MOVEX = -1;
762 if ( stats[clientnum]->weapon->type == HEAVY_CROSSBOW )
763 {
764 #ifdef SOUND
765 bowDrawingSoundChannel = playSound(410, 64);
766 #endif
767 }
768 }
769 }
770 else if ( HUDWEAPON_CHOP == CROSSBOW_CHOP_RELOAD_START || HUDWEAPON_CHOP == CROSSBOW_CHOP_RELOAD_ENDING )
771 {
772 // non-crossbow, if we're in these unique crossbow states then reset the chop.
773 HUDWEAPON_CHOP = 0;
774 }
775
776 if ( thrownWeapon && HUDWEAPON_CHOP > 3 )
777 {
778 // prevent thrown weapon rapid firing.
779 swingweapon = false;
780 HUDWEAPON_CHARGE = 0;
781 HUDWEAPON_OVERCHARGE = 0;
782 HUDWEAPON_CHOP = 0;
783 throwGimpTimer = std::max(throwGimpTimer, 5);
784 }
785
786 if ( rangedweapon && stats[clientnum]->weapon
787 && stats[clientnum]->weapon->type != CROSSBOW
788 && stats[clientnum]->weapon->type != HEAVY_CROSSBOW
789 )
790 {
791 HUDWEAPON_BOW_FORCE_RELOAD = 1;
792 if ( HUDWEAPON_BOW_HAS_QUIVER == 1 )
793 {
794 // reequiped bow, force a reload.
795 bowGimpTimer = std::max(bowGimpTimer, 3);
796 }
797 }
798
799 if ( !HUDWEAPON_CHOP )
800 {
801 HUDWEAPON_MOVEZ = 2;
802 HUDWEAPON_MOVEX = -.5;
803 HUDWEAPON_ROLL = -PI / 2;
804 }
805 }
806 }
807
808 // all items (e.g weapons) have swap equipment gimp timer in multiplayer.
809 if ( HUDWEAPON_CHOP > 0 && swingweapon )
810 {
811 swapWeaponGimpTimer = 20;
812 }
813 else if ( HUDWEAPON_CHOP != 0 && HUDWEAPON_CHOP != 1 && HUDWEAPON_CHOP != 4 && HUDWEAPON_CHOP != 7 && !swingweapon )
814 {
815 // let go of attack button, if the animation is the post-attack portion, then quickly fade the timer.
816 if ( swapWeaponGimpTimer > 5 )
817 {
818 swapWeaponGimpTimer = 5;
819 }
820 }
821
822 if ( HUDWEAPON_CROSSBOW_RELOAD_ANIMATION == CROSSBOW_ANIM_SWAPPED_WEAPON )
823 {
824 swapWeaponGimpTimer = 20;
825 }
826
827 Uint32 bowFireRate = bowDrawBaseTicks;
828 bool shakeRangedWeapon = false;
829 if ( rangedweapon && stats[clientnum]->weapon
830 && stats[clientnum]->weapon->type != CROSSBOW
831 && stats[clientnum]->weapon->type != HEAVY_CROSSBOW
832 && !hideWeapon )
833 {
834 bowFireRate = bowDrawBaseTicks * (rangedAttackGetSpeedModifier(stats[clientnum]));
835 if ( rangedWeaponUseQuiverOnAttack(stats[clientnum]) )
836 {
837 HUDWEAPON_BOW_HAS_QUIVER = 1;
838 }
839 else
840 {
841 if ( HUDWEAPON_BOW_HAS_QUIVER == 1 )
842 {
843 // unequipped quiver, force a reload.
844 HUDWEAPON_BOW_FORCE_RELOAD = 1;
845 bowGimpTimer = std::max(bowGimpTimer, 12);
846 }
847 HUDWEAPON_BOW_HAS_QUIVER = 0;
848 }
849
850 if ( (swingweapon && HUDWEAPON_CHOP != 0) || HUDWEAPON_BOW_FORCE_RELOAD == 1 )
851 {
852 if ( HUDWEAPON_BOW_FORCE_RELOAD == 1 )
853 {
854 if ( HUDWEAPON_SHOOTING_RANGED_WEAPON == RANGED_ANIM_FIRED )
855 {
856 HUDWEAPON_SHOOTING_RANGED_WEAPON = RANGED_ANIM_IDLE;
857 }
858 // don't undo swing weapon.
859 }
860 else
861 {
862 swingweapon = false;
863 }
864 HUDWEAPON_BOW_FORCE_RELOAD = 0;
865 HUDWEAPON_CHARGE = 0;
866 HUDWEAPON_OVERCHARGE = 0;
867 HUDWEAPON_CHOP = 0;
868 bowIsBeingDrawn = false;
869 }
870 else if ( rangedWeaponUseQuiverOnAttack(stats[clientnum]) )
871 {
872 if ( swingweapon )
873 {
874 swapWeaponGimpTimer = 20; // gimp timer for quivers and ranged weapons.
875 }
876 else
877 {
878 // let go of attack button, if the animation is the post-attack portion, then quickly fade the timer.
879 if ( swapWeaponGimpTimer > 5 )
880 {
881 swapWeaponGimpTimer = 5;
882 }
883 }
884 }
885 }
886 // check bow drawing attack and if defending/not firing cancel the SFX.
887 if ( rangedweapon && bowIsBeingDrawn && !stats[clientnum]->defending && !hideWeapon )
888 {
889 if ( stats[clientnum]->weapon->type == HEAVY_CROSSBOW )
890 {
891 bowFireRate = bowDrawBaseTicks * (rangedAttackGetSpeedModifier(stats[clientnum]));
892 }
893
894 if ( (my->ticks - bowStartDrawingTick) < bowFireRate )
895 {
896 bowFire = false;
897 }
898 else
899 {
900 bowIsBeingDrawn = false;
901 bowFire = true; // ready to fire!
902 }
903 #ifdef SOUND
904 #ifdef USE_OPENAL
905 if ( bowDrawingSoundChannel )
906 {
907 OPENAL_Channel_IsPlaying(bowDrawingSoundChannel, &bowDrawingSoundPlaying);
908 }
909 #else
910 if ( bowDrawingSoundChannel )
911 {
912 FMOD_Channel_IsPlaying(bowDrawingSoundChannel, &bowDrawingSoundPlaying);
913 }
914 #endif
915 #endif // SOUND
916 }
917 else
918 {
919 bowFire = false;
920 bowIsBeingDrawn = false;
921 bool stopSound = true;
922 if ( stats[clientnum]->weapon && stats[clientnum]->weapon->type == HEAVY_CROSSBOW
923 && !stats[clientnum]->defending && !hideWeapon )
924 {
925 #ifdef SOUND
926 #ifdef USE_OPENAL
927 if ( bowDrawingSoundChannel )
928 {
929 OPENAL_Channel_IsPlaying(bowDrawingSoundChannel, &bowDrawingSoundPlaying);
930 }
931 #else
932 if ( bowDrawingSoundChannel )
933 {
934 FMOD_Channel_IsPlaying(bowDrawingSoundChannel, &bowDrawingSoundPlaying);
935 }
936 #endif
937 #endif // SOUND
938
939 stopSound = false;
940 if ( HUDWEAPON_CROSSBOW_RELOAD_ANIMATION == CROSSBOW_ANIM_NONE )
941 {
942 stopSound = true;
943 }
944 }
945
946 if ( stopSound )
947 {
948 #ifdef SOUND
949 if ( bowDrawingSoundPlaying && bowDrawingSoundChannel )
950 {
951 #ifdef USE_OPENAL
952 OPENAL_Channel_Stop(bowDrawingSoundChannel);
953 #else
954 FMOD_Channel_Stop(bowDrawingSoundChannel);
955 #endif
956 bowDrawingSoundPlaying = 0;
957 bowDrawingSoundChannel = NULL;
958 }
959 #endif
960 }
961 }
962
963 bool whip = stats[clientnum]->weapon && stats[clientnum]->weapon->type == TOOL_WHIP;
964
965 // main animation
966 if ( HUDWEAPON_CHOP == 0 )
967 {
968 if ( HUDWEAPON_CROSSBOW_RELOAD_ANIMATION != CROSSBOW_ANIM_SWAPPED_WEAPON )
969 {
970 // CROSSBOW_ANIM_SWAPPED_WEAPON requires the weapon to not be in the swapping animation to finish.
971 // otherwise, the animation sets HUDWEAPON_CHOP to 0 so it has finished.
972 HUDWEAPON_CROSSBOW_RELOAD_ANIMATION = CROSSBOW_ANIM_NONE;
973 }
974 bool ignoreAttack = false;
975 if ( swingweapon && throwGimpTimer > 0 && stats[clientnum]->weapon &&
976 ( stats[clientnum]->weapon->type == CROSSBOW
977 || itemCategory(stats[clientnum]->weapon) == POTION
978 || itemCategory(stats[clientnum]->weapon) == GEM
979 || itemCategory(stats[clientnum]->weapon) == THROWN
980 || stats[clientnum]->weapon->type == FOOD_CREAMPIE )
981 )
982 {
983 ignoreAttack = true;
984 }
985 else if ( swingweapon && bowGimpTimer > 0 && rangedweapon && stats[clientnum]->weapon
986 && stats[clientnum]->weapon->type != CROSSBOW
987 && stats[clientnum]->weapon->type != HEAVY_CROSSBOW
988 && !hideWeapon )
989 {
990 ignoreAttack = true;
991 }
992
993 if ( !ignoreAttack && (swingweapon || castStrikeAnimation) )
994 {
995 if ( cast_animation.active || cast_animation.active_spellbook )
996 {
997 messagePlayer(clientnum, language[1301]);
998 spellcastingAnimationManager_deactivate(&cast_animation);
999 }
1000 if ( castStrikeAnimation )
1001 {
1002 HUDWEAPON_CHOP = 10; // special punch
1003 }
1004 else if ( stats[clientnum]->weapon == NULL || hideWeapon )
1005 {
1006 HUDWEAPON_CHOP = 7; // punch
1007 }
1008 else
1009 {
1010 if ( conductGameChallenges[CONDUCT_BRAWLER] || achievementBrawlerMode )
1011 {
1012 if ( itemCategory(stats[clientnum]->weapon) == WEAPON
1013 || rangedweapon
1014 || itemCategory(stats[clientnum]->weapon) == THROWN
1015 || stats[clientnum]->weapon->type == FOOD_CREAMPIE
1016 || itemCategory(stats[clientnum]->weapon) == MAGICSTAFF
1017 || itemCategory(stats[clientnum]->weapon) == GEM
1018 || stats[clientnum]->weapon->type == TOOL_PICKAXE )
1019 {
1020 if ( achievementBrawlerMode && conductGameChallenges[CONDUCT_BRAWLER] )
1021 {
1022 messagePlayer(clientnum, language[2997]); // prevent attack.
1023 return;
1024 }
1025 if ( achievementBrawlerMode )
1026 {
1027 messagePlayer(clientnum, language[2998]); // notify no longer eligible for achievement but still atk.
1028 }
1029 conductGameChallenges[CONDUCT_BRAWLER] = 0;
1030 }
1031 }
1032
1033 if ( itemCategory(stats[clientnum]->weapon) == WEAPON || stats[clientnum]->weapon->type == TOOL_PICKAXE )
1034 {
1035 if ( stats[clientnum]->weapon->type == TOOL_PICKAXE )
1036 {
1037 pickaxeGimpTimer = 40;
1038 }
1039 if ( stats[clientnum]->weapon->type == IRON_SPEAR || stats[clientnum]->weapon->type == ARTIFACT_SPEAR )
1040 {
1041 HUDWEAPON_CHOP = 7; // spear lunges
1042 }
1043 else if ( whip )
1044 {
1045 HUDWEAPON_CHOP = 4;
1046 pickaxeGimpTimer = 20;
1047 }
1048 else if ( rangedweapon )
1049 {
1050 if ( stats[clientnum]->weapon->type == SLING
1051 || stats[clientnum]->weapon->type == SHORTBOW
1052 || stats[clientnum]->weapon->type == ARTIFACT_BOW
1053 || stats[clientnum]->weapon->type == LONGBOW
1054 || stats[clientnum]->weapon->type == COMPOUND_BOW )
1055 {
1056 if ( !stats[clientnum]->defending && !throwGimpTimer )
1057 {
1058 // bows need to be drawn back
1059 if ( !bowIsBeingDrawn )
1060 {
1061 if ( bowFire )
1062 {
1063 bowFire = false;
1064
1065 bool artifactBowSaveAmmo = false;
1066 if ( stats[clientnum]->weapon->type == ARTIFACT_BOW /*&& rangedWeaponUseQuiverOnAttack(stats[clientnum])*/ )
1067 {
1068 real_t amount = 0.f;
1069 real_t percent = getArtifactWeaponEffectChance(ARTIFACT_BOW, *(stats[clientnum]), &amount);
1070 if ( (rand() % 100 < static_cast<int>(percent)) )
1071 {
1072 artifactBowSaveAmmo = true;
1073 }
1074 }
1075
1076 if ( artifactBowSaveAmmo )
1077 {
1078 players[clientnum]->entity->attack(MONSTER_POSE_RANGED_SHOOT2, 0, nullptr);
1079 }
1080 else
1081 {
1082 players[clientnum]->entity->attack(MONSTER_POSE_RANGED_SHOOT1, 0, nullptr);
1083 }
1084 HUDWEAPON_MOVEX = 3;
1085 throwGimpTimer = TICKS_PER_SECOND / 4;
1086
1087 if ( multiplayer == CLIENT )
1088 {
1089 if ( rangedWeaponUseQuiverOnAttack(stats[clientnum]) && !artifactBowSaveAmmo )
1090 {
1091 Item* quiver = stats[clientnum]->shield;
1092 quiver->count--;
1093 if ( quiver->count <= 0 )
1094 {
1095 if ( quiver->node )
1096 {
1097 list_RemoveNode(quiver->node);
1098 }
1099 else
1100 {
1101 free(quiver);
1102 }
1103 stats[clientnum]->shield = NULL;
1104 }
1105 }
1106 }
1107 }
1108 else
1109 {
1110 bowStartDrawingTick = my->ticks;
1111 bowIsBeingDrawn = true;
1112 #ifdef SOUND
1113 bowDrawingSoundChannel = playSound(246, 64);
1114 #endif
1115 }
1116 }
1117
1118 real_t targety = -1.0;
1119 if ( HUDWEAPON_MOVEX > 0 )
1120 {
1121 if ( HUDWEAPON_MOVEX > 1 )
1122 {
1123 HUDWEAPON_MOVEX = std::max<real_t>(HUDWEAPON_MOVEX - 1, 0.0);
1124 }
1125 else
1126 {
1127 HUDWEAPON_MOVEX = std::max<real_t>(HUDWEAPON_MOVEX - 0.2, 0.0);
1128 }
1129 }
1130 else if ( HUDWEAPON_MOVEX < 0 )
1131 {
1132 HUDWEAPON_MOVEX = std::min<real_t>(HUDWEAPON_MOVEX + 1, 0.0);
1133 }
1134 if ( HUDWEAPON_MOVEY > targety )
1135 {
1136 HUDWEAPON_MOVEY = std::max<real_t>(HUDWEAPON_MOVEY - 1, targety);
1137 }
1138 else if ( HUDWEAPON_MOVEY < targety )
1139 {
1140 HUDWEAPON_MOVEY = std::min<real_t>(HUDWEAPON_MOVEY + 1, targety);
1141 }
1142 if ( HUDWEAPON_MOVEZ > 0 )
1143 {
1144 HUDWEAPON_MOVEZ = std::max<real_t>(HUDWEAPON_MOVEZ - 1, 0.0);
1145 }
1146 else if ( HUDWEAPON_MOVEZ < 0 )
1147 {
1148 HUDWEAPON_MOVEZ = std::min<real_t>(HUDWEAPON_MOVEZ + 1, 0.0);
1149 }
1150 if ( HUDWEAPON_YAW > -.1 )
1151 {
1152 HUDWEAPON_YAW = std::max<real_t>(HUDWEAPON_YAW - .1, -.1);
1153 }
1154 else if ( HUDWEAPON_YAW < -.1 )
1155 {
1156 HUDWEAPON_YAW = std::min<real_t>(HUDWEAPON_YAW + .1, -.1);
1157 }
1158 if ( HUDWEAPON_PITCH > 0 )
1159 {
1160 HUDWEAPON_PITCH = std::max<real_t>(HUDWEAPON_PITCH - .1, 0.0);
1161 }
1162 else if ( HUDWEAPON_PITCH < 0 )
1163 {
1164 HUDWEAPON_PITCH = std::min<real_t>(HUDWEAPON_PITCH + .1, 0.0);
1165 }
1166 if ( HUDWEAPON_ROLL > 0 )
1167 {
1168 HUDWEAPON_ROLL = std::max<real_t>(HUDWEAPON_ROLL - .1, 0.0);
1169 }
1170 else if ( HUDWEAPON_ROLL < 0 )
1171 {
1172 HUDWEAPON_ROLL = std::min<real_t>(HUDWEAPON_ROLL + .1, 0.0);
1173 }
1174 }
1175 }
1176 else
1177 {
1178 // crossbows
1179 bool doAttack = false;
1180 if ( throwGimpTimer == 0 )
1181 {
1182 doAttack = true;
1183 bool heavyCrossbow = stats[clientnum]->weapon && stats[clientnum]->weapon->type == HEAVY_CROSSBOW;
1184
1185 if ( heavyCrossbow )
1186 {
1187 doAttack = false;
1188 // need to charge up.
1189 if ( !bowIsBeingDrawn )
1190 {
1191 if ( bowFire )
1192 {
1193 bowFire = false;
1194 doAttack = true;
1195 }
1196 else
1197 {
1198 bowStartDrawingTick = my->ticks;
1199 bowIsBeingDrawn = true;
1200 }
1201 }
1202 else
1203 {
1204 shakeRangedWeapon = true;
1205 }
1206 }
1207
1208 if ( doAttack )
1209 {
1210 players[clientnum]->entity->attack(MONSTER_POSE_RANGED_SHOOT1, 0, nullptr);
1211 HUDWEAPON_MOVEX = -4;
1212
1213 // set delay before crossbow can fire again
1214 throwGimpTimer = 40;
1215
1216 HUDWEAPON_CHOP = CROSSBOW_CHOP_RELOAD_START;
1217 HUDWEAPON_CROSSBOW_RELOAD_ANIMATION = CROSSBOW_ANIM_SHOOT;
1218
1219 if ( heavyCrossbow )
1220 {
1221 players[clientnum]->entity->playerStrafeVelocity = 0.3;
1222 players[clientnum]->entity->playerStrafeDir = players[clientnum]->entity->yaw + PI;
1223 if ( multiplayer != CLIENT )
1224 {
1225 players[clientnum]->entity->setEffect(EFF_KNOCKBACK, true, 30, false);
1226 }
1227 if ( players[clientnum]->entity->skill[3] == 0 ) // debug cam OFF
1228 {
1229 camera_shakex += .06;
1230 camera_shakey += 6;
1231 }
1232 }
1233
1234 if ( multiplayer == CLIENT )
1235 {
1236 if ( rangedWeaponUseQuiverOnAttack(stats[clientnum]) )
1237 {
1238 Item* quiver = stats[clientnum]->shield;
1239 quiver->count--;
1240 if ( quiver->count <= 0 )
1241 {
1242 if ( quiver->node )
1243 {
1244 list_RemoveNode(quiver->node);
1245 }
1246 else
1247 {
1248 free(quiver);
1249 }
1250 stats[clientnum]->shield = NULL;
1251 }
1252 }
1253 }
1254 }
1255 }
1256
1257 if ( !doAttack )
1258 {
1259 // reset animation - this is for weaponswitch animation code to complete while holding attack.
1260 if ( HUDWEAPON_MOVEX > 0 )
1261 {
1262 HUDWEAPON_MOVEX = std::max<real_t>(HUDWEAPON_MOVEX - 1, 0.0);
1263 }
1264 else if ( HUDWEAPON_MOVEX < 0 )
1265 {
1266 HUDWEAPON_MOVEX = std::min<real_t>(HUDWEAPON_MOVEX + 1, 0.0);
1267 }
1268 if ( HUDWEAPON_MOVEY > 0 )
1269 {
1270 HUDWEAPON_MOVEY = std::max<real_t>(HUDWEAPON_MOVEY - 1, 0.0);
1271 }
1272 else if ( HUDWEAPON_MOVEY < 0 )
1273 {
1274 HUDWEAPON_MOVEY = std::min<real_t>(HUDWEAPON_MOVEY + 1, 0.0);
1275 }
1276 if ( HUDWEAPON_MOVEZ > 0 )
1277 {
1278 HUDWEAPON_MOVEZ = std::max<real_t>(HUDWEAPON_MOVEZ - 1, 0.0);
1279 }
1280 else if ( HUDWEAPON_MOVEZ < 0 )
1281 {
1282 HUDWEAPON_MOVEZ = std::min<real_t>(HUDWEAPON_MOVEZ + 1, 0.0);
1283 }
1284 if ( HUDWEAPON_YAW > -.1 )
1285 {
1286 HUDWEAPON_YAW = std::max<real_t>(HUDWEAPON_YAW - .1, -.1);
1287 }
1288 else if ( HUDWEAPON_YAW < -.1 )
1289 {
1290 HUDWEAPON_YAW = std::min<real_t>(HUDWEAPON_YAW + .1, -.1);
1291 }
1292 if ( HUDWEAPON_ROLL > 0 )
1293 {
1294 HUDWEAPON_ROLL = std::max<real_t>(HUDWEAPON_ROLL - .1, 0.0);
1295 }
1296 else if ( HUDWEAPON_ROLL < 0 )
1297 {
1298 HUDWEAPON_ROLL = std::min<real_t>(HUDWEAPON_ROLL + .1, 0.0);
1299 }
1300 if ( HUDWEAPON_PITCH > 0 )
1301 {
1302 HUDWEAPON_PITCH = std::max<real_t>(HUDWEAPON_PITCH - .1, 0.0);
1303 }
1304 else if ( HUDWEAPON_PITCH < 0 )
1305 {
1306 HUDWEAPON_PITCH = std::min<real_t>(HUDWEAPON_PITCH + .1, 0.0);
1307 }
1308 }
1309 }
1310 }
1311 else
1312 {
1313 HUDWEAPON_CHOP = 1;
1314 }
1315 }
1316 else
1317 {
1318 Item* item = stats[clientnum]->weapon;
1319 if (item)
1320 {
1321 if (itemCategory(item) == SPELLBOOK)
1322 {
1323 mousestatus[SDL_BUTTON_LEFT] = 0;
1324 players[clientnum]->entity->attack(2, 0, nullptr); // will need to add some delay to this so you can't rapid fire spells
1325 }
1326 else if ( itemIsThrowableTinkerTool(item) )
1327 {
1328 HUDWEAPON_CHOP = 13;
1329 }
1330 else if (itemCategory(item) == MAGICSTAFF)
1331 {
1332 HUDWEAPON_CHOP = 7; // magicstaffs lunge
1333 }
1334 else if ( itemCategory(item) == THROWN || itemCategory(item) == GEM || item->type == FOOD_CREAMPIE )
1335 {
1336 if ( !throwGimpTimer )
1337 {
1338 throwGimpTimer = TICKS_PER_SECOND / 2; // limits how often you can throw objects
1339 HUDWEAPON_CHOP = 1; // thrown normal swing
1340 }
1341 }
1342 else if (item->type == TOOL_LOCKPICK || item->type == TOOL_SKELETONKEY )
1343 {
1344 // keys and lockpicks
1345 HUDWEAPON_MOVEX = 5;
1346 HUDWEAPON_CHOP = 3;
1347 Entity* player = players[clientnum]->entity;
1348 bool foundBomb = false;
1349 if ( stats[clientnum]->weapon )
1350 {
1351 bool clickedOnGUI = false;
1352 int tmpmousex = omousex;
1353 int tmpmousey = omousey;
1354 omousex = xres / 2; // pretend move the mouse to the centre of screen.
1355 omousey = yres / 2;
1356 Entity* clickedOn = entityClicked(&clickedOnGUI, true, HUDWEAPON_PLAYERNUM); // using objects
1357 omousex = tmpmousex;
1358 omousey = tmpmousey;
1359 if ( clickedOn && clickedOn->behavior == &actBomb && entityDist(clickedOn, players[clientnum]->entity) < STRIKERANGE )
1360 {
1361 // found something
1362 stats[clientnum]->weapon->apply(clientnum, clickedOn);
1363 foundBomb = true;
1364 }
1365 }
1366 if ( !foundBomb )
1367 {
1368 real_t dist = lineTrace(player, player->x, player->y, player->yaw, STRIKERANGE, 0, false);
1369 if ( hit.entity && stats[clientnum]->weapon )
1370 {
1371 stats[clientnum]->weapon->apply(clientnum, hit.entity);
1372 }
1373 else
1374 {
1375 bool foundWall = false;
1376 if ( dist < STRIKERANGE
1377 && !hit.entity && hit.mapx >= 0 && hit.mapx < map.width && hit.mapy >= 0 && hit.mapy < map.height )
1378 {
1379 // arrow traps
1380 if ( map.tiles[OBSTACLELAYER + hit.mapy * MAPLAYERS + hit.mapx * MAPLAYERS * map.height] == 53 )
1381 {
1382 foundWall = true;
1383 stats[clientnum]->weapon->applyLockpickToWall(clientnum, hit.mapx, hit.mapy);
1384 }
1385 }
1386 if ( !foundWall )
1387 {
1388 messagePlayer(clientnum, language[503], item->getName());
1389 }
1390 }
1391 }
1392 }
1393 else if ( item->type >= ARTIFACT_ORB_BLUE && item->type <= ARTIFACT_ORB_GREEN )
1394 {
1395 HUDWEAPON_MOVEX = 5;
1396 HUDWEAPON_CHOP = 3;
1397 Entity* player = players[clientnum]->entity;
1398 lineTrace(player, player->x, player->y, player->yaw, STRIKERANGE, 0, false);
1399 if ( hit.entity && stats[clientnum]->weapon )
1400 {
1401 stats[clientnum]->weapon->apply(clientnum, hit.entity);
1402 }
1403 else
1404 {
1405 if ( statGetINT(stats[clientnum], player) <= 10 )
1406 {
1407 messagePlayer(clientnum, language[2373], item->getName());
1408 }
1409 else
1410 {
1411 messagePlayer(clientnum, language[2372], item->getName());
1412 }
1413 }
1414 }
1415 else if ( item->type == POTION_EMPTY )
1416 {
1417 if ( throwGimpTimer == 0 )
1418 {
1419 HUDWEAPON_MOVEX = 5;
1420 HUDWEAPON_CHOP = 3;
1421 Entity* player = players[clientnum]->entity;
1422 lineTrace(player, player->x, player->y, player->yaw, STRIKERANGE, 0, false);
1423 if ( hit.entity && stats[clientnum]->weapon )
1424 {
1425 stats[clientnum]->weapon->apply(clientnum, hit.entity);
1426 }
1427 else
1428 {
1429 messagePlayer(clientnum, language[3336]);
1430 }
1431 throwGimpTimer = TICKS_PER_SECOND / 2;
1432 }
1433 }
1434 else if ((itemCategory(item) == POTION || itemCategory(item) == GEM || itemCategory(item) == THROWN || item->type == FOOD_CREAMPIE )
1435 && !throwGimpTimer)
1436 {
1437 if ( itemCategory(item) == THROWN )
1438 {
1439 // possibility to change to be unique.
1440 throwGimpTimer = TICKS_PER_SECOND / 2; // limits how often you can throw objects
1441 }
1442 else
1443 {
1444 throwGimpTimer = TICKS_PER_SECOND / 2; // limits how often you can throw objects
1445 }
1446 HUDWEAPON_MOVEZ = 3;
1447 HUDWEAPON_CHOP = 3;
1448 players[clientnum]->entity->attack(1, 0, nullptr);
1449 if (multiplayer == CLIENT)
1450 {
1451 item->count--;
1452 if (item->count <= 0)
1453 {
1454 if (item->node)
1455 {
1456 list_RemoveNode(item->node);
1457 }
1458 else
1459 {
1460 free(item);
1461 }
1462 stats[clientnum]->weapon = NULL;
1463 }
1464 }
1465 }
1466 }
1467 }
1468 }
1469 }
1470 else
1471 {
1472 if ( !stats[clientnum]->defending )
1473 {
1474 if ( stats[clientnum]->weapon || hideWeapon )
1475 {
1476 if ( !hideWeapon &&
1477 (stats[clientnum]->weapon->type == SLING
1478 || stats[clientnum]->weapon->type == SHORTBOW
1479 || stats[clientnum]->weapon->type == ARTIFACT_BOW
1480 || stats[clientnum]->weapon->type == LONGBOW
1481 || stats[clientnum]->weapon->type == COMPOUND_BOW) )
1482 {
1483 // not drawing bow anymore, reset.
1484 bowIsBeingDrawn = false;
1485 #ifdef SOUND
1486 if ( bowDrawingSoundPlaying && bowDrawingSoundChannel )
1487 {
1488 #ifdef USE_OPENAL
1489 OPENAL_Channel_Stop(bowDrawingSoundChannel);
1490 #else
1491 FMOD_Channel_Stop(bowDrawingSoundChannel);
1492 #endif
1493 bowDrawingSoundPlaying = 0;
1494 bowDrawingSoundChannel = NULL;
1495 }
1496 #endif
1497
1498 if ( HUDWEAPON_MOVEX > 0 )
1499 {
1500 HUDWEAPON_MOVEX = std::max<real_t>(HUDWEAPON_MOVEX - 1, 0.0);
1501 }
1502 else if ( HUDWEAPON_MOVEX < 0 )
1503 {
1504 HUDWEAPON_MOVEX = std::min<real_t>(HUDWEAPON_MOVEX + 1, 0.0);
1505 }
1506 if ( HUDWEAPON_MOVEY > 1 )
1507 {
1508 HUDWEAPON_MOVEY = std::max<real_t>(HUDWEAPON_MOVEY - 1, 1.0);
1509 }
1510 else if ( HUDWEAPON_MOVEY < 1 )
1511 {
1512 HUDWEAPON_MOVEY = std::min<real_t>(HUDWEAPON_MOVEY + 1, 1.0);
1513 }
1514 if ( HUDWEAPON_MOVEZ > 0 )
1515 {
1516 HUDWEAPON_MOVEZ = std::max<real_t>(HUDWEAPON_MOVEZ - 1, 0.0);
1517 }
1518 else if ( HUDWEAPON_MOVEZ < 0 )
1519 {
1520 HUDWEAPON_MOVEZ = std::min<real_t>(HUDWEAPON_MOVEZ + 1, 0.0);
1521 }
1522 if ( HUDWEAPON_YAW > -.1 )
1523 {
1524 HUDWEAPON_YAW = std::max<real_t>(HUDWEAPON_YAW - .1, -.1);
1525 }
1526 else if ( HUDWEAPON_YAW < -.1 )
1527 {
1528 HUDWEAPON_YAW = std::min<real_t>(HUDWEAPON_YAW + .1, -.1);
1529 }
1530 if ( HUDWEAPON_PITCH > 0 )
1531 {
1532 HUDWEAPON_PITCH = std::max<real_t>(HUDWEAPON_PITCH - .1, 0.0);
1533 }
1534 else if ( HUDWEAPON_PITCH < 0 )
1535 {
1536 HUDWEAPON_PITCH = std::min<real_t>(HUDWEAPON_PITCH + .1, 0.0);
1537 }
1538 if ( HUDWEAPON_ROLL > -PI / 3 )
1539 {
1540 HUDWEAPON_ROLL = std::max<real_t>(HUDWEAPON_ROLL - .1, -PI / 3);
1541 }
1542 else if ( HUDWEAPON_ROLL < -PI / 3 )
1543 {
1544 HUDWEAPON_ROLL = std::min<real_t>(HUDWEAPON_ROLL + .1, -PI / 3);
1545 }
1546 }
1547 else
1548 {
1549 if ( HUDWEAPON_MOVEX > 0 )
1550 {
1551 HUDWEAPON_MOVEX = std::max<real_t>(HUDWEAPON_MOVEX - 1, 0.0);
1552 }
1553 else if ( HUDWEAPON_MOVEX < 0 )
1554 {
1555 HUDWEAPON_MOVEX = std::min<real_t>(HUDWEAPON_MOVEX + 1, 0.0);
1556 }
1557 if ( HUDWEAPON_MOVEY > 0 )
1558 {
1559 HUDWEAPON_MOVEY = std::max<real_t>(HUDWEAPON_MOVEY - 1, 0.0);
1560 }
1561 else if ( HUDWEAPON_MOVEY < 0 )
1562 {
1563 HUDWEAPON_MOVEY = std::min<real_t>(HUDWEAPON_MOVEY + 1, 0.0);
1564 }
1565 if ( HUDWEAPON_MOVEZ > 0 )
1566 {
1567 HUDWEAPON_MOVEZ = std::max<real_t>(HUDWEAPON_MOVEZ - 1, 0.0);
1568 }
1569 else if ( HUDWEAPON_MOVEZ < 0 )
1570 {
1571 HUDWEAPON_MOVEZ = std::min<real_t>(HUDWEAPON_MOVEZ + 1, 0.0);
1572 }
1573 if ( HUDWEAPON_YAW > -.1 )
1574 {
1575 HUDWEAPON_YAW = std::max<real_t>(HUDWEAPON_YAW - .1, -.1);
1576 }
1577 else if ( HUDWEAPON_YAW < -.1 )
1578 {
1579 HUDWEAPON_YAW = std::min<real_t>(HUDWEAPON_YAW + .1, -.1);
1580 }
1581 if ( HUDWEAPON_PITCH > 0 )
1582 {
1583 HUDWEAPON_PITCH = std::max<real_t>(HUDWEAPON_PITCH - .1, 0.0);
1584 }
1585 else if ( HUDWEAPON_PITCH < 0 )
1586 {
1587 HUDWEAPON_PITCH = std::min<real_t>(HUDWEAPON_PITCH + .1, 0.0);
1588 }
1589 if ( HUDWEAPON_ROLL > 0 )
1590 {
1591 HUDWEAPON_ROLL = std::max<real_t>(HUDWEAPON_ROLL - .1, 0.0);
1592 }
1593 else if ( HUDWEAPON_ROLL < 0 )
1594 {
1595 HUDWEAPON_ROLL = std::min<real_t>(HUDWEAPON_ROLL + .1, 0.0);
1596 }
1597 }
1598 }
1599 else
1600 {
1601 // fix for fists not resetting position after blocking.
1602 if ( HUDWEAPON_MOVEY > 0 )
1603 {
1604 HUDWEAPON_MOVEY = std::max<real_t>(HUDWEAPON_MOVEY - 1, 0.0);
1605 }
1606 else if ( HUDWEAPON_MOVEY < 0 )
1607 {
1608 HUDWEAPON_MOVEY = std::min<real_t>(HUDWEAPON_MOVEY + 1, 0.0);
1609 }
1610 if ( HUDWEAPON_MOVEZ > 0 )
1611 {
1612 HUDWEAPON_MOVEZ = std::max<real_t>(HUDWEAPON_MOVEZ - 1, 0.0);
1613 }
1614 else if ( HUDWEAPON_MOVEZ < 0 )
1615 {
1616 HUDWEAPON_MOVEZ = std::min<real_t>(HUDWEAPON_MOVEZ + 1, 0.0);
1617 }
1618 }
1619 }
1620 else
1621 {
1622 if ( HUDWEAPON_MOVEX > 0 )
1623 {
1624 HUDWEAPON_MOVEX = std::max<real_t>(HUDWEAPON_MOVEX - 1, 0.0);
1625 }
1626 else if ( HUDWEAPON_MOVEX < 0 )
1627 {
1628 HUDWEAPON_MOVEX = std::min<real_t>(HUDWEAPON_MOVEX + 1, 0.0);
1629 }
1630 if ( HUDWEAPON_MOVEY > 1 )
1631 {
1632 HUDWEAPON_MOVEY = std::max<real_t>(HUDWEAPON_MOVEY - 1, 1.0);
1633 }
1634 else if ( HUDWEAPON_MOVEY < 1 )
1635 {
1636 HUDWEAPON_MOVEY = std::min<real_t>(HUDWEAPON_MOVEY + 1, 1.0);
1637 }
1638 if ( HUDWEAPON_MOVEZ > 1 )
1639 {
1640 HUDWEAPON_MOVEZ = std::max<real_t>(HUDWEAPON_MOVEZ - 1, 1.0);
1641 }
1642 else if ( HUDWEAPON_MOVEZ < 1 )
1643 {
1644 HUDWEAPON_MOVEZ = std::min<real_t>(HUDWEAPON_MOVEZ + 1, 1.0);
1645 }
1646 if ( HUDWEAPON_YAW > .1 )
1647 {
1648 HUDWEAPON_YAW = std::max<real_t>(HUDWEAPON_YAW - .1, .1);
1649 }
1650 else if ( HUDWEAPON_YAW < .1 )
1651 {
1652 HUDWEAPON_YAW = std::min<real_t>(HUDWEAPON_YAW + .1, .1);
1653 }
1654 if ( HUDWEAPON_PITCH > PI / 6 )
1655 {
1656 HUDWEAPON_PITCH = std::max<real_t>(HUDWEAPON_PITCH - .1, PI / 6);
1657 }
1658 else if ( HUDWEAPON_PITCH < PI / 6 )
1659 {
1660 HUDWEAPON_PITCH = std::min<real_t>(HUDWEAPON_PITCH + .1, PI / 6);
1661 }
1662 if ( HUDWEAPON_ROLL > PI / 6 )
1663 {
1664 HUDWEAPON_ROLL = std::max<real_t>(HUDWEAPON_ROLL - .1, PI / 6);
1665 }
1666 else if ( HUDWEAPON_ROLL < PI / 6 )
1667 {
1668 HUDWEAPON_ROLL = std::min<real_t>(HUDWEAPON_ROLL + .1, PI / 6);
1669 }
1670 }
1671 }
1672 }
1673 else if ( HUDWEAPON_CHOP == 1 ) // prepare for first swing
1674 {
1675 HUDWEAPON_YAW -= .25;
1676 if ( HUDWEAPON_YAW < 0 )
1677 {
1678 HUDWEAPON_YAW = 0;
1679 }
1680 HUDWEAPON_PITCH -= .1;
1681 if ( HUDWEAPON_PITCH < -PI / 4)
1682 {
1683 result = -PI / 4;
1684 HUDWEAPON_PITCH = result;
1685 }
1686 HUDWEAPON_ROLL += .25;
1687 if ( HUDWEAPON_ROLL > 0 )
1688 {
1689 HUDWEAPON_ROLL = 0;
1690 }
1691 int targetY = -2;
1692 if ( thrownWeapon )
1693 {
1694 targetY = -1;
1695 HUDWEAPON_MOVEY -= .25;
1696 HUDWEAPON_MOVEX -= .15;
1697 }
1698 else
1699 {
1700 HUDWEAPON_MOVEY -= .45;
1701 HUDWEAPON_MOVEX -= .35;
1702 }
1703 if ( HUDWEAPON_MOVEX < -1 )
1704 {
1705 HUDWEAPON_MOVEX = -1;
1706 }
1707 if ( HUDWEAPON_MOVEY < targetY )
1708 {
1709 HUDWEAPON_MOVEY = targetY;
1710 }
1711 int targetZ = -6;
1712 if ( whip )
1713 {
1714 targetZ = -6;
1715 HUDWEAPON_MOVEZ -= .32;
1716 if ( HUDWEAPON_MOVEY > 2 )
1717 {
1718 HUDWEAPON_MOVEY -= .45; // returning from side swing, y offset is larger than normal so assist here.
1719 }
1720 }
1721 else if ( thrownWeapon )
1722 {
1723 targetZ = -3;
1724 HUDWEAPON_MOVEZ -= .35;
1725 }
1726 else
1727 {
1728 HUDWEAPON_MOVEZ -= .65;
1729 }
1730 if ( HUDWEAPON_MOVEZ < targetZ )
1731 {
1732 HUDWEAPON_MOVEZ = targetZ;
1733 if ( HUDWEAPON_PITCH == result && HUDWEAPON_ROLL == 0 && HUDWEAPON_YAW == 0 && HUDWEAPON_MOVEX == -1 && HUDWEAPON_MOVEY == targetY )
1734 {
1735 if ( !swingweapon )
1736 {
1737 HUDWEAPON_CHOP++;
1738 players[clientnum]->entity->attack(1, HUDWEAPON_CHARGE, nullptr);
1739 if ( stats[clientnum]->weapon
1740 && (stats[clientnum]->weapon->type == CROSSBOW || stats[clientnum]->weapon->type == HEAVY_CROSSBOW) )
1741 {
1742 throwGimpTimer = 40; // fix for swapping weapon to crossbow while charging.
1743 }
1744 else if ( (stats[clientnum]->weapon
1745 && stats[clientnum]->weapon->type == TOOL_PICKAXE) || whip )
1746 {
1747 if ( pickaxeGimpTimer < 20 )
1748 {
1749 pickaxeGimpTimer = 20; // fix for swapping weapon from pickaxe causing issues.
1750 }
1751 }
1752
1753
1754 if ( thrownWeapon && multiplayer == CLIENT )
1755 {
1756 Item* item = stats[clientnum]->weapon;
1757 if ( item )
1758 {
1759 item->count--;
1760 if ( item->count <= 0 )
1761 {
1762 if ( item->node )
1763 {
1764 list_RemoveNode(item->node);
1765 }
1766 else
1767 {
1768 free(item);
1769 }
1770 stats[clientnum]->weapon = NULL;
1771 }
1772 }
1773 }
1774
1775 HUDWEAPON_CHARGE = 0;
1776 HUDWEAPON_OVERCHARGE = 0;
1777 if (players[clientnum]->entity->skill[3] == 0) // debug cam OFF
1778 {
1779 camera_shakey += 6;
1780 }
1781 }
1782 else
1783 {
1784 HUDWEAPON_CHARGE = std::min<real_t>(HUDWEAPON_CHARGE + 1, MAXCHARGE);
1785 }
1786 }
1787 }
1788 }
1789 else if ( HUDWEAPON_CHOP == 2 ) // first swing
1790 {
1791 if ( whip )
1792 {
1793 real_t animationMult = 0.8;
1794 HUDWEAPON_MOVEX += 1 * animationMult;
1795 if ( HUDWEAPON_MOVEX > 4 )
1796 {
1797 HUDWEAPON_MOVEX = 4;
1798 }
1799 HUDWEAPON_MOVEY += .45 * animationMult;
1800 if ( HUDWEAPON_MOVEY > 0 )
1801 {
1802 HUDWEAPON_MOVEY = 0;
1803 }
1804 HUDWEAPON_PITCH += .5 * animationMult;
1805 if ( HUDWEAPON_PITCH >= 3 * PI / 4 )
1806 {
1807 HUDWEAPON_PITCH = 3 * PI / 4;
1808 }
1809 HUDWEAPON_MOVEZ += 1 * animationMult;
1810 if ( HUDWEAPON_MOVEZ > 4 )
1811 {
1812 HUDWEAPON_MOVEZ = 4;
1813 }
1814 if ( HUDWEAPON_PITCH >= PI / 8 )
1815 {
1816 my->sprite = items[TOOL_WHIP].fpindex + 1;
1817 }
1818 if ( HUDWEAPON_YAW > -.1 )
1819 {
1820 HUDWEAPON_YAW = std::max<real_t>(HUDWEAPON_YAW - .1, -.1);
1821 }
1822 else if ( HUDWEAPON_YAW < -.1 )
1823 {
1824 HUDWEAPON_YAW = std::min<real_t>(HUDWEAPON_YAW + .1, -.1);
1825 }
1826 if ( HUDWEAPON_MOVEZ == 4 && HUDWEAPON_PITCH == 3 * PI / 4 )
1827 {
1828 HUDWEAPON_CHOP++;
1829 }
1830 }
1831 else
1832 {
1833 HUDWEAPON_PITCH += .75;
1834 if ( HUDWEAPON_PITCH >= (PI * 3) / 4 )
1835 {
1836 HUDWEAPON_PITCH = (PI * 3) / 4;
1837 }
1838 HUDWEAPON_MOVEX += 1;
1839 if ( HUDWEAPON_MOVEX > 4 )
1840 {
1841 HUDWEAPON_MOVEX = 4;
1842 }
1843 HUDWEAPON_MOVEZ += .8;
1844 if ( HUDWEAPON_MOVEZ > 0 )
1845 {
1846 HUDWEAPON_MOVEZ = 0;
1847 HUDWEAPON_CHOP++;
1848 }
1849 }
1850 }
1851 else if ( HUDWEAPON_CHOP == 3 ) // return from first swing
1852 {
1853 if ( swingweapon )
1854 {
1855 // another swing...
1856 Item* item = stats[clientnum]->weapon;
1857 if ( item && !hideWeapon )
1858 {
1859 if ( !rangedweapon
1860 && item->type != TOOL_SKELETONKEY
1861 && item->type != TOOL_LOCKPICK
1862 && itemCategory(item) != POTION
1863 && itemCategory(item) != GEM
1864 && item->type != FOOD_CREAMPIE
1865 && !(item->type >= ARTIFACT_ORB_BLUE && item->type <= ARTIFACT_ORB_GREEN)
1866 && !(itemIsThrowableTinkerTool(item))
1867 && item->type != TOOL_WHIP )
1868 {
1869 if ( stats[clientnum]->weapon->type != TOOL_PICKAXE && itemCategory(item) != THROWN )
1870 {
1871 HUDWEAPON_CHOP = 4;
1872 }
1873 else
1874 {
1875 if ( itemCategory(item) == THROWN )
1876 {
1877 throwGimpTimer = 20;
1878 HUDWEAPON_CHOP = 0;
1879 }
1880 else if ( pickaxeGimpTimer <= 0 )
1881 {
1882 HUDWEAPON_CHOP = 1; // allow another swing of pickaxe
1883 }
1884 else
1885 {
1886 HUDWEAPON_CHOP = 0;
1887 }
1888 }
1889 }
1890 }
1891 }
1892 else
1893 {
1894 if (stats[clientnum]->weapon && !hideWeapon )
1895 {
1896 if ( stats[clientnum]->weapon->type == SLING
1897 || stats[clientnum]->weapon->type == SHORTBOW
1898 || stats[clientnum]->weapon->type == ARTIFACT_BOW
1899 || stats[clientnum]->weapon->type == LONGBOW
1900 || stats[clientnum]->weapon->type == COMPOUND_BOW )
1901 {
1902 if (bowFire)
1903 {
1904 players[clientnum]->entity->attack(0, 0, nullptr);
1905 HUDWEAPON_MOVEX = -2;
1906 }
1907 }
1908 }
1909 }
1910
1911 bool crossbow = stats[clientnum]->weapon && (stats[clientnum]->weapon->type == CROSSBOW || stats[clientnum]->weapon->type == HEAVY_CROSSBOW);
1912
1913 if ( stats[clientnum]->weapon && !hideWeapon )
1914 {
1915 if ( rangedweapon )
1916 {
1917 if ( HUDWEAPON_MOVEX > 0 )
1918 {
1919 HUDWEAPON_MOVEX = std::max<real_t>(HUDWEAPON_MOVEX - 1, 0.0);
1920 }
1921 else if ( HUDWEAPON_MOVEX < 0 )
1922 {
1923 HUDWEAPON_MOVEX = std::min<real_t>(HUDWEAPON_MOVEX + .1, 0.0);
1924 }
1925 if ( HUDWEAPON_MOVEY > 0 )
1926 {
1927 HUDWEAPON_MOVEY = std::max<real_t>(HUDWEAPON_MOVEY - 1, 0.0);
1928 }
1929 else if ( HUDWEAPON_MOVEY < 0 )
1930 {
1931 HUDWEAPON_MOVEY = std::min<real_t>(HUDWEAPON_MOVEY + 1, 0.0);
1932 }
1933 if ( HUDWEAPON_MOVEZ > 0 )
1934 {
1935 HUDWEAPON_MOVEZ = std::max<real_t>(HUDWEAPON_MOVEZ - 1, 0.0);
1936 }
1937 else if ( HUDWEAPON_MOVEZ < 0 )
1938 {
1939 HUDWEAPON_MOVEZ = std::min<real_t>(HUDWEAPON_MOVEZ + 1, 0.0);
1940 }
1941 if ( HUDWEAPON_YAW > -.1 )
1942 {
1943 HUDWEAPON_YAW = std::max<real_t>(HUDWEAPON_YAW - .1, -.1);
1944 }
1945 else if ( HUDWEAPON_YAW < -.1 )
1946 {
1947 HUDWEAPON_YAW = std::min<real_t>(HUDWEAPON_YAW + .1, -.1);
1948 }
1949 if ( HUDWEAPON_PITCH > 0 )
1950 {
1951 HUDWEAPON_PITCH = std::max<real_t>(HUDWEAPON_PITCH - .1, 0.0);
1952 }
1953 else if ( HUDWEAPON_PITCH < 0 )
1954 {
1955 HUDWEAPON_PITCH = std::min<real_t>(HUDWEAPON_PITCH + .1, 0.0);
1956 }
1957 if ( HUDWEAPON_ROLL > 0 )
1958 {
1959 HUDWEAPON_ROLL = std::max<real_t>(HUDWEAPON_ROLL - .1, 0.0);
1960 }
1961 else if ( HUDWEAPON_ROLL < 0 )
1962 {
1963 HUDWEAPON_ROLL = std::min<real_t>(HUDWEAPON_ROLL + .1, 0.0);
1964 }
1965 }
1966 else
1967 {
1968 HUDWEAPON_MOVEX -= .25;
1969 if ( HUDWEAPON_MOVEX < 0 )
1970 {
1971 HUDWEAPON_MOVEX = 0;
1972 }
1973 }
1974 }
1975 else
1976 {
1977 HUDWEAPON_MOVEX -= .25;
1978 if ( HUDWEAPON_MOVEX < 0 )
1979 {
1980 HUDWEAPON_MOVEX = 0;
1981 }
1982 }
1983 HUDWEAPON_PITCH -= .15;
1984 if ( HUDWEAPON_PITCH < 0 )
1985 {
1986 HUDWEAPON_PITCH = 0;
1987 }
1988 HUDWEAPON_MOVEY += .45;
1989 if ( HUDWEAPON_MOVEY > 0 )
1990 {
1991 HUDWEAPON_MOVEY = 0;
1992 }
1993 HUDWEAPON_MOVEZ -= .35;
1994 if ( HUDWEAPON_MOVEZ < 0 )
1995 {
1996 HUDWEAPON_MOVEZ = 0;
1997 if ( HUDWEAPON_PITCH == 0 && HUDWEAPON_MOVEY == 0 && HUDWEAPON_MOVEX == 0 )
1998 {
1999 HUDWEAPON_CHOP = 0;
2000 }
2001 }
2002 }
2003 else if ( HUDWEAPON_CHOP == 4 ) // prepare for second swing
2004 {
2005 int targetZ = -4;
2006 real_t targetRoll = -PI / 2;
2007 real_t rateY = .75;
2008 real_t rateRoll = .25;
2009 int targetY = -6;
2010 real_t targetPitch = 0.f;
2011 if ( whip )
2012 {
2013 HUDWEAPON_PITCH += .25;
2014 targetPitch = PI / 8;
2015 if ( HUDWEAPON_PITCH > targetPitch )
2016 {
2017 HUDWEAPON_PITCH = targetPitch;
2018 }
2019 HUDWEAPON_WHIP_ANGLE = 0;
2020 HUDWEAPON_YAW -= .35;
2021 if ( HUDWEAPON_YAW < -PI / 4 )
2022 {
2023 HUDWEAPON_YAW = -PI / 4;
2024 }
2025 rateY = .55;
2026 rateRoll = .35;
2027 }
2028 else
2029 {
2030 HUDWEAPON_YAW = 0;
2031 HUDWEAPON_PITCH -= .25;
2032 if ( HUDWEAPON_PITCH < targetPitch )
2033 {
2034 HUDWEAPON_PITCH = targetPitch;
2035 }
2036 }
2037 HUDWEAPON_MOVEX -= .35;
2038 if ( HUDWEAPON_MOVEX < 0 )
2039 {
2040 HUDWEAPON_MOVEX = 0;
2041 }
2042 HUDWEAPON_MOVEZ -= .75;
2043 if ( HUDWEAPON_MOVEZ < targetZ )
2044 {
2045 HUDWEAPON_MOVEZ = targetZ;
2046 }
2047 HUDWEAPON_MOVEY -= rateY;
2048 if ( HUDWEAPON_MOVEY < targetY )
2049 {
2050 HUDWEAPON_MOVEY = targetY;
2051 }
2052 HUDWEAPON_ROLL -= rateRoll;
2053
2054 if (HUDWEAPON_ROLL < targetRoll )
2055 {
2056 HUDWEAPON_ROLL = targetRoll;
2057 if (HUDWEAPON_PITCH == targetPitch && HUDWEAPON_MOVEX == 0 && HUDWEAPON_MOVEY == targetY && HUDWEAPON_MOVEZ == targetZ)
2058 {
2059 if (!swingweapon)
2060 {
2061 HUDWEAPON_CHOP++;
2062 players[clientnum]->entity->attack(2, HUDWEAPON_CHARGE, nullptr);
2063 if ( stats[clientnum]->weapon
2064 && (stats[clientnum]->weapon->type == CROSSBOW || stats[clientnum]->weapon->type == HEAVY_CROSSBOW) )
2065 {
2066 throwGimpTimer = 40; // fix for swapping weapon to crossbow while charging.
2067 }
2068 HUDWEAPON_CHARGE = 0;
2069 HUDWEAPON_OVERCHARGE = 0;
2070 if (players[clientnum]->entity->skill[3] == 0) // debug cam OFF
2071 {
2072 camera_shakex += .07;
2073 }
2074 if ( whip && pickaxeGimpTimer < 20 )
2075 {
2076 pickaxeGimpTimer = 20; // fix for swapping weapon from whip causing issues.
2077 }
2078 }
2079 else
2080 {
2081 HUDWEAPON_CHARGE = std::min<real_t>(HUDWEAPON_CHARGE + 1, MAXCHARGE);
2082 }
2083 }
2084 }
2085 }
2086 else if (HUDWEAPON_CHOP == 5) // second swing
2087 {
2088 if ( whip )
2089 {
2090 if ( HUDWEAPON_WHIP_ANGLE == 0 && my->sprite == items[TOOL_WHIP].fpindex )
2091 {
2092 HUDWEAPON_WHIP_ANGLE = 1;
2093 HUDWEAPON_PITCH = 3 * PI / 8; // convert from whip.vox to whip_attack.vox
2094 HUDWEAPON_YAW -= PI / 4; // convert from whip.vox to whip_attack.vox
2095 }
2096
2097 if ( HUDWEAPON_WHIP_ANGLE >= 1 )
2098 {
2099 my->sprite = items[TOOL_WHIP].fpindex + 1;
2100 }
2101
2102 real_t animationMul = 0.8;
2103
2104 HUDWEAPON_MOVEX = sin(HUDWEAPON_YAW + PI / 4) * -4;
2105 HUDWEAPON_MOVEY = -2 + cos(std::min(HUDWEAPON_YAW + PI / 4, 3 * PI / 4)) * -4;
2106 HUDWEAPON_YAW += .35 * animationMul;
2107 if ( HUDWEAPON_YAW > PI / 4 )
2108 {
2109 my->sprite = items[TOOL_WHIP].fpindex;
2110 HUDWEAPON_PITCH = PI / 8; // convert from whip_attack.vox to whip.vox
2111 HUDWEAPON_YAW = PI / 2; // convert from whip_attack.vox to whip.vox
2112 HUDWEAPON_CHOP++;
2113 HUDWEAPON_WHIP_ANGLE = 0;
2114 HUDWEAPON_MOVEX += 2; // trial and error got these numbers for MOVEX and MOVEY :)
2115 HUDWEAPON_MOVEY += 10;
2116 }
2117 }
2118 else
2119 {
2120 HUDWEAPON_MOVEX = sin(HUDWEAPON_YAW) * 1;
2121 HUDWEAPON_MOVEY = cos(HUDWEAPON_YAW) * -6;
2122 HUDWEAPON_YAW += .35;
2123 if ( HUDWEAPON_YAW > (3 * PI) / 4 )
2124 {
2125 HUDWEAPON_YAW = (3 * PI) / 4;
2126 HUDWEAPON_CHOP++;
2127 }
2128 }
2129 }
2130 else if (HUDWEAPON_CHOP == 6) // return from second swing
2131 {
2132 if ( swingweapon )
2133 {
2134 // one more swing...
2135 if ( stats[clientnum]->weapon && !hideWeapon )
2136 {
2137 int weaponSkill = getWeaponSkill(stats[clientnum]->weapon);
2138 if ( weaponSkill == PRO_SWORD || stats[clientnum]->weapon->type == STEEL_HALBERD )
2139 {
2140 HUDWEAPON_CHOP = 7; // swords + halberds can stab
2141 }
2142 else
2143 {
2144 HUDWEAPON_CHOP = 1; // everything else can't
2145 }
2146 }
2147 }
2148
2149 if ( whip )
2150 {
2151 real_t animationMultiplier = 0.8;
2152 if ( HUDWEAPON_MOVEX > 0.f )
2153 {
2154 HUDWEAPON_MOVEX = std::max<real_t>(HUDWEAPON_MOVEX - .2 * animationMultiplier, 0.f);
2155 }
2156 else if ( HUDWEAPON_MOVEX < 0.f )
2157 {
2158 HUDWEAPON_MOVEX = std::min<real_t>(HUDWEAPON_MOVEX + .2 * animationMultiplier, 0.f);
2159 }
2160 HUDWEAPON_MOVEY -= 0.9 * animationMultiplier;
2161 if ( HUDWEAPON_MOVEY < 0 )
2162 {
2163 HUDWEAPON_MOVEY = 0;
2164 }
2165 if ( HUDWEAPON_ROLL > -PI / 8 )
2166 {
2167 HUDWEAPON_PITCH -= .2 * animationMultiplier;
2168 if ( HUDWEAPON_PITCH < 0.f )
2169 {
2170 HUDWEAPON_PITCH = 0.f;
2171 }
2172 }
2173 HUDWEAPON_ROLL += .2 * animationMultiplier;
2174 }
2175 else
2176 {
2177 HUDWEAPON_MOVEX -= .25;
2178 if ( HUDWEAPON_MOVEX < 0 )
2179 {
2180 HUDWEAPON_MOVEX = 0;
2181 }
2182 HUDWEAPON_MOVEY -= .25;
2183 if ( HUDWEAPON_MOVEY < 0 )
2184 {
2185 HUDWEAPON_MOVEY = 0;
2186 }
2187 HUDWEAPON_ROLL += .25;
2188 }
2189 HUDWEAPON_YAW -= .25;
2190 if ( HUDWEAPON_YAW < -.1 )
2191 {
2192 HUDWEAPON_YAW = -.1;
2193 }
2194 HUDWEAPON_MOVEZ += .35;
2195 if ( HUDWEAPON_MOVEZ > 0 )
2196 {
2197 HUDWEAPON_MOVEZ = 0;
2198 }
2199 if ( HUDWEAPON_ROLL > 0 )
2200 {
2201 HUDWEAPON_ROLL = 0;
2202 if ( HUDWEAPON_YAW == -.1 && HUDWEAPON_MOVEZ == 0 && HUDWEAPON_MOVEY == 0 && HUDWEAPON_MOVEX == 0 )
2203 {
2204 HUDWEAPON_CHOP = 0;
2205 }
2206 }
2207 }
2208 else if ( HUDWEAPON_CHOP == 7 ) // prepare for third swing
2209 {
2210 HUDWEAPON_MOVEX -= .35;
2211 if ( HUDWEAPON_MOVEX < 0 )
2212 {
2213 HUDWEAPON_MOVEX = 0;
2214 }
2215 HUDWEAPON_MOVEY -= .45;
2216 if ( HUDWEAPON_MOVEY < -1 )
2217 {
2218 HUDWEAPON_MOVEY = -1;
2219 }
2220 HUDWEAPON_MOVEZ -= .25;
2221 if ( HUDWEAPON_MOVEZ < -2 )
2222 {
2223 HUDWEAPON_MOVEZ = -2;
2224 }
2225 HUDWEAPON_YAW -= .15;
2226 if ( HUDWEAPON_YAW < 2 * PI / 5 )
2227 {
2228 result = 2 * PI / 5;
2229 HUDWEAPON_YAW = result;
2230 }
2231 real_t pitchLimit = .2;
2232 if ( playerRace == SPIDER )
2233 {
2234 pitchLimit = -PI / 2;
2235 HUDWEAPON_PITCH -= .2;
2236 }
2237 else
2238 {
2239 HUDWEAPON_PITCH -= .05;
2240 }
2241 if ( HUDWEAPON_PITCH < pitchLimit )
2242 {
2243 HUDWEAPON_PITCH = pitchLimit;
2244 }
2245 HUDWEAPON_ROLL -= .15;
2246 if (HUDWEAPON_ROLL < -2 * PI / 5)
2247 {
2248 HUDWEAPON_ROLL = -2 * PI / 5;
2249 if (HUDWEAPON_PITCH == pitchLimit && HUDWEAPON_YAW == result
2250 && HUDWEAPON_MOVEX == 0 && HUDWEAPON_MOVEY == -1 && (HUDWEAPON_MOVEZ == -2))
2251 {
2252 if (!swingweapon)
2253 {
2254 HUDWEAPON_CHOP++;
2255 if ( stats[clientnum]->weapon && hideWeapon )
2256 {
2257 players[clientnum]->entity->attack(1, HUDWEAPON_CHARGE, nullptr);
2258 }
2259 else
2260 {
2261 players[clientnum]->entity->attack(3, HUDWEAPON_CHARGE, nullptr);
2262 }
2263 if ( stats[clientnum]->weapon
2264 && (stats[clientnum]->weapon->type == CROSSBOW || stats[clientnum]->weapon->type == HEAVY_CROSSBOW) )
2265 {
2266 throwGimpTimer = 40; // fix for swapping weapon to crossbow while charging.
2267 }
2268 HUDWEAPON_CHARGE = 0;
2269 HUDWEAPON_OVERCHARGE = 0;
2270 if (players[clientnum]->entity->skill[3] == 0) // debug cam OFF
2271 {
2272 camera_shakex += .03;
2273 camera_shakey += 4;
2274 }
2275 }
2276 else
2277 {
2278 HUDWEAPON_CHARGE = std::min<real_t>(HUDWEAPON_CHARGE + 1, MAXCHARGE);
2279 }
2280 }
2281 }
2282 }
2283 else if ( HUDWEAPON_CHOP == 8 ) // third swing
2284 {
2285 if ( playerRace == SPIDER )
2286 {
2287 HUDWEAPON_MOVEX += 1;
2288 if ( HUDWEAPON_MOVEX > 2 )
2289 {
2290 HUDWEAPON_MOVEX = 2;
2291 HUDWEAPON_CHOP++;
2292 }
2293 HUDWEAPON_PITCH += .2;
2294 if ( HUDWEAPON_PITCH > 0 )
2295 {
2296 HUDWEAPON_PITCH = 0;
2297 }
2298 }
2299 else
2300 {
2301 HUDWEAPON_MOVEX += 2;
2302 if ( HUDWEAPON_MOVEX > 4 )
2303 {
2304 HUDWEAPON_MOVEX = 4;
2305 HUDWEAPON_CHOP++;
2306 }
2307 }
2308 }
2309 else if ( HUDWEAPON_CHOP == 9 ) // return from third swing
2310 {
2311 if ( playerRace == SPIDER )
2312 {
2313 HUDWEAPON_MOVEX -= .25;
2314 }
2315 else
2316 {
2317 HUDWEAPON_MOVEX -= .5;
2318 }
2319 if ( HUDWEAPON_MOVEX < 0 )
2320 {
2321 HUDWEAPON_MOVEX = 0;
2322 }
2323 HUDWEAPON_MOVEY += .25;
2324 if ( HUDWEAPON_MOVEY > 0 )
2325 {
2326 HUDWEAPON_MOVEY = 0;
2327 }
2328 HUDWEAPON_MOVEZ += .35;
2329 if ( HUDWEAPON_MOVEZ > 0 )
2330 {
2331 HUDWEAPON_MOVEZ = 0;
2332 }
2333 if ( HUDWEAPON_MOVEX == 0 )
2334 {
2335 if ( swingweapon )
2336 {
2337 // restart the combo...
2338 if ( stats[clientnum]->weapon == NULL || hideWeapon )
2339 {
2340 HUDWEAPON_CHOP = 7;
2341 }
2342 else
2343 {
2344 if ( itemCategory(stats[clientnum]->weapon) != MAGICSTAFF && stats[clientnum]->weapon->type != CRYSTAL_SPEAR && stats[clientnum]->weapon->type != IRON_SPEAR && stats[clientnum]->weapon->type != ARTIFACT_SPEAR )
2345 {
2346 HUDWEAPON_CHOP = 1;
2347 }
2348 else
2349 {
2350 HUDWEAPON_CHOP = 7;
2351 }
2352 }
2353 }
2354 HUDWEAPON_YAW -= .25;
2355 if ( HUDWEAPON_YAW < -.1 )
2356 {
2357 HUDWEAPON_YAW = -.1;
2358 }
2359 if ( playerRace == SPIDER )
2360 {
2361 HUDWEAPON_PITCH += .12;
2362 }
2363 else
2364 {
2365 HUDWEAPON_PITCH += .05;
2366 }
2367 if ( HUDWEAPON_PITCH > 0)
2368 {
2369 HUDWEAPON_PITCH = 0;
2370 }
2371 HUDWEAPON_ROLL += .25;
2372 if ( HUDWEAPON_ROLL > 0 )
2373 {
2374 HUDWEAPON_ROLL = 0;
2375 if ( HUDWEAPON_YAW == -.1 && HUDWEAPON_PITCH == 0 && HUDWEAPON_MOVEZ == 0 && HUDWEAPON_MOVEY == 0 && HUDWEAPON_MOVEX == 0 )
2376 {
2377 HUDWEAPON_CHOP = 0;
2378 }
2379 }
2380 }
2381 }
2382 else if ( HUDWEAPON_CHOP == 10 ) // special punch
2383 {
2384 HUDWEAPON_MOVEX = std::max(static_cast<real_t>(0.0), HUDWEAPON_MOVEX - .15); // forward/back
2385 HUDWEAPON_MOVEY = std::min(static_cast<real_t>(1.0), HUDWEAPON_MOVEY + .25); // left/right
2386 HUDWEAPON_MOVEZ = std::max(static_cast<real_t>(-2.0), HUDWEAPON_MOVEZ - .05); // up/down
2387
2388 HUDWEAPON_YAW -= .15;
2389 if ( HUDWEAPON_YAW < 2 * PI / 5 )
2390 {
2391 result = 2 * PI / 5;
2392 HUDWEAPON_YAW = result;
2393 }
2394 real_t pitchLimit = .2;
2395 HUDWEAPON_PITCH -= .05;
2396 if ( HUDWEAPON_PITCH < pitchLimit )
2397 {
2398 HUDWEAPON_PITCH = pitchLimit;
2399 }
2400 HUDWEAPON_ROLL -= .15;
2401 if ( HUDWEAPON_ROLL < -2 * PI / 5 )
2402 {
2403 HUDWEAPON_ROLL = -2 * PI / 5;
2404 if ( HUDWEAPON_CHARGE < 40 )
2405 {
2406 HUDWEAPON_CHARGE += 1;
2407 }
2408 else
2409 {
2410 HUDWEAPON_CHOP = 11;
2411 HUDWEAPON_CHARGE = 0;
2412 HUDWEAPON_OVERCHARGE = 0;
2413 if ( players[clientnum]->entity->skill[3] == 0 ) // debug cam OFF
2414 {
2415 camera_shakex += .06;
2416 camera_shakey += 6;
2417 }
2418 players[clientnum]->entity->attack(PLAYER_POSE_GOLEM_SMASH, MAXCHARGE, nullptr);
2419 }
2420 }
2421 }
2422 else if ( HUDWEAPON_CHOP == 11 ) // third swing
2423 {
2424 HUDWEAPON_MOVEX += 1;
2425 if ( HUDWEAPON_MOVEX > 4 )
2426 {
2427 HUDWEAPON_MOVEX = 4;
2428 HUDWEAPON_CHOP = 12;
2429 }
2430 }
2431 else if ( HUDWEAPON_CHOP == 12 ) // return from third swing
2432 {
2433 HUDWEAPON_MOVEX -= .2;
2434 if ( HUDWEAPON_MOVEX < 0 )
2435 {
2436 HUDWEAPON_MOVEX = 0;
2437 }
2438 HUDWEAPON_MOVEY += .15;
2439 if ( HUDWEAPON_MOVEY > 0 )
2440 {
2441 HUDWEAPON_MOVEY = 0;
2442 }
2443 HUDWEAPON_MOVEZ += .25;
2444 if ( HUDWEAPON_MOVEZ > 0 )
2445 {
2446 HUDWEAPON_MOVEZ = 0;
2447 }
2448 if ( HUDWEAPON_MOVEX == 0 )
2449 {
2450 HUDWEAPON_YAW -= .25;
2451 if ( HUDWEAPON_YAW < -.1 )
2452 {
2453 HUDWEAPON_YAW = -.1;
2454 }
2455 HUDWEAPON_PITCH += .05;
2456 if ( HUDWEAPON_PITCH > 0 )
2457 {
2458 HUDWEAPON_PITCH = 0;
2459 }
2460 HUDWEAPON_ROLL += .25;
2461 if ( HUDWEAPON_ROLL > 0 )
2462 {
2463 HUDWEAPON_ROLL = 0;
2464 if ( HUDWEAPON_YAW == -.1 && HUDWEAPON_PITCH == 0 && HUDWEAPON_MOVEZ == 0 && HUDWEAPON_MOVEY == 0 && HUDWEAPON_MOVEX == 0 )
2465 {
2466 HUDWEAPON_CHOP = 0;
2467 }
2468 }
2469 }
2470 }
2471 else if ( HUDWEAPON_CHOP == 13 ) // tool placing
2472 {
2473 int targetZ = -4;
2474 real_t targetRoll = -PI / 2;
2475 real_t rateY = .1;
2476 real_t rateRoll = .25;
2477 int targetY = 1;
2478 if ( !swingweapon )
2479 {
2480 //targetY = 0;
2481 }
2482 real_t targetPitch = 0.f;
2483
2484 HUDWEAPON_YAW = 0;
2485 HUDWEAPON_PITCH -= .25;
2486 if ( HUDWEAPON_PITCH < targetPitch )
2487 {
2488 HUDWEAPON_PITCH = targetPitch;
2489 }
2490 HUDWEAPON_MOVEX -= .35;
2491 if ( HUDWEAPON_MOVEX < 0 )
2492 {
2493 HUDWEAPON_MOVEX = 0;
2494 }
2495 HUDWEAPON_MOVEZ -= .75;
2496 if ( HUDWEAPON_MOVEZ < targetZ )
2497 {
2498 HUDWEAPON_MOVEZ = targetZ;
2499 }
2500 HUDWEAPON_MOVEY += rateY;
2501 if ( HUDWEAPON_MOVEY > targetY )
2502 {
2503 HUDWEAPON_MOVEY = targetY;
2504 }
2505 HUDWEAPON_ROLL -= rateRoll;
2506
2507 if ( HUDWEAPON_ROLL < targetRoll )
2508 {
2509 HUDWEAPON_ROLL = targetRoll;
2510 if ( HUDWEAPON_PITCH == targetPitch && HUDWEAPON_MOVEX == 0 && HUDWEAPON_MOVEY == targetY && HUDWEAPON_MOVEZ == targetZ )
2511 {
2512 if ( !swingweapon )
2513 {
2514 HUDWEAPON_CHOP = 14;
2515 if ( players[clientnum]->entity->skill[3] == 0 ) // debug cam OFF
2516 {
2517 camera_shakex += .07;
2518 }
2519 Entity* player = players[clientnum]->entity;
2520 if ( stats[clientnum]->weapon && (stats[clientnum]->weapon->isTinkeringItemWithThrownLimit())
2521 && !playerCanSpawnMoreTinkeringBots(stats[clientnum]) )
2522 {
2523 throwGimpTimer = TICKS_PER_SECOND / 2; // limits how often you can throw objects
2524 if ( stats[clientnum]->PROFICIENCIES[PRO_LOCKPICKING] >= SKILL_LEVEL_LEGENDARY )
2525 {
2526 messagePlayer(clientnum, language[3884]);
2527 }
2528 else
2529 {
2530 messagePlayer(clientnum, language[3883]);
2531 }
2532 }
2533 else if ( stats[clientnum]->weapon && player )
2534 {
2535 //lineTrace(player, player->x, player->y, player->yaw, STRIKERANGE, 0, false);
2536 players[clientnum]->entity->attack(2, HUDWEAPON_CHARGE, nullptr);
2537 throwGimpTimer = TICKS_PER_SECOND / 2; // limits how often you can throw objects
2538 Item* item = stats[clientnum]->weapon;
2539 if ( multiplayer == CLIENT )
2540 {
2541 item->count--;
2542 if ( item->count <= 0 )
2543 {
2544 if ( item->node )
2545 {
2546 list_RemoveNode(item->node);
2547 }
2548 else
2549 {
2550 free(item);
2551 }
2552 stats[clientnum]->weapon = NULL;
2553 }
2554 }
2555 if ( !stats[clientnum]->weapon )
2556 {
2557 HUDWEAPON_ROLL = 0;
2558 HUDWEAPON_MOVEZ = 3;
2559 }
2560 }
2561 HUDWEAPON_CHARGE = 0;
2562 HUDWEAPON_OVERCHARGE = 0;
2563 }
2564 else
2565 {
2566 HUDWEAPON_CHARGE = std::min<real_t>(HUDWEAPON_CHARGE + 1, MAXCHARGE);
2567 }
2568 }
2569 }
2570 }
2571 else if ( HUDWEAPON_CHOP == 14 ) // second swing
2572 {
2573 HUDWEAPON_MOVEX = sin(HUDWEAPON_YAW) * 2;
2574 HUDWEAPON_MOVEY = cos(HUDWEAPON_YAW) * 1;
2575 HUDWEAPON_YAW += .3;
2576 if ( HUDWEAPON_YAW > (2 * PI) / 4 )
2577 {
2578 HUDWEAPON_YAW = (2 * PI) / 4;
2579 HUDWEAPON_CHOP++;
2580 }
2581 }
2582 else if ( HUDWEAPON_CHOP == 15 ) // return from second swing
2583 {
2584 HUDWEAPON_MOVEX -= .25;
2585 if ( HUDWEAPON_MOVEX < 0 )
2586 {
2587 HUDWEAPON_MOVEX = 0;
2588 }
2589 HUDWEAPON_MOVEY -= .25;
2590 if ( HUDWEAPON_MOVEY < 0 )
2591 {
2592 HUDWEAPON_MOVEY = 0;
2593 }
2594 HUDWEAPON_ROLL += .05;
2595
2596 HUDWEAPON_YAW -= .05;
2597 if ( HUDWEAPON_YAW < -.1 )
2598 {
2599 HUDWEAPON_YAW = -.1;
2600 }
2601 HUDWEAPON_MOVEZ += .35;
2602 if ( HUDWEAPON_MOVEZ > 0 )
2603 {
2604 HUDWEAPON_MOVEZ = 0;
2605 }
2606 if ( HUDWEAPON_ROLL > 0 )
2607 {
2608 HUDWEAPON_ROLL = 0;
2609 if ( HUDWEAPON_YAW == -.1 && HUDWEAPON_MOVEZ == 0 && HUDWEAPON_MOVEY == 0 && HUDWEAPON_MOVEX == 0 )
2610 {
2611 HUDWEAPON_CHOP = 0;
2612 }
2613 }
2614 }
2615 else if ( HUDWEAPON_CHOP == CROSSBOW_CHOP_RELOAD_START ) // crossbow reload
2616 {
2617 if ( HUDWEAPON_MOVEX > 0 )
2618 {
2619 HUDWEAPON_MOVEX = std::max<real_t>(HUDWEAPON_MOVEX - 1, 0.0);
2620 }
2621 else if ( HUDWEAPON_MOVEX < 0 )
2622 {
2623 HUDWEAPON_MOVEX = std::min<real_t>(HUDWEAPON_MOVEX + .15, 0.0);
2624 if ( HUDWEAPON_MOVEX > -1 )
2625 {
2626 if ( HUDWEAPON_CROSSBOW_RELOAD_ANIMATION == CROSSBOW_ANIM_SHOOT
2627 && stats[clientnum]->weapon && stats[clientnum]->weapon->type == HEAVY_CROSSBOW )
2628 {
2629 #ifdef SOUND
2630 bowDrawingSoundChannel = playSound(410, 64);
2631 #endif
2632 }
2633
2634 HUDWEAPON_CROSSBOW_RELOAD_ANIMATION = CROSSBOW_ANIM_RELOAD_START;
2635
2636 HUDWEAPON_MOVEZ += .15;
2637 real_t targetZ = 1;
2638 if ( !rangedWeaponUseQuiverOnAttack(stats[clientnum]) )
2639 {
2640 targetZ = 0.5;
2641 }
2642 if ( HUDWEAPON_MOVEZ > targetZ )
2643 {
2644 if ( stats[clientnum]->weapon && stats[clientnum]->weapon->type == HEAVY_CROSSBOW )
2645 {
2646 HUDWEAPON_CROSSBOW_RELOAD_ANIMATION = CROSSBOW_ANIM_HEAVY_RELOAD_MIDPOINT;
2647 }
2648 else
2649 {
2650 HUDWEAPON_CROSSBOW_RELOAD_ANIMATION = CROSSBOW_ANIM_RELOAD_END;
2651 }
2652 HUDWEAPON_MOVEZ = targetZ;
2653 }
2654 }
2655 }
2656 if ( fabs(HUDWEAPON_MOVEX) < 0.01 )
2657 {
2658 HUDWEAPON_CHOP = CROSSBOW_CHOP_RELOAD_ENDING;
2659 }
2660
2661 if ( HUDWEAPON_MOVEY > 0 )
2662 {
2663 HUDWEAPON_MOVEY = std::max<real_t>(HUDWEAPON_MOVEY - 1, 0.0);
2664 }
2665 else if ( HUDWEAPON_MOVEY < 0 )
2666 {
2667 HUDWEAPON_MOVEY = std::min<real_t>(HUDWEAPON_MOVEY + 1, 0.0);
2668 }
2669 if ( HUDWEAPON_YAW > -.1 )
2670 {
2671 HUDWEAPON_YAW = std::max<real_t>(HUDWEAPON_YAW - .1, -.1);
2672 }
2673 else if ( HUDWEAPON_YAW < -.1 )
2674 {
2675 HUDWEAPON_YAW = std::min<real_t>(HUDWEAPON_YAW + .1, -.1);
2676 }
2677 if ( HUDWEAPON_ROLL > 0 )
2678 {
2679 HUDWEAPON_ROLL = std::max<real_t>(HUDWEAPON_ROLL - .1, 0.0);
2680 }
2681 else if ( HUDWEAPON_ROLL < 0 )
2682 {
2683 HUDWEAPON_ROLL = std::min<real_t>(HUDWEAPON_ROLL + .1, 0.0);
2684 }
2685 if ( HUDWEAPON_PITCH > 0 )
2686 {
2687 HUDWEAPON_PITCH = std::max<real_t>(HUDWEAPON_PITCH - .1, 0.0);
2688 }
2689 else if ( HUDWEAPON_PITCH < 0 )
2690 {
2691 HUDWEAPON_PITCH = std::min<real_t>(HUDWEAPON_PITCH + .1, 0.0);
2692 }
2693 }
2694 else if ( HUDWEAPON_CHOP == CROSSBOW_CHOP_RELOAD_ENDING ) // crossbow reload
2695 {
2696 if ( HUDWEAPON_MOVEZ > 1 )
2697 {
2698 HUDWEAPON_MOVEZ = std::max<real_t>(HUDWEAPON_MOVEZ - 0.2, 1);
2699 }
2700 if ( stats[clientnum]->weapon && stats[clientnum]->weapon->type == HEAVY_CROSSBOW )
2701 {
2702 if ( HUDWEAPON_MOVEZ > 1 )
2703 {
2704 HUDWEAPON_MOVEZ -= .1; // just in case we're overshooting from another animation or something move faster.
2705 }
2706 else
2707 {
2708 if ( rangedWeaponUseQuiverOnAttack(stats[clientnum]) )
2709 {
2710 // we fired a quiver shot, the crossbow will be lower here so move faster.
2711 HUDWEAPON_MOVEZ -= .02;
2712 if ( HUDWEAPON_MOVEZ < 0.5 )
2713 {
2714 HUDWEAPON_CROSSBOW_RELOAD_ANIMATION = CROSSBOW_ANIM_RELOAD_END;
2715 }
2716 else if ( HUDWEAPON_MOVEZ < 0.8 )
2717 {
2718 shakeRangedWeapon = true;
2719 }
2720 }
2721 else
2722 {
2723 HUDWEAPON_MOVEZ -= .01;
2724 if ( HUDWEAPON_MOVEZ < 0.25 )
2725 {
2726 HUDWEAPON_CROSSBOW_RELOAD_ANIMATION = CROSSBOW_ANIM_RELOAD_END;
2727 }
2728 else if ( HUDWEAPON_MOVEZ < 0.4 )
2729 {
2730 shakeRangedWeapon = true;
2731 }
2732 }
2733 }
2734 }
2735 else
2736 {
2737 HUDWEAPON_MOVEZ -= .1;
2738 }
2739 if ( HUDWEAPON_MOVEZ < 0 )
2740 {
2741 HUDWEAPON_MOVEZ = 0;
2742 HUDWEAPON_CHOP = 0;
2743 }
2744
2745 if ( HUDWEAPON_MOVEY > 0 )
2746 {
2747 HUDWEAPON_MOVEY = std::max<real_t>(HUDWEAPON_MOVEY - 1, 0.0);
2748 }
2749 else if ( HUDWEAPON_MOVEY < 0 )
2750 {
2751 HUDWEAPON_MOVEY = std::min<real_t>(HUDWEAPON_MOVEY + 1, 0.0);
2752 }
2753 if ( HUDWEAPON_YAW > -.1 )
2754 {
2755 HUDWEAPON_YAW = std::max<real_t>(HUDWEAPON_YAW - .1, -.1);
2756 }
2757 else if ( HUDWEAPON_YAW < -.1 )
2758 {
2759 HUDWEAPON_YAW = std::min<real_t>(HUDWEAPON_YAW + .1, -.1);
2760 }
2761 if ( HUDWEAPON_ROLL > 0 )
2762 {
2763 HUDWEAPON_ROLL = std::max<real_t>(HUDWEAPON_ROLL - .1, 0.0);
2764 }
2765 else if ( HUDWEAPON_ROLL < 0 )
2766 {
2767 HUDWEAPON_ROLL = std::min<real_t>(HUDWEAPON_ROLL + .1, 0.0);
2768 }
2769 if ( HUDWEAPON_PITCH > 0 )
2770 {
2771 HUDWEAPON_PITCH = std::max<real_t>(HUDWEAPON_PITCH - .1, 0.0);
2772 }
2773 else if ( HUDWEAPON_PITCH < 0 )
2774 {
2775 HUDWEAPON_PITCH = std::min<real_t>(HUDWEAPON_PITCH + .1, 0.0);
2776 }
2777 }
2778
2779 if ( HUDWEAPON_CHARGE == MAXCHARGE || castStrikeAnimation
2780 || players[clientnum]->entity->skill[9] == MONSTER_POSE_SPECIAL_WINDUP2
2781 || shakeRangedWeapon )
2782 {
2783 if ( ticks % 5 == 0 && players[clientnum]->entity->skill[9] == MONSTER_POSE_SPECIAL_WINDUP2 )
2784 {
2785 camera_shakey += 6;
2786 }
2787 if ( ticks % 2 == 0 )
2788 {
2789 if ( shakeRangedWeapon )
2790 {
2791 HUDWEAPON_MOVEX -= HUDWEAPON_OLDVIBRATEX;
2792 HUDWEAPON_MOVEY -= HUDWEAPON_OLDVIBRATEY;
2793 HUDWEAPON_OLDVIBRATEX = (rand() % 30 - 10) / 150.f;
2794 HUDWEAPON_OLDVIBRATEY = (rand() % 30 - 10) / 150.f;
2795 HUDWEAPON_MOVEX += HUDWEAPON_OLDVIBRATEX;
2796 HUDWEAPON_MOVEY += HUDWEAPON_OLDVIBRATEY;
2797 }
2798 else
2799 {
2800 // charge vibration
2801 HUDWEAPON_MOVEX -= HUDWEAPON_OLDVIBRATEX;
2802 HUDWEAPON_MOVEY -= HUDWEAPON_OLDVIBRATEY;
2803 HUDWEAPON_MOVEZ -= HUDWEAPON_OLDVIBRATEZ;
2804 HUDWEAPON_OLDVIBRATEX = (rand() % 30 - 10) / 80.f;
2805 HUDWEAPON_OLDVIBRATEY = (rand() % 30 - 10) / 80.f;
2806 HUDWEAPON_OLDVIBRATEZ = (rand() % 30 - 10) / 80.f;
2807 HUDWEAPON_MOVEX += HUDWEAPON_OLDVIBRATEX;
2808 HUDWEAPON_MOVEY += HUDWEAPON_OLDVIBRATEY;
2809 HUDWEAPON_MOVEZ += HUDWEAPON_OLDVIBRATEZ;
2810 }
2811 }
2812 if ( (castStrikeAnimation || players[clientnum]->entity->skill[9] == MONSTER_POSE_SPECIAL_WINDUP2)
2813 || shakeRangedWeapon )
2814 {
2815 // don't overcharge here.
2816 }
2817 else
2818 {
2819 HUDWEAPON_OVERCHARGE++;
2820 }
2821 }
2822 if ( castStrikeAnimation ) // magic sprite particles around the fist
2823 {
2824 if ( ticks % 5 == 0 )
2825 {
2826 Entity* entity = spawnGib(my);
2827 entity->flags[INVISIBLE] = false;
2828 entity->flags[SPRITE] = true;
2829 entity->flags[NOUPDATE] = true;
2830 entity->flags[UPDATENEEDED] = false;
2831 entity->flags[OVERDRAW] = true;
2832 entity->flags[BRIGHT] = true;
2833 entity->scalex = 0.25f; //MAKE 'EM SMALL PLEASE!
2834 entity->scaley = 0.25f;
2835 entity->scalez = 0.25f;
2836 entity->z -= 3.5;
2837 entity->sprite = 16; //TODO: Originally. 22. 16 -- spark sprite instead?
2838 entity->yaw = ((rand() % 6) * 60) * PI / 180.0;
2839 entity->pitch = (rand() % 360) * PI / 180.0;
2840 entity->roll = (rand() % 360) * PI / 180.0;
2841 entity->vel_x = cos(entity->yaw) * .1;
2842 entity->vel_y = sin(entity->yaw) * .1;
2843 entity->vel_z = -.15;
2844 entity->fskill[3] = 0.01;
2845 }
2846 }
2847
2848 // move the weapon
2849 if (players[clientnum] == nullptr || players[clientnum]->entity == nullptr)
2850 {
2851 return;
2852 }
2853 double defaultpitch = PI / 8.f;
2854 if (stats[clientnum]->weapon == nullptr || hideWeapon)
2855 {
2856 if ( playerRace == RAT )
2857 {
2858 my->x = 6 + HUDWEAPON_MOVEX / 3;
2859 my->y = 3;// +HUDWEAPON_MOVEY;
2860 my->z = (cameras[HUDWEAPON_PLAYERNUM].z * .1 - players[clientnum]->entity->z) + 7 + HUDWEAPON_MOVEZ / 10;
2861 my->yaw = 0.f - camera_shakex2;
2862 my->pitch = defaultpitch + HUDWEAPON_PITCH - camera_shakey2 / 200.f;
2863 my->roll = HUDWEAPON_ROLL;
2864
2865 if ( isLevitating(stats[clientnum]) )
2866 {
2867 my->z -= 2 * .4;
2868 }
2869 }
2870 else if ( playerRace == SPIDER )
2871 {
2872 my->x = 6 + HUDWEAPON_MOVEX;
2873 my->y = 3;// +HUDWEAPON_MOVEY;
2874 my->z = (cameras[HUDWEAPON_PLAYERNUM].z * .5 - players[clientnum]->entity->z) + 7 + HUDWEAPON_MOVEZ;
2875 my->yaw = 0.f - camera_shakex2;
2876 my->pitch = defaultpitch + HUDWEAPON_PITCH - camera_shakey2 / 200.f;
2877 my->roll = HUDWEAPON_ROLL;
2878 }
2879 else
2880 {
2881 my->x = 6 + HUDWEAPON_MOVEX;
2882 my->y = 3 + HUDWEAPON_MOVEY;
2883 my->z = (cameras[HUDWEAPON_PLAYERNUM].z * .5 - players[clientnum]->entity->z) + 7 + HUDWEAPON_MOVEZ;
2884 my->yaw = HUDWEAPON_YAW - camera_shakex2;
2885 my->pitch = defaultpitch + HUDWEAPON_PITCH - camera_shakey2 / 200.f;
2886 my->roll = HUDWEAPON_ROLL;
2887 }
2888
2889 // dirty hack because we altered the camera height in actPlayer(). adjusts HUD to match new height.
2890 if ( playerRace == CREATURE_IMP && players[clientnum]->entity->z == -4.5 )
2891 {
2892 my->z -= .5;
2893 }
2894 else if ( playerRace == TROLL && players[clientnum]->entity->z <= -1.5 )
2895 {
2896 my->z -= -2 * .5;
2897 }
2898 }
2899 else
2900 {
2901 Item* item = stats[clientnum]->weapon;
2902 if (item)
2903 {
2904 if (item->type == TOOL_SKELETONKEY || item->type == TOOL_LOCKPICK
2905 || (item->type >= ARTIFACT_ORB_BLUE && item->type <= ARTIFACT_ORB_GREEN))
2906 {
2907 defaultpitch = -PI / 8.f;
2908 }
2909 else if ( item->type == TOOL_WHIP )
2910 {
2911 defaultpitch = -PI / 8.f;
2912 }
2913 if ( item->type == CROSSBOW || item->type == HEAVY_CROSSBOW )
2914 {
2915 my->x = 6 + HUDWEAPON_MOVEX;
2916 my->y = 1.5 + HUDWEAPON_MOVEY;
2917 my->z = (cameras[HUDWEAPON_PLAYERNUM].z * .5 - players[clientnum]->entity->z) + 8 + HUDWEAPON_MOVEZ;
2918 my->yaw = -.05 - camera_shakex2;
2919 my->pitch = HUDWEAPON_PITCH - camera_shakey2 / 200.f;
2920 my->roll = HUDWEAPON_ROLL;
2921 }
2922 else if ( item->type == SLING
2923 || item->type == SHORTBOW
2924 || item->type == ARTIFACT_BOW
2925 || item->type == LONGBOW
2926 || item->type == COMPOUND_BOW )
2927 {
2928 my->x = 6 + HUDWEAPON_MOVEX;
2929 my->y = 3 + HUDWEAPON_MOVEY;
2930 my->z = (cameras[HUDWEAPON_PLAYERNUM].z * .5 - players[clientnum]->entity->z) + 7 + HUDWEAPON_MOVEZ;
2931 my->yaw = HUDWEAPON_YAW - camera_shakex2;
2932 my->pitch = HUDWEAPON_PITCH - camera_shakey2 / 200.f;
2933 my->roll = HUDWEAPON_ROLL;
2934 }
2935 else if ( item->type == TOOL_WHIP )
2936 {
2937 my->x = 6 + HUDWEAPON_MOVEX + 5;
2938 my->y = 3 + HUDWEAPON_MOVEY - 0.5 + 1;
2939 //my->flags[OVERDRAW] = false;
2940 my->z = (cameras[HUDWEAPON_PLAYERNUM].z * .5 - players[clientnum]->entity->z) + 7 + HUDWEAPON_MOVEZ;
2941 my->yaw = HUDWEAPON_YAW - camera_shakex2;
2942 my->pitch = defaultpitch + HUDWEAPON_PITCH - camera_shakey2 / 200.f;
2943 if ( my->sprite == items[TOOL_WHIP].fpindex + 1 )
2944 {
2945 my->pitch -= PI / 4;
2946 my->focalx = 10 + (HUDWEAPON_CHOP == 5 ? 4 : 0);
2947 my->focaly = 0;
2948 my->focalz = -4;
2949 my->x += (HUDWEAPON_CHOP == 5 ? -3 : 0);
2950 my->y += (HUDWEAPON_CHOP == 5 ? 6 : 0);
2951 }
2952 else
2953 {
2954 my->focalx = 0;
2955 my->focaly = 0;
2956 my->focalz = -4;
2957 }
2958 my->roll = HUDWEAPON_ROLL;
2959 }
2960 else
2961 {
2962 my->x = 6 + HUDWEAPON_MOVEX + 3 * (itemCategory(item) == POTION);
2963 my->y = 3 + HUDWEAPON_MOVEY - 3 * (itemCategory(item) == POTION);
2964 my->z = (cameras[HUDWEAPON_PLAYERNUM].z * .5 - players[clientnum]->entity->z) + 7 + HUDWEAPON_MOVEZ - 3 * (itemCategory(item) == POTION);
2965 my->yaw = HUDWEAPON_YAW - camera_shakex2;
2966 my->pitch = defaultpitch + HUDWEAPON_PITCH - camera_shakey2 / 200.f;
2967 my->roll = HUDWEAPON_ROLL + (PI / 2) * (itemCategory(item) == POTION);
2968 my->focalx = 0;
2969 }
2970
2971 if ( item->type >= TOOL_BOMB && item->type <= TOOL_TELEPORT_BOMB )
2972 {
2973 my->z += .5;
2974 }
2975 }
2976
2977 // dirty hack because we altered the camera height in actPlayer(). adjusts HUD to match new height.
2978 if ( playerRace == CREATURE_IMP && players[clientnum]->entity->z == -4.5 )
2979 {
2980 my->z -= .5;
2981 }
2982 else if ( playerRace == TROLL && players[clientnum]->entity->z <= -1.5 )
2983 {
2984 my->z -= -2 * .5;
2985 }
2986 }
2987 if ( !my->flags[OVERDRAW] )
2988 {
2989 my->x += 32;
2990 my->y += 32;
2991 my->z -= 3.5;
2992 }
2993 }
2994
2995 #define HUDSHIELD_DEFEND my->skill[0]
2996 #define HUDSHIELD_SNEAKING my->skill[1]
2997 #define HUDSHIELD_PLAYERNUM my->skill[2]
2998 #define HUDSHIELD_MOVEX my->fskill[0]
2999 #define HUDSHIELD_MOVEY my->fskill[1]
3000 #define HUDSHIELD_MOVEZ my->fskill[2]
3001 #define HUDSHIELD_YAW my->fskill[3]
3002 #define HUDSHIELD_PITCH my->fskill[4]
3003 #define HUDSHIELD_ROLL my->fskill[5]
3004
actHudShield(Entity * my)3005 void actHudShield(Entity* my)
3006 {
3007 my->flags[UNCLICKABLE] = true;
3008
3009 auto& camera_shakex = cameravars[HUDSHIELD_PLAYERNUM].shakex;
3010 auto& camera_shakey = cameravars[HUDSHIELD_PLAYERNUM].shakey;
3011 auto& camera_shakex2 = cameravars[HUDSHIELD_PLAYERNUM].shakex2;
3012 auto& camera_shakey2 = cameravars[HUDSHIELD_PLAYERNUM].shakey2;
3013
3014 // isn't active during intro/menu sequence
3015 if (intro == true)
3016 {
3017 my->flags[INVISIBLE] = true;
3018 return;
3019 }
3020
3021 if (multiplayer == CLIENT)
3022 {
3023 if (stats[clientnum]->HP <= 0)
3024 {
3025 my->flags[INVISIBLE] = true;
3026 return;
3027 }
3028 }
3029
3030 // this entity only exists so long as the player exists
3031 if (players[clientnum] == nullptr || players[clientnum]->entity == nullptr || !hudweapon)
3032 {
3033 list_RemoveNode(my->mynode);
3034 return;
3035 }
3036
3037 // check levitating value
3038 bool levitating = isLevitating(stats[clientnum]);
3039
3040 // select model
3041 bool wearingring = false;
3042 if ( stats[clientnum]->ring != nullptr )
3043 {
3044 if ( stats[clientnum]->ring->type == RING_INVISIBILITY )
3045 {
3046 wearingring = true;
3047 }
3048 }
3049 if ( stats[clientnum]->cloak != nullptr )
3050 {
3051 if ( stats[clientnum]->cloak->type == CLOAK_INVISIBILITY )
3052 {
3053 wearingring = true;
3054 }
3055 }
3056
3057 Monster playerRace = players[clientnum]->entity->getMonsterFromPlayerRace(stats[clientnum]->playerRace);
3058 if ( players[clientnum]->entity->effectShapeshift != NOTHING )
3059 {
3060 playerRace = static_cast<Monster>(players[clientnum]->entity->effectShapeshift);
3061 if ( playerRace == RAT || playerRace == SPIDER )
3062 {
3063 HUD_SHAPESHIFT_HIDE = 1;
3064 }
3065 }
3066 else if ( players[clientnum]->entity->effectPolymorph != NOTHING )
3067 {
3068 if ( players[clientnum]->entity->effectPolymorph > NUMMONSTERS )
3069 {
3070 playerRace = HUMAN;
3071 }
3072 else
3073 {
3074 playerRace = static_cast<Monster>(players[clientnum]->entity->effectPolymorph);
3075 }
3076 }
3077
3078 bool hideShield = false;
3079 if ( playerRace == RAT
3080 || playerRace == CREATURE_IMP
3081 || playerRace == TROLL )
3082 {
3083 hideShield = true;
3084 }
3085
3086 bool spellbook = false;
3087 bool quiver = false;
3088 if ( stats[clientnum]->shield && itemCategory(stats[clientnum]->shield) == SPELLBOOK )
3089 {
3090 spellbook = true;
3091 if ( playerRace == CREATURE_IMP )
3092 {
3093 hideShield = false;
3094 }
3095 }
3096 else if ( stats[clientnum]->shield && itemTypeIsQuiver(stats[clientnum]->shield->type) )
3097 {
3098 quiver = true;
3099 }
3100
3101 // when reverting form, render shield as invisible for 2 ticks as it's position needs to settle.
3102 if ( HUD_LASTSHAPESHIFT_FORM != playerRace )
3103 {
3104 if ( HUD_SHAPESHIFT_HIDE > 0 )
3105 {
3106 hideShield = true;
3107 --HUD_SHAPESHIFT_HIDE;
3108 }
3109 }
3110 HUD_LASTSHAPESHIFT_FORM = playerRace;
3111
3112 if ( players[clientnum]->entity->skill[3] == 1 || players[clientnum]->entity->isInvisible() ) // debug cam or player invisible
3113 {
3114 my->flags[INVISIBLE] = true;
3115 }
3116 else
3117 {
3118 if (stats[clientnum]->shield == nullptr && playerRace != SPIDER )
3119 {
3120 my->flags[INVISIBLE] = true;
3121 }
3122 else
3123 {
3124 if (stats[clientnum]->shield)
3125 {
3126 if (itemModelFirstperson(stats[clientnum]->shield) != itemModel(stats[clientnum]->shield))
3127 {
3128 if ( spellbook )
3129 {
3130 my->scalex = 0.35;
3131 my->scaley = 0.35;
3132 my->scalez = 0.35;
3133 }
3134 else if ( quiver )
3135 {
3136 my->scalex = 0.5f;
3137 my->scaley = 0.5f;
3138 my->scalez = 0.5f;
3139 }
3140 else
3141 {
3142 my->scalex = 0.5f;
3143 my->scaley = 0.5f;
3144 my->scalez = 0.5f;
3145 }
3146 }
3147 else
3148 {
3149 my->scalex = 1.f;
3150 my->scaley = 1.f;
3151 my->scalez = 1.f;
3152 }
3153 }
3154 my->sprite = itemModelFirstperson(stats[clientnum]->shield);
3155 my->flags[INVISIBLE] = false;
3156 }
3157 }
3158
3159 // swimming
3160 bool swimming = false;
3161 if (players[clientnum] && players[clientnum]->entity)
3162 {
3163 if ( isPlayerSwimming(players[clientnum]->entity) || players[clientnum]->entity->skill[13] != 0 ) //skill[13] PLAYER_INWATER
3164 {
3165 my->flags[INVISIBLE] = true;
3166 Entity* parent = uidToEntity(my->parent);
3167 if ( parent )
3168 {
3169 parent->flags[INVISIBLE] = true;
3170 }
3171 swimming = true;
3172 }
3173 }
3174
3175 if ( hideShield )
3176 {
3177 my->flags[INVISIBLE] = true;
3178 }
3179 else if ( cast_animation.active )
3180 {
3181 my->flags[INVISIBLE] = true;
3182 }
3183 else if ( cast_animation.active_spellbook && !spellbook )
3184 {
3185 my->flags[INVISIBLE] = true;
3186 }
3187
3188 bool defending = false;
3189 bool sneaking = false;
3190 if (!command && !swimming)
3191 {
3192 if ( players[clientnum] && players[clientnum]->entity
3193 && (*inputPressed(impulses[IN_DEFEND]) || (shootmode && *inputPressed(joyimpulses[INJOY_GAME_DEFEND])))
3194 && players[clientnum]->entity->isMobile()
3195 && !gamePaused
3196 && !cast_animation.active
3197 && !cast_animation.active_spellbook
3198 && (!spellbook || (spellbook && hideShield)) )
3199 {
3200 if ( stats[clientnum]->shield && (hudweapon->skill[0] % 3 == 0) )
3201 {
3202 defending = true;
3203 }
3204 sneaking = true;
3205 }
3206 }
3207
3208 if ( stats[clientnum]->shield && itemTypeIsQuiver(stats[clientnum]->shield->type) )
3209 {
3210 // can't defend with quivers.
3211 defending = false;
3212 }
3213 if ( playerRace == RAT
3214 || playerRace == CREATURE_IMP
3215 || playerRace == TROLL
3216 || playerRace == SPIDER )
3217 {
3218 defending = false;
3219 }
3220
3221 if (defending)
3222 {
3223 stats[clientnum]->defending = true;
3224 }
3225 else
3226 {
3227 stats[clientnum]->defending = false;
3228 }
3229 if ( sneaking )
3230 {
3231 stats[clientnum]->sneaking = true;
3232 }
3233 else
3234 {
3235 stats[clientnum]->sneaking = false;
3236 }
3237
3238 if (multiplayer == CLIENT)
3239 {
3240 if (HUDSHIELD_DEFEND != defending || ticks % 120 == 0)
3241 {
3242 strcpy((char*)net_packet->data, "SHLD");
3243 net_packet->data[4] = clientnum;
3244 net_packet->data[5] = defending;
3245 net_packet->address.host = net_server.host;
3246 net_packet->address.port = net_server.port;
3247 net_packet->len = 6;
3248 sendPacketSafe(net_sock, -1, net_packet, 0);
3249 }
3250 if ( HUDSHIELD_SNEAKING != sneaking || ticks % 120 == 0 )
3251 {
3252 strcpy((char*)net_packet->data, "SNEK");
3253 net_packet->data[4] = clientnum;
3254 net_packet->data[5] = sneaking;
3255 net_packet->address.host = net_server.host;
3256 net_packet->address.port = net_server.port;
3257 net_packet->len = 6;
3258 sendPacketSafe(net_sock, -1, net_packet, 0);
3259 }
3260 }
3261 HUDSHIELD_DEFEND = defending;
3262 HUDSHIELD_SNEAKING = sneaking;
3263
3264 bool crossbow = (stats[clientnum]->weapon && (stats[clientnum]->weapon->type == CROSSBOW || stats[clientnum]->weapon->type == HEAVY_CROSSBOW) );
3265 bool doCrossbowReloadAnimation = false;
3266 bool doBowReload = false;
3267
3268 // shield switching animation
3269 if ( shieldSwitch )
3270 {
3271 if ( hudweapon )
3272 {
3273 if ( crossbow )
3274 {
3275 if ( rangedWeaponUseQuiverOnAttack(stats[clientnum]) )
3276 {
3277 doCrossbowReloadAnimation = true;
3278 if ( stats[clientnum]->weapon->type == HEAVY_CROSSBOW )
3279 {
3280 playSound(410, 64);
3281 }
3282 }
3283 }
3284 else if ( stats[clientnum]->weapon && isRangedWeapon(*stats[clientnum]->weapon) )
3285 {
3286 if ( rangedWeaponUseQuiverOnAttack(stats[clientnum]) )
3287 {
3288 hudweapon->skill[10] = 1; // HUDWEAPON_BOW_FORCE_RELOAD
3289 hudweapon->skill[7] = RANGED_ANIM_IDLE;
3290 doBowReload = true;
3291 }
3292 }
3293 }
3294
3295 if ( !spellbook )
3296 {
3297 shieldSwitch = false;
3298 }
3299 if ( !(defending || (spellbook && cast_animation.active_spellbook) || doBowReload) )
3300 {
3301 HUDSHIELD_MOVEY = -6;
3302 HUDSHIELD_MOVEZ = 2;
3303 HUDSHIELD_MOVEX = -2;
3304 }
3305 }
3306
3307 bool crossbowReloadAnimation = true;
3308 if ( hudweapon && crossbow
3309 && hudweapon->skill[8] == CROSSBOW_ANIM_SWAPPED_WEAPON && rangedWeaponUseQuiverOnAttack(stats[clientnum]) )
3310 {
3311 // CROSSBOW_ANIM_SWAPPED_WEAPON requires the weapon to not be in the swapping animation to finish.
3312 if ( fabs(hudweapon->fskill[5]) < (3 * PI / 8) )
3313 {
3314 doCrossbowReloadAnimation = true;
3315 }
3316 else
3317 {
3318 crossbowReloadAnimation = false;
3319 }
3320 }
3321
3322 // main animation
3323 if ( defending || cast_animation.active_spellbook )
3324 {
3325 if ( !spellbook )
3326 {
3327 if ( HUDSHIELD_MOVEY < 3 )
3328 {
3329 HUDSHIELD_MOVEY += .5;
3330 if ( HUDSHIELD_MOVEY > 3 )
3331 {
3332 HUDSHIELD_MOVEY = 3;
3333 }
3334 }
3335 if ( HUDSHIELD_MOVEZ > -1 )
3336 {
3337 HUDSHIELD_MOVEZ -= .2;
3338 if ( HUDSHIELD_MOVEZ < -1 )
3339 {
3340 HUDSHIELD_MOVEZ = -1;
3341 }
3342 }
3343 if ( HUDSHIELD_YAW < PI / 3 )
3344 {
3345 HUDSHIELD_YAW += .15;
3346 if ( HUDSHIELD_YAW > PI / 3 )
3347 {
3348 HUDSHIELD_YAW = PI / 3;
3349 }
3350 }
3351 if ( stats[clientnum]->shield )
3352 {
3353 if ( stats[clientnum]->shield->type == TOOL_TORCH || stats[clientnum]->shield->type == TOOL_CRYSTALSHARD )
3354 {
3355 if ( HUDSHIELD_MOVEX < 1.5 )
3356 {
3357 HUDSHIELD_MOVEX += .5;
3358 if ( HUDSHIELD_MOVEX > 1.5 )
3359 {
3360 HUDSHIELD_MOVEX = 1.5;
3361 }
3362 }
3363 if ( HUDSHIELD_ROLL < PI / 5 )
3364 {
3365 HUDSHIELD_ROLL += .15;
3366 if ( HUDSHIELD_ROLL > PI / 5 )
3367 {
3368 HUDSHIELD_ROLL = PI / 5;
3369 }
3370 }
3371 }
3372 }
3373 }
3374 else
3375 {
3376 if ( cast_animation.active_spellbook )
3377 {
3378 HUDSHIELD_ROLL = std::min<real_t>(HUDSHIELD_ROLL + .15, PI / 2);
3379 if ( HUDSHIELD_MOVEZ > -1 )
3380 {
3381 HUDSHIELD_MOVEZ -= .2;
3382 if ( HUDSHIELD_MOVEZ < -1 )
3383 {
3384 HUDSHIELD_MOVEZ = -1;
3385 }
3386 }
3387 }
3388 }
3389 }
3390 else if ( !hideShield && quiver && hudweapon && rangedWeaponUseQuiverOnAttack(stats[clientnum])
3391 && hudweapon->skill[7] != RANGED_ANIM_IDLE
3392 && (!crossbow || (crossbow && crossbowReloadAnimation && hudweapon->skill[8] != CROSSBOW_ANIM_RELOAD_START)) )
3393 {
3394 // skill[7] == 1 is hudweapon bow drawing, skill[8] is the crossbow reload animation state.
3395 if ( hudweapon->skill[7] == RANGED_ANIM_FIRED && (!crossbow || (crossbow && hudweapon->skill[8] == CROSSBOW_ANIM_SHOOT)) )
3396 {
3397 my->flags[INVISIBLE] = true;
3398 HUDSHIELD_MOVEY = 0;
3399 HUDSHIELD_PITCH = 0;
3400 HUDSHIELD_YAW = 0;
3401 HUDSHIELD_MOVEZ = 0;
3402 HUDSHIELD_MOVEX = 0;
3403
3404 if ( crossbow && hudweapon->skill[8] == CROSSBOW_ANIM_SHOOT )
3405 {
3406 // after shooting, lower the Z to create illusion of drawing the next arrow out of your hud.
3407 HUDSHIELD_MOVEZ = 2;
3408 }
3409 }
3410
3411 real_t targetY = 5.05;// +limbs[HUMAN][11][0];
3412 real_t targetPitch = PI / 2;// +limbs[HUMAN][11][1];
3413 real_t targetYaw = PI / 3 - 0.1;// +limbs[HUMAN][11][2];
3414 real_t targetZ = -3.5;// +limbs[HUMAN][12][0];
3415 real_t targetX = -1.75;// +limbs[HUMAN][12][1];
3416 if ( stats[clientnum]->shield->type == QUIVER_LIGHTWEIGHT )
3417 {
3418 targetZ -= 0.25; // offset a bit higher.
3419 }
3420
3421 if ( crossbow )
3422 {
3423 targetY = 4.8 + 0.01;
3424 targetPitch = PI / 2;
3425 targetYaw = PI / 3 - 0.05;
3426 targetZ = -2.75;
3427 targetX = 2.75;
3428
3429 if ( hudweapon && stats[clientnum]->weapon->type == HEAVY_CROSSBOW )
3430 {
3431 if ( hudweapon->sprite == items[HEAVY_CROSSBOW].fpindex - 1 )
3432 {
3433 targetX += 1;
3434 }
3435 else if ( hudweapon->sprite == items[HEAVY_CROSSBOW].fpindex )
3436 {
3437 targetX += -1.5;
3438 if ( HUDSHIELD_MOVEX < targetX )
3439 {
3440 HUDSHIELD_MOVEX += 2.5;
3441 if ( HUDSHIELD_MOVEX > targetX )
3442 {
3443 HUDSHIELD_MOVEX = targetX;
3444 }
3445 }
3446 else if ( HUDSHIELD_MOVEX > targetX )
3447 {
3448 HUDSHIELD_MOVEX -= 2.5;
3449 if ( HUDSHIELD_MOVEX < targetX )
3450 {
3451 HUDSHIELD_MOVEX = targetX;
3452 }
3453 }
3454 }
3455 }
3456
3457 if ( stats[clientnum]->shield->type == QUIVER_LIGHTWEIGHT )
3458 {
3459 targetZ -= 0.25; // offset a bit higher.
3460 }
3461
3462 if ( doCrossbowReloadAnimation )
3463 {
3464 hudweapon->skill[8] = CROSSBOW_ANIM_RELOAD_START;
3465 hudweapon->skill[0] = CROSSBOW_CHOP_RELOAD_START;
3466 hudweapon->fskill[0] = -1;
3467 throwGimpTimer = std::max(throwGimpTimer, 20);
3468
3469 if ( fabs(hudweapon->fskill[5]) < 0.01 )
3470 {
3471 throwGimpTimer = std::max(throwGimpTimer, 20);
3472 my->flags[INVISIBLE] = true;
3473 HUDSHIELD_MOVEY = 0;
3474 HUDSHIELD_PITCH = 0;
3475 HUDSHIELD_YAW = 0;
3476 HUDSHIELD_MOVEZ = 2;
3477 HUDSHIELD_MOVEX = 0;
3478 }
3479 }
3480 }
3481
3482 bool doMovement = true;
3483 if ( doCrossbowReloadAnimation || doBowReload )
3484 {
3485 doMovement = false;
3486 }
3487 if ( doMovement )
3488 {
3489 if ( HUDSHIELD_MOVEY < targetY )
3490 {
3491 HUDSHIELD_MOVEY += 0.5;
3492 if ( HUDSHIELD_MOVEY > targetY )
3493 {
3494 HUDSHIELD_MOVEY = targetY;
3495 }
3496 }
3497 if ( targetX < 0 )
3498 {
3499 if ( HUDSHIELD_MOVEX > targetX )
3500 {
3501 HUDSHIELD_MOVEX -= 0.4;
3502 if ( HUDSHIELD_MOVEX < targetX )
3503 {
3504 HUDSHIELD_MOVEX = targetX;
3505 }
3506 }
3507 }
3508 else if ( targetX >= 0 )
3509 {
3510 if ( HUDSHIELD_MOVEX < targetX )
3511 {
3512 HUDSHIELD_MOVEX += 0.4;
3513 if ( HUDSHIELD_MOVEX > targetX )
3514 {
3515 HUDSHIELD_MOVEX = targetX;
3516 }
3517 }
3518 }
3519
3520 if ( HUDSHIELD_PITCH < targetPitch )
3521 {
3522 HUDSHIELD_PITCH += .2;
3523 if ( HUDSHIELD_PITCH > targetPitch )
3524 {
3525 HUDSHIELD_PITCH = targetPitch;
3526 }
3527 }
3528 if ( HUDSHIELD_YAW < targetYaw )
3529 {
3530 HUDSHIELD_YAW += .3;
3531 if ( HUDSHIELD_YAW > targetYaw )
3532 {
3533 HUDSHIELD_YAW = targetYaw;
3534 }
3535 }
3536 if ( HUDSHIELD_MOVEZ > targetZ )
3537 {
3538 HUDSHIELD_MOVEZ -= 0.4;
3539 if ( HUDSHIELD_MOVEZ < targetZ )
3540 {
3541 HUDSHIELD_MOVEZ = targetZ;
3542 }
3543 }
3544 }
3545 }
3546 else
3547 {
3548 if ( HUDSHIELD_MOVEX > 0 )
3549 {
3550 HUDSHIELD_MOVEX = std::max<real_t>(HUDSHIELD_MOVEX - .5, 0.0);
3551 }
3552 else if ( HUDSHIELD_MOVEX < 0 )
3553 {
3554 HUDSHIELD_MOVEX = std::min<real_t>(HUDSHIELD_MOVEX + .5, 0.0);
3555 }
3556 if ( HUDSHIELD_MOVEY > 0 )
3557 {
3558 HUDSHIELD_MOVEY = std::max<real_t>(HUDSHIELD_MOVEY - .5, 0.0);
3559 }
3560 else if ( HUDSHIELD_MOVEY < 0 )
3561 {
3562 HUDSHIELD_MOVEY = std::min<real_t>(HUDSHIELD_MOVEY + .5, 0.0);
3563 }
3564 if ( HUDSHIELD_MOVEZ > 0 )
3565 {
3566 HUDSHIELD_MOVEZ = std::max<real_t>(HUDSHIELD_MOVEZ - .2, 0.0);
3567 }
3568 else if ( HUDSHIELD_MOVEZ < 0 )
3569 {
3570 HUDSHIELD_MOVEZ = std::min<real_t>(HUDSHIELD_MOVEZ + .2, 0.0);
3571 }
3572 if ( HUDSHIELD_YAW > 0 )
3573 {
3574 HUDSHIELD_YAW = std::max<real_t>(HUDSHIELD_YAW - .15, 0.0);
3575 }
3576 else if ( HUDSHIELD_YAW < 0 )
3577 {
3578 HUDSHIELD_YAW = std::min<real_t>(HUDSHIELD_YAW + .15, 0.0);
3579 }
3580 if ( HUDSHIELD_PITCH > 0 )
3581 {
3582 HUDSHIELD_PITCH = std::max<real_t>(HUDSHIELD_PITCH - .15, 0.0);
3583 }
3584 else if ( HUDSHIELD_PITCH < 0 )
3585 {
3586 HUDSHIELD_PITCH = std::min<real_t>(HUDSHIELD_PITCH + .15, 0.0);
3587 }
3588 if ( HUDSHIELD_ROLL > 0 )
3589 {
3590 HUDSHIELD_ROLL = std::max<real_t>(HUDSHIELD_ROLL - .15, 0);
3591 }
3592 else if ( HUDSHIELD_ROLL < 0 )
3593 {
3594 HUDSHIELD_ROLL = std::min<real_t>(HUDSHIELD_ROLL + .15, 0);
3595 }
3596 }
3597
3598 // set entity position
3599 my->x = 7 + HUDSHIELD_MOVEX;
3600 my->y = -3.5 + HUDSHIELD_MOVEY;
3601 my->z = 6 + HUDSHIELD_MOVEZ + (cameras[HUDSHIELD_PLAYERNUM].z * .5 - players[clientnum]->entity->z);
3602 if ( !my->flags[OVERDRAW] )
3603 {
3604 my->x += 32;
3605 my->y += 32;
3606 my->z -= 3.5;
3607 }
3608 my->yaw = HUDSHIELD_YAW - camera_shakex2 - PI / 3;
3609 my->pitch = HUDSHIELD_PITCH - camera_shakey2 / 200.f;
3610 my->roll = HUDSHIELD_ROLL;
3611 if ( spellbook )
3612 {
3613 my->x += 0.87;
3614 my->y += -0.23;
3615 my->z += -1;
3616
3617 my->pitch += PI / 2 + PI / 5;
3618 my->yaw -= PI / 3 - 2 * PI / 5;
3619 my->roll -= PI / 2;
3620
3621 my->focalx = 0;
3622 my->focaly = -1.2;
3623 my->focalz = -0.1;
3624 }
3625 else if ( quiver && hudweapon && crossbow )
3626 {
3627 if ( hudweapon->skill[8] != CROSSBOW_ANIM_SHOOT
3628 && hudweapon->skill[8] != CROSSBOW_ANIM_RELOAD_START
3629 && hudweapon->skill[8] != CROSSBOW_ANIM_SWAPPED_WEAPON )
3630 {
3631 // only bob the arrow if idle animation or DRAW_END animation
3632 my->x += hudweapon->fskill[0]; // HUDWEAPON_MOVEX
3633 my->y += hudweapon->fskill[1]; // HUDWEAPON_MOVEY
3634 my->z += hudweapon->fskill[2]; // HUDWEAPON_MOVEZ
3635 }
3636 my->focalx = 0;
3637 my->focaly = 0;
3638 my->focalz = 0;
3639 }
3640 else
3641 {
3642 my->focalx = 0;
3643 my->focaly = 0;
3644 my->focalz = 0;
3645 }
3646
3647 if ( my->sprite == items[TOOL_TINKERING_KIT].fpindex && !hideShield )
3648 {
3649 my->yaw += PI / 2 - HUDSHIELD_YAW / 4;
3650 my->focalz -= 0.5;
3651 }
3652
3653 if ( playerRace == SPIDER && hudarm && players[clientnum]->entity->bodyparts.at(0) )
3654 {
3655 my->sprite = 854;
3656 my->x = hudarm->x;
3657 my->y = -hudarm->y;
3658 my->z = hudarm->z;
3659 my->pitch = hudarm->pitch - camera_shakey2 / 200.f;
3660 my->roll = -hudarm->roll;
3661 my->yaw = -players[clientnum]->entity->bodyparts.at(0)->yaw + players[clientnum]->entity->fskill[10] - camera_shakex2;
3662 my->scalex = hudarm->scalex;
3663 my->scaley = hudarm->scaley;
3664 my->scalez = hudarm->scalez;
3665 my->focalz = hudarm->focalz;
3666 }
3667
3668 // dirty hack because we altered the camera height in actPlayer(). adjusts HUD to match new height.
3669 if ( playerRace == CREATURE_IMP && players[clientnum]->entity->z == -4.5 )
3670 {
3671 my->z -= .5;
3672 }
3673 else if ( playerRace == TROLL && players[clientnum]->entity->z <= -1.5 )
3674 {
3675 my->z -= -2 * .5;
3676 }
3677
3678 // torch/lantern flames
3679 my->flags[BRIGHT] = false;
3680 if ( playerRace == TROLL || playerRace == SPIDER || playerRace == CREATURE_IMP || playerRace == RAT )
3681 {
3682 // don't process flames as these don't hold torches.
3683 return;
3684 }
3685 if (stats[clientnum]->shield && !swimming && players[clientnum]->entity->skill[3] == 0 && !cast_animation.active && !cast_animation.active_spellbook && !shieldSwitch)
3686 {
3687 if (itemCategory(stats[clientnum]->shield) == TOOL)
3688 {
3689 if (stats[clientnum]->shield->type == TOOL_TORCH)
3690 {
3691 Entity* entity = spawnFlame(my, SPRITE_FLAME);
3692 entity->flags[OVERDRAW] = true;
3693 entity->z -= 2.5 * cos(HUDSHIELD_ROLL);
3694 entity->y += 2.5 * sin(HUDSHIELD_ROLL);
3695 my->flags[BRIGHT] = true;
3696 }
3697 if ( stats[clientnum]->shield->type == TOOL_CRYSTALSHARD )
3698 {
3699 Entity* entity = spawnFlame(my, SPRITE_CRYSTALFLAME);
3700 entity->flags[OVERDRAW] = true;
3701 entity->z -= 2.5 * cos(HUDSHIELD_ROLL);
3702 entity->y += 2.5 * sin(HUDSHIELD_ROLL);
3703 my->flags[BRIGHT] = true;
3704 }
3705 else if (stats[clientnum]->shield->type == TOOL_LANTERN)
3706 {
3707 Entity* entity = spawnFlame(my, SPRITE_FLAME);
3708 entity->flags[OVERDRAW] = true;
3709 entity->z += 1;
3710 my->flags[BRIGHT] = true;
3711 }
3712 }
3713 }
3714 }
3715
actHudAdditional(Entity * my)3716 void actHudAdditional(Entity* my)
3717 {
3718 bool spellbook = false;
3719 if ( stats[clientnum]->shield && itemCategory(stats[clientnum]->shield) == SPELLBOOK )
3720 {
3721 spellbook = true;
3722 }
3723
3724 my->flags[UNCLICKABLE] = true;
3725
3726 auto& camera_shakex = cameravars[HUDSHIELD_PLAYERNUM].shakex;
3727 auto& camera_shakey = cameravars[HUDSHIELD_PLAYERNUM].shakey;
3728 auto& camera_shakex2 = cameravars[HUDSHIELD_PLAYERNUM].shakex2;
3729 auto& camera_shakey2 = cameravars[HUDSHIELD_PLAYERNUM].shakey2;
3730
3731 // isn't active during intro/menu sequence
3732 if ( intro == true )
3733 {
3734 my->flags[INVISIBLE] = true;
3735 return;
3736 }
3737
3738 if ( multiplayer == CLIENT )
3739 {
3740 if ( stats[clientnum]->HP <= 0 )
3741 {
3742 my->flags[INVISIBLE] = true;
3743 return;
3744 }
3745 }
3746
3747 // this entity only exists so long as the player exists
3748 if ( players[clientnum] == nullptr || players[clientnum]->entity == nullptr || !hudweapon )
3749 {
3750 list_RemoveNode(my->mynode);
3751 return;
3752 }
3753
3754 if ( !spellbook )
3755 {
3756 my->flags[INVISIBLE] = true;
3757 return;
3758 }
3759
3760 if ( !players[clientnum]->entity->bodyparts.at(2)
3761 || players[clientnum]->entity->bodyparts.at(2)->flags[INVISIBLE]
3762 || players[clientnum]->entity->bodyparts.at(2)->sprite == 854 )
3763 {
3764 // if shield invisible or spider arm we're invis.
3765 my->flags[INVISIBLE] = true;
3766 return;
3767 }
3768
3769 if ( stats[clientnum]->shield == nullptr )
3770 {
3771 my->flags[INVISIBLE] = true;
3772 }
3773 else
3774 {
3775 if ( itemModelFirstperson(stats[clientnum]->shield) != itemModel(stats[clientnum]->shield) )
3776 {
3777 if ( spellbook )
3778 {
3779 my->scalex = 0.35;
3780 my->scaley = 0.35;
3781 my->scalez = 0.35;
3782 }
3783 else
3784 {
3785 my->scalex = 0.5f;
3786 my->scaley = 0.5f;
3787 my->scalez = 0.5f;
3788 }
3789 }
3790 else
3791 {
3792 my->scalex = 1.f;
3793 my->scaley = 1.f;
3794 my->scalez = 1.f;
3795 }
3796 my->sprite = itemModelFirstperson(stats[clientnum]->shield);
3797 my->flags[INVISIBLE] = false;
3798 }
3799
3800 if ( cast_animation.active )
3801 {
3802 my->flags[INVISIBLE] = true;
3803 }
3804 else if ( cast_animation.active_spellbook && !spellbook )
3805 {
3806 my->flags[INVISIBLE] = true;
3807 }
3808
3809 bool defending = false;
3810 bool sneaking = false;
3811 /*if ( !command )
3812 {
3813 if ( players[clientnum] && players[clientnum]->entity
3814 && (*inputPressed(impulses[IN_DEFEND]) || (shootmode && *inputPressed(joyimpulses[INJOY_GAME_DEFEND])))
3815 && players[clientnum]->entity->isMobile()
3816 && !gamePaused
3817 && !cast_animation.active )
3818 {
3819 if ( stats[clientnum]->shield && (hudweapon->skill[0] % 3 == 0) )
3820 {
3821 defending = true;
3822 }
3823 sneaking = true;
3824 }
3825 }*/
3826
3827 // shield switching animation
3828 if ( shieldSwitch )
3829 {
3830 if ( spellbook )
3831 {
3832 shieldSwitch = false;
3833 }
3834 if ( !(defending || (spellbook && cast_animation.active_spellbook)) )
3835 {
3836 HUDSHIELD_MOVEY = -6;
3837 HUDSHIELD_MOVEZ = 2;
3838 HUDSHIELD_MOVEX = -2;
3839 }
3840 }
3841
3842 // main animation
3843 if ( cast_animation.active_spellbook && spellbook )
3844 {
3845 HUDSHIELD_ROLL = std::max<real_t>(HUDSHIELD_ROLL - .1, -1 * PI / 3);
3846 if ( HUDSHIELD_MOVEZ > -1 )
3847 {
3848 HUDSHIELD_MOVEZ -= .2;
3849 if ( HUDSHIELD_MOVEZ < -1 )
3850 {
3851 HUDSHIELD_MOVEZ = -1;
3852 }
3853 }
3854 }
3855 else
3856 {
3857 if ( HUDSHIELD_MOVEX > 0 )
3858 {
3859 HUDSHIELD_MOVEX = std::max<real_t>(HUDSHIELD_MOVEX - .5, 0.0);
3860 }
3861 else if ( HUDSHIELD_MOVEX < 0 )
3862 {
3863 HUDSHIELD_MOVEX = std::min<real_t>(HUDSHIELD_MOVEX + .5, 0.0);
3864 }
3865 if ( HUDSHIELD_MOVEY > 0 )
3866 {
3867 HUDSHIELD_MOVEY = std::max<real_t>(HUDSHIELD_MOVEY - .5, 0.0);
3868 }
3869 else if ( HUDSHIELD_MOVEY < 0 )
3870 {
3871 HUDSHIELD_MOVEY = std::min<real_t>(HUDSHIELD_MOVEY + .5, 0.0);
3872 }
3873 if ( HUDSHIELD_MOVEZ > 0 )
3874 {
3875 HUDSHIELD_MOVEZ = std::max<real_t>(HUDSHIELD_MOVEZ - .2, 0.0);
3876 }
3877 else if ( HUDSHIELD_MOVEZ < 0 )
3878 {
3879 HUDSHIELD_MOVEZ = std::min<real_t>(HUDSHIELD_MOVEZ + .2, 0.0);
3880 }
3881 if ( HUDSHIELD_YAW > 0 )
3882 {
3883 HUDSHIELD_YAW = std::max<real_t>(HUDSHIELD_YAW - .15, 0.0);
3884 }
3885 else if ( HUDSHIELD_YAW < 0 )
3886 {
3887 HUDSHIELD_YAW = std::min<real_t>(HUDSHIELD_YAW + .15, 0.0);
3888 }
3889 if ( HUDSHIELD_PITCH > 0 )
3890 {
3891 HUDSHIELD_PITCH = std::max<real_t>(HUDSHIELD_PITCH - .15, 0.0);
3892 }
3893 else if ( HUDSHIELD_PITCH < 0 )
3894 {
3895 HUDSHIELD_PITCH = std::min<real_t>(HUDSHIELD_PITCH + .15, 0.0);
3896 }
3897 if ( HUDSHIELD_ROLL > 0 )
3898 {
3899 HUDSHIELD_ROLL = std::max<real_t>(HUDSHIELD_ROLL - .15, 0);
3900 }
3901 else if ( HUDSHIELD_ROLL < 0 )
3902 {
3903 if ( spellbook )
3904 {
3905 HUDSHIELD_ROLL = std::min<real_t>(HUDSHIELD_ROLL + .10, 0);
3906 }
3907 else
3908 {
3909 HUDSHIELD_ROLL = std::min<real_t>(HUDSHIELD_ROLL + .15, 0);
3910 }
3911 }
3912 }
3913
3914 // set entity position
3915 my->x = 7 + HUDSHIELD_MOVEX;
3916 my->y = -3.5 + HUDSHIELD_MOVEY;
3917 my->z = 6 + HUDSHIELD_MOVEZ + (cameras[HUDSHIELD_PLAYERNUM].z * .5 - players[clientnum]->entity->z);
3918 my->yaw = HUDSHIELD_YAW - camera_shakex2 - PI / 3;
3919 my->pitch = HUDSHIELD_PITCH - camera_shakey2 / 200.f;
3920 my->roll = HUDSHIELD_ROLL;
3921 if ( spellbook )
3922 {
3923 my->sprite += items[(stats[clientnum]->shield->type)].variations;
3924 my->pitch += PI / 2 + PI / 5;
3925 my->yaw -= PI / 3 - 2 * PI / 5;
3926 my->roll -= PI / 2;
3927
3928 my->x += 1;
3929 my->y += -0.2;
3930 my->z -= 0.95;
3931
3932 my->focalx = 0;
3933 my->focaly = -1.1;
3934 my->focalz = 0.25;
3935 }
3936 else
3937 {
3938 my->focalx = 0;
3939 my->focaly = 0;
3940 my->focalz = 0;
3941 }
3942
3943 // dirty hack because we altered the camera height in actPlayer(). adjusts HUD to match new height.
3944 if ( stats[clientnum]->type == CREATURE_IMP && players[clientnum]->entity->z == -4.5 )
3945 {
3946 my->z -= .5;
3947 }
3948 else if ( stats[clientnum]->type == TROLL && players[clientnum]->entity->z <= -1.5 )
3949 {
3950 my->z -= -2 * .5;
3951 }
3952 }
3953
actHudArrowModel(Entity * my)3954 void actHudArrowModel(Entity* my)
3955 {
3956 bool bow = false;
3957 bool crossbow = false;
3958 if ( stats[clientnum]->weapon
3959 && (stats[clientnum]->weapon->type == SHORTBOW
3960 || stats[clientnum]->weapon->type == LONGBOW
3961 || stats[clientnum]->weapon->type == ARTIFACT_BOW
3962 || stats[clientnum]->weapon->type == COMPOUND_BOW )
3963 )
3964 {
3965 bow = true;
3966 }
3967 else if ( stats[clientnum]->weapon
3968 && (stats[clientnum]->weapon->type == CROSSBOW || stats[clientnum]->weapon->type == HEAVY_CROSSBOW) )
3969 {
3970 crossbow = true;
3971 }
3972
3973 my->flags[UNCLICKABLE] = true;
3974
3975 // isn't active during intro/menu sequence
3976 if ( intro == true )
3977 {
3978 my->flags[INVISIBLE] = true;
3979 return;
3980 }
3981
3982 if ( multiplayer == CLIENT )
3983 {
3984 if ( stats[clientnum]->HP <= 0 )
3985 {
3986 my->flags[INVISIBLE] = true;
3987 return;
3988 }
3989 }
3990
3991 // this entity only exists so long as the player exists
3992 if ( players[clientnum] == nullptr || players[clientnum]->entity == nullptr || !hudweapon )
3993 {
3994 list_RemoveNode(my->mynode);
3995 return;
3996 }
3997
3998 if ( (!crossbow && !bow) || cast_animation.active || cast_animation.active_spellbook )
3999 {
4000 my->flags[INVISIBLE] = true;
4001 return;
4002 }
4003
4004 if ( crossbow )
4005 {
4006 if ( hudweapon->flags[INVISIBLE] || hudweapon->skill[6] != 0 ) // skill[6] is hideWeapon
4007 {
4008 my->flags[INVISIBLE] = true;
4009 return;
4010 }
4011 if ( stats[clientnum]->shield && itemTypeIsQuiver(stats[clientnum]->shield->type) )
4012 {
4013 my->flags[INVISIBLE] = true;
4014 return;
4015 }
4016 if ( stats[clientnum]->weapon->type == CROSSBOW )
4017 {
4018 if ( hudweapon->sprite != items[CROSSBOW].fpindex )
4019 {
4020 my->flags[INVISIBLE] = true;
4021 return;
4022 }
4023 }
4024 if ( stats[clientnum]->weapon->type == HEAVY_CROSSBOW )
4025 {
4026 if ( hudweapon->sprite == items[HEAVY_CROSSBOW].fpindex + 1 )
4027 {
4028 my->flags[INVISIBLE] = true;
4029 return;
4030 }
4031 }
4032 }
4033 else if ( hudweapon->flags[INVISIBLE]
4034 || hudweapon->skill[6] != 0
4035 || hudweapon->skill[7] != RANGED_ANIM_FIRED ) // skill[6] is hiding weapon, skill[7] is shooting something
4036 {
4037 my->flags[INVISIBLE] = true;
4038 return;
4039 }
4040
4041 my->scalex = 1.f;
4042 my->scaley = 1.f;
4043 my->scalez = 1.f;
4044
4045 my->flags[INVISIBLE] = false;
4046 my->sprite = 934;
4047
4048 if ( crossbow )
4049 {
4050 my->sprite = 976;
4051 }
4052 else if ( stats[clientnum]->shield && itemTypeIsQuiver(stats[clientnum]->shield->type) )
4053 {
4054 switch ( stats[clientnum]->shield->type )
4055 {
4056 case QUIVER_SILVER:
4057 my->sprite = 935;
4058 break;
4059 case QUIVER_PIERCE:
4060 my->sprite = 936;
4061 break;
4062 case QUIVER_LIGHTWEIGHT:
4063 my->sprite = 937;
4064 break;
4065 case QUIVER_FIRE:
4066 my->sprite = 938;
4067 break;
4068 case QUIVER_KNOCKBACK:
4069 my->sprite = 939;
4070 break;
4071 case QUIVER_CRYSTAL:
4072 my->sprite = 940;
4073 break;
4074 case QUIVER_HUNTING:
4075 my->sprite = 941;
4076 break;
4077 default:
4078 break;
4079 }
4080 }
4081
4082 // set entity position
4083 my->x = hudweapon->x;
4084 my->y = hudweapon->y;
4085 my->z = hudweapon->z;
4086
4087 my->yaw = hudweapon->yaw;
4088 my->pitch = hudweapon->pitch;
4089 my->roll = hudweapon->roll;
4090
4091 my->focalx = hudweapon->focalx - 0.125;
4092 if ( hudweapon->sprite == items[COMPOUND_BOW].fpindex + 1 )
4093 {
4094 my->focalx += 0.125; // shorter bowstring on compound bow, push arrow forward.
4095 }
4096 my->focaly = hudweapon->focaly - 0.25;
4097 my->focalz = hudweapon->focalz - 0.25;
4098
4099 if ( crossbow )
4100 {
4101 if ( hudweapon->sprite == items[CROSSBOW].fpindex )
4102 {
4103 my->focalx += 3.5;
4104 my->focaly += 0.25;
4105 my->focalz += -0.25;
4106 }
4107 else if ( hudweapon->sprite == items[HEAVY_CROSSBOW].fpindex || hudweapon->sprite == items[HEAVY_CROSSBOW].fpindex - 1 )
4108 {
4109 my->focalx += 4.5;
4110 my->focaly += 0.25;
4111 my->focalz += -0.25;
4112
4113 if ( hudweapon->sprite == items[HEAVY_CROSSBOW].fpindex )
4114 {
4115 my->focalx -= 2;
4116 }
4117 }
4118 }
4119
4120 my->scalex = hudweapon->scalex;
4121 my->scaley = hudweapon->scaley;
4122 my->scalez = hudweapon->scalez;
4123 }