/*** * * NEW file for the Mod "Spirit of Half-Life", by Laurie R. Cheers. (LRC) * Created 19/11/00 * ***/ /* ===== alias.cpp ======================================================== Alias entities, replace the less powerful and (IMHO) less intuitive trigger_changetarget entity. */ #include "extdll.h" #include "util.h" #include "cbase.h" TYPEDESCRIPTION CBaseAlias::m_SaveData[] = { DEFINE_FIELD( CBaseAlias, m_pNextAlias, FIELD_CLASSPTR ), }; IMPLEMENT_SAVERESTORE( CBaseAlias, CPointEntity ) /********************* * Worldcraft entity: info_alias * * targetname- alias name * target- alias destination while ON * netname- alias destination while OFF **********************/ #define SF_ALIAS_OFF 1 #define SF_ALIAS_DEBUG 2 class CInfoAlias : public CBaseAlias { public: void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value); void Spawn( void ); STATE GetState() { return (pev->spawnflags & SF_ALIAS_OFF)?STATE_OFF:STATE_ON; } CBaseEntity *FollowAlias( CBaseEntity *pFrom ); void ChangeValue( int iszValue ); void FlushChanges( void ); }; LINK_ENTITY_TO_CLASS( info_alias, CInfoAlias ) void CInfoAlias::Spawn( void ) { if (pev->spawnflags & SF_ALIAS_OFF) pev->message = pev->netname; else pev->message = pev->target; } void CInfoAlias::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { if (pev->spawnflags & SF_ALIAS_OFF) { if (pev->spawnflags & SF_ALIAS_DEBUG) ALERT(at_console,"DEBUG: info_alias %s turns on\n",STRING(pev->targetname)); pev->spawnflags &= ~SF_ALIAS_OFF; pev->noise = pev->target; } else { if (pev->spawnflags & SF_ALIAS_DEBUG) ALERT(at_console,"DEBUG: info_alias %s turns off\n",STRING(pev->targetname)); pev->spawnflags |= SF_ALIAS_OFF; pev->noise = pev->netname; } UTIL_AddToAliasList( this ); } CBaseEntity *CInfoAlias::FollowAlias( CBaseEntity *pFrom ) { return UTIL_FindEntityByTargetname( pFrom, STRING(pev->message) ); } void CInfoAlias::ChangeValue( int iszValue ) { pev->noise = iszValue; UTIL_AddToAliasList( this ); } void CInfoAlias::FlushChanges( void ) { pev->message = pev->noise; if (pev->spawnflags & SF_ALIAS_DEBUG) ALERT(at_console,"DEBUG: info_alias %s now refers to \"%s\"\n", STRING(pev->targetname), STRING(pev->message)); } /********************* * Worldcraft entity: info_group * * targetname- name * target- alias entity to affect * other values are handled in a multi_manager-like way. **********************/ // definition in cbase.h #define SF_GROUP_DEBUG 2 LINK_ENTITY_TO_CLASS( info_group, CInfoGroup ); TYPEDESCRIPTION CInfoGroup::m_SaveData[] = { DEFINE_FIELD( CInfoGroup, m_cMembers, FIELD_INTEGER ), DEFINE_ARRAY( CInfoGroup, m_iszMemberName, FIELD_STRING, MAX_MULTI_TARGETS ), DEFINE_ARRAY( CInfoGroup, m_iszMemberValue, FIELD_STRING, MAX_MULTI_TARGETS ), DEFINE_FIELD( CInfoGroup, m_iszDefaultMember, FIELD_STRING ), }; IMPLEMENT_SAVERESTORE(CInfoGroup,CBaseEntity); void CInfoGroup :: KeyValue( KeyValueData *pkvd ) { if (FStrEq(pkvd->szKeyName, "defaultmember")) { m_iszDefaultMember = ALLOC_STRING(pkvd->szValue); pkvd->fHandled = TRUE; } // this assumes that additional fields are targetnames and their values are delay values. else if ( m_cMembers < MAX_MULTI_TARGETS ) { char tmp[128]; UTIL_StripToken( pkvd->szKeyName, tmp ); m_iszMemberName [ m_cMembers ] = ALLOC_STRING( tmp ); m_iszMemberValue [ m_cMembers ] = ALLOC_STRING (pkvd->szValue); m_cMembers++; pkvd->fHandled = TRUE; } else { ALERT(at_error,"Too many members for info_group %s (limit is %d)\n",STRING(pev->targetname),MAX_MULTI_TARGETS); } } void CInfoGroup::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { CBaseEntity *pTarget = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ) ); if (pTarget && pTarget->IsAlias()) { if (pev->spawnflags & SF_GROUP_DEBUG) ALERT(at_console, "DEBUG: info_group %s changes the contents of %s \"%s\"\n",STRING(pev->targetname), STRING(pTarget->pev->classname), STRING(pTarget->pev->targetname)); ((CBaseAlias*)pTarget)->ChangeValue(this); } else if (pev->target) { ALERT(at_console, "info_group \"%s\": alias \"%s\" was not found or not an alias!", STRING(pev->targetname), STRING(pev->target)); } } int CInfoGroup::GetMember( const char* szMemberName ) { if (!szMemberName) { ALERT(at_console,"info_group: GetMember called with null szMemberName!?\n"); return NULL; } for (int i = 0; i < m_cMembers; i++) { if (FStrEq(szMemberName, STRING(m_iszMemberName[i]))) { // ALERT(at_console,"getMember: found member\n"); return m_iszMemberValue[i]; } } if (m_iszDefaultMember) { static char szBuffer[128]; strcpy(szBuffer, STRING(m_iszDefaultMember)); strcat(szBuffer, szMemberName); return MAKE_STRING(szBuffer); // this is a messy way to do it... but currently, only one // GetMember gets performed at a time, so it works. } ALERT(at_console,"info_group \"%s\" has no member called \"%s\".\n",STRING(pev->targetname),szMemberName); // ALERT(at_console,"getMember: fail\n"); return NULL; } /********************* * Worldcraft entity: multi_alias * * targetname- name * other values are handled in a multi_manager-like way. **********************/ // definition in cbase.h LINK_ENTITY_TO_CLASS( multi_alias, CMultiAlias ); TYPEDESCRIPTION CMultiAlias::m_SaveData[] = { DEFINE_FIELD( CMultiAlias, m_cTargets, FIELD_INTEGER ), DEFINE_ARRAY( CMultiAlias, m_iszTargets, FIELD_STRING, MAX_MULTI_TARGETS ), DEFINE_FIELD( CMultiAlias, m_iTotalValue, FIELD_INTEGER ), DEFINE_ARRAY( CMultiAlias, m_iValues, FIELD_INTEGER, MAX_MULTI_TARGETS ), DEFINE_FIELD( CMultiAlias, m_iMode, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE(CMultiAlias,CBaseAlias); void CMultiAlias :: KeyValue( KeyValueData *pkvd ) { if (FStrEq(pkvd->szKeyName, "m_iMode")) { m_iMode = atoi( pkvd->szValue ); pkvd->fHandled = TRUE; } // this assumes that additional fields are targetnames and their values are probability values. else if ( m_cTargets < MAX_MULTI_TARGETS ) { char tmp[128]; UTIL_StripToken( pkvd->szKeyName, tmp ); m_iszTargets [ m_cTargets ] = ALLOC_STRING( tmp ); m_iValues [ m_cTargets ] = atoi( pkvd->szValue ); m_iTotalValue += m_iValues [ m_cTargets ]; m_cTargets++; pkvd->fHandled = TRUE; } else { ALERT(at_error,"Too many targets for multi_alias %s (limit is %d)\n",STRING(pev->targetname), MAX_MULTI_TARGETS); } } CBaseEntity *CMultiAlias::FollowAlias( CBaseEntity *pStartEntity ) { CBaseEntity* pBestEntity = NULL; // the entity we're currently planning to return. int iBestOffset = -1; // the offset of that entity. CBaseEntity* pTempEntity; int iTempOffset; int i = 0; if (m_iMode) { // During any given 'game moment', this code may be called more than once. It must use the // same random values each time (because otherwise it gets really messy). I'm using srand // to arrange this. srand( (int)(gpGlobals->time * 100) ); rand(); // throw away the first result - it's just the seed value if (m_iMode == 1) // 'choose one' mode { int iRandom = 1 + (rand() % m_iTotalValue); for (i = 0; i < m_cTargets; i++) { iRandom -= m_iValues[i]; if (iRandom <= 0) break; } } else // 'percent chance' mode { for (i = 0; i < m_cTargets; i++) { if (m_iValues[i] >= rand() % 100) break; } } } while (i < m_cTargets) { pTempEntity = UTIL_FindEntityByTargetname(pStartEntity,STRING(m_iszTargets[i])); if ( pTempEntity ) { // We've found an entity; only use it if its offset is lower than the offset we've currently got. iTempOffset = OFFSET(pTempEntity->pev); if (iBestOffset == -1 || iTempOffset < iBestOffset) { iBestOffset = iTempOffset; pBestEntity = pTempEntity; } } if (m_iMode == 1) break; // if it's in "pick one" mode, stop after the first. else if (m_iMode == 2) { i++; // if it's in "percent chance" mode, try to find another one to fire. while (i < m_cTargets) { if (m_iValues[i] > rand() % 100) break; i++; } } else i++; } return pBestEntity; } /********************* * Worldcraft entity: trigger_changealias * * target- alias entity to affect * netname- value to change the alias to **********************/ #define SF_CHANGEALIAS_RESOLVE 1 #define SF_CHANGEALIAS_DEBUG 2 class CTriggerChangeAlias : public CBaseEntity { public: void Spawn( void ); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); int ObjectCaps( void ) { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } }; LINK_ENTITY_TO_CLASS( trigger_changealias, CTriggerChangeAlias ) void CTriggerChangeAlias::Spawn( void ) { } void CTriggerChangeAlias::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { CBaseEntity *pTarget = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ), pActivator ); if (pTarget && pTarget->IsAlias()) { CBaseEntity *pValue = NULL; if (FStrEq(STRING(pev->netname), "*locus")) { pValue = pActivator; } else if (pev->spawnflags & SF_CHANGEALIAS_RESOLVE) { pValue = UTIL_FollowReference(NULL, STRING(pev->netname)); } if (pValue) ((CBaseAlias*)pTarget)->ChangeValue(pValue); else ((CBaseAlias*)pTarget)->ChangeValue(pev->netname); } else { ALERT(at_error, "trigger_changealias %s: alias \"%s\" was not found or not an alias!", STRING(pev->targetname), STRING(pev->target)); } }