// Emacs style mode select   -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id: dd124772b0817ccfec1a09074bd88a7bbf235af2 $
//
// 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:
// [RH] new file to deal with general things
//
//-----------------------------------------------------------------------------


#include "odamex.h"


#include "p_local.h"
#include "c_effect.h"
#include "p_mobj.h"
#include "info.h"
#include "s_sound.h"
#include "tables.h"
#include "m_random.h"
#include "c_console.h"
#include "gi.h"

EXTERN_CVAR(sv_nomonsters)

// List of spawnable things for the Thing_Spawn and Thing_Projectile specials.
// TODO: support SpawnNums mapinfo blocks for modifying this array
std::array<int, 155> SpawnableThings = {
	0,
	MT_SHOTGUY,
	MT_CHAINGUY,
	MT_BRUISER,
	MT_POSSESSED,
	MT_TROOP,
	MT_BABY,
	MT_SPIDER,
	MT_SERGEANT,
	MT_SHADOWS,
	MT_TROOPSHOT,
	MT_CLIP,
	MT_MISC22,	// Shells
	0,
	0,
	0,
	0,
	0,
	0,
	MT_HEAD,
	MT_UNDEAD,
	MT_BRIDGE,
	MT_MISC3,	// Armor bonus
	MT_MISC10,	// Stim pack
	MT_MISC11,	// Medkit
	MT_MISC12,	// Soul sphere
	0,
	MT_SHOTGUN,
	MT_CHAINGUN,
	MT_MISC27,	// Rocket launcher
	MT_MISC28,	// Plasma gun
	MT_MISC25,	// BFG
	MT_MISC26,	// Chainsaw
	MT_SUPERSHOTGUN,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,	// T_MORPHBLAST
	0,	// T_ROCK1
	0,	// T_ROCK2
	0,	// T_ROCK3
	0,	// T_DIRT1
	0,	// T_DIRT2
	0,	// T_DIRT3
	0,	// T_DIRT4
	0,	// T_DIRT5
	0,	// T_DIRT6
	MT_PLASMA,
	0,	// T_POISONDART
	MT_TRACER,
	0,	// T_STAINEDGLASS1
	0,	// T_STAINEDGLASS2
	0,	// T_STAINEDGLASS3
	0,	// T_STAINEDGLASS4
	0,	// T_STAINEDGLASS5
	0,	// T_STAINEDGLASS6
	0,	// T_STAINEDGLASS7
	0,	// T_STAINEDGLASS8
	0,	// T_STAINEDGLASS9
	0,	// T_STAINEDGLASS0
	0,
	0,
	0,
	0,
	MT_MISC0,	// Green armor
	MT_MISC1,	// Blue armor
	0,
	0,
	0,
	0,
	0,
	MT_MISC20,	// Energy cell
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	MT_MISC4,	// Blue key card
	MT_MISC5,	// Red key card
	MT_MISC6,	// Yellow key card
	MT_MISC7,	// Yellow skull key
	MT_MISC8,	// Red skull key
	MT_MISC9,	// Blue skull key
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	MT_FIRE,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	MT_SKULL,
	MT_VILE,
	MT_FATSO,
	MT_KNIGHT,
	MT_CYBORG,
	MT_PAIN,
	MT_WOLFSS,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	MT_BARREL,
	MT_HEADSHOT,
	MT_ROCKET,
	MT_BFG,
	MT_ARACHPLAZ,
	MT_BLOOD,
	MT_PUFF,
	MT_MEGA,	// Mega sphere
	MT_INV,
	MT_MISC13,	// Berserk
	MT_INS,		// Blur sphere
	MT_MISC14,	// Rad suit
	MT_MISC15,	// Computer map
	MT_MISC16,	// Light amp
	MT_MISC17,	// Box of ammo
	MT_MISC18,	// Rocket
	MT_MISC19,	// Box of rockets
	MT_MISC21,	// Battery pack
	MT_MISC23,	// Box of shells
	MT_MISC24,	// Backpack
	MT_MISC68,	// Guts
	MT_MISC71,	// Blood pool
	MT_MISC84,	// Pool o' blood 1
	MT_MISC85,	// Pool o' blood 2
	MT_MISC77,	// Flaming barrel
	MT_MISC86,	// Brains
	0,
	MT_MISC2,		// Health bonus
	MT_FATSHOT,	// Mancubus fireball
	MT_BRUISERSHOT,// Baron projectile
};

