1 /*
2  *  CRace.cpp
3  *  OpenLieroX
4  *
5  *  Created by Albert Zeyer on 10.05.09.
6  *  code under LGPL
7  *
8  */
9 
10 #include <algorithm>
11 #include "CGameMode.h"
12 #include "CServer.h"
13 #include "CWorm.h"
14 #include "FlagInfo.h"
15 #include "CServerConnection.h"
16 #include "CServerNetEngine.h"
17 
18 struct Race : public CGameMode {
19 
NameRace20 	virtual std::string Name() {
21 		return "Race";
22 	}
23 
getGameInfoGroupInOptionsRace24 	virtual GameInfoGroup getGameInfoGroupInOptions() { return GIG_Race; }
25 
MinNeededVersionRace26 	virtual Version MinNeededVersion() { return OLXBetaVersion(0,58,1); }
27 
resetRace28 	virtual void reset() {
29 		wayPoints.clear();
30 		wormSpawnPoints.clear();
31 		nextGoals.clear();
32 	}
33 
RaceRace34 	Race() { reset(); }
35 
36 	static const unsigned int WPNUM = 4;
37 	std::vector<CVec> wayPoints;
38 
flagNameRace39 	std::string flagName(int t) {
40 		return "way point " + itoa(t + 1);
41 	}
42 
sendWormScoreUpdateRace43 	void sendWormScoreUpdate(CWorm* w) {
44 		for(int ii = 0; ii < MAX_CLIENTS; ii++) {
45 			if(cServer->getClients()[ii].getStatus() != NET_CONNECTED) continue;
46 			if(cServer->getClients()[ii].getNetEngine() == NULL) continue;
47 			cServer->getClients()[ii].getNetEngine()->SendWormScore( w );
48 		}
49 	}
50 
PrepareGameRace51 	virtual void PrepareGame() {
52 		CGameMode::PrepareGame();
53 		reset();
54 
55 		wayPoints.resize(WPNUM);
56 		for(int x = 0; x <= 1; x++)
57 			for(int y = 0; y <= 1; y++) {
58 				std::list<CVec> goodPos;
59 				goodPos.push_back(CVec(
60 								  (cServer->getMap()->GetWidth() * 0.8f * x + cServer->getMap()->GetWidth() * 0.2f),
61 								  (cServer->getMap()->GetHeight() * 0.8f * y + cServer->getMap()->GetHeight() * 0.2f)));
62 				std::list<CVec> badPos;
63 				badPos.push_back(CVec(
64 								  (cServer->getMap()->GetWidth() * 0.2f * x + cServer->getMap()->GetWidth() * 0.8f),
65 								  (cServer->getMap()->GetHeight() * 0.2f * y + cServer->getMap()->GetHeight() * 0.8f)));
66 				int t = (y == 0) ? x : (3 - x);
67 				wayPoints[t] = cServer->FindSpotCloseToPos(goodPos, badPos, false);
68 
69 				if(!tLXOptions->tGameInfo.features[FT_InstantAirJump])
70 					// set the place to the ground
71 					wayPoints[t] = cServer->getMap()->groundPos(wayPoints[t]) - CVec(0, (float)(cServer->flagInfo()->getHeight()/4));
72 			}
73 	}
74 
getWormFlagRace75 	virtual int getWormFlag(CWorm* worm) {
76 		return worm->getID();
77 	}
78 
79 	typedef int WormID;
80 	std::map<WormID,CVec> wormSpawnPoints;
81 	typedef int FlagID;
82 	typedef int WayPointID;
83 	std::map<FlagID,WayPointID> nextGoals;
84 
getNextGoalRace85 	WayPointID getNextGoal(FlagID id) {
86 		std::map<FlagID,WayPointID>::iterator f = nextGoals.find(id);
87 		if(f == nextGoals.end()) return 1;
88 		else return f->second;
89 	}
90 
SpawnRace91 	virtual bool Spawn(CWorm* worm, CVec pos) {
92 		if(wayPoints.size() == 0) {
93 			errors << "Race::Spawn: not prepared yet" << endl;
94 			return true;
95 		}
96 
97 		std::map<WormID,CVec>::iterator sp = wormSpawnPoints.find(worm->getID());
98 		if(sp == wormSpawnPoints.end())
99 			pos = wayPoints[0];
100 		else
101 			pos = sp->second;
102 
103 		if(!cServer->flagInfo()->getFlag(getWormFlag(worm))) {
104 			// we have to create the new flag, there isn't any yet
105 			cServer->flagInfo()->applyInitFlag(getWormFlag(worm), wayPoints[1]);
106 			nextGoals[getWormFlag(worm)] = 1;
107 		}
108 
109 		worm->Spawn(pos);
110 
111 		return true;
112 	}
113 
KillRace114 	virtual void Kill(CWorm* victim, CWorm* killer) {
115 		// Victim is out of the game
116 		if(victim->Kill() && networkTexts->sPlayerOut != "<none>")
117 			cServer->SendGlobalText(replacemax(networkTexts->sPlayerOut, "<player>",
118 											   victim->getName(), 1), TXT_NORMAL);
119 
120 		wormSpawnPoints[victim->getID()] = victim->getPos();
121 	}
122 
addScoreRace123 	virtual void addScore(CWorm* w) {
124 		w->addKill();
125 		sendWormScoreUpdate(w);
126 		cServer->SendGlobalText(TeamNameForWorm(w) + " is in round " + itoa(Round(w)), TXT_NORMAL);
127 	}
128 
TeamNameForWormRace129 	virtual std::string TeamNameForWorm(CWorm* w) {
130 		return w->getName();
131 	}
132 
RoundRace133 	virtual int Round(CWorm* w) {
134 		return w->getKills();
135 	}
136 
hitFlagSpawnPointRace137 	virtual void hitFlagSpawnPoint(CWorm* worm, Flag* flag) {
138 		if(getWormFlag(worm) == flag->id) {
139 			if(flag->holderWorm == worm->getID())
140 				cServer->flagInfo()->applySetBack(flag);
141 			else
142 				cServer->flagInfo()->applyHolderWorm(flag, worm->getID());
143 
144 			nextGoals[getWormFlag(worm)] = (nextGoals[getWormFlag(worm)] + 1) % WPNUM;
145 			cServer->flagInfo()->applySpawnPos(flag, wayPoints[nextGoals[getWormFlag(worm)]]);
146 
147 			if(nextGoals[getWormFlag(worm)] == 1) {
148 				// we made one round
149 				addScore(worm);
150 				cServer->RecheckGame();
151 			}
152 		}
153 	}
154 
FlagPointRadiusRace155 	virtual float FlagPointRadius() { return tLXOptions->tGameInfo.features[FT_Race_CheckPointRadius]; }
FlagRadiusRace156 	virtual float FlagRadius() { return tLXOptions->tGameInfo.features[FT_Race_CheckPointRadius]; }
157 
ShootRace158 	virtual bool Shoot(CWorm* worm) {
159 		// get that information from client because both client&server can check this
160 		return cClient->getGameLobby()->features[FT_Race_AllowWeapons];
161 	}
162 
CheckGameOverRace163 	virtual bool CheckGameOver() {
164 		if(int(tLXOptions->tGameInfo.features[FT_Race_Rounds]) > 0) {
165 			for(int i = 0; i < MAX_WORMS; ++i) {
166 				CWorm* w = &cServer->getWorms()[i];
167 				if(!w->isUsed()) continue;
168 				if(w->getKills() >= int(tLXOptions->tGameInfo.features[FT_Race_Rounds])) {
169 					return true;
170 				}
171 			}
172 		}
173 
174 		bool allOut = true;
175 		for(int i = 0; i < MAX_WORMS; i++)
176 			if(cServer->getWorms()[i].isUsed()) {
177 				if(cServer->getWorms()[i].getLives() != WRM_OUT || cServer->getWorms()[i].getAlive()) {
178 					allOut = false;
179 					break;
180 				}
181 			}
182 		if(allOut) return true;
183 
184 		// Check if the timelimit has been reached
185 		if(TimeLimit() > 0) {
186 			if (cServer->getServerTime() > TimeLimit()) {
187 				if(networkTexts->sTimeLimit != "<none>")
188 					cServer->SendGlobalText(networkTexts->sTimeLimit, TXT_NORMAL);
189 				notes << "time limit (" << (tLXOptions->tGameInfo.fTimeLimit*60.0f) << ") reached with current time " << cServer->getServerTime().seconds();
190 				notes << " -> game over" << endl;
191 				return true;
192 			}
193 		}
194 
195 		return false;
196 	}
197 
198 	struct CompareGoals {
199 		Race* race;
CompareGoalsRace::CompareGoals200 		CompareGoals(Race* r) : race(r) {}
201 
compRace::CompareGoals202 		int comp(CWorm* w1, CWorm* w2) {
203 			// this is if we want to do the compare but the game has not started yet
204 			if(race->wayPoints.size() == 0) return 0;
205 
206 			{
207 				// compare next goals
208 				int lastGoal1 = (race->getNextGoal(race->getWormFlag(w1)) + WPNUM - 1) % WPNUM;
209 				int lastGoal2 = (race->getNextGoal(race->getWormFlag(w2)) + WPNUM - 1) % WPNUM;
210 				if(lastGoal1 > lastGoal2) return 1;
211 				if(lastGoal1 < lastGoal2) return -1;
212 			}
213 
214 			{
215 				// compare distance to next goals (they should be the same for both worms)
216 				float dist1 = (race->wayPoints[race->getNextGoal(race->getWormFlag(w1))] - w1->getPos()).GetLength2();
217 				float dist2 = (race->wayPoints[race->getNextGoal(race->getWormFlag(w2))] - w2->getPos()).GetLength2();
218 				long distdiff = (long) (dist2 - dist1);
219 				if(distdiff >= 1) return 1; // it's better to be more close
220 				if(distdiff <= -1) return -1;
221 			}
222 
223 			return 0;
224 		}
225 
operator ()Race::CompareGoals226 		bool operator()(CWorm* w1, CWorm* w2) {
227 			return comp(w1, w2) > 0;
228 		}
229 	};
230 
CompareWormsScoreRace231 	int CompareWormsScore(CWorm* w1, CWorm* w2) {
232 		// Kills very first (that is the amount of rounds)
233 		if (w1->getKills() > w2->getKills()) return 1;
234 		if (w1->getKills() < w2->getKills()) return -1;
235 
236 		{
237 			// compare goals; we can only do that if we are server, because we don't have the waypoints otherwise
238 			int d = (cServer && cServer->isServerRunning()) ? CompareGoals(this).comp(w1, w2) : 0;
239 			if(d != 0) return d;
240 		}
241 
242 		// fallback (very unprobable that this will be used in game)
243 		return CGameMode::CompareWormsScore(w1, w2);
244 	}
245 };
246 
247 struct TeamRace : public Race {
248 
NameTeamRace249 	virtual std::string Name() {
250 		return "Team Race";
251 	}
252 
getGameInfoGroupInOptionsTeamRace253 	virtual GameInfoGroup getGameInfoGroupInOptions() { return GIG_Race; }
254 
255 	static const int MAXTEAMS = 4;
256 	int teamScore[MAXTEAMS];
257 
GameTeamsTeamRace258 	virtual int GameTeams() {
259 		return MAXTEAMS;
260 	}
261 
resetTeamRace262 	void reset() {
263 		Race::reset();
264 		for(int i = 0; i < MAXTEAMS; ++i)
265 			teamScore[i] = 0;
266 	}
267 
TeamRaceTeamRace268 	TeamRace() { reset(); }
269 
PrepareGameTeamRace270 	virtual void PrepareGame() {
271 		Race::PrepareGame();
272 	}
273 
addScoreTeamRace274 	virtual void addScore(CWorm* w) {
275 		teamScore[CLAMP(w->getTeam(),0,MAXTEAMS-1)]++;
276 		cServer->SendTeamScoreUpdate();
277 		// also add score for this specifc worm
278 		Race::addScore(w);
279 	}
280 
getWormFlagTeamRace281 	virtual int getWormFlag(CWorm* worm) {
282 		return worm->getTeam();
283 	}
284 
isTeamUsedTeamRace285 	bool isTeamUsed(int t) {
286 		return !cServer->isTeamEmpty(t);
287 	}
288 
TeamNameForWormTeamRace289 	virtual std::string TeamNameForWorm(CWorm* w) {
290 		return TeamName(w->getTeam());
291 	}
292 
RoundTeamRace293 	virtual int Round(CWorm* w) {
294 		return TeamScores(w->getTeam());
295 	}
296 
TeamScoresTeamRace297 	virtual int TeamScores(int t) {
298 		if(t >= 0 && t < MAXTEAMS) return teamScore[t];
299 		return -1;
300 	}
301 
getTeamWormsTeamRace302 	std::vector<CWorm*> getTeamWorms(int t) {
303 		std::vector<CWorm*> worms;
304 		if(cServer->getWorms() == NULL) return worms;
305 		for(int i = 0; i < MAX_WORMS; i++)
306 			if(cServer->getWorms()[i].isUsed())
307 				if(cServer->getWorms()[i].getTeam() != t)
308 					worms.push_back(&cServer->getWorms()[i]);
309 		return worms;
310 	}
311 
getTeamBestWormTeamRace312 	CWorm* getTeamBestWorm(int t) {
313 		std::vector<CWorm*> teamWorms = getTeamWorms(t);
314 		if(teamWorms.size() == 0) return NULL;
315 		std::sort(teamWorms.begin(), teamWorms.end(), CompareGoals(this));
316 		return teamWorms.front();
317 	}
318 
CompareTeamScoreTeamRace319 	virtual int CompareTeamScore(int t1, int t2) {
320 		// team score is most important in CTF, more important than left lifes
321 		{
322 			int d = TeamScores(t1) - TeamScores(t2);
323 			if(d != 0) return d;
324 		}
325 
326 		{
327 			// compare the goals of the best worms of each team
328 			CWorm* w1 = getTeamBestWorm(t1);
329 			CWorm* w2 = getTeamBestWorm(t2);
330 
331 			int d = (w1 && w2) ? CompareGoals(this).comp(w1, w2) : 0;
332 			if(d != 0) return d;
333 		}
334 
335 		// ok, fallback to default
336 		return CGameMode::CompareTeamsScore(t1, t2);
337 	}
338 
CheckGameOverTeamRace339 	virtual bool CheckGameOver() {
340 		if(int(tLXOptions->tGameInfo.features[FT_Race_Rounds]) > 0) {
341 			for(int i = 0; i < MAXTEAMS; ++i) {
342 				if(teamScore[i] >= int(tLXOptions->tGameInfo.features[FT_Race_Rounds])) {
343 					return true;
344 				}
345 			}
346 		}
347 
348 		bool allOut = true;
349 		for(int i = 0; i < MAX_WORMS; i++)
350 			if(cServer->getWorms()[i].isUsed()) {
351 				if(cServer->getWorms()[i].getLives() != WRM_OUT || cServer->getWorms()[i].getAlive()) {
352 					allOut = false;
353 					break;
354 				}
355 			}
356 		if(allOut) return true;
357 
358 		// Check if the timelimit has been reached
359 		if(TimeLimit() > 0) {
360 			if (cServer->getServerTime() > TimeLimit()) {
361 				if(networkTexts->sTimeLimit != "<none>")
362 					cServer->SendGlobalText(networkTexts->sTimeLimit, TXT_NORMAL);
363 				notes << "time limit (" << (tLXOptions->tGameInfo.fTimeLimit*60.0f) << ") reached with current time " << cServer->getServerTime().seconds();
364 				notes << " -> game over" << endl;
365 				return true;
366 			}
367 		}
368 
369 		if(GameTeams() > 1) {
370 			// Only one team left?
371 			int teams = 0;
372 			for(int i = 0; i < GameTeams(); i++)  {
373 				if(WormsAliveInTeam(i)) {
374 					teams++;
375 				}
376 			}
377 
378 			// Only one team left
379 			if(teams <= 1) {
380 				return true;
381 			}
382 		}
383 
384 		return false;
385 	}
386 
387 
388 
389 };
390 
391 static Race _gameMode_Race;
392 static TeamRace _gameMode_TeamRace;
393 CGameMode* gameMode_Race = &_gameMode_Race;
394 CGameMode* gameMode_TeamRace = &_gameMode_TeamRace;
395 
396