og-odamex/common/p_switch.cpp
2026-02-15 06:12:30 -06:00

425 lines
9.4 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:
// Switches, buttons. Two-state animation. Exits.
//
//-----------------------------------------------------------------------------
#include "odamex.h"
#include "p_local.h"
#include "p_lnspec.h"
#include "s_sound.h"
#include "r_state.h"
#include "z_zone.h"
#include "w_wad.h"
#include "gi.h"
#include "svc_message.h"
#include "p_mapformat.h"
EXTERN_CVAR(co_zdoomsound)
extern int numtextures;
//
// CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE
//
class DActiveButton : public DThinker
{
DECLARE_SERIAL (DActiveButton, DThinker);
public:
enum EWhere
{
BUTTON_Top,
BUTTON_Middle,
BUTTON_Bottom,
BUTTON_Nowhere
};
DActiveButton ();
DActiveButton (line_t *, EWhere, int16_t tex, int32_t time, fixed_t x, fixed_t y);
void RunThink () override;
line_t *m_Line;
EWhere m_Where;
int16_t m_Texture;
int32_t m_Timer;
fixed_t m_X, m_Y; // Location of timer sound
friend FArchive &operator<< (FArchive &arc, EWhere where)
{
return arc << (byte)where;
}
friend FArchive &operator>> (FArchive &arc, EWhere &out)
{
byte in; arc >> in; out = (EWhere)in; return arc;
}
};
static int *switchlist;
static int numswitches;
//
// P_InitSwitchList
// Only called at game initialization.
//
// [RH] Rewritten to use a BOOM-style SWITCHES lump and remove the
// MAXSWITCHES limit.
void P_InitSwitchList(void)
{
byte *alphSwitchList = (byte *)W_CacheLumpName ("SWITCHES", PU_STATIC);
byte *list_p;
int i;
for (i = 0, list_p = alphSwitchList; list_p[18] || list_p[19]; list_p += 20, i++)
;
if (i == 0)
{
switchlist = (int *)Z_Malloc (sizeof(*switchlist), PU_STATIC, 0);
*switchlist = -1;
numswitches = 0;
}
else
{
switchlist = (int *)Z_Malloc (sizeof(*switchlist)*(i*2+1), PU_STATIC, 0);
for (i = 0, list_p = alphSwitchList; list_p[18] || list_p[19]; list_p += 20)
{
if (((gameinfo.maxSwitch & 15) >= (list_p[18] & 15)) &&
((gameinfo.maxSwitch & ~15) == (list_p[18] & ~15)))
{
// [RH] Skip this switch if it can't be found.
if (R_CheckTextureNumForName (list_p /* .name1 */) < 0)
continue;
if (R_CheckTextureNumForName (list_p + 9 /* .name2 */) < 0)
continue;
switchlist[i++] = R_TextureNumForName(list_p /* .name1 */);
switchlist[i++] = R_TextureNumForName(list_p + 9 /* .name2 */);
}
}
numswitches = i/2;
switchlist[i] = -1;
}
Z_Free (alphSwitchList);
}
void P_DestroyButtonThinkers()
{
DActiveButton *button;
TThinkerIterator<DActiveButton> iterator;
while ((button = iterator.Next()))
button->Destroy();
}
//
// Start a button counting down till it turns off.
// [RH] Rewritten to remove MAXBUTTONS limit and use temporary soundorgs.
//
static void P_StartButton (line_t *line, DActiveButton::EWhere w, int texture,
int time, fixed_t x, fixed_t y)
{
DActiveButton *button;
TThinkerIterator<DActiveButton> iterator;
// See if button is already pressed
while ( (button = iterator.Next ()) )
{
if (button->m_Line == line)
return;
}
new DActiveButton (line, w, texture, time, x, y);
}
short* P_GetButtonTexturePtr(const line_t* line, short*& altTexture, DActiveButton::EWhere& where)
{
if (!line->sidenum[0])
return NULL;
int texTop = sides[line->sidenum[0]].toptexture;
int texMid = sides[line->sidenum[0]].midtexture;
int texBot = sides[line->sidenum[0]].bottomtexture;
where = (DActiveButton::EWhere)0;
altTexture = NULL;
for (int i = 0; i < numswitches * 2; i++)
{
if (switchlist[i] == texTop)
{
altTexture = (short*)&switchlist[i ^ 1];
where = DActiveButton::BUTTON_Top;
return &sides[line->sidenum[0]].toptexture;
}
else if (switchlist[i] == texBot)
{
altTexture = (short*)&switchlist[i ^ 1];
where = DActiveButton::BUTTON_Bottom;
return &sides[line->sidenum[0]].bottomtexture;
}
else if (switchlist[i] == texMid)
{
altTexture = (short*)&switchlist[i ^ 1];
where = DActiveButton::BUTTON_Middle;
return &sides[line->sidenum[0]].midtexture;
}
}
return NULL;
}
short P_GetButtonTexture(const line_t* line)
{
DActiveButton::EWhere twhere;
short* alt;
short* texture = P_GetButtonTexturePtr(line, alt, twhere);
if (texture)
return *texture;
return 0;
}
void P_SetButtonTexture(line_t* line, short texture)
{
if (texture > 0 && texture < numtextures)
{
DActiveButton::EWhere twhere;
short* alt;
short* findTexture = P_GetButtonTexturePtr(line, alt, twhere);
if (findTexture)
*findTexture = texture;
}
}
// denis - query button
bool P_GetButtonInfo (const line_t *line, unsigned &state, unsigned &time)
{
DActiveButton *button;
TThinkerIterator<DActiveButton> iterator;
// See if button is already pressed
while ( (button = iterator.Next ()) )
{
if (button->m_Line == line)
{
state = button->m_Where;
time = button->m_Timer;
return true;
}
}
return false;
}
bool P_SetButtonInfo (line_t *line, unsigned state, unsigned time)
{
DActiveButton *button;
TThinkerIterator<DActiveButton> iterator;
// See if button is already pressed
while ( (button = iterator.Next ()) )
{
if (button->m_Line == line)
{
button->m_Where = (DActiveButton::EWhere)state;
button->m_Timer = time;
return true;
}
}
return false;
}
void P_UpdateButtons(client_t *cl)
{
DActiveButton *button;
TThinkerIterator<DActiveButton> iterator;
std::map<unsigned, bool> actedlines;
// See if button is already pressed
while ( (button = iterator.Next ()) )
{
if (button->m_Line == NULL) continue;
unsigned l = button->m_Line - lines;
unsigned state = 0, timer = 0;
state = button->m_Where;
timer = button->m_Timer;
// record that we acted on this line:
actedlines[l] = true;
MSG_WriteSVC(&cl->reliablebuf, SVC_Switch(lines[l], state, timer));
}
for (int l=0; l<numlines; l++)
{
// update all button state except those that have actors assigned:
if (!actedlines[l] && lines[l].wastoggled)
{
MSG_WriteSVC(&cl->reliablebuf, SVC_Switch(lines[l], 0, 0));
}
}
}
//
// Function that changes wall texture.
// Tell it if switch is ok to use again (1=yes, it's a button).
//
void P_ChangeSwitchTexture(line_t* line, int useAgain, bool playsound)
{
const char *sound;
// EXIT SWITCH?
if (P_IsExitLine(line->special))
{
sound = "switches/exitbutn";
}
else
{
sound = "switches/normbutn";
}
if (!useAgain && P_HandleSpecialRepeat(line))
line->special = 0;
DActiveButton::EWhere twhere;
short* altTexture;
short* texture = P_GetButtonTexturePtr(line, altTexture, twhere);
if (texture)
{
// [RH] The original code played the sound at buttonlist->soundorg,
// which wasn't necessarily anywhere near the switch if
// it was facing a big sector.
fixed_t x = line->v1->x + (line->dx >> 1);
fixed_t y = line->v1->y + (line->dy >> 1);
if (playsound)
{
if (co_zdoomsound)
{
// [SL] 2011-05-27 - Play at a normal volume in the center
// of the switch's linedef
S_Sound(x, y, CHAN_BODY, sound, 1, ATTN_NORM);
}
else
{
// [SL] 2011-05-16 - Reverted the code to play the sound at full
// volume anywhere on the map to emulate vanilla doom behavior
S_Sound(CHAN_BODY, sound, 1, ATTN_NONE);
}
}
if (useAgain)
P_StartButton(line, twhere, *texture, BUTTONTIME, x, y);
*texture = *altTexture;
line->switchactive = true;
}
line->wastoggled = true;
}
IMPLEMENT_SERIAL (DActiveButton, DThinker)
DActiveButton::DActiveButton ()
{
m_Line = NULL;
m_Where = BUTTON_Nowhere;
m_Texture = 0;
m_Timer = 0;
m_X = 0;
m_Y = 0;
}
DActiveButton::DActiveButton (line_t *line, EWhere where, int16_t texture,
int32_t time, fixed_t x, fixed_t y)
{
m_Line = line;
m_Where = where;
m_Texture = texture;
m_Timer = time;
m_X = x;
m_Y = y;
}
void DActiveButton::Serialize (FArchive &arc)
{
Super::Serialize (arc);
if (arc.IsStoring ())
{
arc << m_Line << m_Where << m_Texture << m_Timer << m_X << m_Y;
}
else
{
arc >> m_Line >> m_Where >> m_Texture >> m_Timer >> m_X >> m_Y;
}
}
void DActiveButton::RunThink ()
{
if (0 >= --m_Timer)
{
switch (m_Where)
{
case BUTTON_Top:
sides[m_Line->sidenum[0]].toptexture = m_Texture;
break;
case BUTTON_Middle:
sides[m_Line->sidenum[0]].midtexture = m_Texture;
break;
case BUTTON_Bottom:
sides[m_Line->sidenum[0]].bottomtexture = m_Texture;
break;
default:
break;
}
if (co_zdoomsound)
{
// [SL] 2011-05-27 - Play at a normal volume in the center of the
// switch's linedef
fixed_t x = m_Line->v1->x + (m_Line->dx >> 1);
fixed_t y = m_Line->v1->y + (m_Line->dy >> 1);
S_Sound (x, y, CHAN_BODY, "switches/normbutn", 1, ATTN_NORM);
}
else
{
// [SL] 2011-05-16 - Changed to play the switch resetting sound at the
// map location 0,0 to emulate the bug in vanilla doom
S_Sound (0, 0, CHAN_BODY, "switches/normbutn", 1, ATTN_NORM);
}
Destroy ();
m_Line->switchactive = false;
}
}
VERSION_CONTROL (p_switch_cpp, "$Id$")