bool P_Thing_Spawn (int tid, int type, std::optional<angle_t> angle, bool fog, std::optional<int> newtid)
{
	// type is doomednum
	// kind is the mobjtype

	if (static_cast<size_t>(type) >= SpawnableThings.size())
		return false;

	const int kind = SpawnableThings[type];
	if (kind == 0)
		return false;

	if ((mobjinfo[kind].flags & MF_COUNTKILL) && sv_nomonsters == 1)
		return false;

	AActor* spot = nullptr;
	int rtn = 0;
	while ( (spot = AActor::FindByTID (spot, tid)) )
	{
		fixed_t z;
		if (mobjinfo[kind].flags2 & MF2_FLOATBOB)
			z = spot->z - spot->floorz;
		else
			z = spot->z;

		auto mobj = new AActor(spot->x, spot->y, z, (mobjtype_t)kind);

		if (mobj)
		{
			if (P_TestMobjLocation (mobj))
			{
				rtn++;
				mobj->angle = angle.value_or(spot->angle);
				if (fog)
					S_Sound (new AActor (spot->x, spot->y, spot->z + INT2FIXED(gameinfo.telefogHeight), MT_TFOG),
							 CHAN_VOICE, "misc/teleport", 1, ATTN_NORM);
				mobj->flags |= MF_DROPPED;	// Don't respawn
				if (mobj->flags2 & MF2_FLOATBOB)
				{
					mobj->special1 = mobj->z - mobj->floorz;
				}
				mobj->tid = newtid.value_or(0);
			}
			else
			{
				mobj->Destroy ();
				rtn = 0;
			}
		}
	}

	return rtn != 0;
}

bool P_Thing_Projectile (int tid, int type, angle_t angle,
						 fixed_t speed, fixed_t vspeed, bool gravity, std::optional<int> newtid)
{
	if (static_cast<size_t>(type) >= SpawnableThings.size())
		return false;

	const int kind = SpawnableThings[type];
	if (kind == 0)
		return false;

	if ((mobjinfo[kind].flags & MF_COUNTKILL) && sv_nomonsters)
		return false;

	bool rtn = false;
	AActor* spot = nullptr;
	while ( (spot = AActor::FindByTID (spot, tid)) )
	{
		if (spot->type != MT_MAPSPOT && spot->type != MT_MAPSPOTGRAVITY)
			continue;

		auto mobj = new AActor (spot->x, spot->y, spot->z, (mobjtype_t)kind);

		if (mobj)
		{
			if (mobj->info->seesound)
				S_Sound (mobj, CHAN_VOICE, mobj->info->seesound, 1, ATTN_NORM);
			if (gravity)
			{
				mobj->flags &= ~MF_NOGRAVITY;
				if (!(mobj->flags & MF_COUNTKILL))
					mobj->flags2 |= MF2_LOGRAV;
			}
			else
				mobj->flags |= MF_NOGRAVITY;
			mobj->target = spot->ptr();
			mobj->angle = angle;
			mobj->momx = FixedMul (speed, finecosine[angle>>ANGLETOFINESHIFT]);
			mobj->momy = FixedMul (speed, finesine[angle>>ANGLETOFINESHIFT]);
			mobj->momz = vspeed;
			mobj->flags |= MF_DROPPED;
			mobj->tid = newtid.value_or(0);
			if (mobj->flags & MF_MISSILE)
				rtn = P_CheckMissileSpawn (mobj);
			else if (!P_TestMobjLocation (mobj))
				mobj->Destroy ();
		}
	}

	return rtn;
}

bool P_ActivateMobj (AActor *mobj, AActor *activator)
{
	if (mobj->flags & MF_COUNTKILL)
	{
		mobj->flags2 &= ~(MF2_DORMANT | MF2_INVULNERABLE);
		return true;
	}
	else
	{
		switch (mobj->type)
		{
			case MT_SPARK:
			{
				int count = mobj->args[0];
				char sound[16];

				if (count == 0)
					count = 32;

				P_DrawSplash (count, mobj->x, mobj->y, mobj->z, mobj->angle, 1);
				snprintf (sound, 16, "world/spark%d", 1+(M_Random() % 3));
				S_Sound (mobj, CHAN_AUTO, sound, 1, ATTN_STATIC);
				break;
			}

			case MT_FOUNTAIN:
				mobj->effects &= ~FX_FOUNTAINMASK;
				mobj->effects |= mobj->args[0] << FX_FOUNTAINSHIFT;
				break;

			case MT_SECRETTRIGGER:
				if (activator->player->mo == consoleplayer().camera)
				{
					if (mobj->args[0] <= 1)
					{
						C_MidPrint ("A secret is revealed!");
					}
					if (mobj->args[0] == 0 || mobj->args[0] == 2)
					{
						S_Sound (activator, CHAN_AUTO, "misc/secret", 1, ATTN_NORM);
					}
				}
				level.found_secrets++;
				mobj->Destroy ();
				break;

			default:
				break;
		}
	}
	return false;
}

bool P_DeactivateMobj (AActor *mobj)
{
	if (mobj->flags & MF_COUNTKILL)
	{
		mobj->flags2 |= MF2_DORMANT | MF2_INVULNERABLE;
		return true;
	}
	else
	{
		switch (mobj->type)
		{
			case MT_FOUNTAIN:
				mobj->effects &= ~FX_FOUNTAINMASK;
				break;
			default:
				break;
		}
	}
	return false;
}

VERSION_CONTROL (p_things_cpp, "$Id: dd124772b0817ccfec1a09074bd88a7bbf235af2 $")
