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