og-odamex/common/p_quake.cpp
2026-01-05 01:52:47 +00:00

153 lines
3.6 KiB
C++

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id$
//
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 2006-2026 by The Odamex Team.
//
// This program 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.
//
// This program 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.
//
// DESCRIPTION:
// Earthquake implementation.
//
//-----------------------------------------------------------------------------
#include "odamex.h"
#include "p_local.h"
#include "actor.h"
#include "m_bbox.h"
#include "m_random.h"
#include "s_sound.h"
class DEarthquake : public DThinker
{
DECLARE_SERIAL (DEarthquake, DThinker);
public:
DEarthquake (AActor *center, int intensity, int duration, int damrad, int tremrad);
void RunThink () override;
virtual void DestroyedPointer(DObject *obj);
AActor *m_Spot;
fixed_t m_TremorBox[4];
fixed_t m_DamageBox[4];
int m_Intensity;
int m_Countdown;
private:
DEarthquake () {}
};
IMPLEMENT_SERIAL(DEarthquake, DThinker)
void DEarthquake::DestroyedPointer(DObject *obj)
{
if(obj == m_Spot)
m_Spot = NULL;
}
void DEarthquake::Serialize (FArchive &arc)
{
int i;
if (arc.IsStoring ())
{
arc << m_Spot << m_Intensity << m_Countdown;
for (i = 0; i < 4; i++)
arc << m_TremorBox[i] << m_DamageBox[i];
}
else
{
arc >> m_Spot >> m_Intensity >> m_Countdown;
for (i = 0; i < 4; i++)
arc >> m_TremorBox[i] >> m_DamageBox[i];
}
}
void DEarthquake::RunThink ()
{
if (level.time % 48 == 0)
S_Sound (m_Spot, CHAN_BODY, "world/quake", 1, ATTN_NORM);
if (serverside)
{
for (auto& player : players)
{
if (player.ingame() && !(player.cheats & CF_NOCLIP))
{
AActor *mo = player.mo;
if (!(level.time & 7) &&
mo->x >= m_DamageBox[BOXLEFT] && mo->x < m_DamageBox[BOXRIGHT] &&
mo->y >= m_DamageBox[BOXTOP] && mo->y < m_DamageBox[BOXBOTTOM])
{
int mult = 1024 * m_Intensity;
P_DamageMobj (mo, NULL, NULL, m_Intensity / 2, MOD_UNKNOWN);
mo->momx += (P_Random ()-128) * mult;
mo->momy += (P_Random ()-128) * mult;
}
if (mo->x >= m_TremorBox[BOXLEFT] && mo->x < m_TremorBox[BOXRIGHT] &&
mo->y >= m_TremorBox[BOXTOP] && mo->y < m_TremorBox[BOXBOTTOM])
{
player.xviewshift = m_Intensity;
}
}
}
}
if (--m_Countdown == 0)
{
Destroy ();
}
}
static void setbox (fixed_t *box, AActor *c, fixed_t size)
{
if (size)
{
box[BOXLEFT] = c->x - size + 1;
box[BOXRIGHT] = c->x + size;
box[BOXTOP] = c->y - size + 1;
box[BOXBOTTOM] = c->y + size;
} else
box[BOXLEFT] = box[BOXRIGHT] = box[BOXTOP] = box[BOXBOTTOM] = 0;
}
DEarthquake::DEarthquake (AActor *center, int intensity, int duration,
int damrad, int tremrad)
{
m_Spot = center;
setbox (m_TremorBox, center, tremrad * FRACUNIT * 64);
setbox (m_DamageBox, center, damrad * FRACUNIT * 64);
m_Intensity = intensity;
m_Countdown = duration;
}
bool P_StartQuake (int tid, int intensity, int duration, int damrad, int tremrad)
{
AActor *center = NULL;
bool res = false;
if (intensity > 9)
intensity = 9;
else if (intensity < 1)
intensity = 1;
while ( (center = AActor::FindByTID (center, tid)) )
{
res = true;
new DEarthquake (center, intensity, duration, damrad, tremrad);
}
return res;
}