openmohaa/code/fgame/viewthing.cpp

1005 lines
20 KiB
C++

/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// viewthing.cpp: Actor code for the Viewthing.
//
#include "animate.h"
#include "viewthing.h"
#include "game.h"
#include "level.h"
#include "scriptexception.h"
Event EV_ViewThing_Think
(
"viewthing_think",
EV_DEFAULT,
NULL,
NULL,
"Called every frame to process the view thing.",
EV_NORMAL
);
Event EV_ViewThing_ToggleAnimate
(
"viewanimate",
EV_CHEAT,
NULL,
NULL,
"Cycle through the animations modes of the current viewthing\n"
"No Animation\n"
"Animation with no motion\n"
"Animation with looping motion\n"
"Animation with motion\n",
EV_NORMAL
);
Event EV_ViewThing_SetModel
(
"viewmodel",
EV_CHEAT,
"s",
"viewthingModel",
"Set the model of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_NextFrame
(
"viewnext",
EV_CHEAT,
NULL,
NULL,
"Advance to the next frame of animation of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_PrevFrame
(
"viewprev",
EV_CHEAT,
NULL,
NULL,
"Advance to the previous frame of animation of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_NextAnim
(
"viewnextanim",
EV_CHEAT,
NULL,
NULL,
"Advance to the next animation of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_PrevAnim
(
"viewprevanim",
EV_CHEAT,
NULL,
NULL,
"Advance to the previous animation of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_ScaleUp
(
"viewscaleup",
EV_CHEAT,
NULL,
NULL,
"Increase the scale of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_ScaleDown
(
"viewscaledown",
EV_CHEAT,
NULL,
NULL,
"Decrease the scale of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_SetScale
(
"viewscale",
EV_CHEAT,
"f",
"scale",
"Set the scale of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_SetYaw
(
"viewyaw",
EV_CHEAT,
"f",
"yaw",
"Set the yaw of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_SetPitch
(
"viewpitch",
EV_CHEAT,
"f",
"pitch",
"Set the pitch of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_SetRoll
(
"viewroll",
EV_CHEAT,
"f",
"roll",
"Set the roll of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_SetAngles
(
"viewangles",
EV_CHEAT,
"f[0,360]f[0,360]f[0,360]",
"pitch yaw roll",
"Set the angles of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_Spawn
(
"viewspawn",
EV_CHEAT,
"s",
"model",
"Create a viewthing with the specified model",
EV_NORMAL
);
Event EV_ViewThing_Next
(
"viewthingnext",
EV_CHEAT,
NULL,
NULL,
"Change the active viewthing to the next viewthing",
EV_NORMAL
);
Event EV_ViewThing_Prev
(
"viewthingprev",
EV_CHEAT,
NULL,
NULL,
"Change the active viewthing to the previous viewthing",
EV_NORMAL
);
Event EV_ViewThing_Attach
(
"viewattach",
EV_CHEAT,
"ss",
"tagname model",
"Attach a model the the specified tagname",
EV_NORMAL
);
Event EV_ViewThing_Detach
(
"viewdetach",
EV_CHEAT,
NULL,
NULL,
"Detach the current viewthing from its parent",
EV_NORMAL
);
Event EV_ViewThing_DetachAll
(
"viewdetachall",
EV_CHEAT,
NULL,
NULL,
"Detach all the models attached to the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_Delete
(
"viewdelete",
EV_CHEAT,
NULL,
NULL,
"Delete the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_SetOrigin
(
"vieworigin",
EV_CHEAT,
"fff",
"x y z",
"Set the origin of the current viewthing",
EV_NORMAL
);
Event EV_ViewThing_DeleteAll
(
"viewdeleteall",
EV_CHEAT,
NULL,
NULL,
"Delete all viewthings",
EV_NORMAL
);
Event EV_ViewThing_LastFrame
(
"viewlastframe",
EV_DEFAULT,
NULL,
NULL,
"Called when the view things last animation frame is displayed.",
EV_NORMAL
);
Event EV_ViewThing_SaveOffSurfaces
(
"viewsavesurfaces",
EV_DEFAULT,
NULL,
NULL,
"Called after the model is spawned to save off the models original surfaces.",
EV_NORMAL
);
Event EV_ViewThing_PrintTime
(
"_viewthing_printtime",
EV_DEFAULT,
NULL,
NULL,
"Prints out the current level.time.",
EV_NORMAL
);
Event EV_ViewThing_SetAnim
(
"viewsetanim",
EV_CHEAT,
"f",
"animNum",
"Set the animation absolutely based off a floating point value",
EV_NORMAL
);
CLASS_DECLARATION( Animate, ViewMaster, NULL )
{
{ &EV_ViewThing_Spawn, &ViewMaster::Spawn },
{ &EV_ViewThing_Next, &ViewMaster::Next },
{ &EV_ViewThing_Prev, &ViewMaster::Prev },
{ &EV_ViewThing_SetModel, &ViewMaster::SetModelEvent },
{ &EV_ViewThing_DeleteAll, &ViewMaster::DeleteAll },
{ &EV_ViewThing_ToggleAnimate, &ViewMaster::PassEvent },
{ &EV_ViewThing_NextFrame, &ViewMaster::PassEvent },
{ &EV_ViewThing_PrevFrame, &ViewMaster::PassEvent },
{ &EV_ViewThing_NextAnim, &ViewMaster::PassEvent },
{ &EV_ViewThing_PrevAnim, &ViewMaster::PassEvent },
{ &EV_ViewThing_ScaleUp, &ViewMaster::PassEvent },
{ &EV_ViewThing_ScaleDown, &ViewMaster::PassEvent },
{ &EV_ViewThing_SetScale, &ViewMaster::PassEvent },
{ &EV_ViewThing_SetYaw, &ViewMaster::PassEvent },
{ &EV_ViewThing_SetPitch, &ViewMaster::PassEvent },
{ &EV_ViewThing_SetRoll, &ViewMaster::PassEvent },
{ &EV_ViewThing_SetAngles, &ViewMaster::PassEvent },
{ &EV_ViewThing_Attach, &ViewMaster::PassEvent },
{ &EV_ViewThing_Detach, &ViewMaster::PassEvent },
{ &EV_ViewThing_DetachAll, &ViewMaster::PassEvent },
{ &EV_ViewThing_Delete, &ViewMaster::PassEvent },
{ &EV_ViewThing_SetOrigin, &ViewMaster::PassEvent },
{ &EV_ViewThing_SetAnim, &ViewMaster::PassEvent },
{ NULL, NULL }
};
ViewMaster Viewmodel;
ViewMaster::ViewMaster()
{
current_viewthing = NULL;
}
void ViewMaster::Init( void )
{
gi.AddCommand( "viewanimate", NULL );
gi.AddCommand( "viewmodel", NULL );
gi.AddCommand( "viewnext", NULL );
gi.AddCommand( "viewprev", NULL );
gi.AddCommand( "viewnextanim", NULL );
gi.AddCommand( "viewprevanim", NULL );
gi.AddCommand( "viewscaleup", NULL );
gi.AddCommand( "viewscaledown", NULL );
gi.AddCommand( "viewscale", NULL );
gi.AddCommand( "viewyaw", NULL );
gi.AddCommand( "viewpitch", NULL );
gi.AddCommand( "viewroll", NULL );
gi.AddCommand( "viewangles", NULL );
gi.AddCommand( "viewspawn", NULL );
gi.AddCommand( "viewthingnext", NULL );
gi.AddCommand( "viewthingprev", NULL );
gi.AddCommand( "viewattach", NULL );
gi.AddCommand( "viewdetach", NULL );
gi.AddCommand( "viewdetachall", NULL );
gi.AddCommand( "viewdelete", NULL );
gi.AddCommand( "vieworigin", NULL );
gi.AddCommand( "viewdeleteall", NULL );
gi.AddCommand( "viewsetanim", NULL );
}
void ViewMaster::Next
(
Event *ev
)
{
Viewthing *viewthing;
Entity * ent;
ent = ( Entity * )G_FindClass( current_viewthing, "viewthing" );
if ( ent )
{
current_viewthing = ent;
viewthing = ( Viewthing * )( ( Entity * )current_viewthing );
gi.Printf( "current viewthing model %s.\n", viewthing->model.c_str() );
viewthing->UpdateCvars();
}
else
{
gi.Printf( "no more viewthings on map.\n" );
}
}
void ViewMaster::Prev
(
Event *ev
)
{
Viewthing *viewthing;
Entity *prev;
Entity *next;
next = NULL;
do
{
prev = next;
next = ( Entity * )G_FindClass( prev, "viewthing" );
}
while( next != current_viewthing );
if ( prev )
{
current_viewthing = prev;
viewthing = ( Viewthing * )( ( Entity * )current_viewthing );
gi.Printf( "current viewthing model %s.\n", viewthing->model.c_str() );
viewthing->UpdateCvars();
}
else
{
gi.Printf( "no more viewthings on map.\n" );
}
}
void ViewMaster::DeleteAll
(
Event *ev
)
{
Entity *next;
for( next = ( Entity * )G_FindClass( NULL, "viewthing" ); next != NULL; next = ( Entity * )G_FindClass( next, "viewthing" ) )
{
next->PostEvent( EV_Remove, 0 );
}
current_viewthing = NULL;
}
void ViewMaster::Spawn
(
Event *ev
)
{
Viewthing *viewthing;
const char *mdl;
Vector forward;
Vector up;
Vector delta;
Event *event;
Entity *ent;
mdl = ev->GetString( 1 );
if ( !mdl || !mdl[ 0 ] )
{
ScriptError( "Must specify a model name" );
return;
}
// Check if we have a client
ent = g_entities[ 0 ].entity;
assert( ent );
if ( !ent )
{
return;
}
// create a new viewthing
viewthing = new Viewthing;
// set the current_viewthing
current_viewthing = viewthing;
//FIXME FIXME
ent->angles.AngleVectors( &forward, NULL, &up );
viewthing->baseorigin = ent->origin;
viewthing->baseorigin += forward * 48;
viewthing->baseorigin += up * 48;
viewthing->setOrigin( viewthing->baseorigin );
viewthing->droptofloor( 256 );
viewthing->baseorigin = viewthing->origin;
delta = ent->origin - viewthing->origin;
viewthing->setAngles( delta.toAngles() );
event = new Event( EV_ViewThing_SetModel );
event->AddString( mdl );
viewthing->ProcessEvent( event );
if( !gi.modeltiki( viewthing->model ) )
{
ScriptError( "model %s not found, viewmodel not spawned.", mdl );
delete viewthing;
current_viewthing = NULL;
return;
}
}
void ViewMaster::SetModelEvent
(
Event *ev
)
{
const char *mdl;
char str[ 128 ];
Event *event;
Viewthing *viewthing;
mdl = ev->GetString( 1 );
if ( !mdl || !mdl[ 0 ] )
{
ScriptError( "Must specify a model name" );
return;
}
if ( !current_viewthing )
{
// try to find one on the map
current_viewthing = ( Entity * )G_FindClass( NULL, "viewthing" );
if ( !current_viewthing )
{
ScriptError( "No viewmodel" );
return;
}
}
viewthing = ( Viewthing * )( ( Entity * )current_viewthing );
// Prepend 'models/' to make things easier
str[ 0 ] = 0;
if ( ( mdl[ 1 ] != ':' ) && Q_stricmpn( mdl, "models", 6 ) )
{
strcpy( str, "models/" );
}
strcat( str, mdl );
event = new Event( EV_ViewThing_SetModel );
event->AddString( str );
viewthing->ProcessEvent( event );
viewthing->UpdateCvars();
}
void ViewMaster::PassEvent
(
Event *ev
)
{
Viewthing *viewthing;
Event *event;
if ( !current_viewthing )
{
ScriptError( "No viewmodel" );
return;
}
viewthing = ( Viewthing * )( ( Entity * )current_viewthing );
if ( viewthing )
{
viewthing->ProcessEvent( *ev );
}
}
CLASS_DECLARATION( Animate, Viewthing, "viewthing" )
{
{ &EV_ViewThing_Think, &Viewthing::ThinkEvent },
{ &EV_ViewThing_LastFrame, &Viewthing::LastFrameEvent },
{ &EV_ViewThing_ToggleAnimate, &Viewthing::ToggleAnimateEvent },
{ &EV_ViewThing_SetModel, &Viewthing::SetModelEvent },
{ &EV_ViewThing_NextFrame, &Viewthing::NextFrameEvent },
{ &EV_ViewThing_PrevFrame, &Viewthing::PrevFrameEvent },
{ &EV_ViewThing_NextAnim, &Viewthing::NextAnimEvent },
{ &EV_ViewThing_PrevAnim, &Viewthing::PrevAnimEvent },
{ &EV_ViewThing_ScaleUp, &Viewthing::ScaleUpEvent },
{ &EV_ViewThing_ScaleDown, &Viewthing::ScaleDownEvent },
{ &EV_ViewThing_SetScale, &Viewthing::SetScaleEvent },
{ &EV_ViewThing_SetYaw, &Viewthing::SetYawEvent },
{ &EV_ViewThing_SetPitch, &Viewthing::SetPitchEvent },
{ &EV_ViewThing_SetRoll, &Viewthing::SetRollEvent },
{ &EV_ViewThing_SetAngles, &Viewthing::SetAnglesEvent },
{ &EV_ViewThing_Attach, &Viewthing::AttachModel },
{ &EV_ViewThing_Detach, &Viewthing::Delete },
{ &EV_ViewThing_DetachAll, &Viewthing::DetachAll },
{ &EV_ViewThing_Delete, &Viewthing::Delete },
{ &EV_ViewThing_SetOrigin, &Viewthing::ChangeOrigin },
{ &EV_ViewThing_SaveOffSurfaces, &Viewthing::SaveSurfaces },
{ &EV_ViewThing_PrintTime, &Viewthing::PrintTime },
{ &EV_ViewThing_SetAnim, &Viewthing::SetAnim },
{ NULL, NULL }
};
Viewthing::Viewthing
(
void
)
{
frame = 0;
animstate = 0;
setSolidType( SOLID_NOT );
baseorigin = origin;
Viewmodel.current_viewthing = this;
edict->s.eType = ET_MODELANIM;
edict->s.renderfx |= RF_SHADOW;
edict->s.renderfx |= RF_SHADOW_PRECISE;
edict->s.renderfx |= RF_EXTRALIGHT;
// save off surfaces once the model is spawned
PostEvent( EV_ViewThing_SaveOffSurfaces, FRAMETIME );
PostEvent( EV_ViewThing_Think, FRAMETIME );
}
void Viewthing::PrintTime
(
Event *ev
)
{
gi.Printf( "ev current frame %d leveltime %.2f\n", ev->GetInteger( 1 ), level.time );
}
void Viewthing::UpdateCvars
(
qboolean quiet
)
{
gi.cvar_set("viewmodelentity", va("%d", edict->s.number));
}
void Viewthing::ThinkEvent
(
Event *ev
)
{
int f;
if (animstate >= 2)
{
Vector forward;
Vector left;
Vector up;
Vector realmove;
angles.AngleVectors(&forward, &left, &up);
realmove = left * accel[1] + up * accel[2] + forward * accel[0];
setOrigin(baseorigin + realmove);
gi.cvar_set("viewthingorigin", va("%0.2f,%0.2f,%0.2f", edict->s.origin[0], edict->s.origin[1], edict->s.origin[2]));
}
PostEvent(EV_ViewThing_Think, FRAMETIME);
if ((animstate > 0) && (Viewmodel.current_viewthing == this))
{
f = GetTime() / gi.Anim_Frametime(edict->tiki, CurrentAnim());
if (f != lastframe)
{
float time;
lastframe = f;
time = f * AnimTime() / NumFrames();
gi.Printf("current frame %d time %.2f\n", f, time);
gi.cvar_set("viewmodeltime", va("%.2f", time));
gi.cvar_set("viewmodelframe", va("%d", f));
gi.cvar_set("viewmodelanim", AnimName());
}
}
}
void Viewthing::LastFrameEvent
(
Event *ev
)
{
}
void Viewthing::ToggleAnimateEvent
(
Event *ev
)
{
animstate = ( animstate + 1 ) % 4;
setOrigin( baseorigin );
// reset to a known state
switch( animstate )
{
case 0:
SetFrame();
gi.Printf( "Animation stopped.\n" );
gi.cvar_set( "viewmodelanimmode", "Stopped" );
break;
case 1:
NewAnim( CurrentAnim() );
gi.Printf( "Animation no motion.\n" );
gi.cvar_set( "viewmodelanimmode", "No Motion" );
break;
case 2:
NewAnim( CurrentAnim(), EV_ViewThing_LastFrame );
gi.Printf( "Animation with motion and looping.\n" );
gi.cvar_set( "viewmodelanimmode", "Motion and Looping" );
break;
case 3:
NewAnim( CurrentAnim(), EV_ViewThing_LastFrame );
gi.Printf( "Animation with motion no looping.\n" );
gi.cvar_set( "viewmodelanimmode", "Motion and No Looping" );
break;
}
UpdateCvars( qtrue );
}
void Viewthing::SetModelEvent
(
Event *ev
)
{
str modelname;
modelname = ev->GetString( 1 );
setModel( modelname );
if( gi.modeltiki( model ) )
{
NewAnim( 0 );
SetFrame( );
}
UpdateCvars();
}
void Viewthing::NextFrameEvent
(
Event *ev
)
{
int numframes;
numframes = NumFrames();
if ( numframes )
{
frame = (frame+1)%numframes;
SetFrame();
animstate = 0;
UpdateCvars();
}
}
void Viewthing::PrevFrameEvent
(
Event *ev
)
{
int numframes;
numframes = NumFrames();
if ( numframes )
{
frame = (frame-1)%numframes;
SetFrame();
animstate = 0;
UpdateCvars();
}
}
void Viewthing::SetAnim
(
Event *ev
)
{
int numanims, anim;
numanims = NumAnims();
if ( numanims )
{
// restore original surfaces
memcpy( edict->s.surfaces, origSurfaces, sizeof( origSurfaces ) );
anim = ev->GetFloat( 1 ) * numanims;
if ( anim >= numanims )
anim = numanims - 1;
NewAnim( anim % numanims );
frame = 0;
SetFrame();
animstate = 0;
UpdateCvars();
}
}
void Viewthing::NextAnimEvent
(
Event *ev
)
{
int numanims;
numanims = NumAnims();
if ( numanims )
{
// restore original surfaces
memcpy( edict->s.surfaces, origSurfaces, sizeof( origSurfaces ) );
NewAnim( ( CurrentAnim() + 1 ) % numanims );
frame = 0;
SetFrame();
animstate = 0;
UpdateCvars();
}
}
void Viewthing::PrevAnimEvent
(
Event *ev
)
{
int anim;
int numanims;
numanims = NumAnims();
if ( numanims )
{
// restore original surfaces
memcpy( edict->s.surfaces, origSurfaces, sizeof( origSurfaces ) );
anim = CurrentAnim() - 1;
while( anim < 0 )
{
anim += numanims;
}
NewAnim( anim );
frame = 0;
SetFrame();
animstate = 0;
UpdateCvars();
}
}
void Viewthing::ScaleUpEvent
(
Event *ev
)
{
edict->s.scale += 0.01f;
UpdateCvars();
}
void Viewthing::ScaleDownEvent
(
Event *ev
)
{
edict->s.scale -= 0.01f;
UpdateCvars();
}
void Viewthing::SetScaleEvent
(
Event *ev
)
{
float s;
if ( ev->NumArgs() )
{
s = ev->GetFloat( 1 );
edict->s.scale = s;
UpdateCvars();
}
else
{
gi.Printf( "viewscale = %f\n", edict->s.scale );
}
}
void Viewthing::SetYawEvent
(
Event *ev
)
{
if ( ev->NumArgs() > 0 )
{
angles.setYaw( ev->GetFloat( 1 ) );
setAngles( angles );
}
gi.Printf( "yaw = %f\n", angles.yaw() );
}
void Viewthing::SetPitchEvent
(
Event *ev
)
{
if ( ev->NumArgs() > 0 )
{
angles.setPitch( ev->GetFloat( 1 ) );
setAngles( angles );
}
gi.Printf( "pitch = %f\n", angles.pitch() );
}
void Viewthing::SetRollEvent
(
Event *ev
)
{
if ( ev->NumArgs() > 0 )
{
angles.setRoll( ev->GetFloat( 1 ) );
setAngles( angles );
}
gi.Printf( "roll = %f\n", angles.roll() );
}
void Viewthing::SetAnglesEvent
(
Event *ev
)
{
if ( ev->NumArgs() > 2 )
{
angles.x = ev->GetFloat( 1 );
angles.y = ev->GetFloat( 2 );
angles.z = ev->GetFloat( 3 );
setAngles( angles );
}
gi.Printf( "angles = %f, %f, %f\n", angles.x, angles.y, angles.z );
}
void Viewthing::AttachModel
(
Event *ev
)
{
Event * event;
Viewthing * child;
child = new Viewthing;
child->setModel( ev->GetString( 2 ) );
//
// attach the child
//
event = new Event( EV_Attach );
event->AddEntity( this );
event->AddString( ev->GetString( 1 ) );
child->ProcessEvent( event );
}
void Viewthing::Delete
(
Event *ev
)
{
Viewmodel.current_viewthing = NULL;
PostEvent( EV_Remove, 0 );
Viewmodel.PostEvent( EV_ViewThing_Next, 0 );
}
void Viewthing::DetachAll
(
Event *ev
)
{
int i;
int num;
num = numchildren;
for (i=0;i<MAX_MODEL_CHILDREN;i++)
{
Entity * ent;
if (!children[i])
continue;
ent = ( Entity * )G_GetEntity( children[i] );
ent->PostEvent( EV_Remove, 0 );
num--;
if (!num)
break;
}
}
void Viewthing::ChangeOrigin
(
Event *ev
)
{
if ( ev->NumArgs() )
{
origin.x = ev->GetFloat( 1 );
origin.y = ev->GetFloat( 2 );
origin.z = ev->GetFloat( 3 );
setOrigin( origin );
baseorigin = origin;
UpdateCvars();
}
gi.Printf( "vieworigin = x%f y%f z%f\n", origin.x, origin.y, origin.z );
}
void Viewthing::SaveSurfaces
(
Event *ev
)
{
memcpy( origSurfaces, edict->s.surfaces, sizeof( origSurfaces ) );
}