📄 soccerobjects.cpp
字号:
/****************************************************************************
;
; MODULE: SOCCEROBJECTS (.CPP)
;
; PURPOSE: Soccer objects for zombie-head soccer
;
; HISTORY: 1/26/99 [bp] This file was created
;
; COMMENT: Copyright (c) 1999, Monolith Productions Inc.
;
****************************************************************************/
#include "SoccerObjects.h"
#include "ObjectUtilities.h"
#include <mbstring.h>
#include "SoundTypes.h"
#include "ObjectUtilities.h"
#include "LMessage.h"
#include "ClientGibFx.h"
#include "ClientServerShared.h"
#include "Trigger.h"
#include "BloodServerShell.h"
#include "NetDefs.h"
#define TIMEBETWEENBOUNCESOUNDS 0.1f
#define MAXBOUNCEFACTOR 0.5f
#define MINBOUNCEFACTOR 0.2f
#define MAXBALLVEL 1000.0f
#define MINBALLVEL 20.0f
#define BALLDRAG 0.8f
#define MINFORCESOUND 100.0f
#define BALLRESPAWNTIME 60.0f
//////////////////// SOCCER GOAL ///////////////////////////
DList SoccerGoal::m_GoalList =
{
0,
{ DNULL, DNULL, DNULL }
};
BEGIN_CLASS(SoccerGoal)
ADD_LONGINTPROP( TeamID, 1 )
ADD_BOOLPROP( Directional, DFALSE )
ADD_ROTATIONPROP( GoalDirection )
ADD_BOOLPROP( BoxPhysics, DTRUE )
ADD_STRINGPROP( ScoreSound, "" )
ADD_REALPROP( ScoreSoundRadius, 1000.0f )
ADD_STRINGPROP( ScoreTarget, "" )
ADD_STRINGPROP( ScoreMsg, "" )
END_CLASS_DEFAULT_FLAGS(SoccerGoal, B2BaseClass, NULL, NULL, CF_ALWAYSLOAD)
// ----------------------------------------------------------------------- //
//
// ROUTINE: SoccerGoal::SoccerGoal
//
// PURPOSE: Constructor
//
// ----------------------------------------------------------------------- //
SoccerGoal::SoccerGoal() : B2BaseClass( OT_WORLDMODEL )
{
m_nTeamID = TEAM_1;
m_bDirectional = DFALSE;
VEC_SET( m_vGoalDirection, 0.0f, 0.0f, 1.0f );
m_hstrScoreSound = DNULL;
m_fRadius = 1000.0f;
m_hstrScoreTarget = DNULL;
m_hstrScoreMsg = DNULL;
m_bBoxPhysics = DTRUE;
m_hSoccerBall = DNULL;
m_nNumBallsToMake = 0;
m_bWaitOneFrame = DFALSE;
if( m_GoalList.m_nElements == 0 )
dl_InitList( &m_GoalList );
dl_AddTail( &m_GoalList, &m_Link, this );
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: SoccerGoal::~SoccerGoal
//
// PURPOSE: Destructor
//
// ----------------------------------------------------------------------- //
SoccerGoal::~SoccerGoal()
{
dl_RemoveAt( &m_GoalList, &m_Link );
if( m_hstrScoreSound )
g_pServerDE->FreeString( m_hstrScoreSound );
if( m_hstrScoreTarget )
g_pServerDE->FreeString( m_hstrScoreTarget );
if( m_hstrScoreMsg )
g_pServerDE->FreeString( m_hstrScoreMsg );
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: SoccerGoal::EngineMessageFn
//
// PURPOSE: Handler for engine messages
//
// ----------------------------------------------------------------------- //
DDWORD SoccerGoal::EngineMessageFn( DDWORD messageID, void *pData, DFLOAT fData )
{
DDWORD dwRet;
// Handle the engine messages we're interested in...
switch( messageID )
{
case MID_PRECREATE:
{
dwRet = B2BaseClass::EngineMessageFn( messageID, pData, fData );
if (fData == PRECREATE_WORLDFILE || fData == PRECREATE_STRINGPROP)
{
ReadProp((ObjectCreateStruct*)pData);
}
PostPropRead((ObjectCreateStruct*)pData);
return dwRet;
break;
}
case MID_INITIALUPDATE:
{
OnInitialUpdate(pData, fData);
break;
}
case MID_UPDATE:
{
Update( );
break;
}
case MID_TOUCHNOTIFY:
{
OnTouchNotify((HOBJECT)pData);
break;
}
}
return B2BaseClass::EngineMessageFn(messageID, pData, fData);
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: SoccerGoal::ReadProp
//
// PURPOSE: Reads SoccerGoal properties
//
// ----------------------------------------------------------------------- //
void SoccerGoal::ReadProp(ObjectCreateStruct *pStruct)
{
GenericProp genProp;
DVector vUp, vRight;
if( g_pServerDE->GetPropGeneric( "TeamID", &genProp ) == DE_OK )
{
m_nTeamID = ( int )genProp.m_Long;
}
if( g_pServerDE->GetPropGeneric( "Directional", &genProp ) == DE_OK )
{
m_bDirectional = genProp.m_Bool;
}
if( g_pServerDE->GetPropGeneric( "GoalDirection", &genProp ) == DE_OK )
{
g_pServerDE->GetRotationVectors( &genProp.m_Rotation, &vUp, &vRight, &m_vGoalDirection );
}
if( g_pServerDE->GetPropGeneric( "BoxPhysics", &genProp ) == DE_OK )
{
m_bBoxPhysics = genProp.m_Bool;
}
if( m_hstrScoreSound )
{
g_pServerDE->FreeString( m_hstrScoreSound );
m_hstrScoreSound = DNULL;
}
if( g_pServerDE->GetPropGeneric( "ScoreSound", &genProp ) == DE_OK )
{
if( genProp.m_String[0] )
m_hstrScoreSound = g_pServerDE->CreateString( genProp.m_String );
}
if( g_pServerDE->GetPropGeneric( "ScoreSoundRadius", &genProp ) == DE_OK )
{
m_fRadius = genProp.m_Float;
}
if( m_hstrScoreTarget )
{
g_pServerDE->FreeString( m_hstrScoreTarget );
m_hstrScoreTarget = DNULL;
}
if( g_pServerDE->GetPropGeneric( "ScoreTarget", &genProp ) == DE_OK && genProp.m_String[0] )
{
if( genProp.m_String[0] )
m_hstrScoreTarget = g_pServerDE->CreateString( genProp.m_String );
}
if( m_hstrScoreMsg )
{
g_pServerDE->FreeString( m_hstrScoreMsg );
m_hstrScoreMsg = DNULL;
}
if( g_pServerDE->GetPropGeneric( "ScoreMsg", &genProp ) == DE_OK )
{
m_hstrScoreMsg = g_pServerDE->CreateString( genProp.m_String );
}
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: SoccerGoal::PostPropRead()
//
// PURPOSE: Updates the properties now that they have been read
//
// ----------------------------------------------------------------------- //
void SoccerGoal::PostPropRead(ObjectCreateStruct* pStruct)
{
// Sanity checks...
if( !pStruct )
return;
_mbscpy(( unsigned char * )pStruct->m_Filename, ( const unsigned char * )pStruct->m_Name );
// Set the flags we want...
pStruct->m_Flags = FLAG_TOUCH_NOTIFY;
if( m_bBoxPhysics )
pStruct->m_Flags |= FLAG_BOXPHYSICS;
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: SoccerGoal::OnInitialUpdate()
//
// PURPOSE: Handles the MID_INITIALUPDATE engine message
//
// ----------------------------------------------------------------------- //
void SoccerGoal::OnInitialUpdate(void* pData, DFLOAT fData)
{
m_hSoccerBall = g_pServerDE->GetClass( "SoccerBall" );
g_pServerDE->SetNextUpdate( m_hObject, 0.0f );
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: SoccerGoal::ObjectMessageFn
//
// PURPOSE: Handle messages from objects
//
// ----------------------------------------------------------------------- //
/*
DDWORD SoccerGoal::ObjectMessageFn(HOBJECT hSender, DDWORD messageID, HMESSAGEREAD hRead)
{
switch (messageID)
{
default: break;
}
return B2BaseClass::ObjectMessageFn(hSender, messageID, hRead);
}
*/
// ----------------------------------------------------------------------- //
//
// ROUTINE: SoccerGoal::OnTouchNotify()
//
// PURPOSE: Handles the MID_TOUCHNOTIFY engine messsage
//
// ----------------------------------------------------------------------- //
void SoccerGoal::OnTouchNotify( HOBJECT hObj )
{
CollisionInfo colInfo;
DVector vBallVel;
DBOOL bGoal;
SoccerBall *pSoccerBall;
DVector vPos, vDir, vDims;
HOBJECT hPlayer;
LMessage *pMsg;
if( g_pServerDE->IsKindOf( g_pServerDE->GetObjectClass(hObj), m_hSoccerBall ))
{
pSoccerBall = ( SoccerBall * )g_pServerDE->HandleToObject( hObj );
if( !pSoccerBall )
return;
// Already recorded this goal. Ball should delete itself soon.
if( pSoccerBall->IsMadeGoal( ))
return;
// Ball has to enter from correct side for directional goals
if( m_bDirectional )
{
// Assume no goal
bGoal = DFALSE;
g_pServerDE->GetVelocity( hObj, &vBallVel );
// Check if going in the right direction
if( VEC_DOT( vBallVel, m_vGoalDirection ) > 0.0f )
{
bGoal = DTRUE;
}
}
else
bGoal = DTRUE;
if( bGoal )
{
if(( hPlayer = pSoccerBall->GetLastPlayerTouched( )) == DNULL )
return;
// Send message to player and ball
if( g_pServerDE->Common( )->CreateMessage( pMsg ) != LT_OK )
return;
pMsg->WriteByte( m_nTeamID );
g_pServerDE->SendToObject( *pMsg, MID_GOAL, m_hObject, hPlayer, 0 );
g_pServerDE->SendToObject( *pMsg, MID_GOAL, m_hObject, hObj, 0 );
pMsg->Release();
// Create special effects
g_pServerDE->GetObjectPos( hObj, &vPos );
g_pServerDE->GetVelocity( hObj, &vDir );
VEC_MULSCALAR( vDir, vDir, -1.0f );
VEC_SET( vDims, 25.0f, 25.0f, 25.0f );
SetupClientGibFX( &vPos, &vDir, &vDims, ( SURFTYPE_FLESH/10 ) | SIZE_SMALL | TRAIL_BLOOD, 1.0f, 5 );
// Play the sound
if( m_hstrScoreSound )
{
g_pServerDE->GetObjectPos( m_hObject, &vPos );
PlaySoundFromPos( &vPos, g_pServerDE->GetStringData( m_hstrScoreSound ), m_fRadius, SOUNDPRIORITY_MISC_MEDIUM );
}
SendTrigger( );
}
}
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: SoccerGoal::Update()
//
// PURPOSE: Updates the goal
//
// ----------------------------------------------------------------------- //
void SoccerGoal::Update( )
{
DLink *pCur;
int nGoalIndex;
SoccerGoal *pGoal;
// This is needed just in case the world is shutting down. If it is
// then we don't want to make a new ball. If we wait one frame,
// then the world will have completely gone away, and we won't make
// a new ball.
if( m_bWaitOneFrame )
{
m_bWaitOneFrame = DFALSE;
g_pServerDE->SetNextUpdate( m_hObject, 0.001f );
return;
}
while( m_nNumBallsToMake )
{
// Choose a random goal to send trigger
if( !m_GoalList.m_nElements )
return;
nGoalIndex = ( int )( g_pServerDE->IntRandom( 0, m_GoalList.m_nElements - 1 ));
pCur = m_GoalList.m_Head.m_pNext;
while( pCur != &m_GoalList.m_Head && nGoalIndex > 0 )
{
pCur = pCur->m_pNext;
nGoalIndex--;
}
pGoal = ( SoccerGoal * )pCur->m_pData;
if( pGoal )
pGoal->SendTrigger( );
m_nNumBallsToMake--;
}
g_pServerDE->SetNextUpdate( m_hObject, 0.0f );
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: SoccerGoal::SendTrigger()
//
// PURPOSE: Sends trigger message
//
// ----------------------------------------------------------------------- //
void SoccerGoal::SendTrigger( )
{
// Send the message
if( m_hstrScoreTarget && m_hstrScoreMsg )
{
SendTriggerMsgToObjects( this, m_hstrScoreTarget, m_hstrScoreMsg );
}
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: SoccerGoal::SpawnBall()
//
// PURPOSE: Goal must spawn a new ball, cuz something bad happened
// to the other ball
//
// ----------------------------------------------------------------------- //
void SoccerGoal::SpawnBall( )
{
m_nNumBallsToMake++;
g_pServerDE->SetNextUpdate( m_hObject, 0.001f );
m_bWaitOneFrame = DTRUE;
}
//////////////////// SOCCER BALL ///////////////////////////
BEGIN_CLASS( SoccerBall )
END_CLASS_DEFAULT( SoccerBall, B2BaseClass, NULL, NULL )
// ----------------------------------------------------------------------- //
//
// ROUTINE: SoccerBall::SoccerBall
//
// PURPOSE: Constructor
//
// ----------------------------------------------------------------------- //
SoccerBall::SoccerBall() : B2BaseClass( OT_MODEL )
{
m_bOnGround = DFALSE;
m_fLastTimeOnGround = 0.0f;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -