1 /////////////////////////////////////////
2 //
3 // OpenLieroX
4 //
5 // code under LGPL, based on JasonBs work,
6 // enhanced by Dark Charlie and Albert Zeyer
7 //
8 //
9 /////////////////////////////////////////
10
11
12 // Worm class
13 // Created 28/6/02
14 // Jason Boettcher
15
16
17 #include <cassert>
18
19 #include "LieroX.h"
20 #include "ProfileSystem.h"
21 #include "CClient.h"
22 #include "CServer.h"
23 #include "DeprecatedGUI/Graphics.h"
24 #include "GfxPrimitives.h"
25 #include "CWorm.h"
26 #include "DeprecatedGUI/CBar.h"
27 #include "MathLib.h"
28 #include "CServerConnection.h"
29 #include "CWormHuman.h"
30 #include "Debug.h"
31 #include "CGameMode.h"
32 #include "FlagInfo.h"
33 #include "Physics.h"
34 #include "WeaponDesc.h"
35 #include "Mutex.h"
36 #include "InputEvents.h"
37 #include "Touchscreen.h"
38
39
40
41 struct CWorm::SkinDynDrawer : DynDrawIntf {
42 Mutex mutex;
43 CWorm* worm;
SkinDynDrawerCWorm::SkinDynDrawer44 SkinDynDrawer(CWorm* w) : DynDrawIntf(WORM_SKIN_WIDTH,WORM_SKIN_HEIGHT), worm(w) {}
45
drawCWorm::SkinDynDrawer46 virtual void draw(SDL_Surface* bmpDest, int x, int y) {
47 Mutex::ScopedLock lock(mutex);
48 if(worm) {
49 int f = ((int)worm->frame()*7);
50 int ang = (int)( (worm->getAngle()+90)/151 * 7 );
51 f += ang;
52
53 // Draw the worm
54 worm->getSkin().Draw(bmpDest, x, y, f, false, worm->getFaceDirectionSide() == DIR_LEFT);
55 }
56 else
57 DrawCross(bmpDest, x - WORM_SKIN_WIDTH/2, y - WORM_SKIN_HEIGHT/2, WORM_SKIN_WIDTH, WORM_SKIN_HEIGHT, Color(255,0,0));
58 }
59 };
60
CWorm()61 CWorm::CWorm() : cSparkles(this)
62 {
63 // set all pointers to NULL
64 m_inputHandler = NULL;
65 cOwner = NULL;
66 tProfile = NULL;
67 pcHookWorm = NULL;
68 cGameScript = NULL;
69 cWeaponRest = NULL;
70 m_type = NULL;
71 Clear();
72
73 skinPreviewDrawer = skinPreviewDrawerP = new SkinDynDrawer(this);
74 }
75
~CWorm()76 CWorm::~CWorm() {
77 Shutdown();
78
79 Mutex::ScopedLock lock(skinPreviewDrawerP->mutex);
80 skinPreviewDrawerP->worm = NULL;
81 }
82
83
84 ///////////////////
85 // Clear the worm details
Clear()86 void CWorm::Clear()
87 {
88 bUsed = false;
89 bIsPrepared = false;
90 bSpawnedOnce = false;
91 iID = 0;
92 iTeam = 0;
93 bLocal = false;
94 m_type = PRF_HUMAN;
95 iRanking = 0;
96 iClientID = 0;
97 iClientWormID = 0;
98 cOwner = NULL;
99 bSpectating = false;
100 iAFK = AFK_BACK_ONLINE;
101 sAFKMessage = "";
102
103 iKills = 0;
104 iDeaths = 0;
105 iSuicides = 0;
106 iTeamkills = 0;
107 fDamage = 0.0f;
108
109 health = 100.0f;
110 iLives = 10;
111 bAlive = false;
112 iFaceDirectionSide = DIR_RIGHT;
113 fAngle = 0;
114 fAngleSpeed = 0;
115 fMoveSpeedX = 0;
116 fFrame = 0;
117 bDrawMuzzle = false;
118 bVisibleForWorm.clear();
119 fVisibilityChangeTime = 0;
120
121 bOnGround = false;
122 vPos = CVec(0,0);
123 vVelocity = CVec(0,0);
124 vFollowPos = CVec(0,0);
125 bFollowOverride = false;
126 fLastUpdateWritten = AbsTime();
127 tLastState = worm_state_t();
128 fLastAngle = -1;
129 iLastCharge = 255;
130 iLastCurWeapon = 255;
131
132 cNinjaRope.Clear();
133 fRopeTime = AbsTime();
134 bRopeDownOnce = false;
135 bRopeDown = false;
136 setSpeedFactor(1);
137 setCanUseNinja(true);
138 setDamageFactor(1);
139 setShieldFactor(1);
140 setCanAirJump(false);
141 fLastAirJumpTime = 0;
142
143 bWeaponsReady = false;
144 iNumWeaponSlots = 2;
145 iCurrentWeapon = 0;
146 bGameReady = false;
147
148 bGotTarget = false;
149 bHooked = false;
150 pcHookWorm = NULL;
151
152 bTagIT = false;
153 fTagTime = 0;
154 iDirtCount = 0;
155
156 fLastBlood = AbsTime();
157
158 fPreLastPosUpdate = fLastPosUpdate = AbsTime();
159
160 //bUsesMouse = false;
161 fLastInputTime = tLX->currentTime;
162
163 //pcViewport = NULL;//.Setup(0,0,640,480);
164 tProfile = NULL;
165
166 cGameScript = NULL;
167 short i;
168 for(i=0;i<NUM_FRAMES;i++)
169 fFrameTimes[i] = AbsTime();
170
171 for(i=0; i<5; i++)
172 tWeapons[i].Weapon = NULL;
173
174 cWeaponRest = NULL;
175
176
177
178 bmpGibs = NULL;
179
180 bLobbyReady = false;
181
182 bForceWeapon_Name = false;
183
184 // Graphics
185 cHealthBar = DeprecatedGUI::CBar(LoadGameImage("data/frontend/worm_health.png", true), 0, 0, 0, 0, DeprecatedGUI::BAR_LEFTTORIGHT);
186 cHealthBar.SetLabelVisible(false);
187 cWeaponBar = DeprecatedGUI::CBar(LoadGameImage("data/frontend/worm_weapon.png", true), 0, 0, 0, 0, DeprecatedGUI::BAR_LEFTTORIGHT, 2, 2);
188 cWeaponBar.SetLabelVisible(false);
189
190 bAlreadyKilled = false;
191
192 fLastSimulationTime = tLX->currentTime;
193 fLastCarve = tLX->currentTime;
194 fLastShoot = tLX->currentTime;
195
196 if(m_inputHandler) {
197 delete m_inputHandler;
198 m_inputHandler = NULL;
199 }
200
201 cDamageReport.clear();
202
203 iShotCount = 0;
204 }
205
206
207 ///////////////////
208 // Initialize the worm
Init()209 void CWorm::Init()
210 {
211 // TODO: is this needed?
212 // WARNING: this works only because it does not contain any classes
213 tState = worm_state_t();
214 bVisibleForWorm.clear();
215 fVisibilityChangeTime = 0;
216 }
217
218
219 ///////////////////
220 // Shutdown the worm
Shutdown()221 void CWorm::Shutdown()
222 {
223 Unprepare();
224 FreeGraphics();
225 }
226
227
228 ///////////////////
229 // Free the graphics
FreeGraphics()230 void CWorm::FreeGraphics()
231 {
232 bmpGibs = NULL;
233 }
234
235
236
237
238 ///////////////////
239 // Prepare the worm for the game
Prepare(bool serverSide)240 void CWorm::Prepare(bool serverSide)
241 {
242 assert(cGameScript);
243
244 if(bIsPrepared) {
245 warnings << "worm " << getID() << ":" << getName() << " was already prepared!" << endl;
246 }
247
248 bVisibleForWorm.clear();
249 fVisibilityChangeTime = 0;
250
251 setTeamkills(0);
252 setSuicides(0);
253 setDeaths(0);
254
255 setSpeedFactor(1);
256 setCanUseNinja(true);
257 setDamageFactor(1);
258 setShieldFactor(1);
259 setCanAirJump(false);
260
261 // Setup the rope
262 cNinjaRope.Setup(cGameScript);
263
264 iCurrentWeapon = 0;
265
266 if(m_inputHandler) {
267 warnings << "WARNING: worm " << getName() << " has already the following input handler set: "; warnings.flush();
268 warnings << m_inputHandler->name(); warnings << endl;
269 delete m_inputHandler;
270 m_inputHandler = NULL;
271 }
272
273 if(!serverSide && bLocal) {
274 m_inputHandler = m_type->createInputHandler(this);
275 }
276
277 if(serverSide) {
278 setSpeedFactor(tLXOptions->tGameInfo.features[FT_WormSpeedFactor]);
279 setDamageFactor(tLXOptions->tGameInfo.features[FT_WormDamageFactor]);
280 setShieldFactor(tLXOptions->tGameInfo.features[FT_WormShieldFactor]);
281 setCanAirJump(tLXOptions->tGameInfo.features[FT_InstantAirJump]);
282 }
283
284 bIsPrepared = true;
285 }
286
Unprepare()287 void CWorm::Unprepare() {
288 setGameReady(false);
289 setTagIT(false);
290 setTagTime(TimeDiff(0));
291 bVisibleForWorm.clear();
292 fVisibilityChangeTime = 0;
293
294 if(m_inputHandler) {
295 if(!bLocal) {
296 warnings << "WARNING: the following input handler was set for the non-local worm " << getName() << ": "; warnings.flush();
297 warnings << m_inputHandler->name() << endl;
298 }
299 delete m_inputHandler;
300 m_inputHandler = NULL;
301 }
302
303 bIsPrepared = false;
304 }
305
StartGame()306 void CWorm::StartGame() {
307 fTimeofDeath = GetPhysicsTime();
308 setLastAirJumpTime(AbsTime());
309 if(!m_inputHandler) {
310 warnings << "CWorm::StartGame(): input handler not set" << endl;
311 }
312 else
313 m_inputHandler->startGame();
314 }
315
316
fromInt(int type)317 WormType* WormType::fromInt(int type) {
318 switch(type) {
319 case 0: return PRF_HUMAN;
320 case 1: return PRF_COMPUTER;
321 default: return NULL;
322 }
323 }
324
325
getInput()326 void CWorm::getInput() {
327 if(!bLocal) {
328 warnings << "WARNING: called getInput() on non-local worm " << getName() << endl;
329 return;
330 }
331
332 if(!m_inputHandler) {
333 warnings << "WARNING: input handler not set for worm " << getName() << ", cannot get input" << endl;
334 return;
335 }
336
337 m_inputHandler->getInput();
338 }
339
clearInput()340 void CWorm::clearInput() {
341 fLastInputTime = GetPhysicsTime();
342
343 // Clear the state
344 tState.bCarve = false;
345 tState.bMove = false;
346 tState.bShoot = false;
347 tState.bJump = false;
348
349 if(bLocal && m_inputHandler)
350 m_inputHandler->clearInput();
351 }
352
initWeaponSelection()353 void CWorm::initWeaponSelection() {
354 if(!bLocal) {
355 warnings << "WARNING: called initWeaponSelection() on non-local worm " << getName() << endl;
356 return;
357 }
358
359 if(!m_inputHandler) {
360 warnings << "WARNING: input handler not set for worm " << getName() << ", cannot init weapon selection" << endl;
361 return;
362 }
363
364 if(this->shouldDoOwnWeaponSelection())
365 m_inputHandler->initWeaponSelection();
366
367 if(this->isFirstLocalHostWorm() && tLXOptions->tGameInfo.bSameWeaponsAsHostWorm && tLXOptions->tGameInfo.bForceRandomWeapons) {
368 this->GetRandomWeapons();
369 this->bWeaponsReady = true;
370 }
371
372 // bWeaponsReady could be true already for multiple reasons, e.g. in initWeaponSelection()
373 if(this->isFirstLocalHostWorm() && this->bWeaponsReady && tLXOptions->tGameInfo.bSameWeaponsAsHostWorm)
374 cServer->cloneWeaponsToAllWorms(this);
375 }
376
doWeaponSelectionFrame(SDL_Surface * bmpDest,CViewport * v)377 void CWorm::doWeaponSelectionFrame(SDL_Surface * bmpDest, CViewport *v) {
378 if(!bLocal) {
379 warnings << "WARNING: called doWeaponSelectionFrame() on non-local worm " << getName() << endl;
380 return;
381 }
382
383 if(!m_inputHandler) {
384 warnings << "WARNING: input handler not set for worm " << getName() << ", cannot do weapon selection" << endl;
385 return;
386 }
387
388 if(bWeaponsReady) {
389 warnings << "WARNING: doWeaponSelectionFrame: weapons already selected" << endl;
390 return;
391 }
392
393 if(!this->shouldDoOwnWeaponSelection()) {
394 int t = 0;
395 int centrex = 320; // TODO: hardcoded screen width here
396
397 if( v ) {
398 if( v->getUsed() ) {
399 t = v->GetTop();
400 centrex = v->GetLeft() + v->GetVirtW()/2;
401 }
402 }
403
404 tLX->cFont.DrawCentre(bmpDest, centrex, t+48, tLX->clWeaponSelectionTitle, "... Waiting for server selection ...");
405 return;
406 }
407
408 m_inputHandler->doWeaponSelectionFrame(bmpDest, v);
409
410 if(this->bWeaponsReady && this->isFirstLocalHostWorm() && tLXOptions->tGameInfo.bSameWeaponsAsHostWorm) {
411 cServer->cloneWeaponsToAllWorms(this);
412 }
413
414 }
415
416
417
418 ///////////////////
419 // Setup the lobby details
setupLobby()420 void CWorm::setupLobby()
421 {
422 bLobbyReady = false;
423 }
424
425
resetAngleAndDir()426 void CWorm::resetAngleAndDir() {
427 fAngle = 0;
428 fAngleSpeed = 0;
429 iFaceDirectionSide = DIR_RIGHT;
430 }
431
432
433 ///////////////////
434 // Spawn this worm
Spawn(CVec position)435 void CWorm::Spawn(CVec position) {
436 // No spawn if spectating
437 if (bSpectating)
438 return;
439
440 bAlive = true;
441 bAlreadyKilled = false;
442 bSpawnedOnce = true;
443 fVisibilityChangeTime = 0;
444 resetAngleAndDir();
445 fMoveSpeedX = 0;
446 health = 100.0f;
447 iMoveDirectionSide = DIR_RIGHT;
448 fLastInputTime = GetPhysicsTime();
449 vPos = vDrawPos = vLastPos = vPreOldPosOfLastPaket = vOldPosOfLastPaket = position;
450 vPreLastEstimatedVel = vLastEstimatedVel = vVelocity = CVec(0,0);
451 cNinjaRope.Clear();
452
453
454 fFrame = 0;
455 bDrawMuzzle = false;
456 bHooked = false;
457 bForceWeapon_Name = false;
458
459 bOnGround = false;
460 iNumWeaponSlots = 5;
461
462 // Reset the weapons
463 for(ushort n = 0; n < iNumWeaponSlots; n++) {
464 tWeapons[n].Charge = 1;
465 tWeapons[n].Reloading = false;
466 tWeapons[n].SlotNum = n;
467 tWeapons[n].LastFire = 0;
468 }
469
470 fSpawnTime = fPreLastPosUpdate = fLastPosUpdate = fLastSimulationTime = GetPhysicsTime();
471
472 if(bLocal) {
473 clearInput();
474 if(!m_inputHandler) {
475 warnings << "CWorm::Spawn for local worm: input handler not set" << endl;
476 }
477 else
478 m_inputHandler->onRespawn();
479 }
480
481 cDamageReport.clear();
482 }
483
484
485 ///////////////////
486 // Load the graphics
ChangeGraphics(int generalgametype)487 bool CWorm::ChangeGraphics(int generalgametype)
488 {
489 // TODO: create some good way to allow custom colors
490
491 bool team = false;
492
493 // Destroy any previous graphics
494 FreeGraphics();
495
496 Color colour = cSkin.getDefaultColor();
497 // If we are in a team game, use the team colours
498 if(generalgametype == GMT_TEAMS) {
499 team = true;
500 colour = tLX->clTeamColors[CLAMP(iTeam,0,3)];
501 }
502
503 // Use the colours set on the network
504 // Profile or team colours will override this
505
506 // Colourise the giblets
507 bmpGibs = ChangeGraphics("data/gfx/giblets.png", team);
508
509 // Colourise the skin
510 cSkin.Colorize(colour);
511
512 return bmpGibs.get() != NULL;
513 }
514
515 ///////////////////
516 // Change the graphics of an image
ChangeGraphics(const std::string & filename,bool team)517 SmartPointer<SDL_Surface> CWorm::ChangeGraphics(const std::string& filename, bool team)
518 {
519 SmartPointer<SDL_Surface> img;
520 SmartPointer<SDL_Surface> loaded;
521
522 // Load the image
523 loaded = LoadGameImage(filename);
524 if(loaded.get() == NULL) {
525 // Error: Couldn't load image
526 errors << "CWorm::ChangeGraphics: Could not load image " << filename << endl;
527 return NULL;
528 }
529
530 img = gfxCreateSurface(loaded.get()->w,loaded.get()->h);
531 if (img.get() == NULL) {
532 errors << "CWorm::ChangeGraphics: Not enough of memory." << endl;
533 return NULL;
534 }
535 DrawImage(img.get(),loaded,0,0); // Blit to the new surface
536
537 SetColorKey(img.get());
538
539
540 // TODO: clean up this function
541
542 // Set the colour of the img
543 int x,y;
544 Uint32 pixel;
545
546 Color colour = cSkin.getColor();
547 if (team)
548 colour = tLX->clTeamColors[iTeam];
549
550 int ColR = colour.r;
551 int ColG = colour.g;
552 int ColB = colour.b;
553 float r2,g2,b2;
554
555 float dr, dg, db;
556 const Uint32 gun1 = MakeColour(216,216,216);
557 const Uint32 gun2 = MakeColour(180,180,180);
558 const Uint32 gun3 = MakeColour(144,144,144);
559
560 if (!LockSurface(img))
561 return img;
562
563 for(y = 0; y < img.get()->h; y++) {
564 for(x = 0; x < img.get()->w; x++) {
565
566 pixel = GetPixel(img.get(),x,y);
567 Uint8 r,g,b;
568 GetColour3(pixel,img.get()->format,&r,&g,&b);
569
570 // Ignore pink & gun colours
571 if(IsTransparent(img.get(),pixel))
572 continue;
573 else if(pixel == gun1)
574 continue;
575 else if(pixel == gun2)
576 continue;
577 else if(pixel == gun3)
578 continue;
579
580 dr = (float)r / 96.0f;
581 dg = (float)g / 156.0f;
582 db = (float)b / 252.0f;
583
584 r2 = (float)ColR * dr;
585 g2 = (float)ColG * dg;
586 b2 = (float)ColB * db;
587
588 r2 = MIN(255.0f,r2);
589 g2 = MIN(255.0f,g2);
590 b2 = MIN(255.0f,b2);
591
592 // Make sure it isn't exactly 'magic pink'
593 if(Color((int)r2, (int)g2, (int)b2) == tLX->clPink) {
594 r2=240;
595 b2=240;
596 }
597
598 PutPixel(img.get(),x,y, Color((int)r2, (int)g2, (int)b2).get(img.get()->format));
599 }
600 }
601
602 UnlockSurface(img);
603
604 return img;
605 }
606
607
608
609 ///////////////////
610 // Randomize the weapons
GetRandomWeapons()611 void CWorm::GetRandomWeapons()
612 {
613 if(cWeaponRest == NULL) {
614 errors << "CWorm::GetRandomWeapons: cWeaponRest == NULL" << endl;
615 // no break here, the function will anyway work and ignore the restrictions
616 }
617
618 for(short i=0; i<5; i++) {
619 int num = MAX(1, GetRandomInt(cGameScript->GetNumWeapons()-1)); // HINT: num must be >= 1 or else we'll loop forever in the ongoing loop
620
621 // Cycle through weapons starting from the random one until we get an enabled weapon
622 int n=num;
623 int lastenabled = -1;
624 while(true) {
625 // Wrap around
626 if(n >= cGameScript->GetNumWeapons())
627 n = 0;
628
629 // Have we already got this weapon?
630 bool bSelected = false;
631 for(int k=0; k<i; k++) {
632 if(tWeapons[k].Weapon && (cGameScript->GetWeapons()+n)->ID == tWeapons[k].Weapon->ID) {
633 bSelected = true;
634 break;
635 }
636 }
637
638 // If this weapon is enabled AND we have not selected it already, then exit the loop
639 if(!cWeaponRest || cWeaponRest->isEnabled( (cGameScript->GetWeapons()+n)->Name )) {
640 if (!bSelected)
641 break;
642 lastenabled = n;
643 }
644
645 n++;
646
647 // We made a whole loop
648 if(n == num) {
649 n = lastenabled;
650 break;
651 }
652
653 } // while
654 if(n >= 0 && n < cGameScript->GetNumWeapons()) {
655 tWeapons[i].Weapon = cGameScript->GetWeapons()+n;
656 tWeapons[i].Enabled = true;
657 }
658 else {
659 tWeapons[i].Weapon = NULL;
660 tWeapons[i].Enabled = false;
661 }
662 }
663
664 }
665
666 // the first host worm (there can only be one such worm in a game)
isFirstLocalHostWorm()667 bool CWorm::isFirstLocalHostWorm() {
668 if(tLX->iGameType == GME_JOIN) return false;
669 if(!cServer || !cServer->isServerRunning()) return false;
670
671 CServerConnection* localConn = cServer->localClientConnection();
672 if(localConn == NULL) {
673 errors << "CWorm::isFirstLocalHostWorm: localClient not found" << endl;
674 return false;
675 }
676 return localConn->getNumWorms() > 0 && localConn->getWorm(0)->getID() == iID;
677 }
678
isLocalHostWorm()679 bool CWorm::isLocalHostWorm() {
680 if(tLX->iGameType == GME_JOIN) return false;
681 if(!cServer || !cServer->isServerRunning()) return false;
682
683 CServerConnection* localConn = cServer->localClientConnection();
684 if(localConn == NULL) {
685 errors << "CWorm::isLocalHostWorm: localClient not found" << endl;
686 return false;
687 }
688
689 for(int i = 0; i < localConn->getNumWorms(); ++i) {
690 CWorm* w = localConn->getWorm(i);
691 if(w)
692 if(w->getID() == iID) return true;
693 }
694
695 return false;
696 }
697
698
shouldDoOwnWeaponSelection()699 bool CWorm::shouldDoOwnWeaponSelection() {
700 return !cClient->serverChoosesWeapons() || (this->isFirstLocalHostWorm() && tLXOptions->tGameInfo.bSameWeaponsAsHostWorm);
701 }
702
CloneWeaponsFrom(CWorm * w)703 void CWorm::CloneWeaponsFrom(CWorm* w) {
704 for(int i = 0; i < 5; ++i) {
705 tWeapons[i].Weapon = w->getWeapon(i)->Weapon;
706 tWeapons[i].Enabled = w->getWeapon(i)->Enabled;
707
708 tWeapons[i].Charge = 1;
709 tWeapons[i].Reloading = false;
710 tWeapons[i].SlotNum = i;
711 tWeapons[i].LastFire = 0;
712 }
713 }
714
715
716 // Muzzle flash positions for different angles
717 const int RightMuzzle[14] = {2,3, 5,3, 4,0, 5,-8, 3,-9, 2,-13, -2,-12};
718 const int LeftMuzzle[14] = {4,-12, -1,-12, -1,-9, -3,-8, -2,0, -2,4, 1,3};
719
720
UpdateDrawPos()721 void CWorm::UpdateDrawPos() {
722 if( tLXOptions->bAntilagMovementPrediction && !cClient->OwnsWorm(this->getID()) ) {
723 //if(fLastPosUpdate > tLX->currentTime) return; // something is wrong, we probably have not gotten any update yet
724
725 // tmp hack
726 vDrawPos = vPos;
727
728 // update drawing position
729 CVec vDif = vPos - vDrawPos;
730 float dif = vDif.GetLength();
731 if(dif > 0) {
732 /* if(dif < 1)
733 vDrawPos = vPos;
734 else
735 vDrawPos += vDif * (1/dif)
736 * MAX(10.0f, MIN(dif * dif * (1.0f / 50.0f) * 40.0f, 200.0f)) * tLX->fDeltaTime;
737 */
738 }
739
740
741 #ifdef _AI_DEBUG
742 /* SmartPointer<SDL_Surface> bmpDestDebug = cClient->getMap()->GetDebugImage();
743 if (bmpDestDebug) {
744 int node_x = (int)vPos.x*2, node_y = (int)vPos.y*2;
745
746 if(node_x-4 >= 0 && node_y-4 >= 0 && node_x+4 < bmpDestDebug->w && node_y+4 < bmpDestDebug->h) {
747 // Draw the new pos
748 DrawRectFill(bmpDestDebug,node_x-4,node_y-4,node_x+4,node_y+4, Color(0,255,0));
749 }
750 } */
751 #endif
752
753 } else {
754 // no antilag movement prediction
755 vDrawPos = vPos;
756 }
757 }
758
759
isVisibleForWorm(int worm) const760 bool CWorm::isVisibleForWorm(int worm) const {
761 assert(worm >= 0);
762 if((size_t)worm >= bVisibleForWorm.size()) return true;
763 return bVisibleForWorm[worm];
764 }
765
setVisibleForWorm(int worm,bool visibility)766 void CWorm::setVisibleForWorm(int worm, bool visibility) {
767 assert(worm >= 0);
768 if((size_t)worm >= bVisibleForWorm.size()) {
769 bVisibleForWorm.resize(worm + 1, true);
770 }
771 bVisibleForWorm[worm] = visibility;
772 }
773
isVisibleForEverybody() const774 bool CWorm::isVisibleForEverybody() const {
775 for(std::vector<bool>::const_iterator i = bVisibleForWorm.begin(); i != bVisibleForWorm.end(); ++i) {
776 if(!*i) return false;
777 }
778 return true;
779 }
780
isVisible(CWorm * viewerWorm) const781 bool CWorm::isVisible(CWorm* viewerWorm) const {
782 if(viewerWorm == NULL) return isVisibleForEverybody();
783 return isVisibleForWorm(viewerWorm->getID());
784 }
785
isVisible(const CViewport * v) const786 bool CWorm::isVisible(const CViewport* v) const {
787 return isVisible(v->getTarget());
788 }
789
isWormVisible(CWorm * w,CViewport * v)790 static inline bool isWormVisible(CWorm* w, CViewport* v) {
791 return w->isVisible(v);
792 }
793
drawHealthBar(SDL_Surface * bmpDest,int hx,int hy,int health,DeprecatedGUI::CBar * cHealthBar,int barState,Color BorderColor,const Uint8 HealthColors[15])794 static int drawHealthBar(SDL_Surface * bmpDest, int hx, int hy, int health, DeprecatedGUI::CBar* cHealthBar, int barState, Color BorderColor, const Uint8 HealthColors[15])
795 {
796 int WormNameY = 0;
797 if (cHealthBar && cHealthBar->IsProperlyLoaded()) {
798
799 cHealthBar->SetCurrentForeState(barState);
800 cHealthBar->SetCurrentBgState(barState);
801 cHealthBar->SetX(hx - cHealthBar->GetWidth() / 2);
802 cHealthBar->SetY(hy - cHealthBar->GetHeight() - 1);
803 cHealthBar->Draw( bmpDest );
804 cHealthBar->SetPosition( health );
805 WormNameY += cHealthBar->GetHeight()+2; // Leave some space
806
807 } else { // Old style healthbar
808 hy -= 7;
809
810 // Draw the "grid"
811 {
812 DrawRect(bmpDest, hx-10,hy-1,hx+15,hy+5,BorderColor);
813 DrawVLine(bmpDest, hy, hy+4, hx-5,BorderColor);
814 DrawVLine(bmpDest, hy, hy+4, hx,BorderColor);
815 DrawVLine(bmpDest, hy, hy+4, hx+5,BorderColor);
816 DrawVLine(bmpDest, hy, hy+4, hx+10,BorderColor);
817 }
818
819 // Clamp it
820 int iShowHealth = Round((float)((health+15)/20));
821 if (iShowHealth > 5)
822 iShowHealth = 5;
823
824 for (short i=0; i<iShowHealth; i++) {
825 Color CurColor = Color(HealthColors[i*3],HealthColors[i*3+1],HealthColors[i*3+2]);
826 DrawRectFill(bmpDest,hx-10+(i*5+1),hy,hx-10+(i*5+1)+4,hy+5,CurColor);
827 }
828
829 WormNameY += 7;
830 }
831
832 return WormNameY;
833 }
834
835 ///////////////////
836 // Draw the worm
Draw(SDL_Surface * bmpDest,CViewport * v)837 void CWorm::Draw(SDL_Surface * bmpDest, CViewport *v)
838 {
839 if( !v )
840 return;
841
842 int l = v->GetLeft();
843 int t = v->GetTop();
844
845 CMap* map = cClient->getMap();
846 VectorD2<int> p = v->physicToReal(vDrawPos, cClient->getGameLobby()->features[FT_InfiniteMap], map->GetWidth(), map->GetHeight());
847
848 int x = p.x - l;
849 int y = p.y - t;
850
851 // Draw the ninja rope
852 // HINT: draw it before the clipping check because the rope might be visible even if the worm is not
853 if (isWormVisible(this, v) && bAlive)
854 cNinjaRope.Draw(bmpDest,v,vDrawPos);
855
856 // Are we inside the viewport?
857 if(x+l+10 < l || x-10 > v->GetVirtW()
858 || y+t+10 < t || y-10 > v->GetVirtH()) {
859 return;
860 }
861
862
863 int a = (int)fAngle;
864 if(iFaceDirectionSide == DIR_LEFT)
865 a=180-a;
866
867
868 int WormNameY = tLX->cFont.GetHeight()+12; // Font height + worm height/2 + some space
869
870 // Draw the damage amount that worm received
871 // Even the worm is dead draw damage for some time anyway (looks pretty)
872 if( tLXOptions->bDamagePopups && isWormVisible(this, v) )
873 {
874 // Sort them first
875 std::map< AbsTime, int > DamageReportDrawOrder;
876 for( std::map<int, DamageReport> ::iterator it = cDamageReport.begin(); it != cDamageReport.end(); it++ )
877 DamageReportDrawOrder[it->second.lastTime] = it->first;
878 // Draw
879 if( ! DamageReportDrawOrder.empty() )
880 {
881 int damageDrawPos = WormNameY + tLX->cFont.GetHeight();
882 // Make it float up a bit when time passes
883 if( GetPhysicsTime() > DamageReportDrawOrder.begin()->first )
884 damageDrawPos += (int)(( GetPhysicsTime() - DamageReportDrawOrder.begin()->first ).seconds() * 30);
885
886 float damageSum = 0;
887 std::map< AbsTime, int > :: const_iterator it;
888 for( it = DamageReportDrawOrder.begin(); it != DamageReportDrawOrder.end(); it++ )
889 damageSum += cDamageReport[it->second].damage;
890 Color damageColor = damageSum >= 0 ? Color( 0xff, 0x80, 0x40 ) : Color( 0x40, 0xff, 0 ) ; // Red or green
891
892 for( it = DamageReportDrawOrder.begin(); it != DamageReportDrawOrder.end(); it++ )
893 {
894 int id = it->second;
895 //if( !cClient->getRemoteWorms()[id].isUsed() )
896 // continue;
897 if( tLXOptions->bColorizeDamageByWorm )
898 {
899 damageSum = cDamageReport[id].damage;
900 if( id >= 0 && id < MAX_WORMS )
901 damageColor = cClient->getRemoteWorms()[id].getGameColour();
902 }
903 std::string damageStr = itoa( Round(damageSum) );
904 if( damageSum < 0 )
905 damageStr[0] = '+'; // Negative damage = healing
906 if( getClientVersion() < OLXBetaVersion(0,58,1) )
907 damageStr = "? " + damageStr; // + "\xC2\xBF"; // Inverted question mark in UTF-8
908 if(damageSum != 0)
909 tLX->cOutlineFont.DrawCentre(bmpDest, x + l, y + t - damageDrawPos, damageColor, damageStr);
910 //notes << "Print damage for worm " << getID() << " = " << damageStr << " at " << x << " " << (y - damageDrawPos) << endl;
911 damageDrawPos += tLX->cFont.GetHeight();
912
913 if( ! tLXOptions->bColorizeDamageByWorm )
914 break;
915 }
916 // Clean up expired damage report values
917 if( tLXOptions->bColorizeDamageByWorm )
918 {
919 for( std::map<int, DamageReport> ::iterator it = cDamageReport.begin(); it != cDamageReport.end(); )
920 {
921 if( GetPhysicsTime() > it->second.lastTime + 1.5f )
922 {
923 cDamageReport.erase(it);
924 it = cDamageReport.begin();
925 }
926 else
927 it++;
928 }
929 }
930 else
931 {
932 if( GetPhysicsTime() > DamageReportDrawOrder.begin()->first + 1.5f )
933 cDamageReport.clear();
934 }
935 }
936 }
937
938 // Do not draw further if we're not alive
939 if( !getAlive() )
940 return;
941
942 static const Color HealthBorderColor = Color(0x49,0x50,0x65);
943 // Red Orange Yellow Light Green Green
944 static const Uint8 HealthColors[15] = {0xE3,0x04,0x04, 0xFE,0x85,0x03, 0xFE,0xE9,0x03, 0xA8,0xFE,0x03, 0x21,0xFE,0x03};
945 // Dark blue Blue Blue Light blue Lighter blue
946 static const Uint8 WeaponColors[15] = {0x00,0x00,0x60, 0x00,0x00,0x90, 0x00,0x00,0xC0, 0x00,0x20,0xFE, 0x20,0x40,0xFE};
947 static const Uint8 WeaponDisabledColors[15] = {0x00,0x00,0x30, 0x00,0x00,0x40, 0x00,0x00,0x50, 0x00,0x10,0x60, 0x10,0x20,0x70};
948
949 if (tLXOptions->bShowHealth && isWormVisible(this, v)) {
950 if (!bLocal || m_type != PRF_HUMAN) {
951 // int hy = -8 = worm height/2
952 WormNameY += drawHealthBar(bmpDest, x + l, y + t - 9, (int)getHealth(), &cHealthBar, 0, HealthBorderColor, HealthColors);
953 }
954 }
955
956 if (GetTouchscreenControlsShown() && isWormVisible(this, v) && bLocal && m_type == PRF_HUMAN) {
957 // Touchscreen controls cover the health and weapon bars, so draw them below the worm, so they won't cover the crosshair
958 int yOffset = drawHealthBar(bmpDest, x + l, y + t + 20, (int)getHealth(), &cHealthBar, 0, HealthBorderColor, HealthColors);
959 yOffset -= 4;
960
961 if(getCurWeapon()->Weapon) {
962 if(getCurWeapon()->Reloading || !getCurWeapon()->Enabled) {
963 drawHealthBar(bmpDest, x + l, y + t + 20 + yOffset, (int) (getCurWeapon()->Charge * 100.0f), &cWeaponBar, 1, HealthBorderColor, WeaponDisabledColors);
964 } else {
965 drawHealthBar(bmpDest, x + l, y + t + 20 + yOffset, (int) (getCurWeapon()->Charge * 100.0f), &cWeaponBar, 0, HealthBorderColor, WeaponColors);
966 }
967 } else { // no weapon
968 drawHealthBar(bmpDest, x + l, y + t + 20 + yOffset, 0, &cWeaponBar, 0, HealthBorderColor, WeaponColors);
969 }
970
971 // Draw current weapon index, just a colored dot
972 if (bForceWeapon_Name || ((CWormHumanInputHandler*)m_inputHandler)->getInputWeapon().isDown()) {
973 yOffset -= 1;
974 for (int i = 0; i < iNumWeaponSlots; i++) {
975 Color CurColor;
976 if (i == iCurrentWeapon)
977 CurColor = Color(HealthColors[12],HealthColors[13],HealthColors[14]);
978 else if (tWeapons[i].Reloading || !tWeapons[i].Enabled || !tWeapons[i].Weapon)
979 CurColor = Color(WeaponDisabledColors[0],WeaponDisabledColors[1],WeaponDisabledColors[2]);
980 else
981 CurColor = Color(WeaponColors[12],WeaponColors[13],WeaponColors[14]);
982 DrawRectFill(bmpDest, x + l - 11 + (i*5), y + t + 20 + yOffset, x + l - 11 + (i*5) + 2, y + t + 20 + yOffset + 2, CurColor);
983 }
984 }
985 }
986
987 //
988 // Draw the crosshair
989 //
990 CVec forw;
991 GetVecsFromAngle((float)a, &forw, NULL);
992 forw *= tLXOptions->fCrosshairDistance;
993
994 VectorD2<int> cp = p;
995 cp.x += (int)forw.x;
996 cp.y += (int)forw.y;
997
998 // Show a green crosshair if we have a target
999 x = 0;
1000 if (bGotTarget) {
1001 x = 6;
1002 bGotTarget = false;
1003 }
1004
1005 if(bLocal && isWormVisible(this, v))
1006 DrawImageAdv(bmpDest, DeprecatedGUI::gfxGame.bmpCrosshair, x, 0, cp.x - 2, cp.y - 2, 6, 6);
1007
1008 //
1009 // Draw the worm
1010 //
1011 x = p.x;
1012 y = p.y;
1013
1014 // Find the right pic
1015 int f = ((int)fFrame*7);
1016 int ang = MIN( (int)( (fAngle+90)/151 * 7 ), 6 ); // clamp the value because LX skins don't have the very bottom aim
1017 f += ang;
1018
1019
1020 // Snap the position to a slighter bigger pixel grid (2x2)
1021 x -= x % 2;
1022 y -= y % 2;
1023
1024
1025 // Draw the worm
1026 if (isWormVisible(this, v)) {
1027 cSkin.Draw(bmpDest, x - cSkin.getSkinWidth()/2, y - cSkin.getSkinHeight()/2, f, false, iFaceDirectionSide == DIR_LEFT);
1028 cSparkles.Process();
1029 }
1030
1031 /*FillSurfaceTransparent(bmpShadowPic.get());
1032 if(iFaceDirectionSide == DIR_RIGHT)
1033 CopySurface(bmpShadowPic.get(), bmpWormRight, f,0, 6,0, 32,18);
1034 else
1035 CopySurface(bmpShadowPic.get(), bmpWormLeft, bmpWormLeft.get()->w-f-32,0, 0,0, 32,18);
1036
1037 DrawImage(bmpDest, bmpShadowPic, x-18,y-10);*/
1038
1039
1040
1041
1042 // Debug: Show the actual worm pos
1043 /*x = (int)( (vPos.x-wx)*2+l );
1044 y = (int)( (vPos.y-wy)*2+t );
1045
1046 // Snap the position to a slighter bigger pixel grid (2x2)
1047 x -= x % 2;
1048 y -= y % 2;*/
1049
1050 /*x = (int)( (tLX->debug_pos.x-wx)*2+l );
1051 y = (int)( (tLX->debug_pos.y-wy)*2+t );
1052 DrawRectFill(bmpDest, x-5,y-5,x+5,y+5,tLX->clBlack);*/
1053
1054
1055 //
1056 // Draw the muzzle flash
1057 //
1058 if (bDrawMuzzle && isWormVisible(this, v)) {
1059 switch(iFaceDirectionSide) {
1060
1061 case DIR_RIGHT:
1062 ang = (int)( (fAngle+90)/151 * 7 );
1063 ang = 6-ang;
1064 f = ang*16;
1065 DrawImageStretch2Key(bmpDest, DeprecatedGUI::gfxGame.bmpMuzzle, f, 0,
1066 (x-12)+RightMuzzle[ang*2],
1067 (y-10)+RightMuzzle[ang*2+1],
1068 16,16);
1069 break;
1070
1071 case DIR_LEFT:
1072 ang = (int)( (fAngle+90)/151 * 7 );
1073 f = (ang+7)*16;
1074 DrawImageStretch2Key(bmpDest, DeprecatedGUI::gfxGame.bmpMuzzle, f, 0,
1075 (x-21)+LeftMuzzle[ang*2],
1076 (y-10)+LeftMuzzle[ang*2+1],
1077 16,16);
1078 break;
1079
1080 } // switch
1081 } // if
1082 bDrawMuzzle = false;
1083
1084 if(isWormVisible(this, v))
1085 cClient->flagInfo()->drawWormAttachedFlag(this, bmpDest, v);
1086
1087 wpnslot_t *Slot = &tWeapons[iCurrentWeapon];
1088
1089 // Draw the weapon name
1090 if(bLocal && m_type == PRF_HUMAN && isWormVisible(this, v)) {
1091 if(bForceWeapon_Name || ((CWormHumanInputHandler*)m_inputHandler)->getInputWeapon().isDown()) {
1092 if(Slot->Weapon && Slot->Weapon->Name != "")
1093 tLX->cOutlineFont.DrawCentre(bmpDest,x,y-30,tLX->clPlayerName,Slot->Weapon->Name);
1094 else
1095 tLX->cOutlineFont.DrawCentre(bmpDest,x,y-30,tLX->clPlayerName,
1096 (iCurrentWeapon == 0) ? "-" : (iCurrentWeapon == 1) ? "--" :
1097 (iCurrentWeapon == 2) ? "---" : (iCurrentWeapon == 3) ? "----" : "-----");
1098
1099 if( tLX->currentTime > fForceWeapon_Time )
1100 bForceWeapon_Name = false;
1101 }
1102 }
1103
1104 // Draw the worm's name
1105 if (isWormVisible(this, v)) {
1106 std::string WormName = sName;
1107 if( sAFKMessage != "" )
1108 WormName += " " + sAFKMessage;
1109 if(!bLocal || (bLocal && m_type != PRF_HUMAN)) {
1110 if (cClient->getGameLobby()->iGeneralGameType == GMT_TEAMS && tLXOptions->bColorizeNicks)
1111 tLX->cOutlineFont.DrawCentre(bmpDest,x,y-WormNameY,tLX->clTeamColors[iTeam],WormName);
1112 else
1113 tLX->cOutlineFont.DrawCentre(bmpDest,x,y-WormNameY,tLX->clPlayerName,WormName);
1114 } else { // local human worm
1115 if(iAFK != AFK_BACK_ONLINE) {
1116 tLX->cOutlineFont.DrawCentre(bmpDest,x,y-WormNameY,tLX->clPlayerName,".. " + sAFKMessage + " ..");
1117 }
1118 }
1119 }
1120
1121 }
1122
1123 ///////////////////
1124 // Draw the worm's shadow
DrawShadow(SDL_Surface * bmpDest,CViewport * v)1125 void CWorm::DrawShadow(SDL_Surface * bmpDest, CViewport *v)
1126 {
1127 if( tLXOptions->bShadows && v && isWormVisible(this, v) ) {
1128 // Copied from ::Draw
1129 // TODO: a separate function for this
1130 int f = ((int)fFrame*7);
1131 int ang = MIN( (int)( (fAngle+90)/151 * 7 ), 6 ); // clamp the value because LX skins don't have the very bottom aim
1132 f += ang;
1133
1134 // Draw the shadow
1135
1136 // NOTE: the cSkin.DrawShadow function draws a shadow over solid objects
1137 // Later we should render the world layer by layer so this trouble will be gone
1138 // The CMap::DrawObjectShadow function is slow and also logically incorrect - why should a map know about other
1139 // objects?
1140 cSkin.DrawShadowOnMap(cClient->getMap(), v, bmpDest, (int)vPos.x, (int)vPos.y, f, iFaceDirectionSide == DIR_LEFT);
1141 }
1142 }
1143
1144
1145 ///////////////////
1146 // Quickly check if we are on the ground
CheckOnGround()1147 bool CWorm::CheckOnGround()
1148 {
1149 int px = (int)vPos.x;
1150 int py = (int)vPos.y;
1151 bool wrapAround = cClient->getGameLobby()->features[FT_InfiniteMap];
1152
1153 for(short y = 6; y > 0; y--) {
1154
1155 // Optimize: pixelflag + Width
1156 if(!(cClient->getMap()->GetPixelFlag(px - 2, py + y, wrapAround) & PX_EMPTY))
1157 return true;
1158 if(!(cClient->getMap()->GetPixelFlag(px + 2, py + y, wrapAround) & PX_EMPTY))
1159 return true;
1160 }
1161
1162 return false;
1163 }
1164
incrementDirtCount(int d)1165 void CWorm::incrementDirtCount(int d) {
1166 if(d != 0) {
1167 iDirtCount += d;
1168
1169 if( tLX->iGameType != GME_JOIN ) {
1170 cServer->getGameMode()->Carve(this, d);
1171 }
1172 }
1173 }
1174
1175 ///////////////////
1176 // Injure me
1177 // Returns true if i was killed by this injury
1178
1179
1180 ///////////////////
1181 // Kill me
1182 // Returns true if we are out of the game
Kill()1183 bool CWorm::Kill()
1184 {
1185 // notes << "our worm " << iID << " died" << endl;
1186
1187 bAlive = false;
1188 fTimeofDeath = GetPhysicsTime();
1189 addDeath();
1190
1191 // -2 means there is no lives starting value
1192 if(iLives == WRM_UNLIM)
1193 return false;
1194
1195 iLives--;
1196 return iLives == WRM_OUT;
1197 }
1198
getScore() const1199 int CWorm::getScore() const
1200 {
1201 int score = getKills();
1202 if( (float)tLXOptions->tGameInfo.features[FT_DeathDecreasesScore] > 0.0f )
1203 score -= (int)( getDeaths() * (float)tLXOptions->tGameInfo.features[FT_DeathDecreasesScore] + 0.01f ); // + 0.01f to counter possible inprecise truncation
1204 if( tLXOptions->tGameInfo.features[FT_SuicideDecreasesScore] )
1205 score -= getSuicides();
1206 if( tLXOptions->tGameInfo.features[FT_TeamkillDecreasesScore] )
1207 score -= getTeamkills();
1208 if( tLXOptions->tGameInfo.features[FT_CountTeamkills] )
1209 score += getTeamkills();
1210
1211 return score; // May be negative
1212 }
1213
addDeath()1214 void CWorm::addDeath()
1215 {
1216 if( !tLXOptions->tGameInfo.features[FT_AllowNegativeScore] && getScore() <= 0 && (float)tLXOptions->tGameInfo.features[FT_DeathDecreasesScore] > 0.0f )
1217 return;
1218 iDeaths++;
1219 }
1220
addSuicide()1221 void CWorm::addSuicide()
1222 {
1223 if( !tLXOptions->tGameInfo.features[FT_AllowNegativeScore] && getScore() <= 0 && tLXOptions->tGameInfo.features[FT_SuicideDecreasesScore] )
1224 return;
1225 iSuicides++;
1226 }
1227
addTeamkill()1228 void CWorm::addTeamkill()
1229 {
1230 if( !tLXOptions->tGameInfo.features[FT_AllowNegativeScore] && getScore() <= 0 && tLXOptions->tGameInfo.features[FT_TeamkillDecreasesScore] )
1231 return;
1232 iTeamkills++;
1233 }
1234
1235
1236 ///////////////////
1237 // Check if we have collided with a bonus
CheckBonusCollision(CBonus * b)1238 bool CWorm::CheckBonusCollision(CBonus *b)
1239 {
1240 CVec diff = vPos - b->getPosition();
1241
1242 return (fabs(diff.x) < 7) && (fabs(diff.y) < 7);
1243 }
1244
1245
1246 ///////////////////
1247 // Give me a bonus
1248 // Returns true if we picked it up
GiveBonus(CBonus * b)1249 bool CWorm::GiveBonus(CBonus *b)
1250 {
1251 // Weapon
1252 if(b->getType() == BNS_WEAPON) {
1253
1254 // Replace our current weapon
1255 if(b->getWeapon() >= 0 && b->getWeapon() < cGameScript->GetNumWeapons()) {
1256 tWeapons[iCurrentWeapon].Weapon = cGameScript->GetWeapons() + b->getWeapon();
1257 tWeapons[iCurrentWeapon].Charge = 1;
1258 tWeapons[iCurrentWeapon].Reloading = false;
1259 tWeapons[iCurrentWeapon].Enabled = true;
1260
1261 if (getLocal()) {
1262 // Let the weapon name show up for a short moment
1263 bForceWeapon_Name = true;
1264 fForceWeapon_Time = tLX->currentTime + 0.75f;
1265 }
1266 }
1267 else {
1268 warnings << "selected bonus has invalid weapon " << b->getWeapon() << endl;
1269 // do nothing
1270 }
1271
1272 return true;
1273 }
1274
1275
1276 // Health
1277 if(b->getType() == BNS_HEALTH) {
1278
1279 // If our health is at 100 or higher, don't pick it up
1280 if(health >= 100.0f) // Some mods have healing weapons, so the check is >= instead of == (breaks old behavior)
1281 return false;
1282
1283 // Health between 10% - 50%
1284 float health = (float)(GetRandomInt(40)+10);
1285
1286 // Route call to CClient::InjureWorm() so it will send ReportDamage packet, to track valid worm healthbar on server
1287 // Clamp it
1288 health = MIN(health, 100 - getHealth());
1289 cClient->InjureWorm(this, -health, getID());
1290
1291 return true;
1292 }
1293
1294 return false;
1295 }
1296
1297 ///////////////////
1298 // Hide the worm, if immediate is set, no animation will be shown
Hide(int forworm,bool immediate)1299 void CWorm::Hide(int forworm, bool immediate)
1300 {
1301 setVisibleForWorm(forworm, false);
1302 if (!immediate)
1303 fVisibilityChangeTime = tLX->currentTime;
1304 else
1305 fVisibilityChangeTime = 0;
1306 }
1307
1308 //////////////////
1309 // Show the worm, if immediate is set, no animation will be shown
Show(int forworm,bool immediate)1310 void CWorm::Show(int forworm, bool immediate)
1311 {
1312 setVisibleForWorm(forworm, true);
1313 if (!immediate)
1314 fVisibilityChangeTime = tLX->currentTime;
1315 else
1316 fVisibilityChangeTime = 0;
1317 }
1318
1319 ///////////////////
1320 // Get the worm's ping
GetMyPing()1321 int CWorm::GetMyPing()
1322 {
1323 if (!cServer)
1324 return 0;
1325 CServerConnection *cl = cServer->getClient(getID());
1326 if (!cl)
1327 return 0;
1328 return cl->getPing();
1329 }
1330
1331 ///////////////////
1332 // Resturns true, if we can start (auto-)typing
CanType()1333 bool CWorm::CanType()
1334 {
1335 if(!bAlive && iLives == WRM_OUT)
1336 return true; // a worm can always type if out of game (whereby further checks for not-worm related stuff could be done elsewhere)
1337
1338 if(m_type == PRF_HUMAN) {
1339 CWormHumanInputHandler* handler = (CWormHumanInputHandler*)m_inputHandler;
1340 return handler->canType();
1341 } else
1342 return true;
1343 }
1344
canType()1345 bool CWormHumanInputHandler::canType() {
1346 keyboard_t* kb = GetKeyboard();
1347 for (int i = 0; i < kb->queueLength; i++) {
1348 if (cUp.getData() == kb->keyQueue[i].sym ||
1349 cDown.getData() == kb->keyQueue[i].sym ||
1350 cLeft.getData() == kb->keyQueue[i].sym ||
1351 cRight.getData() == kb->keyQueue[i].sym ||
1352 cShoot.getData() == kb->keyQueue[i].sym ||
1353 cJump.getData() == kb->keyQueue[i].sym ||
1354 cSelWeapon.getData() == kb->keyQueue[i].sym ||
1355 cInpRope.getData() == kb->keyQueue[i].sym ||
1356 cStrafe.getData() == kb->keyQueue[i].sym ||
1357 cDig.getData() == kb->keyQueue[i].sym ||
1358 cWeapons[0].getData() == kb->keyQueue[i].sym ||
1359 cWeapons[1].getData() == kb->keyQueue[i].sym ||
1360 cWeapons[2].getData() == kb->keyQueue[i].sym ||
1361 cWeapons[3].getData() == kb->keyQueue[i].sym ||
1362 cWeapons[4].getData() == kb->keyQueue[i].sym)
1363 return false;
1364 }
1365 return true;
1366 }
1367
1368
setUsed(bool _u)1369 void CWorm::setUsed(bool _u)
1370 {
1371 bUsed = _u;
1372 if( ! _u )
1373 return;
1374 fLastSimulationTime = GetPhysicsTime();
1375 iTotalWins = iTotalLosses = iTotalKills = iTotalDeaths = iTotalSuicides = 0;
1376 }
1377
setAFK(AFK_TYPE afkType,const std::string & msg)1378 void CWorm::setAFK(AFK_TYPE afkType, const std::string & msg)
1379 {
1380 iAFK = afkType;
1381 sAFKMessage = msg;
1382 }
1383
setTagIT(bool _t)1384 void CWorm::setTagIT(bool _t)
1385 {
1386 bTagIT = _t;
1387 }
1388
getGameColour()1389 Color CWorm::getGameColour()
1390 {
1391 switch(cClient->getGameLobby()->iGeneralGameType) {
1392 case GMT_TEAMS:
1393 return tLX->clTeamColors[iTeam];
1394 default:
1395 return cSkin.getDefaultColor();
1396 }
1397 }
1398
addDamage(float damage,CWorm * victim,const GameOptions::GameInfo & settings)1399 void CWorm::addDamage(float damage, CWorm* victim, const GameOptions::GameInfo & settings)
1400 {
1401 if( damage < 0 ) // Do not count when we heal ourselves or someone else, only damage counts
1402 return;
1403
1404 if( getID() == victim->getID() )
1405 {
1406 if( settings.features[FT_SuicideDecreasesScore] )
1407 setDamage( getDamage() - damage ); // Decrease damage from score if injured yourself
1408 }
1409 else if( settings.iGeneralGameType == GMT_TEAMS && getTeam() == victim->getTeam() )
1410 {
1411 if( settings.features[FT_TeamkillDecreasesScore] )
1412 setDamage( getDamage() - damage ); // Decrease damage from score if injured teammate
1413 if( settings.features[FT_CountTeamkills] )
1414 setDamage( getDamage() + damage ); // Count team damage along with teamkills
1415 }
1416 else
1417 setDamage( getDamage() + damage );
1418 }
1419
reinitInputHandler()1420 void CWorm::reinitInputHandler() {
1421 if(!bLocal) {
1422 warnings << "reinitInputHandler called for non-local worm " << getID() << endl;
1423 return;
1424 }
1425
1426 if(m_inputHandler)
1427 delete m_inputHandler;
1428 else
1429 warnings << "reinitInputHandler: inputhandler was unset for worm " << getID() << endl;
1430 m_inputHandler = m_type->createInputHandler(this);
1431
1432 // TODO: move this to CWormInputHandler init
1433 // we need to reset the inputs
1434 // code from CClient::SetupGameInputs()
1435 int humanWormNum = 0;
1436 for(int i = 0; i < cClient->getNumWorms(); i++) {
1437 CWormHumanInputHandler* handler = dynamic_cast<CWormHumanInputHandler*> (cClient->getWorm(i)->inputHandler());
1438 if(handler) {
1439 // TODO: Later, let the handler save a rev to his sPlayerControls. This would give
1440 // more flexibility to the player and he can have multiple player control sets.
1441 // Then, we would call a reloadInputs() here.
1442 if(cClient->getWorm(i) == this)
1443 handler->setupInputs( tLXOptions->sPlayerControls[humanWormNum] );
1444 humanWormNum++;
1445 }
1446 }
1447 }
1448