http://jcmick.free.fr/
Beaucoup de newbies se demande comment faire réaparaitre les objets au début d'un nouveau round, et il emploie le plus souvent des techniques rendant le code très lourd... (en tout cas c'est comme ça que j'ai fait au début). Je vais donc vous filez le code de mon mod Over Ground (<= De la PUB ça fait jamais de mal) qui sert à Respawner les entités, chez moi il fonctionne, devrais pas y avoir de problèmes chez vous.
Pour commencer on va définir deux trois trucs dans la classe CBaseEntity, ouvrez cbase.h et à la fin de la déclaration de classe, juste avant le " };", ajoutez :/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/virtual void EntSave ( void);
virtual void EntRestore ( void);
entvars_t pev_old;
KeyValueSave *pkvd_old;
bool firstround;
Maintenant on va s'en sevir, pour commencer, ouvrez cbase.cpp et tout à la fin ajoutez:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/void CBaseEntity::EntSave( void )
{
// On sauvegarde les données
pev_old = *pev;
firstround = true;
}
void CBaseEntity::EntRestore( void )
{
*pev = pev_old;
// Les KeyValues
KeyValueData kvd;
KeyValueSave *ValueKey;
ValueKey = pkvd_old;
while ( ValueKey )
{
kvd.fHandled = false;
kvd.szClassName = ValueKey->Classname;
kvd.szKeyName = ValueKey->Keyname;
kvd.szValue = ValueKey->Value;
KeyValue( &kvd );
ValueKey = ValueKey->next;
}
// On reset le think...
SetThink( NULL );
SetUse( NULL );
SetTouch( NULL );
SetBlocked( NULL );
pev->nextthink = -1;
firstround = false;
}
Toujours dans cbase.cpp, cherchez la fonction DispatchSpawn(), on va y ajouter un bout de code, juste avant l'appel de la fonction Spawn(), ajoutez le code suivant:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/int DispatchSpawn( edict_t *pent )
{
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pent);
if (pEntity)
{
// Initialize these or entities who don't link to the world won't have anything in here
pEntity->pev->absmin = pEntity->pev->origin - Vector(1,1,1);
pEntity->pev->absmax = pEntity->pev->origin + Vector(1,1,1);
// On sauvegarde les données concernant l'entité
pEntity->EntSave();
pEntity->Spawn();
// Try to get the pointer again, in case the spawn function deleted the entity.
// UNDONE: Spawn() should really return a code to ask that the entity be deleted, but
// that would touch too much code for me to do that right now.
pEntity = (CBaseEntity *)GET_PRIVATE(pent);
if ( pEntity )
{
if ( g_pGameRules && !g_pGameRules->IsAllowedToSpawn( pEntity ) )
return -1; // return that this entity should be deleted
if ( pEntity->pev->flags & FL_KILLME )
return -1;
}
}
return 0;
}
Bien, maintenant cherchez la fonction DispatchKeyValue(), et ajoutez y le code suivant:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/void DispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd )
{
KeyValueSave *KValue;
if ( !pkvd || !pentKeyvalue )
return;
EntvarsKeyvalue( VARS(pentKeyvalue), pkvd );
// If the key was an entity variable, or there's no class set yet, don't look for the object, it may
// not exist yet.
if ( pkvd->fHandled || pkvd->szClassName == NULL )
return;
// Get the actualy entity object
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pentKeyvalue);
if ( !pEntity )
return;
KValue = new KeyValueSave;
strncpy( KValue->Classname, pkvd->szClassName, 255 );
strncpy( KValue->Keyname, pkvd->szKeyName, 255 );
strncpy( KValue->Value, pkvd->szValue, 255 );
KValue->next = pEntity->pkvd_old;
pEntity->pkvd_old = KValue;
pEntity->KeyValue( pkvd );
}
On a déja fait pas mal de trucs mais c'est loin d'être fini (enfin j'exagère c'est bientot fini quand même), maintenant on va faire un tour de client.cpp, au début du fichier, après les #includes, ajoutez les lignes de code suivantes:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/edict_t *pEdictList_save;
int edictCount_save;
int clientMax_save;
Hum, mais à quoi ça va bien pouvoir nous servir tout ça, direction la fonction ServerActivate(), remplacez la fonction par celle-ci:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
{
int i;
CBaseEntity *pClass;
// Every call to ServerActivate should be matched by a call to ServerDeactivate
g_serveractive = 1;
// On commence par sauvegarder les valeurs pour les réutiliser
// lors du chargement d'un nouveau round
pEdictList_save = pEdictList;
edictCount_save = edictCount;
clientMax_save = clientMax;
// Clients have not been initialized yet
for ( i = 0; i < edictCount; i++ )
{
if ( pEdictList[i].free )
continue;
// Clients aren't necessarily initialized until ClientPutInServer()
if ( i < clientMax || !pEdictList[i].pvPrivateData )
continue;
pClass = CBaseEntity::Instance( &pEdictList[i] );
// Activate this entity if it's got a class & isn't dormant
if ( pClass && !(pClass->pev->flags & FL_DORMANT) )
pClass->Activate();
else
ALERT( at_console, "Can't instance %sn", STRING(pEdictList[i].v.classname) );
}
// Link user messages here to make sure first client can get them...
LinkUserMessages();
}
Bien maintenant juste en dessous on va ajoutez deux nouvelles fonctions:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/bool IsMapEntity( CBaseEntity *Entity )
{
CBaseEntity *pClass;
for ( int i = 0; i < edictCount_save; i++ )
{
if ( pEdictList_save[i].free )
continue;
if ( i < clientMax_save || !pEdictList_save[i].pvPrivateData )
continue;
pClass = CBaseEntity::Instance( &pEdictList_save[i] );
if ( pClass && pClass == Entity )
return true;
}
return false;
}
void ServerRestore( void )
{
CBaseEntity *pEntity;
// On supprime tout ou on restaure si l'entité fait parties
// de celles placées volontairements par le mappeur
for ( int i = 1; i < gpGlobals->maxEntities; i++ )
{
// on récupère l'entité
pEntity = UTIL_EntityByIndex( i );
if ( !pEntity )
continue;
// On ne fait rien si c'est un joueur, un monstre ou si l'entité est désactivée
if ( pEntity->pev->flags & ( FL_DORMANT | FL_CLIENT | FL_MONSTER ) )
continue;
// Sinon on la supprime ou on la restaure si elle existait en début de partie
if ( IsMapEntity( pEntity ) )
{
pEntity->EntRestore();
pEntity->Spawn();
pEntity->Activate();
}
else
{
// on arrêtte les sons des objets susceptibles d'en jouer un
if ( FClassnameIs( pEntity->pev, "rpg_rocket" ) )
STOP_SOUND( pEntity->edict(), CHAN_VOICE, "weapons/rpg_rocket.wav" );
UTIL_Remove( pEntity );
}
}
}
J'utilise une nouvelle fonction, on va la définir, dans util.cpp, juste en dessous de la définition de UTIL_PlayerByindex, définissez cette fonction:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/CBaseEntity *UTIL_EntityByIndex( int Index )
{
CBaseEntity *pEnt = NULL;
if ( Index > 0 && Index <= gpGlobals->maxEntities )
{
edict_t *pEdict = INDEXENT( Index );
if ( UTIL_IsValidEntity( pEdict ) )
pEnt = (CBaseEntity *)GET_PRIVATE( pEdict );
}
return pEnt;
}
Puis dans util.h, ajoutez:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/extern CBaseEntity *UTIL_EntityByIndex( int Index );
Maintenant allez faire un tour dans eiface.h, et juste en dessous de la définition de la structure KeyValueData, ajoutez ceci:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/// Pour sauvegarder les KeyValues
typedef struct KeyValueSave_s
{
char Classname[255];
char Keyname[255];
char Value[255];
KeyValueSave_s *next;
} KeyValueSave;
Ouah, la plus grosse partie est faites, maintenant il faut s'occuper de deux trois cas particuliers, vous aurez sans doute remarquez au passage puisque vous ne faites pas un simple copier-coller que je stoppe le son de la roquette, en effet, celle-ci joue un son alors si on arrête pas le son, bah vous aurez plus de roquettes mais un son en boucle, c'est assez cool. Enfin vous devez ajouter à cet endroit le code nécessaire pour stopper les sons des diverses entités spécifiques à votre mod susceptibles d'en jouer un.
Bon, on va s'occupper de quelques entités maintenant, pour commencer le ambient_generic, allez dans le fichier sound.cpp et cherchez la fonction Spawn() de l'ambient_generic, bon je vous dit tout de suite je sais plus si y'avait une fonction Precache() ou non puisque je l'ai bougé, alros faites de même et remplacer la fonction Spawn() par:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/void CAmbientGeneric :: Spawn( void )
{
/*
-1 : "Default"
0 : "Everywhere"
200 : "Small Radius"
125 : "Medium Radius"
80 : "Large Radius"
*/
m_fLooping = false;
char* szSoundFile = (char*) STRING(pev->message);
if ( FStringNull( pev->message ) || strlen( szSoundFile ) < 1 )
{
ALERT( at_error, "EMPTY AMBIENT AT: %f, %f, %fn", pev->origin.x, pev->origin.y, pev->origin.z );
pev->nextthink = gpGlobals->time + 0.1;
SetThink( SUB_Remove );
return;
}
if ( !FStringNull( pev->message ) && strlen( szSoundFile ) > 1 )
{
if (*szSoundFile != '!')
PRECACHE_SOUND(szSoundFile);
}
// On arrête le son
UTIL_EmitAmbientSound ( ENT(pev), pev->origin, szSoundFile, 0, 0, SND_STOP, 0);
if ( FBitSet ( pev->spawnflags, AMBIENT_SOUND_EVERYWHERE) )
{
m_flAttenuation = ATTN_NONE;
}
else if ( FBitSet ( pev->spawnflags, AMBIENT_SOUND_SMALLRADIUS) )
{
m_flAttenuation = ATTN_IDLE;
}
else if ( FBitSet ( pev->spawnflags, AMBIENT_SOUND_MEDIUMRADIUS) )
{
m_flAttenuation = ATTN_STATIC;
}
else if ( FBitSet ( pev->spawnflags, AMBIENT_SOUND_LARGERADIUS) )
{
m_flAttenuation = ATTN_NORM;
}
else
{// if the designer didn't set a sound attenuation, default to one.
m_flAttenuation = ATTN_STATIC;
}
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
// Set up think function for dynamic modification
// of ambient sound's pitch or volume. Don't
// start thinking yet.
SetThink(RampThink);
pev->nextthink = -1;
// allow on/off switching via 'use' function.
SetUse ( ToggleUse );
m_fActive = FALSE;
if ( FBitSet ( pev->spawnflags, AMBIENT_SOUND_NOT_LOOPING ) )
m_fLooping = FALSE;
else
m_fLooping = TRUE;
// init all dynamic modulation parms
InitModulationParms();
if ( !FBitSet (pev->spawnflags, AMBIENT_SOUND_START_SILENT ) )
{
// start the sound ASAP
if (m_fLooping)
m_fActive = TRUE;
}
if ( m_fActive )
{
UTIL_EmitAmbientSound ( ENT(pev), pev->origin, szSoundFile,
(m_dpv.vol * 0.01), m_flAttenuation, firstround ? SND_SPAWNING : 0, m_dpv.pitch);
pev->nextthink = gpGlobals->time + 0.1;
}
}
J'ai juste fait deux trois modifs mais rien de bien méchant, vous pourrez vous attarder dessus si ça vous interesse, maintenant le func_rotating, direction le fichier bmodels.cpp et cherchez l'endroit où se trouve le func_rotating, vu que j'ai un peu touché à tout et que je sais plus comment sont les fontions d'origines, contentez vous de remplacez les fonctions Spawn() et Precache() par celles-ci:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/void CFuncRotating :: Spawn( )
{
// set final pitch. Must not be PITCH_NORM, since we
// plan on pitch shifting later.
m_pitch = PITCH_NORM - 1;
Precache( );
// maintain compatibility with previous maps
if (m_flVolume == 0.0)
m_flVolume = 1.0;
// if the designer didn't set a sound attenuation, default to one.
m_flAttenuation = ATTN_NORM;
if ( FBitSet ( pev->spawnflags, SF_BRUSH_ROTATE_SMALLRADIUS) )
{
m_flAttenuation = ATTN_IDLE;
}
else if ( FBitSet ( pev->spawnflags, SF_BRUSH_ROTATE_MEDIUMRADIUS) )
{
m_flAttenuation = ATTN_STATIC;
}
else if ( FBitSet ( pev->spawnflags, SF_BRUSH_ROTATE_LARGERADIUS) )
{
m_flAttenuation = ATTN_NORM;
}
// prevent divide by zero if level designer forgets friction!
if ( m_flFanFriction <= 0 )
{
m_flFanFriction = 1;
}
if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_Z_AXIS) )
pev->movedir = Vector(0,0,1);
else if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_X_AXIS) )
pev->movedir = Vector(1,0,0);
else
pev->movedir = Vector(0,1,0); // y-axis
// check for reverse rotation
if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_BACKWARDS) )
pev->movedir = pev->movedir * -1;
// some rotating objects like fake volumetric lights will not be solid.
if ( FBitSet(pev->spawnflags, SF_ROTATING_NOT_SOLID) )
{
pev->solid = SOLID_NOT;
pev->skin = CONTENTS_EMPTY;
pev->movetype = MOVETYPE_PUSH;
}
else
{
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
}
UTIL_SetOrigin(pev, pev->origin);
SET_MODEL( ENT(pev), STRING(pev->model) );
SetUse( RotatingUse );
// did level designer forget to assign speed?
if (pev->speed <= 0)
pev->speed = 0;
// instant-use brush?
if ( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_INSTANT) )
{
SetThink( SUB_CallUseToggle );
pev->nextthink = pev->ltime + 1.5; // leave a magic delay for client to start up
}
// can this brush inflict pain?
if ( FBitSet (pev->spawnflags, SF_BRUSH_HURT) )
{
SetTouch( HurtTouch );
}
}
void CFuncRotating :: Precache( void )
{
char* szSoundFile = (char*) STRING(pev->message);
// set up fan sounds
if (!FStringNull( pev->message ) && strlen( szSoundFile ) > 0)
{
// if a path is set for a wave, use it
PRECACHE_SOUND(szSoundFile);
pev->noiseRunning = ALLOC_STRING(szSoundFile);
}
else
{
// otherwise use preset sound
switch (m_sounds)
{
case 1:
PRECACHE_SOUND ("fans/fan1.wav");
pev->noiseRunning = ALLOC_STRING("fans/fan1.wav");
break;
case 2:
PRECACHE_SOUND ("fans/fan2.wav");
pev->noiseRunning = ALLOC_STRING("fans/fan2.wav");
break;
case 3:
PRECACHE_SOUND ("fans/fan3.wav");
pev->noiseRunning = ALLOC_STRING("fans/fan3.wav");
break;
case 4:
PRECACHE_SOUND ("fans/fan4.wav");
pev->noiseRunning = ALLOC_STRING("fans/fan4.wav");
break;
case 5:
PRECACHE_SOUND ("fans/fan5.wav");
pev->noiseRunning = ALLOC_STRING("fans/fan5.wav");
break;
case 0:
default:
if (!FStringNull( pev->message ) && strlen( szSoundFile ) > 0)
{
PRECACHE_SOUND(szSoundFile);
pev->noiseRunning = ALLOC_STRING(szSoundFile);
break;
}else
{
pev->noiseRunning = ALLOC_STRING("common/null.wav");
break;
}
}
}
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning /* Stop */),
0, 0, SND_STOP, m_pitch);
}
Stop, ce n'est pas tout, vous aurez aussi un problème avec le multi_manager, => triggers.cpp, ajoutez à la classe CMultiManager la fonction suivante:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/void CMultiManager :: EntRestore( void )
{
// Normalement pas de chances pour que ce soit un clone puisqu'il n'était
// pas présent en début de partie, mais bon
if ( IsClone() )
{
UTIL_Remove( this );
return;
}
m_cTargets = 0;
for ( int i = 0; i < MAX_MULTI_TARGETS; i++ )
{
m_iTargetName [ i ] = NULL;
m_flTargetDelay [ i ] = NULL;
}
CBaseEntity::EntRestore();
}
Je vous laisse l'ajouté dans votre définition de classe c'est pas bien dur.
Voila maintenant dans votre fonction qui vous sert à relancer un nouveau round, ajoutez le bout de code suivant:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/ServerRestore();
Sans oublier de mettre cette ligne au début du fichier dans lequel se trouve votre fonction après les #includes sinon le compilateur va vous dire qu'il ne connait pas la fonction:/****
*
* Over Ground (2002-2003) - Mod pour Half-Life - SDK
*
* Code source de Tigerknee (tigerknee@voila.fr)
* Plus d'infos sur le site internet du mod :
* http://og.portailmods.com
*
*
****/void ServerRestore( void );
Voila, c'est tout pour ce tutorial, j'éspère que j'ai rien oublié au code, mais ça devrait fonctionner, faites quand même attention à bien vérifier si les entités de votre mod n'ont pas besoin d'avoir une fonction EntRestore() particulière ou un son à stopper, mais bon je vous laisse faire.