1 /* bzflag
2  * Copyright (c) 1993-2021 Tim Riker
3  *
4  * This package is free software;  you can redistribute it and/or
5  * modify it under the terms of the license found in the file
6  * named COPYING that should have accompanied this file.
7  *
8  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11  */
12 
13 /* no header other than FlagInfo.h should be included here */
14 
15 /* interface header */
16 #include "FlagInfo.h"
17 
18 /* system headers */
19 #include <iostream>
20 
21 // implementation-specific bzflag headers
22 #include "BZDBCache.h"
23 
24 /* private */
25 
26 /* protected */
27 
28 /* public */
29 
30 // flags list
31 FlagInfo          *FlagInfo::flagList      = NULL;
32 std::vector<FlagType*> FlagInfo::allowedFlags;
33 int         FlagInfo::numExtraFlags = 0;
34 int         FlagInfo::numFlags      = 0;
35 int         FlagInfo::numFlagsInAir;
36 
FlagInfo()37 FlagInfo::FlagInfo(): numShots(0), flagIndex(0)
38 {
39     // prep flag
40     flag.type       = Flags::Null;
41     flag.status         = FlagNoExist;
42     flag.endurance      = FlagNormal;
43     flag.owner          = NoPlayer;
44     flag.position[0]    = 0.0f;
45     flag.position[1]    = 0.0f;
46     flag.position[2]    = 0.0f;
47     flag.launchPosition[0]  = 0.0f;
48     flag.launchPosition[1]  = 0.0f;
49     flag.launchPosition[2]  = 0.0f;
50     flag.landingPosition[0] = 0.0f;
51     flag.landingPosition[1] = 0.0f;
52     flag.landingPosition[2] = 0.0f;
53     flag.flightTime     = 0.0f;
54     flag.flightEnd      = 0.0f;
55     flag.initialVelocity    = 0.0f;
56     player          = -1;
57     grabs           = 0;
58     required        = false;
59 }
60 
setSize(int _numFlags)61 void FlagInfo::setSize(int _numFlags)
62 {
63     // sanity check
64     if (_numFlags > 100000)
65     {
66         std::cerr << "WARNING: FlagInfo::setSize was given an insane flag count of " << _numFlags << std::endl;
67         std::cerr << "clamping to 100000 flags just for kicks" << std::endl;
68         _numFlags = 100000;
69     }
70 
71     numFlags = _numFlags;
72     delete[] flagList;
73     flagList = NULL;
74     if (numFlags)
75         flagList = new FlagInfo[numFlags];
76     for (int i = 0; i < numFlags; i++)
77         flagList[i].flagIndex = i;
78 }
79 
setAllowed(std::vector<FlagType * > allowed)80 void FlagInfo::setAllowed(std::vector<FlagType*> allowed)
81 {
82     allowedFlags = allowed;
83 }
84 
setExtra(int extra)85 void FlagInfo::setExtra(int extra)
86 {
87     numExtraFlags = extra;
88 }
89 
lookupFirstTeamFlag(int teamindex)90 int FlagInfo::lookupFirstTeamFlag(int teamindex)
91 {
92     for (int i = 0; i < numFlags; i++)
93     {
94         if (flagList[i].flag.type->flagTeam == teamindex)
95             return i;
96     }
97     return -1;
98 }
99 
setRequiredFlag(FlagType * desc)100 void FlagInfo::setRequiredFlag(FlagType *desc)
101 {
102     required = true;
103     flag.type = desc;
104 }
105 
addFlag()106 void FlagInfo::addFlag()
107 {
108     const float flagAltitude = BZDB.eval(StateDatabase::BZDB_FLAGALTITUDE);
109     const float gravity      = BZDBCache::gravity;
110 
111     // flag is now entering game
112     numFlagsInAir++;
113     flag.status     = FlagComing;
114 
115     // compute drop time
116     const float flightTime = 2.0f * sqrtf(-2.0f * flagAltitude / gravity);
117     flag.flightTime   = 0.0f;
118     flag.flightEnd     = flightTime;
119     flag.initialVelocity   = -0.5f * gravity * flightTime;
120     dropDone         = TimeKeeper::getCurrent();
121     dropDone        += flightTime;
122 
123     if (flag.type == Flags::Null)
124         // pick a random flag
125         flag.type = allowedFlags[(int)(allowedFlags.size() * (float)bzfrand())];
126 
127     // decide how sticky the flag will be
128     if (flag.type->flagQuality == FlagBad)
129         flag.endurance = FlagSticky;
130     else
131         flag.endurance = FlagUnstable;
132 
133     // how times will it stick around
134     if ((flag.endurance == FlagSticky) || (flag.type == Flags::Thief))
135         grabs = 1;
136     else
137         grabs = BZDB.evalInt(StateDatabase::BZDB_MAXFLAGGRABS);
138 }
139 
pack(void * buf,bool hide)140 void *FlagInfo::pack(void *buf, bool hide)
141 {
142     if (FlagInfo::flagList[flagIndex].flag.type->flagTeam != ::NoTeam)
143         hide = false;
144     if (FlagInfo::flagList[flagIndex].player != -1)
145         hide = false;
146     buf = nboPackUShort(buf, flagIndex);
147     if (hide)
148         buf = FlagInfo::flagList[flagIndex].flag.fakePack(buf);
149     else
150         buf = FlagInfo::flagList[flagIndex].flag.pack(buf);
151     return buf;
152 }
153 
dropFlag(float pos[3],float landingPos[3],bool vanish)154 void FlagInfo::dropFlag(float pos[3], float landingPos[3], bool vanish)
155 {
156     numFlagsInAir++;
157     flag.status        = vanish ? FlagGoing : FlagInAir;
158 
159     flag.landingPosition[0] = landingPos[0];
160     flag.landingPosition[1] = landingPos[1];
161     flag.landingPosition[2] = landingPos[2];
162 
163     flag.position[0]       = landingPos[0];
164     flag.position[1]       = landingPos[1];
165     flag.position[2]       = landingPos[2];
166     flag.launchPosition[0] = pos[0];
167     flag.launchPosition[1] = pos[1];
168     flag.launchPosition[2] = pos[2] + BZDBCache::tankHeight;
169 
170     // compute flight info -- flight time depends depends on start and end
171     // altitudes and desired height above start altitude
172     const float gravity   = BZDBCache::gravity;
173     const float flagAltitude   = BZDB.eval(StateDatabase::BZDB_FLAGALTITUDE);
174     const float thrownAltitude = (flag.type == Flags::Shield) ?
175                                  BZDB.eval(StateDatabase::BZDB_SHIELDFLIGHT) * flagAltitude : flagAltitude;
176     const float maxAltitude    = pos[2] + thrownAltitude;
177     const float upTime     = sqrtf(-2.0f * thrownAltitude / gravity);
178     const float downTime       = sqrtf(-2.0f * (maxAltitude - pos[2]) / gravity);
179     const float flightTime     = upTime + downTime;
180 
181     dropDone       = TimeKeeper::getCurrent();
182     dropDone      += flightTime;
183     flag.flightTime      = 0.0f;
184     flag.flightEnd       = flightTime;
185     flag.initialVelocity = -gravity * upTime;
186 }
187 
resetFlag(float position[3],bool teamIsEmpty)188 void FlagInfo::resetFlag(float position[3], bool teamIsEmpty)
189 {
190     // reset a flag's info
191     player      = -1;
192     // if it's a random flag, reset flag id
193     if (flagIndex >= numFlags - numExtraFlags)
194         flag.type = Flags::Null;
195 
196     flag.position[0] = position[0];
197     flag.position[1] = position[1];
198     flag.position[2] = position[2];
199 
200     // required flags mustn't just disappear
201     if (required)
202     {
203         if (flag.type->flagTeam == ::NoTeam)
204             // flag in now entering game
205             addFlag();
206         else if (teamIsEmpty)
207             flag.status = FlagNoExist;
208         else
209             flag.status = FlagOnGround;
210     }
211     else
212         flag.status = FlagNoExist;
213 }
214 
grab(int playerIndex)215 void FlagInfo::grab(int playerIndex)
216 {
217     flag.status = FlagOnTank;
218     flag.owner  = playerIndex;
219     player      = playerIndex;
220     numShots    = 0;
221 }
222 
teamIndex() const223 TeamColor FlagInfo::teamIndex() const
224 {
225     return flag.type->flagTeam;
226 }
227 
getIndex() const228 int FlagInfo::getIndex() const
229 {
230     return flagIndex;
231 }
232 
getNextDrop(TimeKeeper & tm)233 float FlagInfo::getNextDrop(TimeKeeper &tm)
234 {
235     // find timeout when next flag would hit ground
236     float waitTime = 3.0f;
237     if (numFlagsInAir > 0)
238     {
239         for (int i = 0; i < numFlags; i++)
240         {
241             FlagInfo &flag = flagList[i];
242             if (flag.flag.status != FlagNoExist &&
243                     flag.flag.status != FlagOnTank &&
244                     flag.flag.status != FlagOnGround &&
245                     flag.dropDone - tm < waitTime)
246                 waitTime = float(flag.dropDone - tm);
247         }
248     }
249     return waitTime;
250 }
251 
landing(const TimeKeeper & tm)252 bool FlagInfo::landing(const TimeKeeper &tm)
253 {
254     if (numFlagsInAir <= 0)
255         return false;
256 
257     bool land = false;
258     if (flag.status == FlagInAir || flag.status == FlagComing)
259     {
260         if (dropDone - tm <= 0.0f)
261         {
262             flag.status = FlagOnGround;
263             numFlagsInAir--;
264             land  = true;
265         }
266     }
267     else if (flag.status == FlagGoing)
268     {
269         if (dropDone - tm <= 0.0f)
270         {
271             flag.status = FlagNoExist;
272             numFlagsInAir--;
273             land  = true;
274         }
275     }
276     return land;
277 }
278 
setNoFlagInAir()279 void FlagInfo::setNoFlagInAir()
280 {
281     numFlagsInAir = 0;
282 }
283 
getTextualInfo(char * message)284 void FlagInfo::getTextualInfo(char *message)
285 {
286     sprintf(message, "#%-3d i:%-3s p:%-3d r:%-2d g:%-2d s:%-2d "
287             "p:{%.1f, %.1f, %.1f}",
288             flagIndex, flag.type->flagAbbv.c_str(), player,
289             required ? 1 : 0, grabs, flag.status,
290             flag.position[0], flag.position[1], flag.position[2]);
291 }
292 
exist()293 bool FlagInfo::exist()
294 {
295     return flag.status != FlagNoExist;
296 }
297 
get(int index)298 FlagInfo *FlagInfo::get(int index)
299 {
300     if (index < 0)
301         return NULL;
302     if (index >= numFlags)
303         return NULL;
304     return &flagList[index];
305 }
306 
307 // Local Variables: ***
308 // mode: C++ ***
309 // tab-width: 4 ***
310 // c-basic-offset: 4 ***
311 // indent-tabs-mode: nil ***
312 // End: ***
313 // ex: shiftwidth=4 tabstop=4
314