1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Complete definition of the ControlZone behavioral entity
//
// $NoKeywords: $
//=============================================================================//
#include "tf_shareddefs.h"
#include "cbase.h"
#include "EntityOutput.h"
#include "tf_player.h"
#include "controlzone.h"
#include "team.h"
//-----------------------------------------------------------------------------
// Purpose: Since the control zone is a data only class, force it to always be sent ( shouldn't change often so )
// bandwidth usage should be small.
// Input : **ppSendTable -
// *recipient -
// *pvs -
// clientArea -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
int CControlZone::UpdateTransmitState()
{
if ( IsEffectActive( EF_NODRAW ) )
{
return SetTransmitState( FL_EDICT_DONTSEND );
}
else
{
return SetTransmitState( FL_EDICT_ALWAYS );
}
}
IMPLEMENT_SERVERCLASS_ST(CControlZone, DT_ControlZone)
SendPropInt( SENDINFO(m_nZoneNumber), 8, SPROP_UNSIGNED ),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( trigger_controlzone, CControlZone);
BEGIN_DATADESC( CControlZone )
// outputs
DEFINE_OUTPUT( m_ControllingTeam, "ControllingTeam" ),
// inputs
DEFINE_INPUTFUNC( FIELD_VOID, "SetTeam", InputSetTeam ),
DEFINE_INPUTFUNC( FIELD_VOID, "LockTeam", InputLockControllingTeam ),
// keys
DEFINE_KEYFIELD_NOT_SAVED( m_iLockAfterChange, FIELD_INTEGER, "LockAfterChange" ),
DEFINE_KEYFIELD_NOT_SAVED( m_flTimeTillCaptured, FIELD_FLOAT, "UncontestedTime" ),
DEFINE_KEYFIELD_NOT_SAVED( m_flTimeTillContested, FIELD_FLOAT, "ContestedTime" ),
DEFINE_KEYFIELD_NOT_SAVED( m_nZoneNumber, FIELD_INTEGER, "ZoneNumber" ),
END_DATADESC()
// Control Zone Ent Flags
#define CZF_DONT_USE_TOUCHES 1
//-----------------------------------------------------------------------------
// Purpose: Initializes the control zone
// Records who was the original controlling team (for control locking)
//-----------------------------------------------------------------------------
void CControlZone::Spawn( void )
{
// set the starting controlling team
m_ControllingTeam.Set( GetTeamNumber(), this, this );
// remember who the original controlling team was (for control locking)
m_iDefendingTeam = GetTeamNumber();
// Solid
SetSolid( SOLID_BSP );
AddSolidFlags( FSOLID_TRIGGER );
SetMoveType( MOVETYPE_NONE );
SetModel( STRING( GetModelName() ) ); // set size and link into world
// TF2 rules
m_flTimeTillContested = 10.0; // Go to contested 10 seconds after enemies enter the zone
m_flTimeTillCaptured = 5.0; // Go to captured state as soon as only one team holds the zone
if ( m_nZoneNumber == 0 )
{
Warning( "Warning, trigger_controlzone without Zone Number set\n" );
}
m_ZonePlayerList.Purge();
}
//-----------------------------------------------------------------------------
// Purpose: Records that a player has entered the zone, and updates it's state
// according, maybe starting to change team.
// Input : *pOther - the entity that left the zone
//-----------------------------------------------------------------------------
void CControlZone::StartTouch( CBaseEntity *pOther )
{
CBaseTFPlayer *pl = ToBaseTFPlayer( pOther );
if ( !pl )
return;
CHandle< CBaseTFPlayer > hHandle;
hHandle = pl;
m_ZonePlayerList.AddToTail( hHandle );
ReevaluateControllingTeam();
// Set this player's current zone to this zone
pl->SetCurrentZone( this );
}
//-----------------------------------------------------------------------------
// Purpose: Records that a player has left the zone, and updates it's state
// according, maybe starting to change team.
// Input : *pOther - the entity that left the zone
//-----------------------------------------------------------------------------
void CControlZone::EndTouch( CBaseEntity *pOther )
{
CBaseTFPlayer *pl = ToBaseTFPlayer( pOther );
if ( !pl )
return;
CHandle< CBaseTFPlayer > hHandle;
hHandle = pl;
m_ZonePlayerList.FindAndRemove( hHandle );
ReevaluateControllingTeam();
// Unset this player's current zone if it's this one
if ( pl->GetCurrentZone() == this )
pl->SetCurrentZone( NULL );
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if it's time to change controllers
//-----------------------------------------------------------------------------
void CControlZone::ReevaluateControllingTeam( void )
{
// Count the number of players in each team
int i;
memset( m_iPlayersInZone, 0, sizeof( m_iPlayersInZone ) );
for ( i = 0; i < m_ZonePlayerList.Size(); i++ )
{
if ( m_ZonePlayerList[i] != NULL && (m_ZonePlayerList[i]->GetTeamNumber() > 0) )
{
m_iPlayersInZone[ m_ZonePlayerList[i]->GetTeamNumber() ] += 1;
}
}
// Abort immediately if we're not using touches to changes teams
if ( HasSpawnFlags( CZF_DONT_USE_TOUCHES ) )
return;
// if we're locked in place, no changes can occur to controlling team except through an explicit map ResetTeam
if ( m_iLocked )
return;
bool foundAnyTeam = false;
int teamFound = 0;
// check to see if any teams have no players
for ( i = 0; i < GetNumberOfTeams(); i++ )
{
if ( m_iPlayersInZone[i] )
{
if ( foundAnyTeam )
{
// we've already found a team, so it's being contested;
teamFound = ZONE_CONTESTED;
break;
}
foundAnyTeam = true;
teamFound = i;
}
}
// no one in the area!
if ( teamFound == 0 )
{
// just leave it as it is, let it continue to change team
// exception: if the zone state is contested, and there aren't any players in the zone,
// just return to the team who used to own the zone.
if ( GetTeamNumber() == ZONE_CONTESTED )
{
ChangeTeam(m_iDefendingTeam);
SetControllingTeam( this, m_iDefendingTeam );
}
return;
}
// if it's the same controlling team, don't worry about it
if ( teamFound == GetTeamNumber() )
{
// the right team is in control, don't even think of switching
m_iTryingToChangeToTeam = 0;
SetNextThink( TICK_NEVER_THINK );
return;
}
// Find out if the zone isn't owned by anyone at all (hasn't been touched since the map started, and it started un-owned)
bool bHasBeenOwned = true;
if ( m_iDefendingTeam == 0 && GetTeamNumber() == 0 )
bHasBeenOwned = false;
// if it's not contested, always go to contested mode
if ( GetTeamNumber() != ZONE_CONTESTED && teamFound != GetTeamNumber() )
{
// Unowned zones are captured immediately (no contesting stage)
if ( bHasBeenOwned )
teamFound = ZONE_CONTESTED;
}
// if it's the team we're trying to change to, don't worry about it
if ( teamFound == m_iTryingToChangeToTeam )
return;
// set up the time to change to the new team soon
m_iTryingToChangeToTeam = teamFound;
// changing from contested->uncontested and visa-versa have different delays
if ( m_iTryingToChangeToTeam != ZONE_CONTESTED )
{
if ( !bHasBeenOwned )
{
DevMsg( 1, "trigger_controlzone: (%s) changing team to %d NOW\n", GetDebugName(), m_iTryingToChangeToTeam );
SetNextThink( gpGlobals->curtime + 0.1f );
}
else
{
DevMsg( 1, "trigger_controlzone: (%s) changing team to %d in %.2f seconds\n", GetDebugName(), m_iTryingToChangeToTeam, m_flTimeTillCaptured );
SetNextThink( gpGlobals->curtime + m_flTimeTillCaptured );
}
}
else
{
DevMsg( 1, "trigger_controlzone: (%s) changing to contested in %f seconds\n", GetDebugName(), m_flTimeTillContested );
SetNextThink( gpGlobals->curtime + m_flTimeTillContested );
}
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if an uncontested territory is ready to change state
// to the new controlling team.
//-----------------------------------------------------------------------------
void CControlZone::Think( void )
{
if ( m_iTryingToChangeToTeam != 0 )
{
// held zone long enough
SetControllingTeam( this, m_iTryingToChangeToTeam );
// lock against further change if set
if ( m_iLockAfterChange )
{
LockControllingTeam();
}
// Re-evaluate controlling team if we were changing to Contested (enemy may have withdrawn)
if ( GetTeamNumber() == ZONE_CONTESTED )
{
ReevaluateControllingTeam();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: set it so the team can no longer change, until a set controlling team action occurs
//-----------------------------------------------------------------------------
void CControlZone::InputLockControllingTeam( inputdata_t &inputdata )
{
LockControllingTeam();
}
//-----------------------------------------------------------------------------
// Purpose: Input handler that sets the controlling team to the activator's team.
//-----------------------------------------------------------------------------
void CControlZone::InputSetTeam( inputdata_t &inputdata )
{
// Abort if it's already the defending team
if ( inputdata.pActivator->GetTeamNumber() == GetTeamNumber() )
return;
// set the new team
ChangeTeam(inputdata.pActivator->GetTeamNumber());
SetControllingTeam( inputdata.pActivator, GetTeamNumber() );
}
//-----------------------------------------------------------------------------
// Purpose: Changes the team controlling this zone
// Input : newTeam - the new team to change to
//-----------------------------------------------------------------------------
void CControlZone::SetControllingTeam( CBaseEntity *pActivator, int newTeam )
{
DevMsg( 1, "trigger_controlzone: (%s) changing team to: %d\n", GetDebugName(), newTeam );
// remember this team as the defenders of the zone
m_iDefendingTeam = GetTeamNumber();
// reset state, firing the output
ChangeTeam(newTeam);
m_ControllingTeam.Set( GetTeamNumber(), pActivator, this );
m_iLocked = FALSE;
m_iTryingToChangeToTeam = 0;
SetNextThink( TICK_NEVER_THINK );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CControlZone::LockControllingTeam( void )
{
// never lock a zone in contested mode
if ( GetTeamNumber() == ZONE_CONTESTED )
return;
// zones never lock to the defenders
if ( GetTeamNumber() == m_iDefendingTeam )
return;
m_iLocked = TRUE;
}
|