/* =========================================================================== 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; str 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.c_str()); delete viewthing; current_viewthing = NULL; return; } } void ViewMaster::SetModelEvent(Event *ev) { str 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)); }