mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00
434 lines
9.7 KiB
C++
434 lines
9.7 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
|
|
===========================================================================
|
|
*/
|
|
|
|
// object.cpp : Object (used by common TIKIs)
|
|
|
|
#include "g_local.h"
|
|
#include "object.h"
|
|
#include "sentient.h"
|
|
#include "misc.h"
|
|
#include "explosion.h"
|
|
#include "gibs.h"
|
|
#include "specialfx.h"
|
|
#include "g_phys.h"
|
|
#include "game.h"
|
|
#include "../script/scriptexception.h"
|
|
|
|
Event EV_Object_HandleSetModel
|
|
(
|
|
"handlespawn",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Internal usage",
|
|
EV_NORMAL
|
|
);
|
|
|
|
CLASS_DECLARATION(Animate, Object, NULL) {
|
|
{NULL, NULL}
|
|
};
|
|
|
|
Event EV_InteractObject_Setup
|
|
(
|
|
"_setup",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets up an object."
|
|
);
|
|
|
|
Event EV_InteractObject_KilledEffect
|
|
(
|
|
"killedeffect",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the tiki it will spawn when it's destroyed",
|
|
EV_NORMAL
|
|
);
|
|
|
|
Event EV_InteractObject_HitEffect
|
|
(
|
|
"hiteffect",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the tiki it will spawn when it's hit",
|
|
EV_NORMAL
|
|
);
|
|
|
|
CLASS_DECLARATION(Animate, InteractObject, "interactobject") {
|
|
{&EV_Damage, &InteractObject::Damaged },
|
|
{&EV_Killed, &InteractObject::Killed },
|
|
{&EV_InteractObject_Setup, &InteractObject::Setup },
|
|
{&EV_InteractObject_HitEffect, &InteractObject::EventHitEffect },
|
|
{&EV_InteractObject_KilledEffect, &InteractObject::EventKilledEffect},
|
|
{NULL, NULL }
|
|
};
|
|
|
|
InteractObject::InteractObject() {}
|
|
|
|
void InteractObject::Setup(Event *ev)
|
|
{
|
|
if (!health) {
|
|
// Set the bounding box as health
|
|
health = (maxs - mins).length();
|
|
}
|
|
|
|
max_health = health;
|
|
deadflag = DEAD_NO;
|
|
|
|
NewAnim("idle");
|
|
link();
|
|
}
|
|
|
|
void InteractObject::EventHitEffect(Event *ev)
|
|
{
|
|
m_sHitEffect = ev->GetString(1);
|
|
}
|
|
|
|
void InteractObject::EventKilledEffect(Event *ev)
|
|
{
|
|
m_sKilledEffect = ev->GetString(1);
|
|
}
|
|
|
|
void InteractObject::Damaged(Event *ev)
|
|
{
|
|
if (m_sHitEffect.length()) {
|
|
// Spawn a temporary hit effect
|
|
Animate *temp = new Animate();
|
|
temp->PostEvent(EV_Remove, 1.f);
|
|
temp->setModel(m_sHitEffect);
|
|
temp->NewAnim("idle");
|
|
}
|
|
|
|
Entity::DamageEvent(ev);
|
|
}
|
|
|
|
void InteractObject::Killed(Event *ev)
|
|
{
|
|
Entity *ent;
|
|
Entity *attacker;
|
|
Vector dir;
|
|
const char *name;
|
|
|
|
takedamage = DAMAGE_NO;
|
|
deadflag = DEAD_NO;
|
|
setSolidType(SOLID_NOT);
|
|
edict->s.renderfx |= RF_DONTDRAW;
|
|
|
|
if (edict->solid == SOLID_NOT || edict->solid == SOLID_TRIGGER) {
|
|
edict->r.svFlags |= SVF_NOCLIENT;
|
|
}
|
|
|
|
if (m_sKilledEffect.length()) {
|
|
// Spawn a temporary killed effect
|
|
Animate *temp = new Animate();
|
|
temp->PostEvent(EV_Remove, 1.f);
|
|
temp->setModel(m_sKilledEffect);
|
|
temp->NewAnim("idle");
|
|
}
|
|
|
|
attacker = ev->GetEntity(1);
|
|
if (killtarget.c_str() && killtarget[0]) {
|
|
// Kill all targets
|
|
for (ent = G_FindTarget(NULL, killtarget.c_str()); ent; ent = G_FindTarget(ent, killtarget.c_str())) {
|
|
ent->PostEvent(EV_Remove, 0);
|
|
}
|
|
}
|
|
|
|
if (target.c_str() && target[0]) {
|
|
// Activate all targets
|
|
for (ent = G_FindTarget(NULL, target.c_str()); ent; ent = G_FindTarget(ent, target.c_str())) {
|
|
Event *event = new Event(EV_Activate);
|
|
event->AddEntity(attacker);
|
|
|
|
ent->ProcessEvent(event);
|
|
}
|
|
}
|
|
|
|
// Remove ourself
|
|
PostEvent(EV_Remove, 0);
|
|
}
|
|
|
|
void InteractObject::Archive(Archiver& arc)
|
|
{
|
|
Animate::Archive(arc);
|
|
|
|
arc.ArchiveString(&m_sHitEffect);
|
|
arc.ArchiveString(&m_sKilledEffect);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*QUAKED func_throwobject (0 0.25 0.5) (-16 -16 0) (16 16 32)
|
|
|
|
This is an object you can pickup and throw at people
|
|
******************************************************************************/
|
|
|
|
Event EV_ThrowObject_Pickup
|
|
(
|
|
"pickup",
|
|
EV_DEFAULT,
|
|
"es",
|
|
"entity tag_name",
|
|
"Picks up this throw object and attaches it to the entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_ThrowObject_Throw
|
|
(
|
|
"throw",
|
|
EV_DEFAULT,
|
|
"efeF",
|
|
"owner speed targetent grav",
|
|
"Throw this throw object.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_ThrowObject_PickupOffset
|
|
(
|
|
"pickupoffset",
|
|
EV_DEFAULT,
|
|
"v",
|
|
"pickup_offset",
|
|
"Sets the pickup_offset.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_ThrowObject_ThrowSound
|
|
(
|
|
"throwsound",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"throw_sound",
|
|
"Sets the sound to play when object is thrown.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
CLASS_DECLARATION(Animate, ThrowObject, "func_throwobject") {
|
|
{&EV_Touch, &ThrowObject::Touch },
|
|
{&EV_ThrowObject_Pickup, &ThrowObject::Pickup },
|
|
{&EV_ThrowObject_Throw, &ThrowObject::Throw },
|
|
{&EV_ThrowObject_PickupOffset, &ThrowObject::PickupOffset},
|
|
{&EV_ThrowObject_ThrowSound, &ThrowObject::ThrowSound },
|
|
{NULL, NULL }
|
|
};
|
|
|
|
ThrowObject::ThrowObject()
|
|
{
|
|
if (LoadingSavegame) {
|
|
// Archive function will setup all necessary data
|
|
return;
|
|
}
|
|
pickup_offset = vec_zero;
|
|
}
|
|
|
|
void ThrowObject::PickupOffset(Event *ev)
|
|
{
|
|
pickup_offset = edict->s.scale * ev->GetVector(1);
|
|
}
|
|
|
|
void ThrowObject::ThrowSound(Event *ev)
|
|
{
|
|
throw_sound = ev->GetString(1);
|
|
}
|
|
|
|
void ThrowObject::Touch(Event *ev)
|
|
{
|
|
Entity *other;
|
|
|
|
if (movetype != MOVETYPE_BOUNCE) {
|
|
return;
|
|
}
|
|
|
|
other = ev->GetEntity(1);
|
|
assert(other);
|
|
|
|
if (other->isSubclassOf(Teleporter)) {
|
|
return;
|
|
}
|
|
|
|
if (other->entnum == owner) {
|
|
return;
|
|
}
|
|
|
|
if (throw_sound.length()) {
|
|
StopLoopSound();
|
|
}
|
|
|
|
if (other->takedamage) {
|
|
other->Damage(
|
|
this,
|
|
G_GetEntity(owner),
|
|
size.length() * velocity.length() / 400,
|
|
origin,
|
|
velocity,
|
|
level.impact_trace.plane.normal,
|
|
32,
|
|
0,
|
|
MOD_THROWNOBJECT
|
|
);
|
|
}
|
|
|
|
Damage(this, this, max_health, origin, velocity, level.impact_trace.plane.normal, 32, 0, MOD_THROWNOBJECT);
|
|
}
|
|
|
|
void ThrowObject::Throw(Event *ev)
|
|
{
|
|
Entity *owner;
|
|
Sentient *targetent;
|
|
float speed;
|
|
float traveltime;
|
|
float vertical_speed;
|
|
Vector target;
|
|
Vector dir;
|
|
float grav;
|
|
Vector xydir;
|
|
Event *e;
|
|
|
|
owner = ev->GetEntity(1);
|
|
assert(owner);
|
|
|
|
if (!owner) {
|
|
ScriptError("owner == NULL");
|
|
return;
|
|
}
|
|
|
|
speed = ev->GetFloat(2);
|
|
if (!speed) {
|
|
speed = 1;
|
|
}
|
|
|
|
targetent = (Sentient *)ev->GetEntity(3);
|
|
assert(targetent);
|
|
if (!targetent) {
|
|
ScriptError("targetent == NULL");
|
|
return;
|
|
}
|
|
|
|
if (ev->NumArgs() == 4) {
|
|
grav = ev->GetFloat(4);
|
|
} else {
|
|
grav = 1;
|
|
}
|
|
|
|
e = new Event(EV_Detach);
|
|
ProcessEvent(e);
|
|
|
|
this->owner = owner->entnum;
|
|
edict->r.ownerNum = owner->entnum;
|
|
|
|
gravity = grav;
|
|
|
|
if (targetent->IsSubclassOfSentient()) {
|
|
target = targetent->origin;
|
|
target.z += targetent->viewheight;
|
|
} else {
|
|
target = targetent->centroid;
|
|
}
|
|
|
|
setMoveType(MOVETYPE_BOUNCE);
|
|
setSolidType(SOLID_BBOX);
|
|
edict->clipmask = MASK_PROJECTILE;
|
|
|
|
dir = target - origin;
|
|
xydir = dir;
|
|
xydir.z = 0;
|
|
traveltime = xydir.length() / speed;
|
|
vertical_speed = (dir.z / traveltime) + (0.5f * gravity * sv_gravity->value * traveltime);
|
|
xydir.normalize();
|
|
|
|
// setup ambient flying sound
|
|
if (throw_sound.length()) {
|
|
LoopSound(throw_sound.c_str());
|
|
}
|
|
|
|
velocity = speed * xydir;
|
|
velocity.z = vertical_speed;
|
|
|
|
angles = velocity.toAngles();
|
|
setAngles(angles);
|
|
|
|
avelocity.x = crandom() * 200;
|
|
avelocity.y = crandom() * 200;
|
|
takedamage = DAMAGE_YES;
|
|
}
|
|
|
|
void ThrowObject::Pickup(Event *ev)
|
|
{
|
|
Entity *ent;
|
|
Event *e;
|
|
str bone;
|
|
|
|
ent = ev->GetEntity(1);
|
|
|
|
assert(ent);
|
|
if (!ent) {
|
|
return;
|
|
}
|
|
bone = ev->GetString(2);
|
|
|
|
setOrigin(pickup_offset);
|
|
|
|
e = new Event(EV_Attach);
|
|
e->AddEntity(ent);
|
|
e->AddString(bone);
|
|
ProcessEvent(e);
|
|
|
|
edict->s.renderfx &= ~RF_FRAMELERP;
|
|
}
|
|
|
|
void ThrowObject::Archive(Archiver& arc)
|
|
{
|
|
Animate::Archive(arc);
|
|
|
|
arc.ArchiveInteger(&owner);
|
|
arc.ArchiveVector(&pickup_offset);
|
|
arc.ArchiveString(&throw_sound);
|
|
}
|
|
|
|
CLASS_DECLARATION(Entity, HelmetObject, "helmetobject") {
|
|
{NULL, NULL}
|
|
};
|
|
|
|
HelmetObject::HelmetObject()
|
|
{
|
|
if (LoadingSavegame) {
|
|
return;
|
|
}
|
|
|
|
setSolidType(SOLID_NOT);
|
|
setMoveType(MOVETYPE_TOSS);
|
|
setSize(Vector(-2, -2, -2), Vector(2, 2, 2));
|
|
edict->clipmask = MASK_VIEWSOLID;
|
|
|
|
// Remove the object automatically after 5 seconds
|
|
PostEvent(EV_Remove, 5);
|
|
}
|
|
|
|
void HelmetObject::HelmetTouch(Event *ev)
|
|
{
|
|
avelocity = vec_zero;
|
|
|
|
angles.x = 0;
|
|
angles.z = 0;
|
|
setAngles(angles);
|
|
// Stop moving
|
|
setMoveType(MOVETYPE_NONE);
|
|
}
|