2006-02-24 04:48:15 +00:00
|
|
|
|
// Emacs style mode select -*- C++ -*-
|
2014-05-07 15:18:44 +00:00
|
|
|
|
// Emacs style mode select -*- C++ -*-
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// $Id:$
|
|
|
|
|
//
|
|
|
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
|
|
|
//
|
|
|
|
|
// This source is available for distribution and/or modification
|
|
|
|
|
// only under the terms of the DOOM Source Code License as
|
|
|
|
|
// published by id Software. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
// The source is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
|
|
|
|
|
// for more details.
|
|
|
|
|
//
|
|
|
|
|
// $Log:$
|
|
|
|
|
//
|
|
|
|
|
// DESCRIPTION:
|
|
|
|
|
// Moving object handling. Spawn functions.
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
// HEADER FILES ------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
#include "templates.h"
|
|
|
|
|
#include "i_system.h"
|
|
|
|
|
#include "m_random.h"
|
|
|
|
|
#include "doomdef.h"
|
|
|
|
|
#include "p_local.h"
|
|
|
|
|
#include "p_lnspec.h"
|
|
|
|
|
#include "p_effect.h"
|
|
|
|
|
#include "p_terrain.h"
|
|
|
|
|
#include "st_stuff.h"
|
|
|
|
|
#include "hu_stuff.h"
|
|
|
|
|
#include "s_sound.h"
|
|
|
|
|
#include "doomstat.h"
|
|
|
|
|
#include "v_video.h"
|
|
|
|
|
#include "c_cvars.h"
|
2012-08-01 03:12:43 +00:00
|
|
|
|
#include "c_dispatch.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
|
#include "b_bot.h" //Added by MC:
|
|
|
|
|
#include "stats.h"
|
|
|
|
|
#include "a_hexenglobal.h"
|
|
|
|
|
#include "a_sharedglobal.h"
|
|
|
|
|
#include "gi.h"
|
|
|
|
|
#include "sbar.h"
|
|
|
|
|
#include "p_acs.h"
|
|
|
|
|
#include "cmdlib.h"
|
|
|
|
|
#include "decallib.h"
|
|
|
|
|
#include "ravenshared.h"
|
|
|
|
|
#include "a_action.h"
|
|
|
|
|
#include "a_keys.h"
|
|
|
|
|
#include "p_conversation.h"
|
2007-05-28 14:46:49 +00:00
|
|
|
|
#include "thingdef/thingdef.h"
|
2006-11-25 04:26:04 +00:00
|
|
|
|
#include "g_game.h"
|
2007-12-20 20:22:31 +00:00
|
|
|
|
#include "teaminfo.h"
|
2011-07-06 08:50:15 +00:00
|
|
|
|
#include "r_data/r_translate.h"
|
2008-06-15 18:36:26 +00:00
|
|
|
|
#include "r_sky.h"
|
2008-09-14 23:54:38 +00:00
|
|
|
|
#include "g_level.h"
|
|
|
|
|
#include "d_event.h"
|
|
|
|
|
#include "colormatcher.h"
|
2008-09-15 14:11:05 +00:00
|
|
|
|
#include "v_palette.h"
|
2009-05-15 17:21:45 +00:00
|
|
|
|
#include "p_enemy.h"
|
2010-05-25 03:53:13 +00:00
|
|
|
|
#include "gstrings.h"
|
2011-07-06 14:20:54 +00:00
|
|
|
|
#include "farchive.h"
|
2011-07-06 07:35:36 +00:00
|
|
|
|
#include "r_data/colormaps.h"
|
2011-07-07 15:37:47 +00:00
|
|
|
|
#include "r_renderer.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// MACROS ------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
#define WATER_SINK_FACTOR 3
|
|
|
|
|
#define WATER_SINK_SMALL_FACTOR 4
|
|
|
|
|
#define WATER_SINK_SPEED (FRACUNIT/2)
|
|
|
|
|
#define WATER_JUMP_SPEED (FRACUNIT*7/2)
|
|
|
|
|
|
|
|
|
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
|
|
|
|
|
|
|
|
|
void G_PlayerReborn (int player);
|
|
|
|
|
|
|
|
|
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
|
|
|
|
|
|
|
|
|
static void PlayerLandedOnThing (AActor *mo, AActor *onmobj);
|
|
|
|
|
|
|
|
|
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
|
|
|
|
|
2008-08-10 03:25:08 +00:00
|
|
|
|
extern int BotWTG;
|
2008-04-04 14:31:20 +00:00
|
|
|
|
EXTERN_CVAR (Int, cl_rockettrails)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
|
|
|
|
|
|
|
|
|
static FRandom pr_explodemissile ("ExplodeMissile");
|
2009-09-15 06:35:46 +00:00
|
|
|
|
FRandom pr_bounce ("Bounce");
|
2006-02-24 04:48:15 +00:00
|
|
|
|
static FRandom pr_reflect ("Reflect");
|
|
|
|
|
static FRandom pr_nightmarerespawn ("NightmareRespawn");
|
|
|
|
|
static FRandom pr_botspawnmobj ("BotSpawnActor");
|
|
|
|
|
static FRandom pr_spawnmapthing ("SpawnMapThing");
|
|
|
|
|
static FRandom pr_spawnpuff ("SpawnPuff");
|
|
|
|
|
static FRandom pr_spawnblood ("SpawnBlood");
|
|
|
|
|
static FRandom pr_splatter ("BloodSplatter");
|
2006-05-07 00:27:22 +00:00
|
|
|
|
static FRandom pr_takedamage ("TakeDamage");
|
2006-11-29 10:03:35 +00:00
|
|
|
|
static FRandom pr_splat ("FAxeSplatter");
|
2006-02-24 04:48:15 +00:00
|
|
|
|
static FRandom pr_ripperblood ("RipperBlood");
|
|
|
|
|
static FRandom pr_chunk ("Chunk");
|
|
|
|
|
static FRandom pr_checkmissilespawn ("CheckMissileSpawn");
|
|
|
|
|
static FRandom pr_spawnmissile ("SpawnMissile");
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
|
static FRandom pr_missiledamage ("MissileDamage");
|
2006-02-24 04:48:15 +00:00
|
|
|
|
static FRandom pr_multiclasschoice ("MultiClassChoice");
|
2008-04-04 14:31:20 +00:00
|
|
|
|
static FRandom pr_rockettrail("RocketTrail");
|
2012-08-01 03:12:43 +00:00
|
|
|
|
static FRandom pr_uniquetid("UniqueTID");
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
|
|
|
|
|
|
FRandom pr_spawnmobj ("SpawnActor");
|
|
|
|
|
|
|
|
|
|
CUSTOM_CVAR (Float, sv_gravity, 800.f, CVAR_SERVERINFO|CVAR_NOSAVE)
|
|
|
|
|
{
|
|
|
|
|
level.gravity = self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CVAR (Bool, cl_missiledecals, true, CVAR_ARCHIVE)
|
|
|
|
|
CVAR (Bool, addrocketexplosion, false, CVAR_ARCHIVE)
|
|
|
|
|
CVAR (Int, cl_pufftype, 0, CVAR_ARCHIVE);
|
|
|
|
|
CVAR (Int, cl_bloodtype, 0, CVAR_ARCHIVE);
|
|
|
|
|
|
|
|
|
|
// CODE --------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
IMPLEMENT_POINTY_CLASS (AActor)
|
|
|
|
|
DECLARE_POINTER (target)
|
|
|
|
|
DECLARE_POINTER (lastenemy)
|
|
|
|
|
DECLARE_POINTER (tracer)
|
|
|
|
|
DECLARE_POINTER (goal)
|
2008-03-12 02:56:11 +00:00
|
|
|
|
DECLARE_POINTER (LastLookActor)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
DECLARE_POINTER (Inventory)
|
|
|
|
|
DECLARE_POINTER (LastHeard)
|
|
|
|
|
DECLARE_POINTER (master)
|
2010-07-29 06:54:00 +00:00
|
|
|
|
DECLARE_POINTER (Poisoner)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
END_POINTERS
|
|
|
|
|
|
|
|
|
|
AActor::~AActor ()
|
|
|
|
|
{
|
|
|
|
|
// Please avoid calling the destructor directly (or through delete)!
|
|
|
|
|
// Use Destroy() instead.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AActor::Serialize (FArchive &arc)
|
|
|
|
|
{
|
|
|
|
|
Super::Serialize (arc);
|
|
|
|
|
|
|
|
|
|
if (arc.IsStoring ())
|
|
|
|
|
{
|
|
|
|
|
arc.WriteSprite (sprite);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sprite = arc.ReadSprite ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arc << x
|
|
|
|
|
<< y
|
|
|
|
|
<< z
|
|
|
|
|
<< angle
|
|
|
|
|
<< frame
|
2006-11-14 16:54:02 +00:00
|
|
|
|
<< scaleX
|
|
|
|
|
<< scaleY
|
2006-02-24 04:48:15 +00:00
|
|
|
|
<< RenderStyle
|
2008-06-15 18:36:26 +00:00
|
|
|
|
<< renderflags
|
|
|
|
|
<< picnum
|
|
|
|
|
<< floorpic
|
|
|
|
|
<< ceilingpic
|
|
|
|
|
<< TIDtoHate
|
|
|
|
|
<< LastLookPlayerNumber
|
|
|
|
|
<< LastLookActor
|
|
|
|
|
<< effects
|
2006-02-24 04:48:15 +00:00
|
|
|
|
<< alpha
|
- Updated lempar.c to v1.31.
- Added .txt files to the list of types (wad, zip, and pk3) that can be
loaded without listing them after -file.
- Fonts that are created by the ACS setfont command to wrap a texture now
support animated textures.
- FON2 fonts can now use their full palette for CR_UNTRANSLATED when drawn
with the hardware 2D path instead of being restricted to the game palette.
- Fixed: Toggling vid_vsync would reset the displayed fullscreen gamma to 1
on a Radeon 9000.
- Added back the off-by-one palette handling, but in a much more limited
scope than before. The skipped entry is assumed to always be at 248, and
it is assumed that all Shader Model 1.4 cards suffer from this. That's
because all SM1.4 cards are based on variants of the ATI R200 core, and the
RV250 in a Radeon 9000 craps up like this. I see no reason to assume that
other flavors of the R200 are any different. (Interesting note: With the
Radeon 9000, D3DTADDRESS_CLAMP is an invalid address mode when using the
debug Direct3D 9 runtime, but it works perfectly fine with the retail
Direct3D 9 runtime.) (Insight: The R200 probably uses bytes for all its
math inside pixel shaders. That would explain perfectly why I can't use
constants greater than 1 with PS1.4 and why it can't do an exact mapping to
every entry in the color palette.
- Fixed: The software shaded drawer did not work for 2D, because its selected
"color"map was replaced with the identitymap before being used.
- Fixed: I cannot use Printf to output messages before the framebuffer was
completely setup, meaning that Shader Model 1.4 cards could not change
resolution.
- I have decided to let remap palettes specify variable alpha values for
their colors. D3DFB no longer forces them to 255.
- Updated re2c to version 0.12.3.
- Fixed: A_Wander used threshold as a timer, when it should have used
reactiontime.
- Fixed: A_CustomRailgun would not fire at all for actors without a target
when the aim parameter was disabled.
- Made the warp command work in multiplayer, again courtesy of Karate Chris.
- Fixed: Trying to spawn a bot while not in a game made for a crashing time.
(Patch courtesy of Karate Chris.)
- Removed some floating point math from hu_scores.cpp that somebody's GCC
gave warnings for (not mine, though).
- Fixed: The SBarInfo drawbar command crashed if the sprite image was
unavailable.
- Fixed: FString::operator=(const char *) did not release its old buffer when
being assigned to the null string.
- The scanner no longer has an upper limit on the length of strings it
accepts, though short strings will be faster than long ones.
- Moved all the text scanning functions into a class. Mainly, this means that
multiple script scanner states can be stored without being forced to do so
recursively. I think I might be taking advantage of that in the near
future. Possibly. Maybe.
- Removed some potential buffer overflows from the decal parser.
- Applied Blzut3's SBARINFO update #9:
* Fixed: When using even length values in drawnumber it would cap to a 98
value instead of a 99 as intended.
* The SBarInfo parser can now accept negatives for coordinates. This
doesn't allow much right now, but later I plan to add better fullscreen
hud support in which the negatives will be more useful. This also cleans
up the source a bit since all calls for (x, y) coordinates are with the
function getCoordinates().
- Added support for stencilling actors.
- Added support for non-black colors specified with DTA_ColorOverlay to the
software renderer.
- Fixed: The inverse, gold, red, and green fixed colormaps each allocated
space for 32 different colormaps, even though each only used the first one.
- Added two new blending flags to make reverse subtract blending more useful:
STYLEF_InvertSource and STYLEF_InvertOverlay. These invert the color that
gets blended with the background, since that seems like a good idea for
reverse subtraction. They also work with the other two blending operations.
- Added subtract and reverse subtract blending operations to the renderer.
Since the ERenderStyle enumeration was getting rather unwieldy, I converted
it into a new FRenderStyle structure that lets each parameter of the
blending equation be set separately. This simplified the set up for the
blend quite a bit, and it means a number of new combinations are available
by setting the parameters properly.
SVN r710 (trunk)
2008-01-25 23:57:44 +00:00
|
|
|
|
<< fillcolor
|
2006-02-24 04:48:15 +00:00
|
|
|
|
<< pitch
|
|
|
|
|
<< roll
|
|
|
|
|
<< Sector
|
|
|
|
|
<< floorz
|
|
|
|
|
<< ceilingz
|
|
|
|
|
<< dropoffz
|
2006-04-11 16:27:41 +00:00
|
|
|
|
<< floorsector
|
2006-04-17 13:53:34 +00:00
|
|
|
|
<< ceilingsector
|
2006-04-11 16:27:41 +00:00
|
|
|
|
<< radius
|
2006-02-24 04:48:15 +00:00
|
|
|
|
<< height
|
2008-10-05 11:23:41 +00:00
|
|
|
|
<< projectilepassheight
|
2009-06-30 20:57:51 +00:00
|
|
|
|
<< velx
|
|
|
|
|
<< vely
|
|
|
|
|
<< velz
|
2006-02-24 04:48:15 +00:00
|
|
|
|
<< tics
|
|
|
|
|
<< state
|
2012-03-13 02:43:24 +00:00
|
|
|
|
<< Damage;
|
|
|
|
|
if (SaveVersion >= 3227)
|
|
|
|
|
{
|
|
|
|
|
arc << projectileKickback;
|
|
|
|
|
}
|
|
|
|
|
arc << flags
|
2006-02-24 04:48:15 +00:00
|
|
|
|
<< flags2
|
|
|
|
|
<< flags3
|
|
|
|
|
<< flags4
|
2006-04-16 13:29:50 +00:00
|
|
|
|
<< flags5
|
2013-08-12 18:09:21 +00:00
|
|
|
|
<< flags6;
|
|
|
|
|
if (SaveVersion >= 4504)
|
|
|
|
|
{
|
|
|
|
|
arc << flags7;
|
|
|
|
|
}
|
2014-09-16 06:42:27 +00:00
|
|
|
|
if (SaveVersion >= 4512)
|
2014-09-13 10:38:16 +00:00
|
|
|
|
{
|
|
|
|
|
arc << weaponspecial;
|
|
|
|
|
}
|
2013-08-12 18:09:21 +00:00
|
|
|
|
arc << special1
|
2006-02-24 04:48:15 +00:00
|
|
|
|
<< special2
|
|
|
|
|
<< health
|
|
|
|
|
<< movedir
|
|
|
|
|
<< visdir
|
|
|
|
|
<< movecount
|
2009-09-16 21:03:09 +00:00
|
|
|
|
<< strafecount
|
2006-02-24 04:48:15 +00:00
|
|
|
|
<< target
|
|
|
|
|
<< lastenemy
|
|
|
|
|
<< LastHeard
|
|
|
|
|
<< reactiontime
|
|
|
|
|
<< threshold
|
|
|
|
|
<< player
|
|
|
|
|
<< SpawnPoint[0] << SpawnPoint[1] << SpawnPoint[2]
|
2014-04-10 22:58:59 +00:00
|
|
|
|
<< SpawnAngle;
|
|
|
|
|
if (SaveVersion >= 4506)
|
|
|
|
|
{
|
|
|
|
|
arc << StartHealth;
|
|
|
|
|
}
|
|
|
|
|
arc << skillrespawncount
|
2006-02-24 04:48:15 +00:00
|
|
|
|
<< tracer
|
|
|
|
|
<< floorclip
|
|
|
|
|
<< tid
|
2012-02-26 02:25:33 +00:00
|
|
|
|
<< special;
|
|
|
|
|
if (P_IsACSSpecial(special))
|
|
|
|
|
{
|
|
|
|
|
P_SerializeACSScriptNumber(arc, args[0], false);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
arc << args[0];
|
|
|
|
|
}
|
2012-03-13 02:43:24 +00:00
|
|
|
|
arc << args[1] << args[2] << args[3] << args[4];
|
|
|
|
|
if (SaveVersion >= 3427)
|
|
|
|
|
{
|
|
|
|
|
arc << accuracy << stamina;
|
|
|
|
|
}
|
|
|
|
|
arc << goal
|
2006-02-24 04:48:15 +00:00
|
|
|
|
<< waterlevel
|
2010-12-12 15:43:35 +00:00
|
|
|
|
<< MinMissileChance
|
|
|
|
|
<< SpawnFlags
|
|
|
|
|
<< Inventory
|
2014-10-13 07:45:36 +00:00
|
|
|
|
<< InventoryID;
|
|
|
|
|
if (SaveVersion < 4513)
|
|
|
|
|
{
|
|
|
|
|
SDWORD id;
|
|
|
|
|
arc << id;
|
|
|
|
|
}
|
|
|
|
|
arc << FloatBobPhase
|
2006-02-24 04:48:15 +00:00
|
|
|
|
<< Translation
|
2008-06-15 02:25:09 +00:00
|
|
|
|
<< SeeSound
|
|
|
|
|
<< AttackSound
|
|
|
|
|
<< PainSound
|
|
|
|
|
<< DeathSound
|
|
|
|
|
<< ActiveSound
|
|
|
|
|
<< UseSound
|
2009-05-23 08:30:36 +00:00
|
|
|
|
<< BounceSound
|
2010-12-12 15:43:35 +00:00
|
|
|
|
<< WallBounceSound
|
|
|
|
|
<< CrushPainSound
|
|
|
|
|
<< Speed
|
2006-05-14 14:30:13 +00:00
|
|
|
|
<< FloatSpeed
|
2006-02-24 04:48:15 +00:00
|
|
|
|
<< Mass
|
|
|
|
|
<< PainChance
|
|
|
|
|
<< SpawnState
|
|
|
|
|
<< SeeState
|
|
|
|
|
<< MeleeState
|
|
|
|
|
<< MissileState
|
2006-04-11 16:27:41 +00:00
|
|
|
|
<< MaxDropOffHeight
|
2009-09-14 21:41:44 +00:00
|
|
|
|
<< MaxStepHeight
|
|
|
|
|
<< BounceFlags
|
|
|
|
|
<< bouncefactor
|
2008-06-10 09:16:01 +00:00
|
|
|
|
<< wallbouncefactor
|
2006-04-18 22:15:05 +00:00
|
|
|
|
<< bouncecount
|
2007-04-22 21:01:35 +00:00
|
|
|
|
<< maxtargetrange
|
|
|
|
|
<< meleethreshold
|
2006-04-11 16:27:41 +00:00
|
|
|
|
<< meleerange
|
2012-03-13 02:43:24 +00:00
|
|
|
|
<< DamageType;
|
2013-06-24 13:40:17 +00:00
|
|
|
|
if (SaveVersion >= 4501)
|
|
|
|
|
{
|
|
|
|
|
arc << DamageTypeReceived;
|
|
|
|
|
}
|
2012-03-13 02:43:24 +00:00
|
|
|
|
if (SaveVersion >= 3237)
|
|
|
|
|
{
|
|
|
|
|
arc
|
2011-06-13 17:15:09 +00:00
|
|
|
|
<< PainType
|
2012-03-13 02:43:24 +00:00
|
|
|
|
<< DeathType;
|
|
|
|
|
}
|
|
|
|
|
arc << gravity
|
2008-03-12 13:48:35 +00:00
|
|
|
|
<< FastChaseStrafeCount
|
2008-04-04 14:31:20 +00:00
|
|
|
|
<< master
|
2008-04-08 20:52:49 +00:00
|
|
|
|
<< smokecounter
|
|
|
|
|
<< BlockingMobj
|
2009-05-15 10:39:40 +00:00
|
|
|
|
<< BlockingLine
|
2011-09-08 01:28:26 +00:00
|
|
|
|
<< VisibleToTeam // [BB]
|
2009-06-06 15:21:57 +00:00
|
|
|
|
<< pushfactor
|
2009-09-14 21:41:44 +00:00
|
|
|
|
<< Species
|
2012-03-13 02:43:24 +00:00
|
|
|
|
<< Score;
|
|
|
|
|
if (SaveVersion >= 3113)
|
|
|
|
|
{
|
|
|
|
|
arc << DesignatedTeam;
|
|
|
|
|
}
|
|
|
|
|
arc << lastpush << lastbump
|
2010-12-12 15:43:35 +00:00
|
|
|
|
<< PainThreshold
|
2014-11-20 05:57:40 +00:00
|
|
|
|
<< DamageFactor;
|
|
|
|
|
if (SaveVersion >= 4516)
|
|
|
|
|
{
|
|
|
|
|
arc << DamageMultiply;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DamageMultiply = FRACUNIT;
|
|
|
|
|
}
|
|
|
|
|
arc << WeaveIndexXY << WeaveIndexZ
|
2010-12-12 15:43:35 +00:00
|
|
|
|
<< PoisonDamageReceived << PoisonDurationReceived << PoisonPeriodReceived << Poisoner
|
2012-03-13 02:43:24 +00:00
|
|
|
|
<< PoisonDamage << PoisonDuration << PoisonPeriod;
|
|
|
|
|
if (SaveVersion >= 3235)
|
|
|
|
|
{
|
|
|
|
|
arc << PoisonDamageType << PoisonDamageTypeReceived;
|
|
|
|
|
}
|
|
|
|
|
arc << ConversationRoot << Conversation;
|
2014-07-13 02:32:43 +00:00
|
|
|
|
if (SaveVersion >= 4509)
|
|
|
|
|
{
|
|
|
|
|
arc << FriendPlayer;
|
|
|
|
|
}
|
2014-12-18 03:51:47 +00:00
|
|
|
|
if (SaveVersion >= 4517)
|
2014-12-18 03:47:00 +00:00
|
|
|
|
{
|
|
|
|
|
arc << TeleFogSourceType
|
|
|
|
|
<< TeleFogDestType;
|
|
|
|
|
}
|
2014-12-31 01:59:31 +00:00
|
|
|
|
if (SaveVersion >= 4518)
|
|
|
|
|
{
|
|
|
|
|
arc << RipperLevel
|
|
|
|
|
<< RipLevelMin
|
|
|
|
|
<< RipLevelMax;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-12 00:17:13 +00:00
|
|
|
|
{
|
|
|
|
|
FString tagstr;
|
|
|
|
|
if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag;
|
|
|
|
|
arc << tagstr;
|
|
|
|
|
if (arc.IsLoading())
|
|
|
|
|
{
|
|
|
|
|
if (tagstr.Len() == 0) Tag = NULL;
|
|
|
|
|
else Tag = mStringPropertyData.Alloc(tagstr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (arc.IsLoading ())
|
|
|
|
|
{
|
|
|
|
|
touching_sectorlist = NULL;
|
|
|
|
|
LinkToWorld (Sector);
|
|
|
|
|
AddToHash ();
|
- Updated lempar.c to v1.31.
- Added .txt files to the list of types (wad, zip, and pk3) that can be
loaded without listing them after -file.
- Fonts that are created by the ACS setfont command to wrap a texture now
support animated textures.
- FON2 fonts can now use their full palette for CR_UNTRANSLATED when drawn
with the hardware 2D path instead of being restricted to the game palette.
- Fixed: Toggling vid_vsync would reset the displayed fullscreen gamma to 1
on a Radeon 9000.
- Added back the off-by-one palette handling, but in a much more limited
scope than before. The skipped entry is assumed to always be at 248, and
it is assumed that all Shader Model 1.4 cards suffer from this. That's
because all SM1.4 cards are based on variants of the ATI R200 core, and the
RV250 in a Radeon 9000 craps up like this. I see no reason to assume that
other flavors of the R200 are any different. (Interesting note: With the
Radeon 9000, D3DTADDRESS_CLAMP is an invalid address mode when using the
debug Direct3D 9 runtime, but it works perfectly fine with the retail
Direct3D 9 runtime.) (Insight: The R200 probably uses bytes for all its
math inside pixel shaders. That would explain perfectly why I can't use
constants greater than 1 with PS1.4 and why it can't do an exact mapping to
every entry in the color palette.
- Fixed: The software shaded drawer did not work for 2D, because its selected
"color"map was replaced with the identitymap before being used.
- Fixed: I cannot use Printf to output messages before the framebuffer was
completely setup, meaning that Shader Model 1.4 cards could not change
resolution.
- I have decided to let remap palettes specify variable alpha values for
their colors. D3DFB no longer forces them to 255.
- Updated re2c to version 0.12.3.
- Fixed: A_Wander used threshold as a timer, when it should have used
reactiontime.
- Fixed: A_CustomRailgun would not fire at all for actors without a target
when the aim parameter was disabled.
- Made the warp command work in multiplayer, again courtesy of Karate Chris.
- Fixed: Trying to spawn a bot while not in a game made for a crashing time.
(Patch courtesy of Karate Chris.)
- Removed some floating point math from hu_scores.cpp that somebody's GCC
gave warnings for (not mine, though).
- Fixed: The SBarInfo drawbar command crashed if the sprite image was
unavailable.
- Fixed: FString::operator=(const char *) did not release its old buffer when
being assigned to the null string.
- The scanner no longer has an upper limit on the length of strings it
accepts, though short strings will be faster than long ones.
- Moved all the text scanning functions into a class. Mainly, this means that
multiple script scanner states can be stored without being forced to do so
recursively. I think I might be taking advantage of that in the near
future. Possibly. Maybe.
- Removed some potential buffer overflows from the decal parser.
- Applied Blzut3's SBARINFO update #9:
* Fixed: When using even length values in drawnumber it would cap to a 98
value instead of a 99 as intended.
* The SBarInfo parser can now accept negatives for coordinates. This
doesn't allow much right now, but later I plan to add better fullscreen
hud support in which the negatives will be more useful. This also cleans
up the source a bit since all calls for (x, y) coordinates are with the
function getCoordinates().
- Added support for stencilling actors.
- Added support for non-black colors specified with DTA_ColorOverlay to the
software renderer.
- Fixed: The inverse, gold, red, and green fixed colormaps each allocated
space for 32 different colormaps, even though each only used the first one.
- Added two new blending flags to make reverse subtract blending more useful:
STYLEF_InvertSource and STYLEF_InvertOverlay. These invert the color that
gets blended with the background, since that seems like a good idea for
reverse subtraction. They also work with the other two blending operations.
- Added subtract and reverse subtract blending operations to the renderer.
Since the ERenderStyle enumeration was getting rather unwieldy, I converted
it into a new FRenderStyle structure that lets each parameter of the
blending equation be set separately. This simplified the set up for the
blend quite a bit, and it means a number of new combinations are available
by setting the parameters properly.
SVN r710 (trunk)
2008-01-25 23:57:44 +00:00
|
|
|
|
SetShade (fillcolor);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (player)
|
|
|
|
|
{
|
2006-07-13 10:17:56 +00:00
|
|
|
|
if (playeringame[player - players] &&
|
|
|
|
|
player->cls != NULL &&
|
2012-03-23 03:37:55 +00:00
|
|
|
|
!(flags4 & MF4_NOSKIN) &&
|
|
|
|
|
state->sprite == GetDefaultByType (player->cls)->SpawnState->sprite)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{ // Give player back the skin
|
2013-05-12 18:27:03 +00:00
|
|
|
|
sprite = skins[player->userinfo.GetSkin()].sprite;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
if (Speed == 0)
|
|
|
|
|
{
|
|
|
|
|
Speed = GetDefault()->Speed;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-30 18:53:14 +00:00
|
|
|
|
PrevX = x;
|
|
|
|
|
PrevY = y;
|
|
|
|
|
PrevZ = z;
|
|
|
|
|
PrevAngle = angle;
|
2007-01-07 09:43:58 +00:00
|
|
|
|
UpdateWaterLevel(z, false);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AActor::AActor () throw()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AActor::AActor (const AActor &other) throw()
|
2011-03-11 00:44:38 +00:00
|
|
|
|
: DThinker()
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2006-09-14 00:02:31 +00:00
|
|
|
|
memcpy (&x, &other.x, (BYTE *)&this[1] - (BYTE *)&x);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AActor &AActor::operator= (const AActor &other)
|
|
|
|
|
{
|
2006-09-14 00:02:31 +00:00
|
|
|
|
memcpy (&x, &other.x, (BYTE *)&this[1] - (BYTE *)&x);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-16 13:29:50 +00:00
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor::InStateSequence
|
|
|
|
|
//
|
|
|
|
|
// Checks whether the current state is in a contiguous sequence that
|
|
|
|
|
// starts with basestate
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
|
|
bool AActor::InStateSequence(FState * newstate, FState * basestate)
|
|
|
|
|
{
|
|
|
|
|
if (basestate == NULL) return false;
|
|
|
|
|
|
|
|
|
|
FState * thisstate = basestate;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (newstate == thisstate) return true;
|
|
|
|
|
basestate = thisstate;
|
|
|
|
|
thisstate = thisstate->GetNextState();
|
|
|
|
|
}
|
|
|
|
|
while (thisstate == basestate+1);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor::GetTics
|
|
|
|
|
//
|
|
|
|
|
// Get the actual duration of the next state
|
2012-06-16 08:35:51 +00:00
|
|
|
|
// We are using a state flag now to indicate a state that should be
|
2014-04-15 19:01:49 +00:00
|
|
|
|
// accelerated in Fast mode or slowed in Slow mode.
|
2006-04-16 13:29:50 +00:00
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
|
|
int AActor::GetTics(FState * newstate)
|
|
|
|
|
{
|
|
|
|
|
int tics = newstate->GetTics();
|
2012-06-16 08:35:51 +00:00
|
|
|
|
if (isFast() && newstate->Fast)
|
2006-04-16 13:29:50 +00:00
|
|
|
|
{
|
2012-06-16 08:35:51 +00:00
|
|
|
|
return tics - (tics>>1);
|
2006-04-16 13:29:50 +00:00
|
|
|
|
}
|
2014-04-15 19:01:49 +00:00
|
|
|
|
else if (isSlow() && newstate->Slow)
|
|
|
|
|
{
|
|
|
|
|
return tics<<1;
|
|
|
|
|
}
|
2006-04-16 13:29:50 +00:00
|
|
|
|
return tics;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor::SetState
|
|
|
|
|
//
|
|
|
|
|
// Returns true if the mobj is still present.
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
2010-04-19 02:46:50 +00:00
|
|
|
|
bool AActor::SetState (FState *newstate, bool nofunction)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
if (debugfile && player && (player->cheats & CF_PREDICTING))
|
2008-05-14 03:39:30 +00:00
|
|
|
|
fprintf (debugfile, "for pl %td: SetState while predicting!\n", player-players);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (newstate == NULL)
|
|
|
|
|
{
|
|
|
|
|
state = NULL;
|
|
|
|
|
Destroy ();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
int prevsprite, newsprite;
|
|
|
|
|
|
|
|
|
|
if (state != NULL)
|
|
|
|
|
{
|
2008-08-10 14:19:47 +00:00
|
|
|
|
prevsprite = state->sprite;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
prevsprite = -1;
|
|
|
|
|
}
|
|
|
|
|
state = newstate;
|
2006-04-16 13:29:50 +00:00
|
|
|
|
tics = GetTics(newstate);
|
2015-04-04 16:40:43 +00:00
|
|
|
|
renderflags = (renderflags & ~RF_FULLBRIGHT) | ActorRenderFlags::FromInt (newstate->GetFullbright());
|
2008-08-10 14:19:47 +00:00
|
|
|
|
newsprite = newstate->sprite;
|
2010-04-19 02:46:50 +00:00
|
|
|
|
if (newsprite != SPR_FIXED)
|
|
|
|
|
{ // okay to change sprite and/or frame
|
|
|
|
|
if (!newstate->GetSameFrame())
|
|
|
|
|
{ // okay to change frame
|
|
|
|
|
frame = newstate->GetFrame();
|
|
|
|
|
}
|
|
|
|
|
if (newsprite != SPR_NOCHANGE)
|
|
|
|
|
{ // okay to change sprite
|
|
|
|
|
if (!(flags4 & MF4_NOSKIN) && newsprite == SpawnState->sprite)
|
|
|
|
|
{ // [RH] If the new sprite is the same as the original sprite, and
|
|
|
|
|
// this actor is attached to a player, use the player's skin's
|
|
|
|
|
// sprite. If a player is not attached, do not change the sprite
|
|
|
|
|
// unless it is different from the previous state's sprite; a
|
|
|
|
|
// player may have been attached, died, and respawned elsewhere,
|
|
|
|
|
// and we do not want to lose the skin on the body. If it wasn't
|
|
|
|
|
// for Dehacked, I would move sprite changing out of the states
|
|
|
|
|
// altogether, since actors rarely change their sprites after
|
|
|
|
|
// spawning.
|
2010-09-23 01:56:04 +00:00
|
|
|
|
if (player != NULL && skins != NULL)
|
2010-04-19 02:46:50 +00:00
|
|
|
|
{
|
2013-05-12 18:27:03 +00:00
|
|
|
|
sprite = skins[player->userinfo.GetSkin()].sprite;
|
2010-04-19 02:46:50 +00:00
|
|
|
|
}
|
|
|
|
|
else if (newsprite != prevsprite)
|
|
|
|
|
{
|
|
|
|
|
sprite = newsprite;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2010-04-19 02:46:50 +00:00
|
|
|
|
else
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
sprite = newsprite;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-07-28 12:38:10 +00:00
|
|
|
|
|
2010-04-19 02:46:50 +00:00
|
|
|
|
if (!nofunction && newstate->CallAction(this, this))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2006-09-17 10:43:51 +00:00
|
|
|
|
// Check whether the called action function resulted in destroying the actor
|
2008-03-12 02:56:11 +00:00
|
|
|
|
if (ObjectFlags & OF_EuthanizeMe)
|
|
|
|
|
return false;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
newstate = newstate->GetNextState();
|
|
|
|
|
} while (tics == 0);
|
|
|
|
|
|
2012-03-07 01:03:56 +00:00
|
|
|
|
if (Renderer != NULL)
|
2010-04-11 06:43:42 +00:00
|
|
|
|
{
|
2011-07-07 15:37:47 +00:00
|
|
|
|
Renderer->StateChanged(this);
|
2010-04-11 06:43:42 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: AddInventory
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
void AActor::AddInventory (AInventory *item)
|
|
|
|
|
{
|
|
|
|
|
// Check if it's already attached to an actor
|
|
|
|
|
if (item->Owner != NULL)
|
|
|
|
|
{
|
|
|
|
|
// Is it attached to us?
|
|
|
|
|
if (item->Owner == this)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// No, then remove it from the other actor first
|
|
|
|
|
item->Owner->RemoveInventory (item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
item->Owner = this;
|
|
|
|
|
item->Inventory = Inventory;
|
|
|
|
|
Inventory = item;
|
|
|
|
|
|
|
|
|
|
// Each item receives an unique ID when added to an actor's inventory.
|
|
|
|
|
// This is used by the DEM_INVUSE command to identify the item. Simply
|
|
|
|
|
// using the item's position in the list won't work, because ticcmds get
|
|
|
|
|
// run sometime in the future, so by the time it runs, the inventory
|
|
|
|
|
// might not be in the same state as it was when DEM_INVUSE was sent.
|
|
|
|
|
Inventory->InventoryID = InventoryID++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: RemoveInventory
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
2015-02-26 11:13:17 +00:00
|
|
|
|
void AActor::RemoveInventory(AInventory *item)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
AInventory *inv, **invp;
|
|
|
|
|
|
2015-02-26 11:13:17 +00:00
|
|
|
|
if (item != NULL && item->Owner != NULL) // can happen if the owner was destroyed by some action from an item's use state.
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-02-26 11:13:17 +00:00
|
|
|
|
invp = &item->Owner->Inventory;
|
|
|
|
|
for (inv = *invp; inv != NULL; invp = &inv->Inventory, inv = *invp)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-02-26 11:13:17 +00:00
|
|
|
|
if (inv == item)
|
|
|
|
|
{
|
|
|
|
|
*invp = item->Inventory;
|
|
|
|
|
item->DetachFromOwner();
|
|
|
|
|
item->Owner = NULL;
|
|
|
|
|
item->Inventory = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-28 13:34:13 +00:00
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: TakeInventory
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
bool AActor::TakeInventory(const PClass *itemclass, int amount, bool fromdecorate, bool notakeinfinite)
|
|
|
|
|
{
|
|
|
|
|
AInventory *item = FindInventory(itemclass);
|
|
|
|
|
|
|
|
|
|
if (item == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!fromdecorate)
|
|
|
|
|
{
|
|
|
|
|
item->Amount -= amount;
|
|
|
|
|
if (item->Amount <= 0)
|
|
|
|
|
{
|
|
|
|
|
item->DepleteOrDestroy();
|
|
|
|
|
}
|
|
|
|
|
// It won't be used in non-decorate context, so return false here
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool result = false;
|
|
|
|
|
if (item->Amount > 0)
|
|
|
|
|
{
|
|
|
|
|
result = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (item->IsKindOf(RUNTIME_CLASS(AHexenArmor)))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Do not take ammo if the "no take infinite/take as ammo depletion" flag is set
|
|
|
|
|
// and infinite ammo is on
|
|
|
|
|
if (notakeinfinite &&
|
|
|
|
|
((dmflags & DF_INFINITE_AMMO) || (player && player->cheats & CF_INFINITEAMMO)) &&
|
|
|
|
|
item->IsKindOf(RUNTIME_CLASS(AAmmo)))
|
|
|
|
|
{
|
|
|
|
|
// Nothing to do here, except maybe res = false;? Would it make sense?
|
|
|
|
|
}
|
|
|
|
|
else if (!amount || amount>=item->Amount)
|
|
|
|
|
{
|
|
|
|
|
item->DepleteOrDestroy();
|
|
|
|
|
}
|
|
|
|
|
else item->Amount-=amount;
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-06-18 04:10:47 +00:00
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: DestroyAllInventory
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
void AActor::DestroyAllInventory ()
|
|
|
|
|
{
|
|
|
|
|
while (Inventory != NULL)
|
|
|
|
|
{
|
2006-06-20 20:30:39 +00:00
|
|
|
|
AInventory *item = Inventory;
|
|
|
|
|
item->Destroy ();
|
|
|
|
|
assert (item != Inventory);
|
2006-06-18 04:10:47 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: FirstInv
|
|
|
|
|
//
|
|
|
|
|
// Returns the first item in this actor's inventory that has IF_INVBAR set.
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
2008-03-12 02:56:11 +00:00
|
|
|
|
AInventory *AActor::FirstInv ()
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
if (Inventory == NULL)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
if (Inventory->ItemFlags & IF_INVBAR)
|
|
|
|
|
{
|
|
|
|
|
return Inventory;
|
|
|
|
|
}
|
|
|
|
|
return Inventory->NextInv ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: UseInventory
|
|
|
|
|
//
|
|
|
|
|
// Attempts to use an item. If the use succeeds, one copy of the item is
|
|
|
|
|
// removed from the inventory. If all copies are removed, then the item is
|
|
|
|
|
// destroyed.
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
bool AActor::UseInventory (AInventory *item)
|
|
|
|
|
{
|
|
|
|
|
// No using items if you're dead.
|
|
|
|
|
if (health <= 0)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2007-03-07 02:24:24 +00:00
|
|
|
|
// Don't use it if you don't actually have any of it.
|
2009-02-28 21:38:20 +00:00
|
|
|
|
if (item->Amount <= 0 || (item->ObjectFlags & OF_EuthanizeMe))
|
2007-03-07 02:24:24 +00:00
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (!item->Use (false))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2008-01-09 02:53:38 +00:00
|
|
|
|
|
|
|
|
|
if (dmflags2 & DF2_INFINITE_INVENTORY)
|
|
|
|
|
return true;
|
|
|
|
|
|
2015-04-28 13:34:13 +00:00
|
|
|
|
if (--item->Amount <= 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-04-28 13:34:13 +00:00
|
|
|
|
item->DepleteOrDestroy ();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: DropInventory
|
|
|
|
|
//
|
|
|
|
|
// Removes a single copy of an item and throws it out in front of the actor.
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
AInventory *AActor::DropInventory (AInventory *item)
|
|
|
|
|
{
|
|
|
|
|
angle_t an;
|
|
|
|
|
AInventory *drop = item->CreateTossable ();
|
|
|
|
|
|
|
|
|
|
if (drop == NULL)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
an = angle >> ANGLETOFINESHIFT;
|
2012-05-04 02:56:28 +00:00
|
|
|
|
drop->SetOrigin(x, y, z + 10*FRACUNIT);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
drop->angle = angle;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
drop->velx = velx + 5 * finecosine[an];
|
|
|
|
|
drop->vely = vely + 5 * finesine[an];
|
|
|
|
|
drop->velz = velz + FRACUNIT;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
drop->flags &= ~MF_NOGRAVITY; // Don't float
|
2010-09-19 00:06:45 +00:00
|
|
|
|
drop->ClearCounters(); // do not count for statistics again
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return drop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: FindInventory
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
2010-06-13 10:11:50 +00:00
|
|
|
|
AInventory *AActor::FindInventory (const PClass *type, bool subclass)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
AInventory *item;
|
|
|
|
|
|
2006-07-09 20:15:38 +00:00
|
|
|
|
if (type == NULL) return NULL;
|
|
|
|
|
|
2006-07-08 02:17:35 +00:00
|
|
|
|
assert (type->ActorInfo != NULL);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
for (item = Inventory; item != NULL; item = item->Inventory)
|
|
|
|
|
{
|
2010-06-13 10:11:50 +00:00
|
|
|
|
if (!subclass)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2010-06-13 10:11:50 +00:00
|
|
|
|
if (item->GetClass() == type)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (item->IsKindOf(type))
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-12 02:56:11 +00:00
|
|
|
|
AInventory *AActor::FindInventory (FName type)
|
2006-12-25 13:43:11 +00:00
|
|
|
|
{
|
|
|
|
|
return FindInventory(PClass::FindClass(type));
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: GiveInventoryType
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
|
AInventory *AActor::GiveInventoryType (const PClass *type)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
- Ported vlinetallasm4 to AMD64 assembly. Even with the increased number of
registers AMD64 provides, this routine still needs to be written as self-
modifying code for maximum performance. The additional registers do allow
for further optimization over the x86 version by allowing all four pixels
to be in flight at the same time. The end result is that AMD64 ASM is about
2.18 times faster than AMD64 C and about 1.06 times faster than x86 ASM.
(For further comparison, AMD64 C and x86 C are practically the same for
this function.) Should I port any more assembly to AMD64, mvlineasm4 is the
most likely candidate, but it's not used enough at this point to bother.
Also, this may or may not work with Linux at the moment, since it doesn't
have the eh_handler metadata. Win64 is easier, since I just need to
structure the function prologue and epilogue properly and use some
assembler directives/macros to automatically generate the metadata. And
that brings up another point: You need YASM to assemble the AMD64 code,
because NASM doesn't support the Win64 metadata directives.
- Added an SSE version of DoBlending. This is strictly C intrinsics.
VC++ still throws around unneccessary register moves. GCC seems to be
pretty close to optimal, requiring only about 2 cycles/color. They're
both faster than my hand-written MMX routine, so I don't need to feel
bad about not hand-optimizing this for x64 builds.
- Removed an extra instruction from DoBlending_MMX, transposed two
instructions, and unrolled it once, shaving off about 80 cycles from the
time required to blend 256 palette entries. Why? Because I tried writing
a C version of the routine using compiler intrinsics and was appalled by
all the extra movq's VC++ added to the code. GCC was better, but still
generated extra instructions. I only wanted a C version because I can't
use inline assembly with VC++'s x64 compiler, and x64 assembly is a bit
of a pain. (It's a pain because Linux and Windows have different calling
conventions, and you need to maintain extra metadata for functions.) So,
the assembly version stays and the C version stays out.
- Removed all the pixel doubling r_detail modes, since the one platform they
were intended to assist (486) actually sees very little benefit from them.
- Rewrote CheckMMX in C and renamed it to CheckCPU.
- Fixed: CPUID function 0x80000005 is specified to return detailed L1 cache
only for AMD processors, so we must not use it on other architectures, or
we end up overwriting the L1 cache line size with 0 or some other number
we don't actually understand.
SVN r1134 (trunk)
2008-08-09 03:13:43 +00:00
|
|
|
|
AInventory *item = NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-08-06 19:25:59 +00:00
|
|
|
|
if (type != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-08-06 19:25:59 +00:00
|
|
|
|
item = static_cast<AInventory *>(Spawn (type, 0,0,0, NO_REPLACE));
|
2008-09-13 22:08:41 +00:00
|
|
|
|
if (!item->CallTryPickup (this))
|
2008-08-06 19:25:59 +00:00
|
|
|
|
{
|
|
|
|
|
item->Destroy ();
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: GiveAmmo
|
|
|
|
|
//
|
|
|
|
|
// Returns true if the ammo was added, false if not.
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
|
bool AActor::GiveAmmo (const PClass *type, int amount)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2007-10-29 20:27:40 +00:00
|
|
|
|
if (type != NULL)
|
2006-05-18 04:25:26 +00:00
|
|
|
|
{
|
2007-10-29 20:27:40 +00:00
|
|
|
|
AInventory *item = static_cast<AInventory *>(Spawn (type, 0, 0, 0, NO_REPLACE));
|
|
|
|
|
if (item)
|
|
|
|
|
{
|
|
|
|
|
item->Amount = amount;
|
|
|
|
|
item->flags |= MF_DROPPED;
|
2008-09-13 22:08:41 +00:00
|
|
|
|
if (!item->CallTryPickup (this))
|
2007-10-29 20:27:40 +00:00
|
|
|
|
{
|
|
|
|
|
item->Destroy ();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2006-05-18 04:25:26 +00:00
|
|
|
|
}
|
2007-10-29 20:27:40 +00:00
|
|
|
|
return false;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-05-31 09:46:07 +00:00
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: ClearInventory
|
|
|
|
|
//
|
|
|
|
|
// Clears the inventory of a single actor.
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
void AActor::ClearInventory()
|
|
|
|
|
{
|
|
|
|
|
// In case destroying an inventory item causes another to be destroyed
|
|
|
|
|
// (e.g. Weapons destroy their sisters), keep track of the pointer to
|
|
|
|
|
// the next inventory item rather than the next inventory item itself.
|
|
|
|
|
// For example, if a weapon is immediately followed by its sister, the
|
|
|
|
|
// next weapon we had tracked would be to the sister, so it is now
|
|
|
|
|
// invalid and we won't be able to find the complete inventory by
|
|
|
|
|
// following it.
|
|
|
|
|
//
|
|
|
|
|
// When we destroy an item, we leave invp alone, since the destruction
|
|
|
|
|
// process will leave it pointing to the next item we want to check. If
|
|
|
|
|
// we don't destroy an item, then we move invp to point to its Inventory
|
|
|
|
|
// pointer.
|
|
|
|
|
//
|
|
|
|
|
// It should be safe to assume that an item being destroyed will only
|
|
|
|
|
// destroy items further down in the chain, because if it was going to
|
|
|
|
|
// destroy something we already processed, we've already destroyed it,
|
|
|
|
|
// so it won't have anything to destroy.
|
|
|
|
|
|
|
|
|
|
AInventory **invp = &Inventory;
|
|
|
|
|
|
|
|
|
|
while (*invp != NULL)
|
|
|
|
|
{
|
|
|
|
|
AInventory *inv = *invp;
|
|
|
|
|
if (!(inv->ItemFlags & IF_UNDROPPABLE))
|
|
|
|
|
{
|
|
|
|
|
// For the sake of undroppable weapons, never remove ammo once
|
|
|
|
|
// it has been acquired; just set its amount to 0.
|
|
|
|
|
if (inv->IsKindOf(RUNTIME_CLASS(AAmmo)))
|
|
|
|
|
{
|
|
|
|
|
AAmmo *ammo = static_cast<AAmmo*>(inv);
|
|
|
|
|
ammo->Amount = 0;
|
|
|
|
|
invp = &inv->Inventory;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
inv->Destroy ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (inv->GetClass() == RUNTIME_CLASS(AHexenArmor))
|
|
|
|
|
{
|
|
|
|
|
AHexenArmor *harmor = static_cast<AHexenArmor *> (inv);
|
|
|
|
|
harmor->Slots[3] = harmor->Slots[2] = harmor->Slots[1] = harmor->Slots[0] = 0;
|
|
|
|
|
invp = &inv->Inventory;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
invp = &inv->Inventory;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (player != NULL)
|
|
|
|
|
{
|
|
|
|
|
player->ReadyWeapon = NULL;
|
|
|
|
|
player->PendingWeapon = WP_NOCHANGE;
|
|
|
|
|
player->psprites[ps_weapon].state = NULL;
|
|
|
|
|
player->psprites[ps_flash].state = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: CopyFriendliness
|
|
|
|
|
//
|
|
|
|
|
// Makes this actor hate (or like) the same things another actor does.
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
2011-03-18 08:02:23 +00:00
|
|
|
|
void AActor::CopyFriendliness (AActor *other, bool changeTarget, bool resetHealth)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2006-04-16 13:29:50 +00:00
|
|
|
|
level.total_monsters -= CountsAsKill();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
TIDtoHate = other->TIDtoHate;
|
2008-03-12 02:56:11 +00:00
|
|
|
|
LastLookActor = other->LastLookActor;
|
|
|
|
|
LastLookPlayerNumber = other->LastLookPlayerNumber;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
flags = (flags & ~MF_FRIENDLY) | (other->flags & MF_FRIENDLY);
|
|
|
|
|
flags3 = (flags3 & ~(MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)) | (other->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS));
|
2013-02-05 02:33:11 +00:00
|
|
|
|
flags4 = (flags4 & ~(MF4_NOHATEPLAYERS | MF4_BOSSSPAWNED)) | (other->flags4 & (MF4_NOHATEPLAYERS | MF4_BOSSSPAWNED));
|
2006-02-24 04:48:15 +00:00
|
|
|
|
FriendPlayer = other->FriendPlayer;
|
2011-01-22 03:35:33 +00:00
|
|
|
|
DesignatedTeam = other->DesignatedTeam;
|
2013-08-12 18:41:33 +00:00
|
|
|
|
if (changeTarget && other->target != NULL && !(other->target->flags3 & MF3_NOTARGET) && !(other->target->flags7 & MF7_NEVERTARGET))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2006-08-01 22:11:49 +00:00
|
|
|
|
// LastHeard must be set as well so that A_Look can react to the new target if called
|
|
|
|
|
LastHeard = target = other->target;
|
2009-07-04 18:17:44 +00:00
|
|
|
|
}
|
2011-03-18 08:02:23 +00:00
|
|
|
|
if (resetHealth) health = SpawnHealth();
|
2006-04-16 13:29:50 +00:00
|
|
|
|
level.total_monsters += CountsAsKill();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: ObtainInventory
|
|
|
|
|
//
|
|
|
|
|
// Removes the items from the other actor and puts them in this actor's
|
|
|
|
|
// inventory. The actor receiving the inventory must not have any items.
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
void AActor::ObtainInventory (AActor *other)
|
|
|
|
|
{
|
2007-03-14 01:48:19 +00:00
|
|
|
|
assert (Inventory == NULL);
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
Inventory = other->Inventory;
|
|
|
|
|
InventoryID = other->InventoryID;
|
|
|
|
|
other->Inventory = NULL;
|
|
|
|
|
other->InventoryID = 0;
|
|
|
|
|
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
|
if (other->IsKindOf(RUNTIME_CLASS(APlayerPawn)) && this->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
|
|
|
|
|
{
|
|
|
|
|
APlayerPawn *you = static_cast<APlayerPawn *>(other);
|
|
|
|
|
APlayerPawn *me = static_cast<APlayerPawn *>(this);
|
|
|
|
|
me->InvFirst = you->InvFirst;
|
|
|
|
|
me->InvSel = you->InvSel;
|
|
|
|
|
you->InvFirst = NULL;
|
|
|
|
|
you->InvSel = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
AInventory *item = Inventory;
|
|
|
|
|
while (item != NULL)
|
|
|
|
|
{
|
|
|
|
|
item->Owner = this;
|
|
|
|
|
item = item->Inventory;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: CheckLocalView
|
|
|
|
|
//
|
|
|
|
|
// Returns true if this actor is local for the player. Here, local means the
|
|
|
|
|
// player is either looking out this actor's eyes, or this actor is the player
|
|
|
|
|
// and the player is looking out the eyes of something non-"sentient."
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
bool AActor::CheckLocalView (int playernum) const
|
|
|
|
|
{
|
|
|
|
|
if (players[playernum].camera == this)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (players[playernum].mo != this || players[playernum].camera == NULL)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (players[playernum].camera->player == NULL &&
|
|
|
|
|
!(players[playernum].camera->flags3 & MF3_ISMONSTER))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-08 01:28:26 +00:00
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: IsVisibleToPlayer
|
|
|
|
|
//
|
|
|
|
|
// Returns true if this actor should be seen by the console player.
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
bool AActor::IsVisibleToPlayer() const
|
|
|
|
|
{
|
|
|
|
|
// [BB] Safety check. This should never be NULL. Nevertheless, we return true to leave the default ZDoom behavior unaltered.
|
|
|
|
|
if ( players[consoleplayer].camera == NULL )
|
|
|
|
|
return true;
|
|
|
|
|
|
2013-05-12 18:27:03 +00:00
|
|
|
|
if (VisibleToTeam != 0 && teamplay &&
|
2013-05-26 04:03:47 +00:00
|
|
|
|
(signed)(VisibleToTeam-1) != players[consoleplayer].userinfo.GetTeam())
|
2011-09-08 01:28:26 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const player_t* pPlayer = players[consoleplayer].camera->player;
|
|
|
|
|
|
2011-09-10 04:24:40 +00:00
|
|
|
|
if(pPlayer && pPlayer->mo && GetClass()->ActorInfo->VisibleToPlayerClass.Size() > 0)
|
|
|
|
|
{
|
|
|
|
|
bool visible = false;
|
|
|
|
|
for(unsigned int i = 0;i < GetClass()->ActorInfo->VisibleToPlayerClass.Size();++i)
|
|
|
|
|
{
|
|
|
|
|
const PClass *cls = GetClass()->ActorInfo->VisibleToPlayerClass[i];
|
|
|
|
|
if(cls && pPlayer->mo->GetClass()->IsDescendantOf(cls))
|
|
|
|
|
{
|
|
|
|
|
visible = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(!visible)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-09-08 01:28:26 +00:00
|
|
|
|
|
|
|
|
|
// [BB] Passed all checks.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: ConversationAnimation
|
|
|
|
|
//
|
|
|
|
|
// Plays a conversation-related animation:
|
|
|
|
|
// 0 = greeting
|
|
|
|
|
// 1 = "yes"
|
|
|
|
|
// 2 = "no"
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
void AActor::ConversationAnimation (int animnum)
|
|
|
|
|
{
|
2006-10-31 14:53:21 +00:00
|
|
|
|
FState * state = NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
switch (animnum)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
2006-10-31 14:53:21 +00:00
|
|
|
|
state = FindState(NAME_Greetings);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
break;
|
|
|
|
|
case 1:
|
2006-10-31 14:53:21 +00:00
|
|
|
|
state = FindState(NAME_Yes);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
break;
|
|
|
|
|
case 2:
|
2006-10-31 14:53:21 +00:00
|
|
|
|
state = FindState(NAME_No);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2006-10-31 14:53:21 +00:00
|
|
|
|
if (state != NULL) SetState(state);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: Touch
|
|
|
|
|
//
|
|
|
|
|
// Something just touched this actor. Normally used only for inventory items,
|
|
|
|
|
// but some Strife monsters also use it.
|
|
|
|
|
//
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
void AActor::Touch (AActor *toucher)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-15 17:21:45 +00:00
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: Grind
|
|
|
|
|
//
|
|
|
|
|
// Handles the an actor being crushed by a door, crusher or polyobject.
|
|
|
|
|
// Originally part of P_DoCrunch(), it has been made into its own actor
|
|
|
|
|
// function so that it could be called from a polyobject without hassle.
|
|
|
|
|
// Bool items is true if it should destroy() dropped items, false otherwise.
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
bool AActor::Grind(bool items)
|
|
|
|
|
{
|
|
|
|
|
// crunch bodies to giblets
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if ((flags & MF_CORPSE) && !(flags3 & MF3_DONTGIB) && (health <= 0))
|
2009-05-15 17:21:45 +00:00
|
|
|
|
{
|
2009-09-14 20:47:53 +00:00
|
|
|
|
FState * state = FindState(NAME_Crush);
|
2014-05-14 10:54:03 +00:00
|
|
|
|
|
|
|
|
|
// In Heretic and Chex Quest we don't change the actor's sprite, just its size.
|
|
|
|
|
if (state == NULL && gameinfo.dontcrunchcorpses)
|
|
|
|
|
{
|
|
|
|
|
flags &= ~MF_SOLID;
|
|
|
|
|
flags3 |= MF3_DONTGIB;
|
|
|
|
|
height = radius = 0;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-14 20:47:53 +00:00
|
|
|
|
bool isgeneric = false;
|
|
|
|
|
// ZDoom behavior differs from standard as crushed corpses cannot be raised.
|
|
|
|
|
// The reason for the change was originally because of a problem with players,
|
|
|
|
|
// see rh_log entry for February 21, 1999. Don't know if it is still relevant.
|
2013-04-16 17:55:17 +00:00
|
|
|
|
if (state == NULL // Only use the default crushed state if:
|
2009-09-14 20:47:53 +00:00
|
|
|
|
&& !(flags & MF_NOBLOOD) // 1. the monster bleeeds,
|
|
|
|
|
&& (i_compatflags & COMPATF_CORPSEGIBS) // 2. the compat setting is on,
|
2009-10-30 00:59:34 +00:00
|
|
|
|
&& player == NULL) // 3. and the thing isn't a player.
|
2009-09-14 20:47:53 +00:00
|
|
|
|
{
|
|
|
|
|
isgeneric = true;
|
|
|
|
|
state = FindState(NAME_GenericCrush);
|
|
|
|
|
if (state != NULL && (sprites[state->sprite].numframes <= 0))
|
|
|
|
|
state = NULL; // If one of these tests fails, do not use that state.
|
|
|
|
|
}
|
|
|
|
|
if (state != NULL && !(flags & MF_ICECORPSE))
|
2009-05-15 17:21:45 +00:00
|
|
|
|
{
|
|
|
|
|
if (this->flags4 & MF4_BOSSDEATH)
|
|
|
|
|
{
|
|
|
|
|
CALL_ACTION(A_BossDeath, this);
|
|
|
|
|
}
|
2009-09-14 20:47:53 +00:00
|
|
|
|
flags &= ~MF_SOLID;
|
|
|
|
|
flags3 |= MF3_DONTGIB;
|
|
|
|
|
height = radius = 0;
|
|
|
|
|
SetState (state);
|
|
|
|
|
if (isgeneric) // Not a custom crush state, so colorize it appropriately.
|
|
|
|
|
{
|
|
|
|
|
S_Sound (this, CHAN_BODY, "misc/fallingsplat", 1, ATTN_IDLE);
|
2010-03-21 08:09:45 +00:00
|
|
|
|
PalEntry bloodcolor = GetBloodColor();
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (bloodcolor!=0) Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
|
|
|
|
|
}
|
2009-05-15 17:21:45 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (!(flags & MF_NOBLOOD))
|
2009-05-15 17:21:45 +00:00
|
|
|
|
{
|
|
|
|
|
if (this->flags4 & MF4_BOSSDEATH)
|
|
|
|
|
{
|
|
|
|
|
CALL_ACTION(A_BossDeath, this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const PClass *i = PClass::FindClass("RealGibs");
|
|
|
|
|
|
|
|
|
|
if (i != NULL)
|
|
|
|
|
{
|
2010-08-26 20:59:15 +00:00
|
|
|
|
i = i->GetReplacement();
|
2009-05-15 17:21:45 +00:00
|
|
|
|
|
|
|
|
|
const AActor *defaults = GetDefaultByType (i);
|
|
|
|
|
if (defaults->SpawnState == NULL ||
|
|
|
|
|
sprites[defaults->SpawnState->sprite].numframes == 0)
|
|
|
|
|
{
|
|
|
|
|
i = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (i == NULL)
|
|
|
|
|
{
|
|
|
|
|
// if there's no gib sprite don't crunch it.
|
2009-09-14 20:47:53 +00:00
|
|
|
|
flags &= ~MF_SOLID;
|
|
|
|
|
flags3 |= MF3_DONTGIB;
|
|
|
|
|
height = radius = 0;
|
2009-05-15 17:21:45 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-14 20:47:53 +00:00
|
|
|
|
AActor *gib = Spawn (i, x, y, z, ALLOW_REPLACE);
|
2009-05-15 17:21:45 +00:00
|
|
|
|
if (gib != NULL)
|
|
|
|
|
{
|
2009-09-14 20:47:53 +00:00
|
|
|
|
gib->RenderStyle = RenderStyle;
|
|
|
|
|
gib->alpha = alpha;
|
2009-05-15 17:21:45 +00:00
|
|
|
|
gib->height = 0;
|
|
|
|
|
gib->radius = 0;
|
2013-05-12 18:29:28 +00:00
|
|
|
|
|
|
|
|
|
PalEntry bloodcolor = GetBloodColor();
|
|
|
|
|
if (bloodcolor != 0)
|
|
|
|
|
gib->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
|
2009-05-15 17:21:45 +00:00
|
|
|
|
}
|
|
|
|
|
S_Sound (this, CHAN_BODY, "misc/fallingsplat", 1, ATTN_IDLE);
|
|
|
|
|
}
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (flags & MF_ICECORPSE)
|
2009-05-15 17:21:45 +00:00
|
|
|
|
{
|
2009-09-14 20:47:53 +00:00
|
|
|
|
tics = 1;
|
|
|
|
|
velx = vely = velz = 0;
|
2009-05-15 17:21:45 +00:00
|
|
|
|
}
|
2009-09-14 20:47:53 +00:00
|
|
|
|
else if (player)
|
2009-05-15 17:21:45 +00:00
|
|
|
|
{
|
2009-09-14 20:47:53 +00:00
|
|
|
|
flags |= MF_NOCLIP;
|
|
|
|
|
flags3 |= MF3_DONTGIB;
|
|
|
|
|
renderflags |= RF_INVISIBLE;
|
2009-05-15 17:21:45 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-09-14 20:47:53 +00:00
|
|
|
|
Destroy ();
|
2009-05-15 17:21:45 +00:00
|
|
|
|
}
|
|
|
|
|
return false; // keep checking
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-14 20:47:53 +00:00
|
|
|
|
// killough 11/98: kill touchy things immediately
|
|
|
|
|
if (flags6 & MF6_TOUCHY && (flags6 & MF6_ARMED || IsSentient()))
|
|
|
|
|
{
|
|
|
|
|
flags6 &= ~MF6_ARMED; // Disarm
|
|
|
|
|
P_DamageMobj (this, NULL, NULL, health, NAME_Crush, DMG_FORCED); // kill object
|
|
|
|
|
return true; // keep checking
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(flags & MF_SOLID) || (flags & MF_NOCLIP))
|
2009-05-15 17:21:45 +00:00
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (!(flags & MF_SHOOTABLE))
|
2009-05-15 17:21:45 +00:00
|
|
|
|
{
|
|
|
|
|
return false; // assume it is bloody gibs or something
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//============================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: Massacre
|
|
|
|
|
//
|
|
|
|
|
// Called by the massacre cheat to kill monsters. Returns true if the monster
|
|
|
|
|
// was killed and false if it was already dead.
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
bool AActor::Massacre ()
|
|
|
|
|
{
|
2006-04-11 08:36:23 +00:00
|
|
|
|
int prevhealth;
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (health > 0)
|
|
|
|
|
{
|
|
|
|
|
flags |= MF_SHOOTABLE;
|
|
|
|
|
flags2 &= ~(MF2_DORMANT|MF2_INVULNERABLE);
|
|
|
|
|
do
|
|
|
|
|
{
|
2006-04-11 08:36:23 +00:00
|
|
|
|
prevhealth = health;
|
2009-08-07 03:57:03 +00:00
|
|
|
|
P_DamageMobj (this, NULL, NULL, TELEFRAG_DAMAGE, NAME_Massacre);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2006-04-11 08:36:23 +00:00
|
|
|
|
while (health != prevhealth && health > 0); //abort if the actor wasn't hurt.
|
2014-10-31 20:08:13 +00:00
|
|
|
|
return health <= 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// PROC P_ExplodeMissile
|
|
|
|
|
//
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
2006-10-22 10:32:41 +00:00
|
|
|
|
void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
if (mo->flags3 & MF3_EXPLOCOUNT)
|
|
|
|
|
{
|
|
|
|
|
if (++mo->special2 < mo->special1)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = mo->vely = mo->velz = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
mo->effects = 0; // [RH]
|
2012-08-14 02:50:29 +00:00
|
|
|
|
mo->flags &= ~MF_SHOOTABLE;
|
2006-10-22 10:32:41 +00:00
|
|
|
|
|
|
|
|
|
FState *nextstate=NULL;
|
|
|
|
|
|
2009-12-25 22:57:42 +00:00
|
|
|
|
if (target != NULL && ((target->flags & (MF_SHOOTABLE|MF_CORPSE)) || (target->flags6 & MF6_KILLED)) )
|
2006-10-22 10:32:41 +00:00
|
|
|
|
{
|
2014-12-15 16:26:22 +00:00
|
|
|
|
if (mo->flags7 & MF7_HITTARGET) mo->target = target;
|
|
|
|
|
if (mo->flags7 & MF7_HITMASTER) mo->master = target;
|
|
|
|
|
if (mo->flags7 & MF7_HITTRACER) mo->tracer = target;
|
2006-10-31 14:53:21 +00:00
|
|
|
|
if (target->flags & MF_NOBLOOD) nextstate = mo->FindState(NAME_Crash);
|
2006-12-16 14:06:21 +00:00
|
|
|
|
if (nextstate == NULL) nextstate = mo->FindState(NAME_Death, NAME_Extreme);
|
2006-10-22 10:32:41 +00:00
|
|
|
|
}
|
2006-10-31 14:53:21 +00:00
|
|
|
|
if (nextstate == NULL) nextstate = mo->FindState(NAME_Death);
|
2006-10-22 10:32:41 +00:00
|
|
|
|
|
2009-12-20 05:11:30 +00:00
|
|
|
|
if (line != NULL && line->special == Line_Horizon && !(mo->flags3 & MF3_SKYEXPLODE))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
// [RH] Don't explode missiles on horizon lines.
|
|
|
|
|
mo->Destroy ();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line != NULL && cl_missiledecals)
|
|
|
|
|
{
|
|
|
|
|
int side = P_PointOnLineSide (mo->x, mo->y, line);
|
2009-09-06 20:45:56 +00:00
|
|
|
|
if (line->sidedef[side] == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
side ^= 1;
|
2009-09-06 20:45:56 +00:00
|
|
|
|
if (line->sidedef[side] != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
FDecalBase *base = mo->DecalGenerator;
|
|
|
|
|
if (base != NULL)
|
|
|
|
|
{
|
|
|
|
|
// Find the nearest point on the line, and stick a decal there
|
|
|
|
|
fixed_t x, y, z;
|
|
|
|
|
SQWORD num, den;
|
|
|
|
|
|
|
|
|
|
den = (SQWORD)line->dx*line->dx + (SQWORD)line->dy*line->dy;
|
|
|
|
|
if (den != 0)
|
|
|
|
|
{
|
|
|
|
|
SDWORD frac;
|
|
|
|
|
|
|
|
|
|
num = (SQWORD)(mo->x-line->v1->x)*line->dx+(SQWORD)(mo->y-line->v1->y)*line->dy;
|
|
|
|
|
if (num <= 0)
|
|
|
|
|
{
|
|
|
|
|
frac = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (num >= den)
|
|
|
|
|
{
|
|
|
|
|
frac = 1<<30;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
frac = (SDWORD)(num / (den>>30));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x = line->v1->x + MulScale30 (line->dx, frac);
|
|
|
|
|
y = line->v1->y + MulScale30 (line->dy, frac);
|
|
|
|
|
z = mo->z;
|
|
|
|
|
|
2009-01-04 15:00:29 +00:00
|
|
|
|
F3DFloor * ffloor=NULL;
|
2009-09-06 20:45:56 +00:00
|
|
|
|
if (line->sidedef[side^1] != NULL)
|
2009-01-04 15:00:29 +00:00
|
|
|
|
{
|
2009-09-06 20:45:56 +00:00
|
|
|
|
sector_t * backsector = line->sidedef[side^1]->sector;
|
2009-01-04 15:00:29 +00:00
|
|
|
|
extsector_t::xfloor &xf = backsector->e->XFloor;
|
|
|
|
|
// find a 3D-floor to stick to
|
|
|
|
|
for(unsigned int i=0;i<xf.ffloors.Size();i++)
|
|
|
|
|
{
|
|
|
|
|
F3DFloor * rover=xf.ffloors[i];
|
|
|
|
|
|
|
|
|
|
if ((rover->flags&(FF_EXISTS|FF_SOLID|FF_RENDERSIDES))==(FF_EXISTS|FF_SOLID|FF_RENDERSIDES))
|
|
|
|
|
{
|
|
|
|
|
if (z<=rover->top.plane->ZatPoint(x, y) && z>=rover->bottom.plane->ZatPoint( x, y))
|
|
|
|
|
{
|
|
|
|
|
ffloor=rover;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-12 01:50:09 +00:00
|
|
|
|
DImpactDecal::StaticCreate (base->GetDecal (),
|
2009-09-06 20:45:56 +00:00
|
|
|
|
x, y, z, line->sidedef[side], ffloor);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-25 11:10:38 +00:00
|
|
|
|
// play the sound before changing the state, so that AActor::Destroy can call S_RelinkSounds on it and the death state can override it.
|
|
|
|
|
if (mo->DeathSound)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2014-10-25 11:10:38 +00:00
|
|
|
|
S_Sound (mo, CHAN_VOICE, mo->DeathSound, 1,
|
|
|
|
|
(mo->flags3 & MF3_FULLVOLDEATH) ? ATTN_NONE : ATTN_NORM);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mo->SetState (nextstate);
|
|
|
|
|
if (!(mo->ObjectFlags & OF_EuthanizeMe))
|
|
|
|
|
{
|
|
|
|
|
// The rest only applies if the missile actor still exists.
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// [RH] Change render style of exploding rockets
|
2006-10-15 20:27:16 +00:00
|
|
|
|
if (mo->flags5 & MF5_DEHEXPLOSION)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
if (deh.ExplosionStyle == 255)
|
|
|
|
|
{
|
|
|
|
|
if (addrocketexplosion)
|
|
|
|
|
{
|
|
|
|
|
mo->RenderStyle = STYLE_Add;
|
|
|
|
|
mo->alpha = FRACUNIT;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mo->RenderStyle = STYLE_Translucent;
|
|
|
|
|
mo->alpha = FRACUNIT*2/3;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
- Updated lempar.c to v1.31.
- Added .txt files to the list of types (wad, zip, and pk3) that can be
loaded without listing them after -file.
- Fonts that are created by the ACS setfont command to wrap a texture now
support animated textures.
- FON2 fonts can now use their full palette for CR_UNTRANSLATED when drawn
with the hardware 2D path instead of being restricted to the game palette.
- Fixed: Toggling vid_vsync would reset the displayed fullscreen gamma to 1
on a Radeon 9000.
- Added back the off-by-one palette handling, but in a much more limited
scope than before. The skipped entry is assumed to always be at 248, and
it is assumed that all Shader Model 1.4 cards suffer from this. That's
because all SM1.4 cards are based on variants of the ATI R200 core, and the
RV250 in a Radeon 9000 craps up like this. I see no reason to assume that
other flavors of the R200 are any different. (Interesting note: With the
Radeon 9000, D3DTADDRESS_CLAMP is an invalid address mode when using the
debug Direct3D 9 runtime, but it works perfectly fine with the retail
Direct3D 9 runtime.) (Insight: The R200 probably uses bytes for all its
math inside pixel shaders. That would explain perfectly why I can't use
constants greater than 1 with PS1.4 and why it can't do an exact mapping to
every entry in the color palette.
- Fixed: The software shaded drawer did not work for 2D, because its selected
"color"map was replaced with the identitymap before being used.
- Fixed: I cannot use Printf to output messages before the framebuffer was
completely setup, meaning that Shader Model 1.4 cards could not change
resolution.
- I have decided to let remap palettes specify variable alpha values for
their colors. D3DFB no longer forces them to 255.
- Updated re2c to version 0.12.3.
- Fixed: A_Wander used threshold as a timer, when it should have used
reactiontime.
- Fixed: A_CustomRailgun would not fire at all for actors without a target
when the aim parameter was disabled.
- Made the warp command work in multiplayer, again courtesy of Karate Chris.
- Fixed: Trying to spawn a bot while not in a game made for a crashing time.
(Patch courtesy of Karate Chris.)
- Removed some floating point math from hu_scores.cpp that somebody's GCC
gave warnings for (not mine, though).
- Fixed: The SBarInfo drawbar command crashed if the sprite image was
unavailable.
- Fixed: FString::operator=(const char *) did not release its old buffer when
being assigned to the null string.
- The scanner no longer has an upper limit on the length of strings it
accepts, though short strings will be faster than long ones.
- Moved all the text scanning functions into a class. Mainly, this means that
multiple script scanner states can be stored without being forced to do so
recursively. I think I might be taking advantage of that in the near
future. Possibly. Maybe.
- Removed some potential buffer overflows from the decal parser.
- Applied Blzut3's SBARINFO update #9:
* Fixed: When using even length values in drawnumber it would cap to a 98
value instead of a 99 as intended.
* The SBarInfo parser can now accept negatives for coordinates. This
doesn't allow much right now, but later I plan to add better fullscreen
hud support in which the negatives will be more useful. This also cleans
up the source a bit since all calls for (x, y) coordinates are with the
function getCoordinates().
- Added support for stencilling actors.
- Added support for non-black colors specified with DTA_ColorOverlay to the
software renderer.
- Fixed: The inverse, gold, red, and green fixed colormaps each allocated
space for 32 different colormaps, even though each only used the first one.
- Added two new blending flags to make reverse subtract blending more useful:
STYLEF_InvertSource and STYLEF_InvertOverlay. These invert the color that
gets blended with the background, since that seems like a good idea for
reverse subtraction. They also work with the other two blending operations.
- Added subtract and reverse subtract blending operations to the renderer.
Since the ERenderStyle enumeration was getting rather unwieldy, I converted
it into a new FRenderStyle structure that lets each parameter of the
blending equation be set separately. This simplified the set up for the
blend quite a bit, and it means a number of new combinations are available
by setting the parameters properly.
SVN r710 (trunk)
2008-01-25 23:57:44 +00:00
|
|
|
|
mo->RenderStyle = ERenderStyle(deh.ExplosionStyle);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
mo->alpha = deh.ExplosionAlpha;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mo->flags4 & MF4_RANDOMIZE)
|
|
|
|
|
{
|
|
|
|
|
mo->tics -= (pr_explodemissile() & 3) * TICRATE / 35;
|
|
|
|
|
if (mo->tics < 1)
|
|
|
|
|
mo->tics = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mo->flags &= ~MF_MISSILE;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-23 08:30:36 +00:00
|
|
|
|
|
|
|
|
|
void AActor::PlayBounceSound(bool onfloor)
|
|
|
|
|
{
|
2009-09-06 01:49:17 +00:00
|
|
|
|
if (!onfloor && (BounceFlags & BOUNCE_NoWallSound))
|
2009-05-23 08:30:36 +00:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-06 01:49:17 +00:00
|
|
|
|
if (!(BounceFlags & BOUNCE_Quiet))
|
2009-05-23 08:30:36 +00:00
|
|
|
|
{
|
2009-09-06 01:49:17 +00:00
|
|
|
|
if (BounceFlags & BOUNCE_UseSeeSound)
|
2009-05-23 08:30:36 +00:00
|
|
|
|
{
|
|
|
|
|
S_Sound (this, CHAN_VOICE, SeeSound, 1, ATTN_IDLE);
|
|
|
|
|
}
|
|
|
|
|
else if (onfloor || WallBounceSound <= 0)
|
|
|
|
|
{
|
|
|
|
|
S_Sound (this, CHAN_VOICE, BounceSound, 1, ATTN_IDLE);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
S_Sound (this, CHAN_VOICE, WallBounceSound, 1, ATTN_IDLE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// PROC P_FloorBounceMissile
|
|
|
|
|
//
|
|
|
|
|
// Returns true if the missile was destroyed
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
bool AActor::FloorBounceMissile (secplane_t &plane)
|
|
|
|
|
{
|
2006-04-18 22:15:05 +00:00
|
|
|
|
if (z <= floorz && P_HitFloor (this))
|
|
|
|
|
{
|
|
|
|
|
// Landed in some sort of liquid
|
2009-09-06 01:49:17 +00:00
|
|
|
|
if (BounceFlags & BOUNCE_ExplodeOnWater)
|
2006-04-18 22:15:05 +00:00
|
|
|
|
{
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (flags & MF_MISSILE)
|
|
|
|
|
P_ExplodeMissile(this, NULL, NULL);
|
|
|
|
|
else
|
|
|
|
|
Die(NULL, NULL);
|
2006-04-18 22:15:05 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2009-09-06 01:49:17 +00:00
|
|
|
|
if (!(BounceFlags & BOUNCE_CanBounceWater))
|
2006-04-18 22:15:05 +00:00
|
|
|
|
{
|
|
|
|
|
Destroy ();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-06 01:49:17 +00:00
|
|
|
|
if (plane.c < 0)
|
|
|
|
|
{ // on ceiling
|
|
|
|
|
if (!(BounceFlags & BOUNCE_Ceilings))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{ // on floor
|
|
|
|
|
if (!(BounceFlags & BOUNCE_Floors))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-18 22:15:05 +00:00
|
|
|
|
// The amount of bounces is limited
|
|
|
|
|
if (bouncecount>0 && --bouncecount==0)
|
|
|
|
|
{
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (flags & MF_MISSILE)
|
|
|
|
|
P_ExplodeMissile(this, NULL, NULL);
|
|
|
|
|
else
|
|
|
|
|
Die(NULL, NULL);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
|
fixed_t dot = TMulScale16 (velx, plane.a, vely, plane.b, velz, plane.c);
|
2009-05-23 08:30:36 +00:00
|
|
|
|
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (BounceFlags & (BOUNCE_HereticType | BOUNCE_MBF))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
velx -= MulScale15 (plane.a, dot);
|
|
|
|
|
vely -= MulScale15 (plane.b, dot);
|
|
|
|
|
velz -= MulScale15 (plane.c, dot);
|
|
|
|
|
angle = R_PointToAngle2 (0, 0, velx, vely);
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (!(BounceFlags & BOUNCE_MBF)) // Heretic projectiles die, MBF projectiles don't.
|
|
|
|
|
{
|
|
|
|
|
flags |= MF_INBOUNCE;
|
|
|
|
|
SetState (FindState(NAME_Death));
|
|
|
|
|
flags &= ~MF_INBOUNCE;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else velz = FixedMul(velz, bouncefactor);
|
|
|
|
|
}
|
|
|
|
|
else // Don't run through this for MBF-style bounces
|
|
|
|
|
{
|
|
|
|
|
// The reflected velocity keeps only about 70% of its original speed
|
|
|
|
|
velx = FixedMul (velx - MulScale15 (plane.a, dot), bouncefactor);
|
|
|
|
|
vely = FixedMul (vely - MulScale15 (plane.b, dot), bouncefactor);
|
|
|
|
|
velz = FixedMul (velz - MulScale15 (plane.c, dot), bouncefactor);
|
|
|
|
|
angle = R_PointToAngle2 (0, 0, velx, vely);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-05-23 08:30:36 +00:00
|
|
|
|
PlayBounceSound(true);
|
2013-05-04 22:52:37 +00:00
|
|
|
|
|
|
|
|
|
// Set bounce state
|
|
|
|
|
if (BounceFlags & BOUNCE_UseBounceState)
|
|
|
|
|
{
|
|
|
|
|
FName names[2];
|
|
|
|
|
FState *bouncestate;
|
|
|
|
|
|
|
|
|
|
names[0] = NAME_Bounce;
|
|
|
|
|
names[1] = plane.c < 0 ? NAME_Ceiling : NAME_Floor;
|
|
|
|
|
bouncestate = FindState(2, names);
|
|
|
|
|
if (bouncestate != NULL)
|
|
|
|
|
{
|
|
|
|
|
SetState(bouncestate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (BounceFlags & BOUNCE_MBF) // Bring it to rest below a certain speed
|
|
|
|
|
{
|
|
|
|
|
if (abs(velz) < (fixed_t)(Mass * GetGravity() / 64))
|
|
|
|
|
velz = 0;
|
|
|
|
|
}
|
2012-06-10 10:17:49 +00:00
|
|
|
|
else if (BounceFlags & (BOUNCE_AutoOff|BOUNCE_AutoOffFloorOnly))
|
|
|
|
|
{
|
|
|
|
|
if (plane.c > 0 || (BounceFlags & BOUNCE_AutoOff))
|
|
|
|
|
{
|
|
|
|
|
// AutoOff only works when bouncing off a floor, not a ceiling (or in compatibility mode.)
|
|
|
|
|
if (!(flags & MF_NOGRAVITY) && (velz < 3*FRACUNIT))
|
|
|
|
|
BounceFlags &= ~BOUNCE_TypeMask;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// PROC P_ThrustMobj
|
|
|
|
|
//
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void P_ThrustMobj (AActor *mo, angle_t angle, fixed_t move)
|
|
|
|
|
{
|
|
|
|
|
angle >>= ANGLETOFINESHIFT;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx += FixedMul (move, finecosine[angle]);
|
|
|
|
|
mo->vely += FixedMul (move, finesine[angle]);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// FUNC P_FaceMobj
|
|
|
|
|
//
|
|
|
|
|
// Returns 1 if 'source' needs to turn clockwise, or 0 if 'source' needs
|
|
|
|
|
// to turn counter clockwise. 'delta' is set to the amount 'source'
|
|
|
|
|
// needs to turn.
|
|
|
|
|
//
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
int P_FaceMobj (AActor *source, AActor *target, angle_t *delta)
|
|
|
|
|
{
|
|
|
|
|
angle_t diff;
|
|
|
|
|
angle_t angle1;
|
|
|
|
|
angle_t angle2;
|
|
|
|
|
|
|
|
|
|
angle1 = source->angle;
|
|
|
|
|
angle2 = R_PointToAngle2 (source->x, source->y, target->x, target->y);
|
|
|
|
|
if (angle2 > angle1)
|
|
|
|
|
{
|
|
|
|
|
diff = angle2 - angle1;
|
|
|
|
|
if (diff > ANGLE_180)
|
|
|
|
|
{
|
|
|
|
|
*delta = ANGLE_MAX - diff;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*delta = diff;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
diff = angle1 - angle2;
|
|
|
|
|
if (diff > ANGLE_180)
|
|
|
|
|
{
|
|
|
|
|
*delta = ANGLE_MAX - diff;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*delta = diff;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-30 08:56:40 +00:00
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// CanSeek
|
|
|
|
|
//
|
|
|
|
|
// Checks if a seeker missile can home in on its target
|
|
|
|
|
//
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
bool AActor::CanSeek(AActor *target) const
|
|
|
|
|
{
|
|
|
|
|
if (target->flags5 & MF5_CANTSEEK) return false;
|
|
|
|
|
if ((flags2 & MF2_DONTSEEKINVISIBLE) &&
|
|
|
|
|
((target->flags & MF_SHADOW) ||
|
2010-08-10 16:22:57 +00:00
|
|
|
|
(target->renderflags & RF_INVISIBLE) ||
|
|
|
|
|
!target->RenderStyle.IsVisible(target->alpha)
|
2009-05-30 08:56:40 +00:00
|
|
|
|
)
|
|
|
|
|
) return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// FUNC P_SeekerMissile
|
|
|
|
|
//
|
|
|
|
|
// The missile's tracer field must be the target. Returns true if
|
|
|
|
|
// target was tracked, false if not.
|
|
|
|
|
//
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
2011-11-24 04:27:47 +00:00
|
|
|
|
bool P_SeekerMissile (AActor *actor, angle_t thresh, angle_t turnMax, bool precise, bool usecurspeed)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
int dir;
|
2010-04-10 11:12:29 +00:00
|
|
|
|
int dist;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
angle_t delta;
|
2010-04-10 11:12:29 +00:00
|
|
|
|
angle_t angle;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
AActor *target;
|
2011-11-24 04:27:47 +00:00
|
|
|
|
fixed_t speed;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2011-11-24 04:27:47 +00:00
|
|
|
|
speed = !usecurspeed ? actor->Speed : xs_CRoundToInt(TVector3<double>(actor->velx, actor->vely, actor->velz).Length());
|
2006-02-24 04:48:15 +00:00
|
|
|
|
target = actor->tracer;
|
2012-05-01 03:14:34 +00:00
|
|
|
|
if (target == NULL || !actor->CanSeek(target))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!(target->flags & MF_SHOOTABLE))
|
|
|
|
|
{ // Target died
|
|
|
|
|
actor->tracer = NULL;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2012-05-01 03:14:34 +00:00
|
|
|
|
if (speed == 0)
|
|
|
|
|
{ // Technically, we're not seeking since our speed is 0, but the target *is* seekable.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
dir = P_FaceMobj (actor, target, &delta);
|
|
|
|
|
if (delta > thresh)
|
|
|
|
|
{
|
|
|
|
|
delta >>= 1;
|
|
|
|
|
if (delta > turnMax)
|
|
|
|
|
{
|
|
|
|
|
delta = turnMax;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (dir)
|
|
|
|
|
{ // Turn clockwise
|
|
|
|
|
actor->angle += delta;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{ // Turn counter clockwise
|
|
|
|
|
actor->angle -= delta;
|
|
|
|
|
}
|
2010-04-10 11:12:29 +00:00
|
|
|
|
angle = actor->angle>>ANGLETOFINESHIFT;
|
|
|
|
|
|
|
|
|
|
if (!precise)
|
|
|
|
|
{
|
2011-11-24 04:27:47 +00:00
|
|
|
|
actor->velx = FixedMul (speed, finecosine[angle]);
|
|
|
|
|
actor->vely = FixedMul (speed, finesine[angle]);
|
2010-04-10 11:12:29 +00:00
|
|
|
|
|
|
|
|
|
if (!(actor->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)))
|
2010-04-07 03:37:07 +00:00
|
|
|
|
{
|
2010-04-10 11:12:29 +00:00
|
|
|
|
if (actor->z + actor->height < target->z ||
|
|
|
|
|
target->z + target->height < actor->z)
|
|
|
|
|
{ // Need to seek vertically
|
|
|
|
|
dist = P_AproxDistance (target->x - actor->x, target->y - actor->y);
|
2011-11-24 04:27:47 +00:00
|
|
|
|
dist = dist / speed;
|
2010-04-10 11:12:29 +00:00
|
|
|
|
if (dist < 1)
|
|
|
|
|
{
|
|
|
|
|
dist = 1;
|
|
|
|
|
}
|
|
|
|
|
actor->velz = ((target->z+target->height/2) - (actor->z+actor->height/2)) / dist;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2010-04-10 11:12:29 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2010-08-11 03:56:31 +00:00
|
|
|
|
angle_t pitch = 0;
|
2010-04-10 11:12:29 +00:00
|
|
|
|
if (!(actor->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)))
|
|
|
|
|
{ // Need to seek vertically
|
|
|
|
|
double dist = MAX(1.0, FVector2(target->x - actor->x, target->y - actor->y).Length());
|
|
|
|
|
// Aim at a player's eyes and at the middle of the actor for everything else.
|
|
|
|
|
fixed_t aimheight = target->height/2;
|
|
|
|
|
if (target->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
|
|
|
|
|
{
|
|
|
|
|
aimheight = static_cast<APlayerPawn *>(target)->ViewHeight;
|
|
|
|
|
}
|
|
|
|
|
pitch = R_PointToAngle2(0, actor->z + actor->height/2, xs_CRoundToInt(dist), target->z + aimheight);
|
|
|
|
|
pitch >>= ANGLETOFINESHIFT;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-24 04:27:47 +00:00
|
|
|
|
fixed_t xyscale = FixedMul(speed, finecosine[pitch]);
|
|
|
|
|
actor->velz = FixedMul(speed, finesine[pitch]);
|
2010-04-10 11:12:29 +00:00
|
|
|
|
actor->velx = FixedMul(xyscale, finecosine[angle]);
|
|
|
|
|
actor->vely = FixedMul(xyscale, finesine[angle]);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-08-30 10:43:51 +00:00
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-10 11:12:29 +00:00
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//
|
2009-04-14 01:20:44 +00:00
|
|
|
|
// P_XYMovement
|
|
|
|
|
//
|
|
|
|
|
// Returns the actor's old floorz.
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//
|
|
|
|
|
#define STOPSPEED 0x1000
|
|
|
|
|
#define CARRYSTOPSPEED (STOPSPEED*32/3)
|
|
|
|
|
|
2009-04-14 01:20:44 +00:00
|
|
|
|
fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-06-14 13:47:38 +00:00
|
|
|
|
static int pushtime = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
bool bForceSlide = scrollx || scrolly;
|
|
|
|
|
angle_t angle;
|
|
|
|
|
fixed_t ptryx, ptryy;
|
|
|
|
|
player_t *player;
|
|
|
|
|
fixed_t xmove, ymove;
|
2009-01-04 15:00:29 +00:00
|
|
|
|
const secplane_t * walkplane;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
static const int windTab[3] = {2048*5, 2048*10, 2048*25};
|
|
|
|
|
int steps, step, totalsteps;
|
|
|
|
|
fixed_t startx, starty;
|
2009-04-14 01:20:44 +00:00
|
|
|
|
fixed_t oldfloorz = mo->floorz;
|
2014-12-09 18:09:36 +00:00
|
|
|
|
fixed_t oldz = mo->z;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
|
fixed_t maxmove = (mo->waterlevel < 1) || (mo->flags & MF_MISSILE) ||
|
|
|
|
|
(mo->player && mo->player->crouchoffset<-10*FRACUNIT) ? MAXMOVE : MAXMOVE/4;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
if (mo->flags2 & MF2_WINDTHRUST && mo->waterlevel < 2 && !(mo->flags & MF_NOCLIP))
|
|
|
|
|
{
|
|
|
|
|
int special = mo->Sector->special;
|
|
|
|
|
switch (special)
|
|
|
|
|
{
|
|
|
|
|
case 40: case 41: case 42: // Wind_East
|
|
|
|
|
P_ThrustMobj (mo, 0, windTab[special-40]);
|
|
|
|
|
break;
|
|
|
|
|
case 43: case 44: case 45: // Wind_North
|
|
|
|
|
P_ThrustMobj (mo, ANG90, windTab[special-43]);
|
|
|
|
|
break;
|
|
|
|
|
case 46: case 47: case 48: // Wind_South
|
|
|
|
|
P_ThrustMobj (mo, ANG270, windTab[special-46]);
|
|
|
|
|
break;
|
|
|
|
|
case 49: case 50: case 51: // Wind_West
|
|
|
|
|
P_ThrustMobj (mo, ANG180, windTab[special-49]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [RH] No need to clamp these now. However, wall running needs it so
|
|
|
|
|
// that large thrusts can't propel an actor through a wall, because wall
|
|
|
|
|
// running depends on the player's original movement continuing even after
|
|
|
|
|
// it gets blocked.
|
2008-06-25 22:16:04 +00:00
|
|
|
|
if ((mo->player != NULL && (i_compatflags & COMPATF_WALLRUN)) || (mo->waterlevel >= 1) ||
|
2006-05-14 14:30:13 +00:00
|
|
|
|
(mo->player != NULL && mo->player->crouchfactor < FRACUNIT*3/4))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2006-05-14 14:30:13 +00:00
|
|
|
|
// preserve the direction instead of clamping x and y independently.
|
2009-06-30 20:57:51 +00:00
|
|
|
|
xmove = clamp (mo->velx, -maxmove, maxmove);
|
|
|
|
|
ymove = clamp (mo->vely, -maxmove, maxmove);
|
2006-04-11 08:36:23 +00:00
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
|
fixed_t xfac = FixedDiv(xmove, mo->velx);
|
|
|
|
|
fixed_t yfac = FixedDiv(ymove, mo->vely);
|
2006-04-11 08:36:23 +00:00
|
|
|
|
fixed_t fac = MIN(xfac, yfac);
|
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
|
xmove = mo->velx = FixedMul(mo->velx, fac);
|
|
|
|
|
ymove = mo->vely = FixedMul(mo->vely, fac);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
xmove = mo->velx;
|
|
|
|
|
ymove = mo->vely;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
// [RH] Carrying sectors didn't work with low speeds in BOOM. This is
|
|
|
|
|
// because BOOM relied on the speed being fast enough to accumulate
|
|
|
|
|
// despite friction. If the speed is too low, then its movement will get
|
|
|
|
|
// cancelled, and it won't accumulate to the desired speed.
|
2009-01-25 21:59:38 +00:00
|
|
|
|
mo->flags4 &= ~MF4_SCROLLMOVE;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (abs(scrollx) > CARRYSTOPSPEED)
|
|
|
|
|
{
|
|
|
|
|
scrollx = FixedMul (scrollx, CARRYFACTOR);
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx += scrollx;
|
2009-01-25 21:59:38 +00:00
|
|
|
|
mo->flags4 |= MF4_SCROLLMOVE;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
if (abs(scrolly) > CARRYSTOPSPEED)
|
|
|
|
|
{
|
|
|
|
|
scrolly = FixedMul (scrolly, CARRYFACTOR);
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->vely += scrolly;
|
2009-01-25 21:59:38 +00:00
|
|
|
|
mo->flags4 |= MF4_SCROLLMOVE;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
xmove += scrollx;
|
|
|
|
|
ymove += scrolly;
|
|
|
|
|
|
|
|
|
|
if ((xmove | ymove) == 0)
|
|
|
|
|
{
|
|
|
|
|
if (mo->flags & MF_SKULLFLY)
|
|
|
|
|
{
|
|
|
|
|
// the skull slammed into something
|
|
|
|
|
mo->flags &= ~MF_SKULLFLY;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = mo->vely = mo->velz = 0;
|
- Fixed: EV_Teleport() did not set players to their idle state, so if they
were running when the teleported, they would still be running afterward
even though they weren't moving anywhere. Normally, P_XYMovement() does
this when they stop due to friction.
- Fixed: AActor::TakeSpecialDamage() completely bypassed the standard rules
for target switching on actors with MF5_NODAMAGE set.
- Changed the return values of the ACS spawn, spawnspot, and spawnspotfacing
commands to be the total count of things spawned, rather than a pretty
much useless reference to the actor spawned at the last map spot.
- Fixed: DLevelScript::DoSpawn() takes a byte angle, but DoSpawnSpotFacing()
passed it a full-length angle.
- Fixed: When MF_SKULLFLY is removed because an actor slams into something,
it was set to a see or spawn state, resetting its tic count and bypassing
the effectiveness of the MF2_DORMANT flag. While I was at it, I decided
dormant skulls shouldn't do slamming damage, either.
- Fixed: P_Thing_Spawn() returned success only if all thing instances were
successfully spawned. As long as at least one thing was spawned, it should
be considered a success.
- Fixed: Flipped single rotation sprites were only flipped every other 22.5
degree interval.
SVN r484 (trunk)
2007-02-14 22:47:01 +00:00
|
|
|
|
if (!(mo->flags2 & MF2_DORMANT))
|
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (mo->SeeState != NULL) mo->SetState (mo->SeeState);
|
|
|
|
|
else mo->SetIdle();
|
- Fixed: EV_Teleport() did not set players to their idle state, so if they
were running when the teleported, they would still be running afterward
even though they weren't moving anywhere. Normally, P_XYMovement() does
this when they stop due to friction.
- Fixed: AActor::TakeSpecialDamage() completely bypassed the standard rules
for target switching on actors with MF5_NODAMAGE set.
- Changed the return values of the ACS spawn, spawnspot, and spawnspotfacing
commands to be the total count of things spawned, rather than a pretty
much useless reference to the actor spawned at the last map spot.
- Fixed: DLevelScript::DoSpawn() takes a byte angle, but DoSpawnSpotFacing()
passed it a full-length angle.
- Fixed: When MF_SKULLFLY is removed because an actor slams into something,
it was set to a see or spawn state, resetting its tic count and bypassing
the effectiveness of the MF2_DORMANT flag. While I was at it, I decided
dormant skulls shouldn't do slamming damage, either.
- Fixed: P_Thing_Spawn() returned success only if all thing instances were
successfully spawned. As long as at least one thing was spawned, it should
be considered a success.
- Fixed: Flipped single rotation sprites were only flipped every other 22.5
degree interval.
SVN r484 (trunk)
2007-02-14 22:47:01 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
mo->SetIdle();
|
- Fixed: EV_Teleport() did not set players to their idle state, so if they
were running when the teleported, they would still be running afterward
even though they weren't moving anywhere. Normally, P_XYMovement() does
this when they stop due to friction.
- Fixed: AActor::TakeSpecialDamage() completely bypassed the standard rules
for target switching on actors with MF5_NODAMAGE set.
- Changed the return values of the ACS spawn, spawnspot, and spawnspotfacing
commands to be the total count of things spawned, rather than a pretty
much useless reference to the actor spawned at the last map spot.
- Fixed: DLevelScript::DoSpawn() takes a byte angle, but DoSpawnSpotFacing()
passed it a full-length angle.
- Fixed: When MF_SKULLFLY is removed because an actor slams into something,
it was set to a see or spawn state, resetting its tic count and bypassing
the effectiveness of the MF2_DORMANT flag. While I was at it, I decided
dormant skulls shouldn't do slamming damage, either.
- Fixed: P_Thing_Spawn() returned success only if all thing instances were
successfully spawned. As long as at least one thing was spawned, it should
be considered a success.
- Fixed: Flipped single rotation sprites were only flipped every other 22.5
degree interval.
SVN r484 (trunk)
2007-02-14 22:47:01 +00:00
|
|
|
|
mo->tics = -1;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-04-14 01:20:44 +00:00
|
|
|
|
return oldfloorz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
player = mo->player;
|
|
|
|
|
|
|
|
|
|
// [RH] Adjust player movement on sloped floors
|
|
|
|
|
fixed_t startxmove = xmove;
|
|
|
|
|
fixed_t startymove = ymove;
|
|
|
|
|
walkplane = P_CheckSlopeWalk (mo, xmove, ymove);
|
|
|
|
|
|
|
|
|
|
// [RH] Take smaller steps when moving faster than the object's size permits.
|
|
|
|
|
// Moving as fast as the object's "diameter" is bad because it could skip
|
|
|
|
|
// some lines because the actor could land such that it is just touching the
|
|
|
|
|
// line. For Doom to detect that the line is there, it needs to actually cut
|
|
|
|
|
// through the actor.
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
maxmove = mo->radius - FRACUNIT;
|
|
|
|
|
|
|
|
|
|
if (maxmove <= 0)
|
|
|
|
|
{ // gibs can have radius 0, so don't divide by zero below!
|
|
|
|
|
maxmove = MAXMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const fixed_t xspeed = abs (xmove);
|
|
|
|
|
const fixed_t yspeed = abs (ymove);
|
|
|
|
|
|
|
|
|
|
steps = 1;
|
|
|
|
|
|
|
|
|
|
if (xspeed > yspeed)
|
|
|
|
|
{
|
|
|
|
|
if (xspeed > maxmove)
|
|
|
|
|
{
|
|
|
|
|
steps = 1 + xspeed / maxmove;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (yspeed > maxmove)
|
|
|
|
|
{
|
|
|
|
|
steps = 1 + yspeed / maxmove;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// P_SlideMove needs to know the step size before P_CheckSlopeWalk
|
|
|
|
|
// because it also calls P_CheckSlopeWalk on its clipped steps.
|
|
|
|
|
fixed_t onestepx = startxmove / steps;
|
|
|
|
|
fixed_t onestepy = startymove / steps;
|
|
|
|
|
|
|
|
|
|
startx = mo->x;
|
|
|
|
|
starty = mo->y;
|
|
|
|
|
step = 1;
|
|
|
|
|
totalsteps = steps;
|
|
|
|
|
|
|
|
|
|
// [RH] Instead of doing ripping damage each step, do it each tic.
|
|
|
|
|
// This makes it compatible with Heretic and Hexen, which only did
|
|
|
|
|
// one step for their missiles with ripping damage (excluding those
|
|
|
|
|
// that don't use P_XYMovement). It's also more intuitive since it
|
|
|
|
|
// makes the damage done dependant on the amount of time the projectile
|
|
|
|
|
// spends inside a target rather than on the projectile's size. The
|
|
|
|
|
// last actor ripped through is recorded so that if the projectile
|
|
|
|
|
// passes through more than one actor this tic, each one takes damage
|
|
|
|
|
// and not just the first one.
|
2009-06-14 13:47:38 +00:00
|
|
|
|
pushtime++;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-04-10 14:38:43 +00:00
|
|
|
|
FCheckPosition tm(!!(mo->flags2 & MF2_RIP));
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-06-14 13:47:38 +00:00
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
do
|
|
|
|
|
{
|
2009-06-14 13:47:38 +00:00
|
|
|
|
if (i_compatflags & COMPATF_WALLRUN) pushtime++;
|
|
|
|
|
tm.PushTime = pushtime;
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
ptryx = startx + Scale (xmove, step, steps);
|
|
|
|
|
ptryy = starty + Scale (ymove, step, steps);
|
|
|
|
|
|
|
|
|
|
/* if (mo->player)
|
|
|
|
|
Printf ("%d,%d/%d: %d %d %d %d %d %d %d\n", level.time, step, steps, startxmove, Scale(xmove,step,steps), startymove, Scale(ymove,step,steps), mo->x, mo->y, mo->z);
|
|
|
|
|
*/
|
|
|
|
|
// [RH] If walking on a slope, stay on the slope
|
|
|
|
|
// killough 3/15/98: Allow objects to drop off
|
2009-09-25 02:54:13 +00:00
|
|
|
|
fixed_t startvelx = mo->velx, startvely = mo->vely;
|
|
|
|
|
|
2008-04-08 20:52:49 +00:00
|
|
|
|
if (!P_TryMove (mo, ptryx, ptryy, true, walkplane, tm))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
// blocked move
|
2009-09-14 20:47:53 +00:00
|
|
|
|
AActor *BlockingMobj = mo->BlockingMobj;
|
|
|
|
|
line_t *BlockingLine = mo->BlockingLine;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (!(mo->flags & MF_MISSILE) && (mo->BounceFlags & BOUNCE_MBF)
|
2012-04-26 03:50:11 +00:00
|
|
|
|
&& (BlockingMobj != NULL ? P_BounceActor(mo, BlockingMobj, false) : P_BounceWall(mo)))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-09-14 20:47:53 +00:00
|
|
|
|
// Do nothing, relevant actions already done in the condition.
|
|
|
|
|
// This allows to avoid setting velocities to 0 in the final else of this series.
|
|
|
|
|
}
|
|
|
|
|
else if ((mo->flags2 & (MF2_SLIDE|MF2_BLASTED) || bForceSlide) && !(mo->flags&MF_MISSILE))
|
|
|
|
|
{ // try to slide along it
|
|
|
|
|
if (BlockingMobj == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{ // slide against wall
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (BlockingLine != NULL &&
|
2006-02-24 04:48:15 +00:00
|
|
|
|
mo->player && mo->waterlevel && mo->waterlevel < 3 &&
|
|
|
|
|
(mo->player->cmd.ucmd.forwardmove | mo->player->cmd.ucmd.sidemove) &&
|
2009-09-06 20:45:56 +00:00
|
|
|
|
mo->BlockingLine->sidedef[1] != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velz = WATER_JUMP_SPEED;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-09-25 02:54:13 +00:00
|
|
|
|
// If the blocked move executed any push specials that changed the
|
|
|
|
|
// actor's velocity, do not attempt to slide.
|
|
|
|
|
if (mo->velx == startvelx && mo->vely == startvely)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-09-25 02:54:13 +00:00
|
|
|
|
if (player && (i_compatflags & COMPATF_WALLRUN))
|
|
|
|
|
{
|
|
|
|
|
// [RH] Here is the key to wall running: The move is clipped using its full speed.
|
|
|
|
|
// If the move is done a second time (because it was too fast for one move), it
|
|
|
|
|
// is still clipped against the wall at its full speed, so you effectively
|
|
|
|
|
// execute two moves in one tic.
|
|
|
|
|
P_SlideMove (mo, mo->velx, mo->vely, 1);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
P_SlideMove (mo, onestepx, onestepy, totalsteps);
|
|
|
|
|
}
|
|
|
|
|
if ((mo->velx | mo->vely) == 0)
|
|
|
|
|
{
|
|
|
|
|
steps = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!player || !(i_compatflags & COMPATF_WALLRUN))
|
|
|
|
|
{
|
|
|
|
|
xmove = mo->velx;
|
|
|
|
|
ymove = mo->vely;
|
|
|
|
|
onestepx = xmove / steps;
|
|
|
|
|
onestepy = ymove / steps;
|
|
|
|
|
P_CheckSlopeWalk (mo, xmove, ymove);
|
|
|
|
|
}
|
|
|
|
|
startx = mo->x - Scale (xmove, step, steps);
|
|
|
|
|
starty = mo->y - Scale (ymove, step, steps);
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
steps = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{ // slide against another actor
|
|
|
|
|
fixed_t tx, ty;
|
|
|
|
|
tx = 0, ty = onestepy;
|
|
|
|
|
walkplane = P_CheckSlopeWalk (mo, tx, ty);
|
2009-06-14 13:47:38 +00:00
|
|
|
|
if (P_TryMove (mo, mo->x + tx, mo->y + ty, true, walkplane, tm))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tx = onestepx, ty = 0;
|
|
|
|
|
walkplane = P_CheckSlopeWalk (mo, tx, ty);
|
2009-06-14 13:47:38 +00:00
|
|
|
|
if (P_TryMove (mo, mo->x + tx, mo->y + ty, true, walkplane, tm))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->vely = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = mo->vely = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (player && player->mo == mo)
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if (mo->velx == 0)
|
|
|
|
|
player->velx = 0;
|
|
|
|
|
if (mo->vely == 0)
|
|
|
|
|
player->vely = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
steps = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (mo->flags & MF_MISSILE)
|
|
|
|
|
{
|
|
|
|
|
steps = 0;
|
|
|
|
|
if (BlockingMobj)
|
|
|
|
|
{
|
2009-09-06 01:49:17 +00:00
|
|
|
|
if (mo->BounceFlags & BOUNCE_Actors)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-09-14 20:47:53 +00:00
|
|
|
|
// Bounce test and code moved to P_BounceActor
|
2012-04-26 03:50:11 +00:00
|
|
|
|
if (!P_BounceActor(mo, BlockingMobj, false))
|
2009-09-14 20:47:53 +00:00
|
|
|
|
{ // Struck a player/creature
|
2006-10-22 10:32:41 +00:00
|
|
|
|
P_ExplodeMissile (mo, NULL, BlockingMobj);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-09-14 20:47:53 +00:00
|
|
|
|
return oldfloorz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Struck a wall
|
|
|
|
|
if (P_BounceWall (mo))
|
|
|
|
|
{
|
2009-05-23 08:30:36 +00:00
|
|
|
|
mo->PlayBounceSound(false);
|
2009-04-14 01:20:44 +00:00
|
|
|
|
return oldfloorz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (BlockingMobj && (BlockingMobj->flags2 & MF2_REFLECTIVE))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2014-12-09 18:09:36 +00:00
|
|
|
|
bool seeker = (mo->flags2 & MF2_SEEKERMISSILE) ? true : false;
|
|
|
|
|
// Don't change the angle if there's THRUREFLECT on the monster.
|
|
|
|
|
if (!(BlockingMobj->flags7 & MF7_THRUREFLECT))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-01-18 15:21:59 +00:00
|
|
|
|
angle = R_PointToAngle2(BlockingMobj->x, BlockingMobj->y, mo->x, mo->y);
|
2014-12-27 18:47:48 +00:00
|
|
|
|
bool dontReflect = (mo->AdjustReflectionAngle(BlockingMobj, angle));
|
2014-12-09 18:09:36 +00:00
|
|
|
|
// Change angle for deflection/reflection
|
|
|
|
|
|
2014-12-27 18:47:48 +00:00
|
|
|
|
if (!dontReflect)
|
2014-12-09 18:09:36 +00:00
|
|
|
|
{
|
2014-12-27 18:47:48 +00:00
|
|
|
|
bool tg = (mo->target != NULL);
|
|
|
|
|
bool blockingtg = (BlockingMobj->target != NULL);
|
2015-02-09 02:39:55 +00:00
|
|
|
|
if ((BlockingMobj->flags7 & MF7_AIMREFLECT) && (tg | blockingtg))
|
2014-12-27 18:47:48 +00:00
|
|
|
|
{
|
2015-02-09 02:39:55 +00:00
|
|
|
|
AActor *origin = tg ? mo->target : BlockingMobj->target;
|
2014-12-27 18:47:48 +00:00
|
|
|
|
|
|
|
|
|
float speed = (float)(mo->Speed);
|
|
|
|
|
//dest->x - source->x
|
|
|
|
|
FVector3 velocity(origin->x - mo->x, origin->y - mo->y, (origin->z + (origin->height/2)) - mo->z);
|
|
|
|
|
velocity.Resize(speed);
|
|
|
|
|
mo->velx = (fixed_t)(velocity.X);
|
|
|
|
|
mo->vely = (fixed_t)(velocity.Y);
|
|
|
|
|
mo->velz = (fixed_t)(velocity.Z);
|
2014-12-09 18:09:36 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2014-12-27 18:47:48 +00:00
|
|
|
|
{
|
2015-02-09 02:39:55 +00:00
|
|
|
|
if ((BlockingMobj->flags7 & MF7_MIRRORREFLECT) && (tg | blockingtg))
|
2015-01-30 22:34:24 +00:00
|
|
|
|
{
|
|
|
|
|
mo->angle += ANGLE_180;
|
|
|
|
|
mo->velx = -mo->velx / 2;
|
|
|
|
|
mo->vely = -mo->vely / 2;
|
|
|
|
|
mo->velz = -mo->velz / 2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mo->angle = angle;
|
|
|
|
|
angle >>= ANGLETOFINESHIFT;
|
|
|
|
|
mo->velx = FixedMul(mo->Speed >> 1, finecosine[angle]);
|
|
|
|
|
mo->vely = FixedMul(mo->Speed >> 1, finesine[angle]);
|
|
|
|
|
mo->velz = -mo->velz / 2;
|
|
|
|
|
}
|
2014-12-09 18:09:36 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2014-12-27 18:47:48 +00:00
|
|
|
|
goto explode;
|
|
|
|
|
}
|
2014-12-09 18:09:36 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (mo->flags2 & MF2_SEEKERMISSILE)
|
|
|
|
|
{
|
|
|
|
|
mo->tracer = mo->target;
|
|
|
|
|
}
|
|
|
|
|
mo->target = BlockingMobj;
|
2009-04-14 01:20:44 +00:00
|
|
|
|
return oldfloorz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
explode:
|
|
|
|
|
// explode a missile
|
2009-12-20 05:11:30 +00:00
|
|
|
|
if (!(mo->flags3 & MF3_SKYEXPLODE))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-12-20 05:11:30 +00:00
|
|
|
|
if (tm.ceilingline &&
|
|
|
|
|
tm.ceilingline->backsector &&
|
|
|
|
|
tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum &&
|
|
|
|
|
mo->z >= tm.ceilingline->backsector->ceilingplane.ZatPoint (mo->x, mo->y))
|
|
|
|
|
{
|
|
|
|
|
// Hack to prevent missiles exploding against the sky.
|
|
|
|
|
// Does not handle sky floors.
|
|
|
|
|
mo->Destroy ();
|
|
|
|
|
return oldfloorz;
|
|
|
|
|
}
|
|
|
|
|
// [RH] Don't explode on horizon lines.
|
|
|
|
|
if (mo->BlockingLine != NULL && mo->BlockingLine->special == Line_Horizon)
|
|
|
|
|
{
|
|
|
|
|
mo->Destroy ();
|
|
|
|
|
return oldfloorz;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-08 20:52:49 +00:00
|
|
|
|
P_ExplodeMissile (mo, mo->BlockingLine, BlockingMobj);
|
2009-04-14 01:20:44 +00:00
|
|
|
|
return oldfloorz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = mo->vely = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
steps = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (mo->x != ptryx || mo->y != ptryy)
|
|
|
|
|
{
|
|
|
|
|
// If the new position does not match the desired position, the player
|
|
|
|
|
// must have gone through a teleporter, so stop moving right now if it
|
|
|
|
|
// was a regular teleporter. If it was a line-to-line or fogless teleporter,
|
|
|
|
|
// the move should continue, but startx and starty need to change.
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if (mo->velx == 0 && mo->vely == 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
step = steps;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
startx = mo->x - Scale (xmove, step, steps);
|
|
|
|
|
starty = mo->y - Scale (ymove, step, steps);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (++step <= steps);
|
|
|
|
|
|
|
|
|
|
// Friction
|
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if (player && player->mo == mo && player->cheats & CF_NOVELOCITY)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{ // debug option for no sliding at all
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = mo->vely = 0;
|
|
|
|
|
player->velx = player->vely = 0;
|
2009-04-14 01:20:44 +00:00
|
|
|
|
return oldfloorz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mo->flags & (MF_MISSILE | MF_SKULLFLY))
|
|
|
|
|
{ // no friction for missiles
|
2009-04-14 01:20:44 +00:00
|
|
|
|
return oldfloorz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mo->z > mo->floorz && !(mo->flags2 & MF2_ONMOBJ) &&
|
2012-08-22 21:31:48 +00:00
|
|
|
|
!mo->IsNoClip2() &&
|
|
|
|
|
(!(mo->flags2 & MF2_FLY) || !(mo->flags & MF_NOGRAVITY)) &&
|
|
|
|
|
!mo->waterlevel)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{ // [RH] Friction when falling is available for larger aircontrols
|
|
|
|
|
if (player != NULL && level.airfriction != FRACUNIT)
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = FixedMul (mo->velx, level.airfriction);
|
|
|
|
|
mo->vely = FixedMul (mo->vely, level.airfriction);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
if (player->mo == mo) // Not voodoo dolls
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
player->velx = FixedMul (player->velx, level.airfriction);
|
|
|
|
|
player->vely = FixedMul (player->vely, level.airfriction);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2009-04-14 01:20:44 +00:00
|
|
|
|
return oldfloorz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-09-14 20:47:53 +00:00
|
|
|
|
// killough 8/11/98: add bouncers
|
|
|
|
|
// killough 9/15/98: add objects falling off ledges
|
|
|
|
|
// killough 11/98: only include bouncers hanging off ledges
|
|
|
|
|
if ((mo->flags & MF_CORPSE) || (mo->BounceFlags & BOUNCE_MBF && mo->z > mo->dropoffz) || (mo->flags6 & MF6_FALLING))
|
2009-06-30 20:57:51 +00:00
|
|
|
|
{ // Don't stop sliding if halfway off a step with some velocity
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if (mo->velx > FRACUNIT/4 || mo->velx < -FRACUNIT/4 || mo->vely > FRACUNIT/4 || mo->vely < -FRACUNIT/4)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
if (mo->floorz > mo->Sector->floorplane.ZatPoint (mo->x, mo->y))
|
2008-05-17 17:57:50 +00:00
|
|
|
|
{
|
|
|
|
|
if (mo->dropoffz != mo->floorz) // 3DMidtex or other special cases that must be excluded
|
|
|
|
|
{
|
2009-01-04 15:00:29 +00:00
|
|
|
|
unsigned i;
|
|
|
|
|
for(i=0;i<mo->Sector->e->XFloor.ffloors.Size();i++)
|
|
|
|
|
{
|
|
|
|
|
// Sliding around on 3D floors looks extremely bad so
|
|
|
|
|
// if the floor comes from one in the current sector stop sliding the corpse!
|
|
|
|
|
F3DFloor * rover=mo->Sector->e->XFloor.ffloors[i];
|
|
|
|
|
if (!(rover->flags&FF_EXISTS)) continue;
|
|
|
|
|
if (rover->flags&FF_SOLID && rover->top.plane->ZatPoint(mo->x,mo->y)==mo->floorz) break;
|
|
|
|
|
}
|
|
|
|
|
if (i==mo->Sector->e->XFloor.ffloors.Size())
|
2009-04-14 01:20:44 +00:00
|
|
|
|
return oldfloorz;
|
2008-05-17 17:57:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// killough 11/98:
|
|
|
|
|
// Stop voodoo dolls that have come to rest, despite any
|
|
|
|
|
// moving corresponding player:
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if (mo->velx > -STOPSPEED && mo->velx < STOPSPEED
|
|
|
|
|
&& mo->vely > -STOPSPEED && mo->vely < STOPSPEED
|
2006-02-24 04:48:15 +00:00
|
|
|
|
&& (!player || (player->mo != mo)
|
|
|
|
|
|| !(player->cmd.ucmd.forwardmove | player->cmd.ucmd.sidemove)))
|
|
|
|
|
{
|
|
|
|
|
// if in a walking frame, stop moving
|
|
|
|
|
// killough 10/98:
|
|
|
|
|
// Don't affect main player when voodoo dolls stop:
|
|
|
|
|
if (player && player->mo == mo && !(player->cheats & CF_PREDICTING))
|
|
|
|
|
{
|
|
|
|
|
player->mo->PlayIdle ();
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = mo->vely = 0;
|
2009-01-25 21:59:38 +00:00
|
|
|
|
mo->flags4 &= ~MF4_SCROLLMOVE;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
|
// killough 10/98: kill any bobbing velocity too (except in voodoo dolls)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (player && player->mo == mo)
|
2009-06-30 20:57:51 +00:00
|
|
|
|
player->velx = player->vely = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// phares 3/17/98
|
|
|
|
|
// Friction will have been adjusted by friction thinkers for icy
|
|
|
|
|
// or muddy floors. Otherwise it was never touched and
|
|
|
|
|
// remained set at ORIG_FRICTION
|
|
|
|
|
//
|
|
|
|
|
// killough 8/28/98: removed inefficient thinker algorithm,
|
|
|
|
|
// instead using touching_sectorlist in P_GetFriction() to
|
|
|
|
|
// determine friction (and thus only when it is needed).
|
|
|
|
|
//
|
|
|
|
|
// killough 10/98: changed to work with new bobbing method.
|
2009-06-30 20:57:51 +00:00
|
|
|
|
// Reducing player velocity is no longer needed to reduce
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// bobbing, so ice works much better now.
|
|
|
|
|
|
|
|
|
|
fixed_t friction = P_GetFriction (mo, NULL);
|
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = FixedMul (mo->velx, friction);
|
|
|
|
|
mo->vely = FixedMul (mo->vely, friction);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// killough 10/98: Always decrease player bobbing by ORIG_FRICTION.
|
|
|
|
|
// This prevents problems with bobbing on ice, where it was not being
|
|
|
|
|
// reduced fast enough, leading to all sorts of kludges being developed.
|
|
|
|
|
|
|
|
|
|
if (player && player->mo == mo) // Not voodoo dolls
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
player->velx = FixedMul (player->velx, ORIG_FRICTION);
|
|
|
|
|
player->vely = FixedMul (player->vely, ORIG_FRICTION);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2009-04-14 01:20:44 +00:00
|
|
|
|
return oldfloorz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Move this to p_inter ***
|
|
|
|
|
void P_MonsterFallingDamage (AActor *mo)
|
|
|
|
|
{
|
|
|
|
|
int damage;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
int vel;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-02-03 19:11:43 +00:00
|
|
|
|
if (!(level.flags2 & LEVEL2_MONSTERFALLINGDAMAGE))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return;
|
2008-05-11 21:16:32 +00:00
|
|
|
|
if (mo->floorsector->Flags & SECF_NOFALLINGDAMAGE)
|
|
|
|
|
return;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
|
vel = abs(mo->velz);
|
|
|
|
|
if (vel > 35*FRACUNIT)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{ // automatic death
|
2009-08-07 03:57:03 +00:00
|
|
|
|
damage = TELEFRAG_DAMAGE;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
damage = ((vel - (23*FRACUNIT))*6)>>FRACBITS;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-08-07 03:57:03 +00:00
|
|
|
|
damage = TELEFRAG_DAMAGE; // always kill 'em
|
2006-10-31 14:53:21 +00:00
|
|
|
|
P_DamageMobj (mo, NULL, NULL, damage, NAME_Falling);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// P_ZMovement
|
|
|
|
|
//
|
2009-09-14 19:44:14 +00:00
|
|
|
|
|
2009-04-14 01:20:44 +00:00
|
|
|
|
void P_ZMovement (AActor *mo, fixed_t oldfloorz)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
fixed_t dist;
|
|
|
|
|
fixed_t delta;
|
2010-03-22 22:02:31 +00:00
|
|
|
|
fixed_t oldz = mo->z;
|
|
|
|
|
fixed_t grav = mo->GetGravity();
|
|
|
|
|
|
2014-04-03 22:51:15 +00:00
|
|
|
|
//
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// check for smooth step up
|
|
|
|
|
//
|
|
|
|
|
if (mo->player && mo->player->mo == mo && mo->z < mo->floorz)
|
|
|
|
|
{
|
|
|
|
|
mo->player->viewheight -= mo->floorz - mo->z;
|
2006-04-11 16:27:41 +00:00
|
|
|
|
mo->player->deltaviewheight = mo->player->GetDeltaViewHeight();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2006-04-11 08:36:23 +00:00
|
|
|
|
|
2012-07-06 03:42:03 +00:00
|
|
|
|
mo->z += mo->velz;
|
2006-04-11 08:36:23 +00:00
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//
|
|
|
|
|
// apply gravity
|
|
|
|
|
//
|
|
|
|
|
if (mo->z > mo->floorz && !(mo->flags & MF_NOGRAVITY))
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
fixed_t startvelz = mo->velz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2012-05-31 04:32:37 +00:00
|
|
|
|
if (mo->waterlevel == 0 || (mo->player &&
|
2006-02-24 04:48:15 +00:00
|
|
|
|
!(mo->player->cmd.ucmd.forwardmove | mo->player->cmd.ucmd.sidemove)))
|
|
|
|
|
{
|
2009-04-14 01:20:44 +00:00
|
|
|
|
// [RH] Double gravity only if running off a ledge. Coming down from
|
|
|
|
|
// an upward thrust (e.g. a jump) should not double it.
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if (mo->velz == 0 && oldfloorz > mo->floorz && mo->z == oldfloorz)
|
2009-04-14 01:20:44 +00:00
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velz -= grav + grav;
|
2009-04-14 01:20:44 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velz -= grav;
|
2009-04-14 01:20:44 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2012-06-09 18:54:53 +00:00
|
|
|
|
if (mo->player == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2012-06-09 18:54:53 +00:00
|
|
|
|
if (mo->waterlevel >= 1)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2012-06-09 18:54:53 +00:00
|
|
|
|
fixed_t sinkspeed;
|
2012-05-31 04:32:37 +00:00
|
|
|
|
|
2012-06-09 18:54:53 +00:00
|
|
|
|
if ((mo->flags & MF_SPECIAL) && !(mo->flags3 & MF3_ISMONSTER))
|
|
|
|
|
{ // Pickup items don't sink if placed and drop slowly if dropped
|
|
|
|
|
sinkspeed = (mo->flags & MF_DROPPED) ? -WATER_SINK_SPEED / 8 : 0;
|
2012-05-31 04:32:37 +00:00
|
|
|
|
}
|
2012-06-09 18:54:53 +00:00
|
|
|
|
else
|
2012-05-31 04:32:37 +00:00
|
|
|
|
{
|
2012-06-09 18:54:53 +00:00
|
|
|
|
sinkspeed = -WATER_SINK_SPEED;
|
|
|
|
|
|
|
|
|
|
// If it's not a player, scale sinkspeed by its mass, with
|
|
|
|
|
// 100 being equivalent to a player.
|
|
|
|
|
if (mo->player == NULL)
|
|
|
|
|
{
|
|
|
|
|
sinkspeed = Scale(sinkspeed, clamp(mo->Mass, 1, 4000), 100);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (mo->velz < sinkspeed)
|
|
|
|
|
{ // Dropping too fast, so slow down toward sinkspeed.
|
|
|
|
|
mo->velz -= MAX(sinkspeed*2, -FRACUNIT*8);
|
|
|
|
|
if (mo->velz > sinkspeed)
|
|
|
|
|
{
|
|
|
|
|
mo->velz = sinkspeed;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (mo->velz > sinkspeed)
|
|
|
|
|
{ // Dropping too slow/going up, so trend toward sinkspeed.
|
|
|
|
|
mo->velz = startvelz + MAX(sinkspeed/3, -FRACUNIT*8);
|
|
|
|
|
if (mo->velz < sinkspeed)
|
|
|
|
|
{
|
|
|
|
|
mo->velz = sinkspeed;
|
|
|
|
|
}
|
2012-05-31 04:32:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-06-09 18:54:53 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (mo->waterlevel > 1)
|
|
|
|
|
{
|
|
|
|
|
fixed_t sinkspeed = -WATER_SINK_SPEED;
|
|
|
|
|
|
2012-05-31 04:32:37 +00:00
|
|
|
|
if (mo->velz < sinkspeed)
|
|
|
|
|
{
|
2012-06-09 18:54:53 +00:00
|
|
|
|
mo->velz = (startvelz < sinkspeed) ? startvelz : sinkspeed;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mo->velz = startvelz + ((mo->velz - startvelz) >>
|
|
|
|
|
(mo->waterlevel == 1 ? WATER_SINK_SMALL_FACTOR : WATER_SINK_FACTOR));
|
2012-05-31 04:32:37 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-04-11 08:36:23 +00:00
|
|
|
|
|
2015-02-12 17:57:06 +00:00
|
|
|
|
// Hexen compatibility handling for floatbobbing. Ugh...
|
|
|
|
|
// Hexen yanked all items to the floor, except those being spawned at map start in the air.
|
|
|
|
|
// Those were kept at their original height.
|
|
|
|
|
// Do this only if the item was actually spawned by the map above ground to avoid problems.
|
|
|
|
|
if (mo->special1 > 0 && (mo->flags2 & MF2_FLOATBOB) && (ib_compatflags & BCOMPATF_FLOATBOB))
|
|
|
|
|
{
|
|
|
|
|
mo->z = mo->floorz + mo->special1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//
|
|
|
|
|
// adjust height
|
|
|
|
|
//
|
2006-04-16 13:29:50 +00:00
|
|
|
|
if ((mo->flags & MF_FLOAT) && !(mo->flags2 & MF2_DORMANT) && mo->target)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{ // float down towards target if too close
|
2009-09-14 09:41:09 +00:00
|
|
|
|
if (!(mo->flags & (MF_SKULLFLY | MF_INFLOAT)))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
dist = P_AproxDistance (mo->x - mo->target->x, mo->y - mo->target->y);
|
|
|
|
|
delta = (mo->target->z + (mo->height>>1)) - mo->z;
|
|
|
|
|
if (delta < 0 && dist < -(delta*3))
|
2012-05-13 00:27:51 +00:00
|
|
|
|
mo->z -= mo->FloatSpeed;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
else if (delta > 0 && dist < (delta*3))
|
2012-05-13 00:27:51 +00:00
|
|
|
|
mo->z += mo->FloatSpeed;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (mo->player && (mo->flags & MF_NOGRAVITY) && (mo->z > mo->floorz))
|
|
|
|
|
{
|
2012-08-22 21:31:48 +00:00
|
|
|
|
if (!mo->IsNoClip2())
|
|
|
|
|
{
|
|
|
|
|
mo->z += finesine[(FINEANGLES/80*level.maptime)&FINEMASK]/8;
|
|
|
|
|
}
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velz = FixedMul (mo->velz, FRICTION_FLY);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
if (mo->waterlevel && !(mo->flags & MF_NOGRAVITY))
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velz = FixedMul (mo->velz, mo->Sector->friction);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// clip movement
|
|
|
|
|
//
|
|
|
|
|
if (mo->z <= mo->floorz)
|
|
|
|
|
{ // Hit the floor
|
|
|
|
|
if ((!mo->player || !(mo->player->cheats & CF_PREDICTING)) &&
|
|
|
|
|
mo->Sector->SecActTarget != NULL &&
|
|
|
|
|
mo->Sector->floorplane.ZatPoint (mo->x, mo->y) == mo->floorz)
|
|
|
|
|
{ // [RH] Let the sector do something to the actor
|
|
|
|
|
mo->Sector->SecActTarget->TriggerAction (mo, SECSPAC_HitFloor);
|
|
|
|
|
}
|
2009-01-04 15:00:29 +00:00
|
|
|
|
P_CheckFor3DFloorHit(mo);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// [RH] Need to recheck this because the sector action might have
|
|
|
|
|
// teleported the actor so it is no longer below the floor.
|
|
|
|
|
if (mo->z <= mo->floorz)
|
|
|
|
|
{
|
2010-03-04 08:24:49 +00:00
|
|
|
|
if ((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
mo->z = mo->floorz;
|
2009-09-06 01:49:17 +00:00
|
|
|
|
if (mo->BounceFlags & BOUNCE_Floors)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
mo->FloorBounceMissile (mo->floorsector->floorplane);
|
2009-09-14 20:47:53 +00:00
|
|
|
|
/* if (!(mo->flags6 & MF6_CANJUMP)) */ return;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
else if (mo->flags3 & MF3_NOEXPLODEFLOOR)
|
|
|
|
|
{
|
|
|
|
|
P_HitFloor (mo);
|
2009-09-14 09:41:09 +00:00
|
|
|
|
mo->velz = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (mo->flags3 & MF3_FLOORHUGGER)
|
|
|
|
|
{ // Floor huggers can go up steps
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2006-04-17 13:53:34 +00:00
|
|
|
|
if (mo->floorpic == skyflatnum && !(mo->flags3 & MF3_SKYEXPLODE))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
// [RH] Just remove the missile without exploding it
|
|
|
|
|
// if this is a sky floor.
|
|
|
|
|
mo->Destroy ();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
P_HitFloor (mo);
|
2006-10-22 10:32:41 +00:00
|
|
|
|
P_ExplodeMissile (mo, NULL, NULL);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-09-14 20:47:53 +00:00
|
|
|
|
else if (mo->BounceFlags & BOUNCE_MBF && mo->velz) // check for MBF-like bounce on non-missiles
|
|
|
|
|
{
|
|
|
|
|
mo->FloorBounceMissile(mo->floorsector->floorplane);
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (mo->flags3 & MF3_ISMONSTER) // Blasted mobj falling
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if (mo->velz < -(23*FRACUNIT))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
P_MonsterFallingDamage (mo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mo->z = mo->floorz;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if (mo->velz < 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2010-11-03 02:11:06 +00:00
|
|
|
|
const fixed_t minvel = -8*FRACUNIT; // landing speed from a jump with normal gravity
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// Spawn splashes, etc.
|
|
|
|
|
P_HitFloor (mo);
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if (mo->DamageType == NAME_Ice && mo->velz < minvel)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
mo->tics = 1;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = 0;
|
|
|
|
|
mo->vely = 0;
|
|
|
|
|
mo->velz = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Let the actor do something special for hitting the floor
|
|
|
|
|
mo->HitFloor ();
|
|
|
|
|
if (mo->player)
|
|
|
|
|
{
|
- Revised usage of jumpTics. In Hexen, it went like this:
* When you jump, it gets set to 18.
* When you land, it gets set to 7.
* As long as it is non-zero, it counts down, and you cannot jump.
Of note here, is that setting it to 18 upon jumping seems useless, since you can't jump unless
you're on the ground, and when you reach the ground, it will always be set to 7. With that in
mind, the new behavior is:
* When you jump, it gets set to -1.
* When you land, if it is less than zero or you fall far enough to squat, jumpTics will
be set to 7. Otherwise, jumpTics is left alone.
* If jumpTics is positive, it will count down each tic.
* As long as JumpTics is non-zero, you cannot jump.
SVN r2970 (trunk)
2010-11-03 02:07:56 +00:00
|
|
|
|
if (mo->player->jumpTics < 0 || mo->velz < minvel)
|
2010-03-22 22:02:31 +00:00
|
|
|
|
{ // delay any jumping for a short while
|
|
|
|
|
mo->player->jumpTics = 7;
|
|
|
|
|
}
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if (mo->velz < minvel && !(mo->flags & MF_NOGRAVITY))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
// Squat down.
|
|
|
|
|
// Decrease viewheight for a moment after hitting the ground (hard),
|
|
|
|
|
// and utter appropriate sound.
|
|
|
|
|
PlayerLandedOnThing (mo, NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velz = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
if (mo->flags & MF_SKULLFLY)
|
|
|
|
|
{ // The skull slammed into something
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velz = -mo->velz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2006-11-29 10:03:35 +00:00
|
|
|
|
mo->Crash();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mo->flags2 & MF2_FLOORCLIP)
|
|
|
|
|
{
|
|
|
|
|
mo->AdjustFloorClip ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mo->z + mo->height > mo->ceilingz)
|
|
|
|
|
{ // hit the ceiling
|
|
|
|
|
if ((!mo->player || !(mo->player->cheats & CF_PREDICTING)) &&
|
|
|
|
|
mo->Sector->SecActTarget != NULL &&
|
|
|
|
|
mo->Sector->ceilingplane.ZatPoint (mo->x, mo->y) == mo->ceilingz)
|
|
|
|
|
{ // [RH] Let the sector do something to the actor
|
|
|
|
|
mo->Sector->SecActTarget->TriggerAction (mo, SECSPAC_HitCeiling);
|
|
|
|
|
}
|
2009-01-04 15:00:29 +00:00
|
|
|
|
P_CheckFor3DCeilingHit(mo);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// [RH] Need to recheck this because the sector action might have
|
|
|
|
|
// teleported the actor so it is no longer above the ceiling.
|
|
|
|
|
if (mo->z + mo->height > mo->ceilingz)
|
|
|
|
|
{
|
|
|
|
|
mo->z = mo->ceilingz - mo->height;
|
2009-09-06 01:49:17 +00:00
|
|
|
|
if (mo->BounceFlags & BOUNCE_Ceilings)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{ // ceiling bounce
|
2006-04-17 13:53:34 +00:00
|
|
|
|
mo->FloorBounceMissile (mo->ceilingsector->ceilingplane);
|
2009-09-14 20:47:53 +00:00
|
|
|
|
/*if (!(mo->flags6 & MF6_CANJUMP))*/ return;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
if (mo->flags & MF_SKULLFLY)
|
|
|
|
|
{ // the skull slammed into something
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velz = -mo->velz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-09-14 09:41:09 +00:00
|
|
|
|
if (mo->velz > 0)
|
|
|
|
|
mo->velz = 0;
|
2010-03-04 08:24:49 +00:00
|
|
|
|
if ((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
if (mo->flags3 & MF3_CEILINGHUGGER)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2006-04-17 13:53:34 +00:00
|
|
|
|
if (mo->ceilingpic == skyflatnum && !(mo->flags3 & MF3_SKYEXPLODE))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
mo->Destroy ();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2006-10-22 10:32:41 +00:00
|
|
|
|
P_ExplodeMissile (mo, NULL, NULL);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
P_CheckFakeFloorTriggers (mo, oldz);
|
|
|
|
|
}
|
|
|
|
|
|
2007-12-09 03:40:02 +00:00
|
|
|
|
void P_CheckFakeFloorTriggers (AActor *mo, fixed_t oldz, bool oldz_has_viewheight)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
if (mo->player && (mo->player->cheats & CF_PREDICTING))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
sector_t *sec = mo->Sector;
|
2006-05-19 05:14:37 +00:00
|
|
|
|
assert (sec != NULL);
|
|
|
|
|
if (sec == NULL)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (sec->heightsec != NULL && sec->SecActTarget != NULL)
|
|
|
|
|
{
|
|
|
|
|
sector_t *hs = sec->heightsec;
|
|
|
|
|
fixed_t waterz = hs->floorplane.ZatPoint (mo->x, mo->y);
|
|
|
|
|
fixed_t newz;
|
|
|
|
|
fixed_t viewheight;
|
|
|
|
|
|
|
|
|
|
if (mo->player != NULL)
|
|
|
|
|
{
|
|
|
|
|
viewheight = mo->player->viewheight;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
viewheight = mo->height / 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (oldz > waterz && mo->z <= waterz)
|
|
|
|
|
{ // Feet hit fake floor
|
|
|
|
|
sec->SecActTarget->TriggerAction (mo, SECSPAC_HitFakeFloor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newz = mo->z + viewheight;
|
2007-12-09 03:40:02 +00:00
|
|
|
|
if (!oldz_has_viewheight)
|
|
|
|
|
{
|
|
|
|
|
oldz += viewheight;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
if (oldz <= waterz && newz > waterz)
|
|
|
|
|
{ // View went above fake floor
|
|
|
|
|
sec->SecActTarget->TriggerAction (mo, SECSPAC_EyesSurface);
|
|
|
|
|
}
|
|
|
|
|
else if (oldz > waterz && newz <= waterz)
|
|
|
|
|
{ // View went below fake floor
|
|
|
|
|
sec->SecActTarget->TriggerAction (mo, SECSPAC_EyesDive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(hs->MoreFlags & SECF_FAKEFLOORONLY))
|
|
|
|
|
{
|
|
|
|
|
waterz = hs->ceilingplane.ZatPoint (mo->x, mo->y);
|
|
|
|
|
if (oldz <= waterz && newz > waterz)
|
|
|
|
|
{ // View went above fake ceiling
|
|
|
|
|
sec->SecActTarget->TriggerAction (mo, SECSPAC_EyesAboveC);
|
|
|
|
|
}
|
|
|
|
|
else if (oldz > waterz && newz <= waterz)
|
|
|
|
|
{ // View went below fake ceiling
|
|
|
|
|
sec->SecActTarget->TriggerAction (mo, SECSPAC_EyesBelowC);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
// PlayerLandedOnThing
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
static void PlayerLandedOnThing (AActor *mo, AActor *onmobj)
|
|
|
|
|
{
|
|
|
|
|
bool grunted;
|
|
|
|
|
|
|
|
|
|
if (!mo->player)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (mo->player->mo == mo)
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->player->deltaviewheight = mo->velz >> 3;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mo->player->cheats & CF_PREDICTING)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
P_FallingDamage (mo);
|
|
|
|
|
|
|
|
|
|
// [RH] only make noise if alive
|
|
|
|
|
if (!mo->player->morphTics && mo->health > 0)
|
|
|
|
|
{
|
|
|
|
|
grunted = false;
|
|
|
|
|
// Why should this number vary by gravity?
|
2012-08-14 03:24:59 +00:00
|
|
|
|
if (mo->health > 0 && mo->velz < -mo->player->mo->GruntSpeed)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
S_Sound (mo, CHAN_VOICE, "*grunt", 1, ATTN_NORM);
|
|
|
|
|
grunted = true;
|
|
|
|
|
}
|
|
|
|
|
if (onmobj != NULL || !Terrains[P_GetThingFloorType (mo)].IsLiquid)
|
|
|
|
|
{
|
|
|
|
|
if (!grunted || !S_AreSoundsEquivalent (mo, "*grunt", "*land"))
|
|
|
|
|
{
|
|
|
|
|
S_Sound (mo, CHAN_AUTO, "*land", 1, ATTN_NORM);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// mo->player->centering = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// P_NightmareRespawn
|
|
|
|
|
//
|
|
|
|
|
void P_NightmareRespawn (AActor *mobj)
|
|
|
|
|
{
|
|
|
|
|
fixed_t x, y, z;
|
|
|
|
|
AActor *mo;
|
|
|
|
|
AActor *info = mobj->GetDefault();
|
|
|
|
|
|
2008-01-26 16:42:16 +00:00
|
|
|
|
mobj->skillrespawncount++;
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// spawn the new monster (assume the spawn will be good)
|
|
|
|
|
if (info->flags & MF_SPAWNCEILING)
|
|
|
|
|
z = ONCEILINGZ;
|
|
|
|
|
else if (info->flags2 & MF2_SPAWNFLOAT)
|
|
|
|
|
z = FLOATRANDZ;
|
|
|
|
|
else
|
|
|
|
|
z = ONFLOORZ;
|
|
|
|
|
|
|
|
|
|
// spawn it
|
2008-05-08 08:06:26 +00:00
|
|
|
|
x = mobj->SpawnPoint[0];
|
|
|
|
|
y = mobj->SpawnPoint[1];
|
2012-03-28 02:23:54 +00:00
|
|
|
|
mo = AActor::StaticSpawn(RUNTIME_TYPE(mobj), x, y, z, NO_REPLACE, true);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
if (z == ONFLOORZ)
|
2012-04-06 04:46:45 +00:00
|
|
|
|
{
|
2012-04-03 03:45:05 +00:00
|
|
|
|
mo->z += mobj->SpawnPoint[2];
|
2012-04-06 04:46:45 +00:00
|
|
|
|
if (mo->z < mo->floorz)
|
|
|
|
|
{ // Do not respawn monsters in the floor, even if that's where they
|
|
|
|
|
// started. The initial P_ZMovement() call would have put them on
|
|
|
|
|
// the floor right away, but we need them on the floor now so we
|
|
|
|
|
// can use P_CheckPosition() properly.
|
|
|
|
|
mo->z = mo->floorz;
|
|
|
|
|
}
|
2012-04-08 05:39:46 +00:00
|
|
|
|
if (mo->z + mo->height > mo->ceilingz)
|
|
|
|
|
{
|
|
|
|
|
mo->z = mo->ceilingz - mo->height;
|
|
|
|
|
}
|
2012-04-06 04:46:45 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
else if (z == ONCEILINGZ)
|
2012-04-06 04:46:45 +00:00
|
|
|
|
{
|
2012-04-03 03:45:05 +00:00
|
|
|
|
mo->z -= mobj->SpawnPoint[2];
|
2012-04-06 04:46:45 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2012-04-08 05:39:46 +00:00
|
|
|
|
// If there are 3D floors, we need to find floor/ceiling again.
|
2012-04-14 03:55:46 +00:00
|
|
|
|
P_FindFloorCeiling(mo, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT);
|
2012-04-08 05:39:46 +00:00
|
|
|
|
|
|
|
|
|
if (z == ONFLOORZ)
|
|
|
|
|
{
|
|
|
|
|
if (mo->z < mo->floorz)
|
|
|
|
|
{ // Do not respawn monsters in the floor, even if that's where they
|
|
|
|
|
// started. The initial P_ZMovement() call would have put them on
|
|
|
|
|
// the floor right away, but we need them on the floor now so we
|
|
|
|
|
// can use P_CheckPosition() properly.
|
|
|
|
|
mo->z = mo->floorz;
|
|
|
|
|
}
|
|
|
|
|
if (mo->z + mo->height > mo->ceilingz)
|
|
|
|
|
{ // Do the same for the ceiling.
|
|
|
|
|
mo->z = mo->ceilingz - mo->height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// something is occupying its position?
|
2012-04-06 04:46:45 +00:00
|
|
|
|
if (!P_CheckPosition(mo, mo->x, mo->y, true))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2006-04-16 13:29:50 +00:00
|
|
|
|
//[GrafZahl] MF_COUNTKILL still needs to be checked here.
|
2010-09-19 00:06:45 +00:00
|
|
|
|
mo->ClearCounters();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
mo->Destroy ();
|
|
|
|
|
return; // no respawn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
z = mo->z;
|
|
|
|
|
|
|
|
|
|
// inherit attributes from deceased one
|
|
|
|
|
mo->SpawnPoint[0] = mobj->SpawnPoint[0];
|
|
|
|
|
mo->SpawnPoint[1] = mobj->SpawnPoint[1];
|
|
|
|
|
mo->SpawnPoint[2] = mobj->SpawnPoint[2];
|
|
|
|
|
mo->SpawnAngle = mobj->SpawnAngle;
|
2008-11-14 23:12:15 +00:00
|
|
|
|
mo->SpawnFlags = mobj->SpawnFlags & ~MTF_DORMANT; // It wasn't dormant when it died, so it's not dormant now, either.
|
2006-02-24 04:48:15 +00:00
|
|
|
|
mo->angle = ANG45 * (mobj->SpawnAngle/45);
|
|
|
|
|
|
|
|
|
|
mo->HandleSpawnFlags ();
|
|
|
|
|
mo->reactiontime = 18;
|
|
|
|
|
mo->CopyFriendliness (mobj, false);
|
|
|
|
|
mo->Translation = mobj->Translation;
|
|
|
|
|
|
2008-01-26 16:42:16 +00:00
|
|
|
|
mo->skillrespawncount = mobj->skillrespawncount;
|
|
|
|
|
|
2012-04-08 21:12:14 +00:00
|
|
|
|
mo->PrevZ = z; // Do not interpolate Z position if we changed it since spawning.
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// spawn a teleport fog at old spot because of removal of the body?
|
2015-04-07 16:14:02 +00:00
|
|
|
|
P_SpawnTeleportFog(mobj, mobj->x, mobj->y, mobj->z + TELEFOGHEIGHT, true, true);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// spawn a teleport fog at the new spot
|
2015-04-07 16:14:02 +00:00
|
|
|
|
P_SpawnTeleportFog(mobj, x, y, z + TELEFOGHEIGHT, false, true);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// remove the old monster
|
|
|
|
|
mobj->Destroy ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AActor *AActor::TIDHash[128];
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// P_ClearTidHashes
|
|
|
|
|
//
|
|
|
|
|
// Clears the tid hashtable.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
void AActor::ClearTIDHashes ()
|
|
|
|
|
{
|
2013-01-21 22:30:30 +00:00
|
|
|
|
memset(TIDHash, 0, sizeof(TIDHash));
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// P_AddMobjToHash
|
|
|
|
|
//
|
|
|
|
|
// Inserts an mobj into the correct chain based on its tid.
|
|
|
|
|
// If its tid is 0, this function does nothing.
|
|
|
|
|
//
|
|
|
|
|
void AActor::AddToHash ()
|
|
|
|
|
{
|
|
|
|
|
if (tid == 0)
|
|
|
|
|
{
|
|
|
|
|
iprev = NULL;
|
|
|
|
|
inext = NULL;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int hash = TIDHASH (tid);
|
|
|
|
|
|
|
|
|
|
inext = TIDHash[hash];
|
|
|
|
|
iprev = &TIDHash[hash];
|
|
|
|
|
TIDHash[hash] = this;
|
|
|
|
|
if (inext)
|
|
|
|
|
{
|
|
|
|
|
inext->iprev = &inext;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// P_RemoveMobjFromHash
|
|
|
|
|
//
|
|
|
|
|
// Removes an mobj from its hash chain.
|
|
|
|
|
//
|
|
|
|
|
void AActor::RemoveFromHash ()
|
|
|
|
|
{
|
|
|
|
|
if (tid != 0 && iprev)
|
|
|
|
|
{
|
|
|
|
|
*iprev = inext;
|
|
|
|
|
if (inext)
|
|
|
|
|
{
|
|
|
|
|
inext->iprev = iprev;
|
|
|
|
|
}
|
|
|
|
|
iprev = NULL;
|
|
|
|
|
inext = NULL;
|
|
|
|
|
}
|
|
|
|
|
tid = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-01 03:12:43 +00:00
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// P_IsTIDUsed
|
|
|
|
|
//
|
|
|
|
|
// Returns true if there is at least one actor with the specified TID
|
|
|
|
|
// (dead or alive).
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
|
|
bool P_IsTIDUsed(int tid)
|
|
|
|
|
{
|
|
|
|
|
AActor *probe = AActor::TIDHash[tid & 127];
|
|
|
|
|
while (probe != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (probe->tid == tid)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
probe = probe->inext;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// P_FindUniqueTID
|
|
|
|
|
//
|
|
|
|
|
// Returns an unused TID. If start_tid is 0, then a random TID will be
|
|
|
|
|
// chosen. Otherwise, it will perform a linear search starting from
|
|
|
|
|
// start_tid. If limit is non-0, then it will not check more than <limit>
|
|
|
|
|
// number of TIDs. Returns 0 if no suitable TID was found.
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
|
|
int P_FindUniqueTID(int start_tid, int limit)
|
|
|
|
|
{
|
|
|
|
|
int tid;
|
|
|
|
|
|
|
|
|
|
if (start_tid != 0)
|
|
|
|
|
{ // Do a linear search.
|
2014-03-09 09:02:07 +00:00
|
|
|
|
if (start_tid > INT_MAX-limit+1)
|
|
|
|
|
{ // If 'limit+start_tid-1' overflows, clamp 'limit' to INT_MAX
|
|
|
|
|
limit = INT_MAX;
|
2012-08-01 03:12:43 +00:00
|
|
|
|
}
|
2014-01-13 00:48:31 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
2014-03-09 09:02:07 +00:00
|
|
|
|
limit += start_tid-1;
|
2014-01-13 00:48:31 +00:00
|
|
|
|
}
|
2014-03-09 09:02:07 +00:00
|
|
|
|
for (tid = start_tid; tid <= limit; ++tid)
|
2012-08-01 03:12:43 +00:00
|
|
|
|
{
|
|
|
|
|
if (tid != 0 && !P_IsTIDUsed(tid))
|
|
|
|
|
{
|
|
|
|
|
return tid;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Nothing free found.
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
// Do a random search. To try and be a bit more performant, this
|
|
|
|
|
// actually does several linear searches. In the case of *very*
|
|
|
|
|
// dense TID usage, this could potentially perform worse than doing
|
|
|
|
|
// a complete linear scan starting at 1. However, you would need
|
|
|
|
|
// to use an absolutely ridiculous number of actors before this
|
|
|
|
|
// becomes a real concern.
|
|
|
|
|
if (limit == 0)
|
|
|
|
|
{
|
|
|
|
|
limit = INT_MAX;
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < limit; i += 5)
|
|
|
|
|
{
|
|
|
|
|
// Use a positive starting TID.
|
|
|
|
|
tid = pr_uniquetid.GenRand32() & INT_MAX;
|
|
|
|
|
tid = P_FindUniqueTID(tid == 0 ? 1 : tid, 5);
|
|
|
|
|
if (tid != 0)
|
|
|
|
|
{
|
|
|
|
|
return tid;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Nothing free found.
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CCMD(utid)
|
|
|
|
|
{
|
|
|
|
|
Printf("%d\n",
|
|
|
|
|
P_FindUniqueTID(argv.argc() > 1 ? atoi(argv[1]) : 0,
|
2014-01-13 00:48:31 +00:00
|
|
|
|
(argv.argc() > 2 && atoi(argv[2]) >= 0) ? atoi(argv[2]) : 0));
|
2012-08-01 03:12:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: GetMissileDamage
|
|
|
|
|
//
|
|
|
|
|
// If the actor's damage amount is an expression, evaluate it and return
|
|
|
|
|
// the result. Otherwise, return ((random() & mask) + add) * damage.
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
|
|
int AActor::GetMissileDamage (int mask, int add)
|
|
|
|
|
{
|
|
|
|
|
if ((Damage & 0xC0000000) == 0x40000000)
|
|
|
|
|
{
|
|
|
|
|
return EvalExpressionI (Damage & 0x3FFFFFFF, this);
|
|
|
|
|
}
|
|
|
|
|
if (Damage == 0)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else if (mask == 0)
|
|
|
|
|
{
|
|
|
|
|
return add * Damage;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return ((pr_missiledamage() & mask) + add) * Damage;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
void AActor::Howl ()
|
|
|
|
|
{
|
2006-11-27 00:01:30 +00:00
|
|
|
|
int howl = GetClass()->Meta.GetMetaInt(AMETA_HowlSound);
|
2006-12-24 23:08:49 +00:00
|
|
|
|
if (!S_IsActorPlayingSomething(this, CHAN_BODY, howl))
|
2006-11-27 00:01:30 +00:00
|
|
|
|
{
|
2008-06-15 02:25:09 +00:00
|
|
|
|
S_Sound (this, CHAN_BODY, howl, 1, ATTN_NORM);
|
2006-11-27 00:01:30 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AActor::HitFloor ()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AActor::Slam (AActor *thing)
|
|
|
|
|
{
|
|
|
|
|
flags &= ~MF_SKULLFLY;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
velx = vely = velz = 0;
|
2007-07-28 12:38:10 +00:00
|
|
|
|
if (health > 0)
|
- Fixed: EV_Teleport() did not set players to their idle state, so if they
were running when the teleported, they would still be running afterward
even though they weren't moving anywhere. Normally, P_XYMovement() does
this when they stop due to friction.
- Fixed: AActor::TakeSpecialDamage() completely bypassed the standard rules
for target switching on actors with MF5_NODAMAGE set.
- Changed the return values of the ACS spawn, spawnspot, and spawnspotfacing
commands to be the total count of things spawned, rather than a pretty
much useless reference to the actor spawned at the last map spot.
- Fixed: DLevelScript::DoSpawn() takes a byte angle, but DoSpawnSpotFacing()
passed it a full-length angle.
- Fixed: When MF_SKULLFLY is removed because an actor slams into something,
it was set to a see or spawn state, resetting its tic count and bypassing
the effectiveness of the MF2_DORMANT flag. While I was at it, I decided
dormant skulls shouldn't do slamming damage, either.
- Fixed: P_Thing_Spawn() returned success only if all thing instances were
successfully spawned. As long as at least one thing was spawned, it should
be considered a success.
- Fixed: Flipped single rotation sprites were only flipped every other 22.5
degree interval.
SVN r484 (trunk)
2007-02-14 22:47:01 +00:00
|
|
|
|
{
|
2007-07-28 12:38:10 +00:00
|
|
|
|
if (!(flags2 & MF2_DORMANT))
|
|
|
|
|
{
|
|
|
|
|
int dam = GetMissileDamage (7, 1);
|
2013-01-02 04:39:59 +00:00
|
|
|
|
int newdam = P_DamageMobj (thing, this, this, dam, NAME_Melee);
|
|
|
|
|
P_TraceBleed (newdam > 0 ? newdam : dam, thing, this);
|
2010-09-19 22:33:21 +00:00
|
|
|
|
// The charging monster may have died by the target's actions here.
|
|
|
|
|
if (health > 0)
|
|
|
|
|
{
|
|
|
|
|
if (SeeState != NULL) SetState (SeeState);
|
|
|
|
|
else SetIdle();
|
|
|
|
|
}
|
2007-07-28 12:38:10 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
SetIdle();
|
2007-07-28 12:38:10 +00:00
|
|
|
|
tics = -1;
|
|
|
|
|
}
|
- Fixed: EV_Teleport() did not set players to their idle state, so if they
were running when the teleported, they would still be running afterward
even though they weren't moving anywhere. Normally, P_XYMovement() does
this when they stop due to friction.
- Fixed: AActor::TakeSpecialDamage() completely bypassed the standard rules
for target switching on actors with MF5_NODAMAGE set.
- Changed the return values of the ACS spawn, spawnspot, and spawnspotfacing
commands to be the total count of things spawned, rather than a pretty
much useless reference to the actor spawned at the last map spot.
- Fixed: DLevelScript::DoSpawn() takes a byte angle, but DoSpawnSpotFacing()
passed it a full-length angle.
- Fixed: When MF_SKULLFLY is removed because an actor slams into something,
it was set to a see or spawn state, resetting its tic count and bypassing
the effectiveness of the MF2_DORMANT flag. While I was at it, I decided
dormant skulls shouldn't do slamming damage, either.
- Fixed: P_Thing_Spawn() returned success only if all thing instances were
successfully spawned. As long as at least one thing was spawned, it should
be considered a success.
- Fixed: Flipped single rotation sprites were only flipped every other 22.5
degree interval.
SVN r484 (trunk)
2007-02-14 22:47:01 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return false; // stop moving
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AActor::SpecialBlastHandling (AActor *source, fixed_t strength)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int AActor::SpecialMissileHit (AActor *victim)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AActor::AdjustReflectionAngle (AActor *thing, angle_t &angle)
|
|
|
|
|
{
|
2007-02-24 12:09:36 +00:00
|
|
|
|
if (flags2 & MF2_DONTREFLECT) return true;
|
2014-12-09 18:09:36 +00:00
|
|
|
|
if (thing->flags7 & MF7_THRUREFLECT) return false;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// Change angle for reflection
|
2015-01-30 22:34:24 +00:00
|
|
|
|
if (thing->flags4&MF4_SHIELDREFLECT)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
// Shield reflection (from the Centaur
|
2015-03-08 22:21:15 +00:00
|
|
|
|
if (absangle(angle - thing->angle)>>24 > 45)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return true; // Let missile explode
|
|
|
|
|
|
|
|
|
|
if (thing->IsKindOf (RUNTIME_CLASS(AHolySpirit))) // shouldn't this be handled by another flag???
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (pr_reflect () < 128)
|
|
|
|
|
angle += ANGLE_45;
|
|
|
|
|
else
|
|
|
|
|
angle -= ANGLE_45;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else if (thing->flags4&MF4_DEFLECT)
|
|
|
|
|
{
|
|
|
|
|
// deflect (like the Heresiarch)
|
|
|
|
|
if(pr_reflect() < 128)
|
|
|
|
|
angle += ANG45;
|
|
|
|
|
else
|
|
|
|
|
angle -= ANG45;
|
|
|
|
|
}
|
2015-01-30 22:34:24 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
angle += ANGLE_1 * ((pr_reflect() % 16) - 8);
|
|
|
|
|
}
|
|
|
|
|
//Always check for AIMREFLECT, no matter what else is checked above.
|
|
|
|
|
if (thing->flags7 & MF7_AIMREFLECT)
|
2014-12-27 18:47:48 +00:00
|
|
|
|
{
|
|
|
|
|
if (this->target != NULL)
|
2015-01-30 22:34:24 +00:00
|
|
|
|
{
|
2014-12-27 18:47:48 +00:00
|
|
|
|
A_Face(this, this->target);
|
2015-01-30 22:34:24 +00:00
|
|
|
|
}
|
2014-12-27 18:47:48 +00:00
|
|
|
|
else if (thing->target != NULL)
|
2015-01-30 22:34:24 +00:00
|
|
|
|
{
|
2014-12-27 18:47:48 +00:00
|
|
|
|
A_Face(this, thing->target);
|
2015-01-30 22:34:24 +00:00
|
|
|
|
}
|
2014-12-27 18:47:48 +00:00
|
|
|
|
}
|
2015-01-30 22:34:24 +00:00
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AActor::PlayActiveSound ()
|
|
|
|
|
{
|
2006-08-31 00:16:12 +00:00
|
|
|
|
if (ActiveSound && !S_IsActorPlayingSomething (this, CHAN_VOICE, -1))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-06-15 02:25:09 +00:00
|
|
|
|
S_Sound (this, CHAN_VOICE, ActiveSound, 1,
|
2006-02-24 04:48:15 +00:00
|
|
|
|
(flags3 & MF3_FULLVOLACTIVE) ? ATTN_NONE : ATTN_IDLE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AActor::IsOkayToAttack (AActor *link)
|
|
|
|
|
{
|
2009-10-09 20:54:28 +00:00
|
|
|
|
if (!(player // Original AActor::IsOkayToAttack was only for players
|
|
|
|
|
// || (flags & MF_FRIENDLY) // Maybe let friendly monsters use the function as well?
|
|
|
|
|
|| (flags5 & MF5_SUMMONEDMONSTER) // AMinotaurFriend has its own version, generalized to other summoned monsters
|
|
|
|
|
|| (flags2 & MF2_SEEKERMISSILE))) // AHolySpirit and AMageStaffFX2 as well, generalized to other seeker missiles
|
|
|
|
|
{ // Normal monsters and other actors always return false.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Standard things to eliminate: an actor shouldn't attack itself,
|
|
|
|
|
// or a non-shootable, dormant, non-player-and-non-monster actor.
|
|
|
|
|
if (link == this) return false;
|
|
|
|
|
if (!(link->player||(link->flags3 & MF3_ISMONSTER)))return false;
|
|
|
|
|
if (!(link->flags & MF_SHOOTABLE)) return false;
|
|
|
|
|
if (link->flags2 & MF2_DORMANT) return false;
|
|
|
|
|
|
|
|
|
|
// An actor shouldn't attack friendly actors. The reference depends
|
|
|
|
|
// on the type of actor: for a player's actor, itself; for a projectile,
|
|
|
|
|
// its target; and for a summoned minion, its tracer.
|
|
|
|
|
AActor * Friend = NULL;
|
|
|
|
|
if (player) Friend = this;
|
|
|
|
|
else if (flags5 & MF5_SUMMONEDMONSTER) Friend = tracer;
|
|
|
|
|
else if (flags2 & MF2_SEEKERMISSILE) Friend = target;
|
|
|
|
|
else if ((flags & MF_FRIENDLY) && FriendPlayer) Friend = players[FriendPlayer-1].mo;
|
|
|
|
|
|
|
|
|
|
// Friend checks
|
|
|
|
|
if (link == Friend) return false;
|
|
|
|
|
if (Friend == NULL) return false;
|
|
|
|
|
if (Friend->IsFriend(link)) return false;
|
|
|
|
|
if ((link->flags5 & MF5_SUMMONEDMONSTER) // No attack against minions on the same side
|
|
|
|
|
&& (link->tracer == Friend)) return false;
|
|
|
|
|
if (multiplayer && !deathmatch // No attack against fellow players in coop
|
|
|
|
|
&& link->player && Friend->player) return false;
|
|
|
|
|
if (((flags & link->flags) & MF_FRIENDLY) // No friendly infighting amongst minions
|
|
|
|
|
&& IsFriend(link)) return false;
|
|
|
|
|
|
|
|
|
|
// Now that all the actor checks are made, the line of sight can be checked
|
|
|
|
|
if (P_CheckSight (this, link))
|
|
|
|
|
{
|
|
|
|
|
// AMageStaffFX2::IsOkayToAttack had an extra check here, generalized with a flag,
|
2014-03-31 10:57:43 +00:00
|
|
|
|
// to only allow the check to succeed if the enemy was in a ~84<38> FOV of the player
|
2009-10-09 20:54:28 +00:00
|
|
|
|
if (flags3 & MF3_SCREENSEEKER)
|
|
|
|
|
{
|
|
|
|
|
angle_t angle = R_PointToAngle2(Friend->x,
|
|
|
|
|
Friend->y, link->x, link->y) - Friend->angle;
|
|
|
|
|
angle >>= 24;
|
|
|
|
|
if (angle>226 || angle<30)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-09 20:54:28 +00:00
|
|
|
|
// Other actors are not concerned by this check
|
|
|
|
|
else return true;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-10-09 20:54:28 +00:00
|
|
|
|
// The sight check was failed, or the angle wasn't right for a screenseeker
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AActor::SetShade (DWORD rgb)
|
|
|
|
|
{
|
|
|
|
|
PalEntry *entry = (PalEntry *)&rgb;
|
- Updated lempar.c to v1.31.
- Added .txt files to the list of types (wad, zip, and pk3) that can be
loaded without listing them after -file.
- Fonts that are created by the ACS setfont command to wrap a texture now
support animated textures.
- FON2 fonts can now use their full palette for CR_UNTRANSLATED when drawn
with the hardware 2D path instead of being restricted to the game palette.
- Fixed: Toggling vid_vsync would reset the displayed fullscreen gamma to 1
on a Radeon 9000.
- Added back the off-by-one palette handling, but in a much more limited
scope than before. The skipped entry is assumed to always be at 248, and
it is assumed that all Shader Model 1.4 cards suffer from this. That's
because all SM1.4 cards are based on variants of the ATI R200 core, and the
RV250 in a Radeon 9000 craps up like this. I see no reason to assume that
other flavors of the R200 are any different. (Interesting note: With the
Radeon 9000, D3DTADDRESS_CLAMP is an invalid address mode when using the
debug Direct3D 9 runtime, but it works perfectly fine with the retail
Direct3D 9 runtime.) (Insight: The R200 probably uses bytes for all its
math inside pixel shaders. That would explain perfectly why I can't use
constants greater than 1 with PS1.4 and why it can't do an exact mapping to
every entry in the color palette.
- Fixed: The software shaded drawer did not work for 2D, because its selected
"color"map was replaced with the identitymap before being used.
- Fixed: I cannot use Printf to output messages before the framebuffer was
completely setup, meaning that Shader Model 1.4 cards could not change
resolution.
- I have decided to let remap palettes specify variable alpha values for
their colors. D3DFB no longer forces them to 255.
- Updated re2c to version 0.12.3.
- Fixed: A_Wander used threshold as a timer, when it should have used
reactiontime.
- Fixed: A_CustomRailgun would not fire at all for actors without a target
when the aim parameter was disabled.
- Made the warp command work in multiplayer, again courtesy of Karate Chris.
- Fixed: Trying to spawn a bot while not in a game made for a crashing time.
(Patch courtesy of Karate Chris.)
- Removed some floating point math from hu_scores.cpp that somebody's GCC
gave warnings for (not mine, though).
- Fixed: The SBarInfo drawbar command crashed if the sprite image was
unavailable.
- Fixed: FString::operator=(const char *) did not release its old buffer when
being assigned to the null string.
- The scanner no longer has an upper limit on the length of strings it
accepts, though short strings will be faster than long ones.
- Moved all the text scanning functions into a class. Mainly, this means that
multiple script scanner states can be stored without being forced to do so
recursively. I think I might be taking advantage of that in the near
future. Possibly. Maybe.
- Removed some potential buffer overflows from the decal parser.
- Applied Blzut3's SBARINFO update #9:
* Fixed: When using even length values in drawnumber it would cap to a 98
value instead of a 99 as intended.
* The SBarInfo parser can now accept negatives for coordinates. This
doesn't allow much right now, but later I plan to add better fullscreen
hud support in which the negatives will be more useful. This also cleans
up the source a bit since all calls for (x, y) coordinates are with the
function getCoordinates().
- Added support for stencilling actors.
- Added support for non-black colors specified with DTA_ColorOverlay to the
software renderer.
- Fixed: The inverse, gold, red, and green fixed colormaps each allocated
space for 32 different colormaps, even though each only used the first one.
- Added two new blending flags to make reverse subtract blending more useful:
STYLEF_InvertSource and STYLEF_InvertOverlay. These invert the color that
gets blended with the background, since that seems like a good idea for
reverse subtraction. They also work with the other two blending operations.
- Added subtract and reverse subtract blending operations to the renderer.
Since the ERenderStyle enumeration was getting rather unwieldy, I converted
it into a new FRenderStyle structure that lets each parameter of the
blending equation be set separately. This simplified the set up for the
blend quite a bit, and it means a number of new combinations are available
by setting the parameters properly.
SVN r710 (trunk)
2008-01-25 23:57:44 +00:00
|
|
|
|
fillcolor = rgb | (ColorMatcher.Pick (entry->r, entry->g, entry->b) << 24);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AActor::SetShade (int r, int g, int b)
|
|
|
|
|
{
|
- Updated lempar.c to v1.31.
- Added .txt files to the list of types (wad, zip, and pk3) that can be
loaded without listing them after -file.
- Fonts that are created by the ACS setfont command to wrap a texture now
support animated textures.
- FON2 fonts can now use their full palette for CR_UNTRANSLATED when drawn
with the hardware 2D path instead of being restricted to the game palette.
- Fixed: Toggling vid_vsync would reset the displayed fullscreen gamma to 1
on a Radeon 9000.
- Added back the off-by-one palette handling, but in a much more limited
scope than before. The skipped entry is assumed to always be at 248, and
it is assumed that all Shader Model 1.4 cards suffer from this. That's
because all SM1.4 cards are based on variants of the ATI R200 core, and the
RV250 in a Radeon 9000 craps up like this. I see no reason to assume that
other flavors of the R200 are any different. (Interesting note: With the
Radeon 9000, D3DTADDRESS_CLAMP is an invalid address mode when using the
debug Direct3D 9 runtime, but it works perfectly fine with the retail
Direct3D 9 runtime.) (Insight: The R200 probably uses bytes for all its
math inside pixel shaders. That would explain perfectly why I can't use
constants greater than 1 with PS1.4 and why it can't do an exact mapping to
every entry in the color palette.
- Fixed: The software shaded drawer did not work for 2D, because its selected
"color"map was replaced with the identitymap before being used.
- Fixed: I cannot use Printf to output messages before the framebuffer was
completely setup, meaning that Shader Model 1.4 cards could not change
resolution.
- I have decided to let remap palettes specify variable alpha values for
their colors. D3DFB no longer forces them to 255.
- Updated re2c to version 0.12.3.
- Fixed: A_Wander used threshold as a timer, when it should have used
reactiontime.
- Fixed: A_CustomRailgun would not fire at all for actors without a target
when the aim parameter was disabled.
- Made the warp command work in multiplayer, again courtesy of Karate Chris.
- Fixed: Trying to spawn a bot while not in a game made for a crashing time.
(Patch courtesy of Karate Chris.)
- Removed some floating point math from hu_scores.cpp that somebody's GCC
gave warnings for (not mine, though).
- Fixed: The SBarInfo drawbar command crashed if the sprite image was
unavailable.
- Fixed: FString::operator=(const char *) did not release its old buffer when
being assigned to the null string.
- The scanner no longer has an upper limit on the length of strings it
accepts, though short strings will be faster than long ones.
- Moved all the text scanning functions into a class. Mainly, this means that
multiple script scanner states can be stored without being forced to do so
recursively. I think I might be taking advantage of that in the near
future. Possibly. Maybe.
- Removed some potential buffer overflows from the decal parser.
- Applied Blzut3's SBARINFO update #9:
* Fixed: When using even length values in drawnumber it would cap to a 98
value instead of a 99 as intended.
* The SBarInfo parser can now accept negatives for coordinates. This
doesn't allow much right now, but later I plan to add better fullscreen
hud support in which the negatives will be more useful. This also cleans
up the source a bit since all calls for (x, y) coordinates are with the
function getCoordinates().
- Added support for stencilling actors.
- Added support for non-black colors specified with DTA_ColorOverlay to the
software renderer.
- Fixed: The inverse, gold, red, and green fixed colormaps each allocated
space for 32 different colormaps, even though each only used the first one.
- Added two new blending flags to make reverse subtract blending more useful:
STYLEF_InvertSource and STYLEF_InvertOverlay. These invert the color that
gets blended with the background, since that seems like a good idea for
reverse subtraction. They also work with the other two blending operations.
- Added subtract and reverse subtract blending operations to the renderer.
Since the ERenderStyle enumeration was getting rather unwieldy, I converted
it into a new FRenderStyle structure that lets each parameter of the
blending equation be set separately. This simplified the set up for the
blend quite a bit, and it means a number of new combinations are available
by setting the parameters properly.
SVN r710 (trunk)
2008-01-25 23:57:44 +00:00
|
|
|
|
fillcolor = MAKEARGB(ColorMatcher.Pick (r, g, b), r, g, b);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-27 22:41:07 +00:00
|
|
|
|
void AActor::SetPitch(int p, bool interpolate, bool forceclamp)
|
2013-10-10 02:50:24 +00:00
|
|
|
|
{
|
2015-05-27 22:41:07 +00:00
|
|
|
|
if (player != NULL || forceclamp)
|
|
|
|
|
{ // clamp the pitch we set
|
|
|
|
|
int min, max;
|
|
|
|
|
|
|
|
|
|
if (player != NULL)
|
|
|
|
|
{
|
|
|
|
|
min = player->MinPitch;
|
|
|
|
|
max = player->MaxPitch;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
min = -ANGLE_90 + (1 << ANGLETOFINESHIFT);
|
|
|
|
|
max = ANGLE_90 - (1 << ANGLETOFINESHIFT);
|
|
|
|
|
}
|
|
|
|
|
p = clamp<int>(p, min, max);
|
|
|
|
|
}
|
2013-10-10 02:50:24 +00:00
|
|
|
|
if (p != pitch)
|
|
|
|
|
{
|
|
|
|
|
pitch = p;
|
2014-05-08 07:15:56 +00:00
|
|
|
|
if (player != NULL && interpolate)
|
2013-10-10 02:50:24 +00:00
|
|
|
|
{
|
|
|
|
|
player->cheats |= CF_INTERPVIEW;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-08 07:15:56 +00:00
|
|
|
|
void AActor::SetAngle(angle_t ang, bool interpolate)
|
2013-10-10 02:50:24 +00:00
|
|
|
|
{
|
|
|
|
|
if (ang != angle)
|
|
|
|
|
{
|
|
|
|
|
angle = ang;
|
2014-05-08 07:15:56 +00:00
|
|
|
|
if (player != NULL && interpolate)
|
2013-10-10 02:50:24 +00:00
|
|
|
|
{
|
|
|
|
|
player->cheats |= CF_INTERPVIEW;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-05 09:51:32 +00:00
|
|
|
|
void AActor::SetRoll(angle_t r, bool interpolate)
|
|
|
|
|
{
|
|
|
|
|
if (r != roll)
|
|
|
|
|
{
|
|
|
|
|
roll = r;
|
|
|
|
|
if (player != NULL && interpolate)
|
|
|
|
|
{
|
|
|
|
|
player->cheats |= CF_INTERPVIEW;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//
|
|
|
|
|
// P_MobjThinker
|
|
|
|
|
//
|
2009-12-30 18:53:14 +00:00
|
|
|
|
void AActor::Tick ()
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
// [RH] Data for Heretic/Hexen scrolling sectors
|
2006-09-14 00:02:31 +00:00
|
|
|
|
static const BYTE HexenScrollDirs[8] = { 64, 0, 192, 128, 96, 32, 224, 160 };
|
|
|
|
|
static const BYTE HexenSpeedMuls[3] = { 5, 10, 25 };
|
|
|
|
|
static const SBYTE HexenScrollies[24][2] =
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
{ 0, 1 }, { 0, 2 }, { 0, 4 },
|
|
|
|
|
{ -1, 0 }, { -2, 0 }, { -4, 0 },
|
|
|
|
|
{ 0, -1 }, { 0, -2 }, { 0, -4 },
|
|
|
|
|
{ 1, 0 }, { 2, 0 }, { 4, 0 },
|
|
|
|
|
{ 1, 1 }, { 2, 2 }, { 4, 4 },
|
|
|
|
|
{ -1, 1 }, { -2, 2 }, { -4, 4 },
|
|
|
|
|
{ -1, -1 }, { -2, -2 }, { -4, -4 },
|
|
|
|
|
{ 1, -1 }, { 2, -2 }, { 4, -4 }
|
|
|
|
|
};
|
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
|
static const BYTE HereticScrollDirs[4] = { 6, 9, 1, 4 };
|
|
|
|
|
static const BYTE HereticSpeedMuls[5] = { 5, 10, 25, 30, 35 };
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2011-12-06 01:25:37 +00:00
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
AActor *onmo;
|
2014-11-15 08:58:29 +00:00
|
|
|
|
int i;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2010-02-13 08:56:08 +00:00
|
|
|
|
//assert (state != NULL);
|
2006-09-09 01:14:13 +00:00
|
|
|
|
if (state == NULL)
|
|
|
|
|
{
|
2010-02-13 08:56:08 +00:00
|
|
|
|
Printf("Actor of type %s at (%f,%f) left without a state\n", GetClass()->TypeName.GetChars(),
|
|
|
|
|
x/65536., y/65536.);
|
2006-09-09 01:14:13 +00:00
|
|
|
|
Destroy();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-30 18:53:14 +00:00
|
|
|
|
// This is necessary to properly interpolate movement outside this function
|
|
|
|
|
// like from an ActorMover
|
|
|
|
|
PrevX = x;
|
|
|
|
|
PrevY = y;
|
|
|
|
|
PrevZ = z;
|
|
|
|
|
PrevAngle = angle;
|
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (flags5 & MF5_NOINTERACTION)
|
2007-01-12 15:24:10 +00:00
|
|
|
|
{
|
2008-12-14 19:12:41 +00:00
|
|
|
|
// only do the minimally necessary things here to save time:
|
|
|
|
|
// Check the time freezer
|
2009-06-30 20:57:51 +00:00
|
|
|
|
// apply velocity
|
2008-12-14 19:12:41 +00:00
|
|
|
|
// ensure that the actor is not linked into the blockmap
|
|
|
|
|
|
|
|
|
|
if (!(flags5 & MF5_NOTIMEFREEZE))
|
|
|
|
|
{
|
|
|
|
|
//Added by MC: Freeze mode.
|
2009-02-03 19:11:43 +00:00
|
|
|
|
if (bglobal.freeze || level.flags2 & LEVEL2_FROZEN)
|
2008-12-14 19:12:41 +00:00
|
|
|
|
{
|
2011-01-23 08:33:30 +00:00
|
|
|
|
// Boss cubes shouldn't be accelerated by timefreeze
|
|
|
|
|
if (flags6 & MF6_BOSSCUBE)
|
|
|
|
|
{
|
|
|
|
|
special2++;
|
|
|
|
|
}
|
2008-12-14 19:12:41 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
UnlinkFromWorld ();
|
|
|
|
|
flags |= MF_NOBLOCKMAP;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
x += velx;
|
|
|
|
|
y += vely;
|
|
|
|
|
z += velz;
|
2008-04-03 10:49:54 +00:00
|
|
|
|
LinkToWorld ();
|
2007-01-12 15:24:10 +00:00
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
else
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
AInventory * item = Inventory;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
// Handle powerup effects here so that the order is controlled
|
|
|
|
|
// by the order in the inventory, not the order in the thinker table
|
|
|
|
|
while (item != NULL && item->Owner == this)
|
|
|
|
|
{
|
|
|
|
|
item->DoEffect();
|
|
|
|
|
item = item->Inventory;
|
|
|
|
|
}
|
2007-05-01 15:09:44 +00:00
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (flags & MF_UNMORPHED)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-05-01 15:09:44 +00:00
|
|
|
|
|
2008-04-06 09:24:41 +00:00
|
|
|
|
if (!(flags5 & MF5_NOTIMEFREEZE))
|
2008-04-03 10:49:54 +00:00
|
|
|
|
{
|
2011-01-23 08:33:30 +00:00
|
|
|
|
// Boss cubes shouldn't be accelerated by timefreeze
|
|
|
|
|
if (flags6 & MF6_BOSSCUBE)
|
|
|
|
|
{
|
|
|
|
|
special2++;
|
|
|
|
|
}
|
2008-04-06 09:24:41 +00:00
|
|
|
|
//Added by MC: Freeze mode.
|
2014-10-14 18:57:11 +00:00
|
|
|
|
if (bglobal.freeze && !(player && player->Bot == NULL))
|
2008-04-06 09:24:41 +00:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-04-06 09:24:41 +00:00
|
|
|
|
// Apply freeze mode.
|
2012-04-27 01:40:50 +00:00
|
|
|
|
if ((level.flags2 & LEVEL2_FROZEN) && (player == NULL || player->timefreezer == 0))
|
2008-04-06 09:24:41 +00:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-12-14 19:12:41 +00:00
|
|
|
|
|
2013-03-28 01:01:19 +00:00
|
|
|
|
if (effects & FX_ROCKET)
|
2008-04-04 14:31:20 +00:00
|
|
|
|
{
|
2013-03-28 01:01:19 +00:00
|
|
|
|
if (++smokecounter == 4)
|
2008-04-04 14:31:20 +00:00
|
|
|
|
{
|
2013-03-28 01:01:19 +00:00
|
|
|
|
// add some smoke behind the rocket
|
|
|
|
|
smokecounter = 0;
|
|
|
|
|
AActor *th = Spawn("RocketSmokeTrail", x-velx, y-vely, z-velz, ALLOW_REPLACE);
|
|
|
|
|
if (th)
|
2008-04-04 14:31:20 +00:00
|
|
|
|
{
|
2013-03-28 01:01:19 +00:00
|
|
|
|
th->tics -= pr_rockettrail()&3;
|
|
|
|
|
if (th->tics < 1) th->tics = 1;
|
|
|
|
|
if (!(cl_rockettrails & 2)) th->renderflags |= RF_INVISIBLE;
|
2008-04-04 14:31:20 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2013-03-28 01:01:19 +00:00
|
|
|
|
}
|
|
|
|
|
else if (effects & FX_GRENADE)
|
|
|
|
|
{
|
|
|
|
|
if (++smokecounter == 8)
|
2008-04-04 14:31:20 +00:00
|
|
|
|
{
|
2013-03-28 01:01:19 +00:00
|
|
|
|
smokecounter = 0;
|
|
|
|
|
angle_t moveangle = R_PointToAngle2(0,0,velx,vely);
|
|
|
|
|
AActor * th = Spawn("GrenadeSmokeTrail",
|
|
|
|
|
x - FixedMul (finecosine[(moveangle)>>ANGLETOFINESHIFT], radius*2) + (pr_rockettrail()<<10),
|
|
|
|
|
y - FixedMul (finesine[(moveangle)>>ANGLETOFINESHIFT], radius*2) + (pr_rockettrail()<<10),
|
|
|
|
|
z - (height>>3) * (velz>>16) + (2*height)/3, ALLOW_REPLACE);
|
|
|
|
|
if (th)
|
2008-04-04 14:31:20 +00:00
|
|
|
|
{
|
2013-03-28 01:01:19 +00:00
|
|
|
|
th->tics -= pr_rockettrail()&3;
|
|
|
|
|
if (th->tics < 1) th->tics = 1;
|
|
|
|
|
if (!(cl_rockettrails & 2)) th->renderflags |= RF_INVISIBLE;
|
2008-04-04 14:31:20 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
|
|
|
|
|
fixed_t oldz = z;
|
|
|
|
|
|
|
|
|
|
// [RH] Give the pain elemental vertical friction
|
|
|
|
|
// This used to be in APainElemental::Tick but in order to use
|
2008-04-04 14:31:20 +00:00
|
|
|
|
// A_PainAttack with other monsters it has to be here
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (flags4 & MF4_VFRICTION)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (health >0)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if (abs (velz) < FRACUNIT/4)
|
2008-04-03 10:49:54 +00:00
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
velz = 0;
|
2008-04-03 10:49:54 +00:00
|
|
|
|
flags4 &= ~MF4_VFRICTION;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
velz = FixedMul (velz, 0xe800);
|
2008-04-03 10:49:54 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
|
|
|
|
|
// [RH] Pulse in and out of visibility
|
|
|
|
|
if (effects & FX_VISIBILITYPULSE)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (visdir > 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
alpha += 0x800;
|
|
|
|
|
if (alpha >= OPAQUE)
|
|
|
|
|
{
|
|
|
|
|
alpha = OPAQUE;
|
|
|
|
|
visdir = -1;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
else
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
alpha -= 0x800;
|
|
|
|
|
if (alpha <= TRANSLUC25)
|
|
|
|
|
{
|
|
|
|
|
alpha = TRANSLUC25;
|
|
|
|
|
visdir = 1;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
else if (flags & MF_STEALTH)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
// [RH] Fade a stealth monster in and out of visibility
|
|
|
|
|
RenderStyle.Flags &= ~STYLEF_Alpha1;
|
|
|
|
|
if (visdir > 0)
|
|
|
|
|
{
|
|
|
|
|
alpha += 2*FRACUNIT/TICRATE;
|
|
|
|
|
if (alpha > OPAQUE)
|
|
|
|
|
{
|
|
|
|
|
alpha = OPAQUE;
|
|
|
|
|
visdir = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (visdir < 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
alpha -= 3*FRACUNIT/TICRATE/2;
|
|
|
|
|
if (alpha < 0)
|
|
|
|
|
{
|
|
|
|
|
alpha = 0;
|
|
|
|
|
visdir = 0;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-08 17:38:09 +00:00
|
|
|
|
if (bglobal.botnum && !demoplayback &&
|
2008-06-25 22:16:04 +00:00
|
|
|
|
((flags & (MF_SPECIAL|MF_MISSILE)) || (flags3 & MF3_ISMONSTER)))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-08-10 03:25:08 +00:00
|
|
|
|
BotSupportCycles.Clock();
|
2008-04-03 10:49:54 +00:00
|
|
|
|
bglobal.m_Thinking = true;
|
2014-11-15 08:58:29 +00:00
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
2014-11-14 16:54:56 +00:00
|
|
|
|
{
|
2014-11-15 08:58:29 +00:00
|
|
|
|
if (!playeringame[i] || players[i].Bot == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (flags3 & MF3_ISMONSTER)
|
|
|
|
|
{
|
|
|
|
|
if (health > 0
|
2014-11-15 08:58:29 +00:00
|
|
|
|
&& !players[i].Bot->enemy
|
|
|
|
|
&& player ? !IsTeammate (players[i].mo) : true
|
|
|
|
|
&& P_AproxDistance (players[i].mo->x-x, players[i].mo->y-y) < MAX_MONSTER_TARGET_DIST
|
|
|
|
|
&& P_CheckSight (players[i].mo, this, SF_SEEPASTBLOCKEVERYTHING))
|
2008-04-03 10:49:54 +00:00
|
|
|
|
{ //Probably a monster, so go kill it.
|
2014-11-15 08:58:29 +00:00
|
|
|
|
players[i].Bot->enemy = this;
|
2008-04-03 10:49:54 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
else if (flags & MF_SPECIAL)
|
|
|
|
|
{ //Item pickup time
|
|
|
|
|
//clock (BotWTG);
|
2014-11-29 17:03:58 +00:00
|
|
|
|
players[i].Bot->WhatToGet (this);
|
2008-04-03 10:49:54 +00:00
|
|
|
|
//unclock (BotWTG);
|
|
|
|
|
BotWTG++;
|
|
|
|
|
}
|
|
|
|
|
else if (flags & MF_MISSILE)
|
|
|
|
|
{
|
2014-11-15 08:58:29 +00:00
|
|
|
|
if (!players[i].Bot->missile && (flags3 & MF3_WARNBOT))
|
2008-04-03 10:49:54 +00:00
|
|
|
|
{ //warn for incoming missiles.
|
2014-11-29 17:03:58 +00:00
|
|
|
|
if (target != players[i].mo && players[i].Bot->Check_LOS (this, ANGLE_90))
|
2014-11-15 08:58:29 +00:00
|
|
|
|
players[i].Bot->missile = this;
|
2008-04-03 10:49:54 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
bglobal.m_Thinking = false;
|
2008-08-10 03:25:08 +00:00
|
|
|
|
BotSupportCycles.Unclock();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
//End of MC
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
// [RH] Consider carrying sectors here
|
|
|
|
|
fixed_t cummx = 0, cummy = 0;
|
|
|
|
|
if ((level.Scrolls != NULL || player != NULL) && !(flags & MF_NOCLIP) && !(flags & MF_NOSECTOR))
|
|
|
|
|
{
|
|
|
|
|
fixed_t height, waterheight; // killough 4/4/98: add waterheight
|
|
|
|
|
const msecnode_t *node;
|
|
|
|
|
int countx, county;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
// killough 3/7/98: Carry things on floor
|
|
|
|
|
// killough 3/20/98: use new sector list which reflects true members
|
|
|
|
|
// killough 3/27/98: fix carrier bug
|
|
|
|
|
// killough 4/4/98: Underwater, carry things even w/o gravity
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
// Move objects only if on floor or underwater,
|
|
|
|
|
// non-floating, and clipped.
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
countx = county = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
for (node = touching_sectorlist; node; node = node->m_tnext)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
const sector_t *sec = node->m_sector;
|
|
|
|
|
fixed_t scrollx, scrolly;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (level.Scrolls != NULL)
|
|
|
|
|
{
|
|
|
|
|
const FSectorScrollValues *scroll = &level.Scrolls[sec - sectors];
|
|
|
|
|
scrollx = scroll->ScrollX;
|
|
|
|
|
scrolly = scroll->ScrollY;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
scrollx = scrolly = 0;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (player != NULL)
|
|
|
|
|
{
|
2016-01-06 11:56:35 +00:00
|
|
|
|
int scrolltype = sec->special;
|
2008-04-03 10:49:54 +00:00
|
|
|
|
|
|
|
|
|
if (scrolltype >= Scroll_North_Slow &&
|
|
|
|
|
scrolltype <= Scroll_SouthWest_Fast)
|
|
|
|
|
{ // Hexen scroll special
|
|
|
|
|
scrolltype -= Scroll_North_Slow;
|
|
|
|
|
if (i_compatflags&COMPATF_RAVENSCROLL)
|
|
|
|
|
{
|
|
|
|
|
angle_t fineangle = HexenScrollDirs[scrolltype / 3] * 32;
|
|
|
|
|
fixed_t carryspeed = DivScale32 (HexenSpeedMuls[scrolltype % 3], 32*CARRYFACTOR);
|
|
|
|
|
scrollx += FixedMul (carryspeed, finecosine[fineangle]);
|
|
|
|
|
scrolly += FixedMul (carryspeed, finesine[fineangle]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Use speeds that actually match the scrolling textures!
|
|
|
|
|
scrollx -= HexenScrollies[scrolltype][0] << (FRACBITS-1);
|
|
|
|
|
scrolly += HexenScrollies[scrolltype][1] << (FRACBITS-1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (scrolltype >= Carry_East5 &&
|
|
|
|
|
scrolltype <= Carry_West35)
|
|
|
|
|
{ // Heretic scroll special
|
|
|
|
|
scrolltype -= Carry_East5;
|
|
|
|
|
BYTE dir = HereticScrollDirs[scrolltype / 5];
|
|
|
|
|
fixed_t carryspeed = DivScale32 (HereticSpeedMuls[scrolltype % 5], 32*CARRYFACTOR);
|
|
|
|
|
if (scrolltype<=Carry_East35 && !(i_compatflags&COMPATF_RAVENSCROLL))
|
|
|
|
|
{
|
|
|
|
|
// Use speeds that actually match the scrolling textures!
|
|
|
|
|
carryspeed = (1 << ((scrolltype%5) + FRACBITS-1));
|
|
|
|
|
}
|
|
|
|
|
scrollx += carryspeed * ((dir & 3) - 1);
|
|
|
|
|
scrolly += carryspeed * (((dir & 12) >> 2) - 1);
|
|
|
|
|
}
|
|
|
|
|
else if (scrolltype == dScroll_EastLavaDamage)
|
|
|
|
|
{ // Special Heretic scroll special
|
|
|
|
|
if (i_compatflags&COMPATF_RAVENSCROLL)
|
|
|
|
|
{
|
|
|
|
|
scrollx += DivScale32 (28, 32*CARRYFACTOR);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Use a speed that actually matches the scrolling texture!
|
|
|
|
|
scrollx += DivScale32 (12, 32*CARRYFACTOR);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (scrolltype == Scroll_StrifeCurrent)
|
|
|
|
|
{ // Strife scroll special
|
2015-04-19 10:33:27 +00:00
|
|
|
|
int anglespeed = tagManager.GetFirstSectorTag(sec) - 100;
|
2008-04-03 10:49:54 +00:00
|
|
|
|
fixed_t carryspeed = DivScale32 (anglespeed % 10, 16*CARRYFACTOR);
|
|
|
|
|
angle_t fineangle = (anglespeed / 10) << (32-3);
|
|
|
|
|
fineangle >>= ANGLETOFINESHIFT;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
scrollx += FixedMul (carryspeed, finecosine[fineangle]);
|
|
|
|
|
scrolly += FixedMul (carryspeed, finesine[fineangle]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
|
|
|
|
|
if ((scrollx | scrolly) == 0)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-05-23 10:21:33 +00:00
|
|
|
|
sector_t *heightsec = sec->GetHeightSec();
|
|
|
|
|
if (flags & MF_NOGRAVITY && heightsec == NULL)
|
2008-04-03 10:49:54 +00:00
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
height = sec->floorplane.ZatPoint (x, y);
|
|
|
|
|
if (z > height)
|
|
|
|
|
{
|
2009-05-23 10:21:33 +00:00
|
|
|
|
if (heightsec == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
continue;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
|
2009-05-23 10:21:33 +00:00
|
|
|
|
waterheight = heightsec->floorplane.ZatPoint (x, y);
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (waterheight > height && z >= waterheight)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
continue;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
cummx += scrollx;
|
|
|
|
|
cummy += scrolly;
|
|
|
|
|
if (scrollx) countx++;
|
|
|
|
|
if (scrolly) county++;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
|
|
|
|
|
// Some levels designed with Boom in mind actually want things to accelerate
|
|
|
|
|
// at neighboring scrolling sector boundaries. But it is only important for
|
|
|
|
|
// non-player objects.
|
|
|
|
|
if (player != NULL || !(i_compatflags & COMPATF_BOOMSCROLL))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (countx > 1)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
cummx /= countx;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (county > 1)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
cummy /= county;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
// [RH] If standing on a steep slope, fall down it
|
|
|
|
|
if ((flags & MF_SOLID) && !(flags & (MF_NOCLIP|MF_NOGRAVITY)) &&
|
|
|
|
|
!(flags & MF_NOBLOCKMAP) &&
|
2009-06-30 20:57:51 +00:00
|
|
|
|
velz <= 0 &&
|
2008-04-03 10:49:54 +00:00
|
|
|
|
floorz == z)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-02-08 09:03:49 +00:00
|
|
|
|
secplane_t floorplane;
|
2009-01-04 15:00:29 +00:00
|
|
|
|
|
|
|
|
|
// Check 3D floors as well
|
2010-08-12 07:05:31 +00:00
|
|
|
|
floorplane = P_FindFloorPlane(floorsector, x, y, floorz);
|
2009-01-04 15:00:29 +00:00
|
|
|
|
|
2010-08-12 07:05:31 +00:00
|
|
|
|
if (floorplane.c < STEEPSLOPE &&
|
|
|
|
|
floorplane.ZatPoint (x, y) <= floorz)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
const msecnode_t *node;
|
|
|
|
|
bool dopush = true;
|
2006-04-14 16:25:57 +00:00
|
|
|
|
|
2010-08-12 07:05:31 +00:00
|
|
|
|
if (floorplane.c > STEEPSLOPE*2/3)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
for (node = touching_sectorlist; node; node = node->m_tnext)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
const sector_t *sec = node->m_sector;
|
|
|
|
|
if (sec->floorplane.c >= STEEPSLOPE)
|
2006-04-14 16:25:57 +00:00
|
|
|
|
{
|
2010-08-12 07:05:31 +00:00
|
|
|
|
if (floorplane.ZatPoint (x, y) >= z - MaxStepHeight)
|
2008-04-03 10:49:54 +00:00
|
|
|
|
{
|
|
|
|
|
dopush = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2006-04-14 16:25:57 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (dopush)
|
|
|
|
|
{
|
2010-08-12 07:05:31 +00:00
|
|
|
|
velx += floorplane.a;
|
|
|
|
|
vely += floorplane.b;
|
2008-04-03 10:49:54 +00:00
|
|
|
|
}
|
2006-04-14 16:25:57 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
// [RH] Missiles moving perfectly vertical need some X/Y movement, or they
|
|
|
|
|
// won't hurt anything. Don't do this if damage is 0! That way, you can
|
|
|
|
|
// still have missiles that go straight up and down through actors without
|
|
|
|
|
// damaging anything.
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if ((flags & MF_MISSILE) && (velx|vely) == 0 && Damage != 0)
|
2008-04-03 10:49:54 +00:00
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
velx = 1;
|
2008-04-03 10:49:54 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
|
// Handle X and Y velocities
|
2008-04-03 10:49:54 +00:00
|
|
|
|
BlockingMobj = NULL;
|
2009-04-14 01:20:44 +00:00
|
|
|
|
fixed_t oldfloorz = P_XYMovement (this, cummx, cummy);
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (ObjectFlags & OF_EuthanizeMe)
|
|
|
|
|
{ // actor was destroyed
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-09-14 20:47:53 +00:00
|
|
|
|
if ((velx | vely) == 0) // Actors at rest
|
|
|
|
|
{
|
|
|
|
|
if (flags2 & MF2_BLASTED)
|
|
|
|
|
{ // Reset to not blasted when velocities are gone
|
|
|
|
|
flags2 &= ~MF2_BLASTED;
|
|
|
|
|
}
|
|
|
|
|
if ((flags6 & MF6_TOUCHY) && !IsSentient())
|
|
|
|
|
{ // Arm a mine which has come to rest
|
|
|
|
|
flags6 |= MF6_ARMED;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-09-14 20:47:53 +00:00
|
|
|
|
}
|
2012-07-06 03:42:03 +00:00
|
|
|
|
if (velz || BlockingMobj || z != floorz)
|
2009-06-30 20:57:51 +00:00
|
|
|
|
{ // Handle Z velocity and gravity
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (((flags2 & MF2_PASSMOBJ) || (flags & MF_SPECIAL)) && !(i_compatflags & COMPATF_NO_PASSMOBJ))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (!(onmo = P_CheckOnmobj (this)))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-04-14 01:20:44 +00:00
|
|
|
|
P_ZMovement (this, oldfloorz);
|
2008-04-03 10:49:54 +00:00
|
|
|
|
flags2 &= ~MF2_ONMOBJ;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
else
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
if (player)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if (velz < (fixed_t)(level.gravity * Sector->gravity * -655.36f)
|
2008-04-03 10:49:54 +00:00
|
|
|
|
&& !(flags&MF_NOGRAVITY))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
PlayerLandedOnThing (this, onmo);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
}
|
|
|
|
|
if (onmo->z + onmo->height - z <= MaxStepHeight)
|
|
|
|
|
{
|
|
|
|
|
if (player && player->mo == this)
|
|
|
|
|
{
|
|
|
|
|
player->viewheight -= onmo->z + onmo->height - z;
|
|
|
|
|
fixed_t deltaview = player->GetDeltaViewHeight();
|
|
|
|
|
if (deltaview > player->deltaviewheight)
|
|
|
|
|
{
|
|
|
|
|
player->deltaviewheight = deltaview;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
z = onmo->z + onmo->height;
|
|
|
|
|
}
|
2012-07-07 02:28:29 +00:00
|
|
|
|
// Check for MF6_BUMPSPECIAL
|
|
|
|
|
// By default, only players can activate things by bumping into them
|
|
|
|
|
// We trigger specials as long as we are on top of it and not just when
|
|
|
|
|
// we land on it. This could be considered as gravity making us continually
|
|
|
|
|
// bump into it, but it also avoids having to worry about walking on to
|
|
|
|
|
// something without dropping and not triggering anything.
|
|
|
|
|
if ((onmo->flags6 & MF6_BUMPSPECIAL) && ((player != NULL)
|
|
|
|
|
|| ((onmo->activationtype & THINGSPEC_MonsterTrigger) && (flags3 & MF3_ISMONSTER))
|
|
|
|
|
|| ((onmo->activationtype & THINGSPEC_MissileTrigger) && (flags & MF_MISSILE))
|
|
|
|
|
) && (level.maptime > onmo->lastbump)) // Leave the bumper enough time to go away
|
|
|
|
|
{
|
2012-08-28 02:52:53 +00:00
|
|
|
|
if (player == NULL || !(player->cheats & CF_PREDICTING))
|
|
|
|
|
{
|
|
|
|
|
if (P_ActivateThingSpecial(onmo, this))
|
|
|
|
|
onmo->lastbump = level.maptime + TICRATE;
|
|
|
|
|
}
|
2012-07-07 02:28:29 +00:00
|
|
|
|
}
|
2012-04-26 03:50:11 +00:00
|
|
|
|
if (velz != 0 && (BounceFlags & BOUNCE_Actors))
|
2012-04-26 03:30:02 +00:00
|
|
|
|
{
|
2012-04-26 03:50:11 +00:00
|
|
|
|
P_BounceActor(this, onmo, true);
|
2012-04-26 03:30:02 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
flags2 |= MF2_ONMOBJ;
|
|
|
|
|
velz = 0;
|
|
|
|
|
Crash();
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
2009-04-14 01:20:44 +00:00
|
|
|
|
P_ZMovement (this, oldfloorz);
|
2008-04-03 10:49:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ObjectFlags & OF_EuthanizeMe)
|
|
|
|
|
return; // actor was destroyed
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
else if (z <= floorz)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-03 10:49:54 +00:00
|
|
|
|
Crash();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
UpdateWaterLevel (oldz);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-04-03 10:49:54 +00:00
|
|
|
|
// [RH] Don't advance if predicting a player
|
|
|
|
|
if (player && (player->cheats & CF_PREDICTING))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-07-23 21:36:17 +00:00
|
|
|
|
|
|
|
|
|
// Check for poison damage, but only once per PoisonPeriod tics (or once per second if none).
|
|
|
|
|
if (PoisonDurationReceived && (level.time % (PoisonPeriodReceived ? PoisonPeriodReceived : TICRATE) == 0))
|
|
|
|
|
{
|
2011-06-13 10:39:14 +00:00
|
|
|
|
P_DamageMobj(this, NULL, Poisoner, PoisonDamageReceived, PoisonDamageTypeReceived ? PoisonDamageTypeReceived : (FName)NAME_Poison, 0);
|
2010-07-23 21:36:17 +00:00
|
|
|
|
|
|
|
|
|
--PoisonDurationReceived;
|
|
|
|
|
|
|
|
|
|
// Must clear damage when duration is done, otherwise it
|
|
|
|
|
// could be added to with ADDITIVEPOISONDAMAGE.
|
|
|
|
|
if (!PoisonDurationReceived) PoisonDamageReceived = 0;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-04-30 04:20:09 +00:00
|
|
|
|
assert (state != NULL);
|
2013-05-01 02:21:43 +00:00
|
|
|
|
if (state == NULL)
|
|
|
|
|
{
|
|
|
|
|
Destroy();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-09-18 01:44:13 +00:00
|
|
|
|
if ((flags7 & MF7_HANDLENODELAY) && !(flags2 & MF2_DORMANT))
|
|
|
|
|
{
|
|
|
|
|
flags7 &= ~MF7_HANDLENODELAY;
|
|
|
|
|
if (state->GetNoDelay())
|
|
|
|
|
{
|
|
|
|
|
// For immediately spawned objects with the NoDelay flag set for their
|
2015-02-25 02:37:58 +00:00
|
|
|
|
// Spawn state, explicitly call the current state's function.
|
|
|
|
|
if (state->CallAction(this, this) && (ObjectFlags & OF_EuthanizeMe))
|
2013-09-18 01:44:13 +00:00
|
|
|
|
return; // freed itself
|
2013-04-30 04:20:09 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2013-05-01 02:21:43 +00:00
|
|
|
|
// cycle through states, calling action functions at transitions
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (tics != -1)
|
|
|
|
|
{
|
2013-04-29 21:12:57 +00:00
|
|
|
|
// [RH] Use tics <= 0 instead of == 0 so that spawnstates
|
|
|
|
|
// of 0 tics work as expected.
|
2013-05-01 02:21:43 +00:00
|
|
|
|
if (--tics <= 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2013-04-30 04:20:09 +00:00
|
|
|
|
if (!SetState(state->GetNextState()))
|
2013-04-29 21:12:57 +00:00
|
|
|
|
return; // freed itself
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2007-10-29 22:15:46 +00:00
|
|
|
|
int respawn_monsters = G_SkillProperty(SKILLP_Respawn);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// check for nightmare respawn
|
2008-03-01 16:59:17 +00:00
|
|
|
|
if (!(flags5 & MF5_ALWAYSRESPAWN))
|
|
|
|
|
{
|
|
|
|
|
if (!respawn_monsters || !(flags3 & MF3_ISMONSTER) || (flags2 & MF2_DORMANT) || (flags5 & MF5_NEVERRESPAWN))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int limit = G_SkillProperty (SKILLP_RespawnLimit);
|
|
|
|
|
if (limit > 0 && skillrespawncount >= limit)
|
|
|
|
|
return;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
movecount++;
|
|
|
|
|
|
2007-10-29 22:15:46 +00:00
|
|
|
|
if (movecount < respawn_monsters)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (level.time & 31)
|
|
|
|
|
return;
|
|
|
|
|
|
2012-03-29 04:33:37 +00:00
|
|
|
|
if (pr_nightmarerespawn() > 4)
|
|
|
|
|
return;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
P_NightmareRespawn (this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-18 03:52:18 +00:00
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: CheckSectorTransition
|
|
|
|
|
//
|
|
|
|
|
// Fire off some sector triggers if the actor has changed sectors.
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
|
|
void AActor::CheckSectorTransition(sector_t *oldsec)
|
|
|
|
|
{
|
|
|
|
|
if (oldsec != Sector)
|
|
|
|
|
{
|
|
|
|
|
if (oldsec->SecActTarget != NULL)
|
|
|
|
|
{
|
|
|
|
|
oldsec->SecActTarget->TriggerAction(this, SECSPAC_Exit);
|
|
|
|
|
}
|
|
|
|
|
if (Sector->SecActTarget != NULL)
|
|
|
|
|
{
|
|
|
|
|
int act = SECSPAC_Enter;
|
|
|
|
|
if (z <= Sector->floorplane.ZatPoint(x, y))
|
|
|
|
|
{
|
|
|
|
|
act |= SECSPAC_HitFloor;
|
|
|
|
|
}
|
|
|
|
|
if (z + height >= Sector->ceilingplane.ZatPoint(x, y))
|
|
|
|
|
{
|
|
|
|
|
act |= SECSPAC_HitCeiling;
|
|
|
|
|
}
|
|
|
|
|
if (Sector->heightsec != NULL && z == Sector->heightsec->floorplane.ZatPoint(x, y))
|
|
|
|
|
{
|
|
|
|
|
act |= SECSPAC_HitFakeFloor;
|
|
|
|
|
}
|
|
|
|
|
Sector->SecActTarget->TriggerAction(this, act);
|
|
|
|
|
}
|
2012-03-20 03:15:23 +00:00
|
|
|
|
if (z == floorz)
|
|
|
|
|
{
|
|
|
|
|
P_CheckFor3DFloorHit(this);
|
|
|
|
|
}
|
|
|
|
|
if (z + height == ceilingz)
|
|
|
|
|
{
|
|
|
|
|
P_CheckFor3DCeilingHit(this);
|
|
|
|
|
}
|
2012-03-18 03:52:18 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor::UpdateWaterLevel
|
|
|
|
|
//
|
|
|
|
|
// Returns true if actor should splash
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
2007-01-07 09:43:58 +00:00
|
|
|
|
bool AActor::UpdateWaterLevel (fixed_t oldz, bool dosplash)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2006-09-14 00:02:31 +00:00
|
|
|
|
BYTE lastwaterlevel = waterlevel;
|
2009-12-18 05:38:14 +00:00
|
|
|
|
fixed_t fh = FIXED_MIN;
|
2007-01-07 09:43:58 +00:00
|
|
|
|
bool reset=false;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
waterlevel = 0;
|
|
|
|
|
|
|
|
|
|
if (Sector == NULL)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-10 09:16:01 +00:00
|
|
|
|
if (Sector->MoreFlags & SECF_UNDERWATER) // intentionally not SECF_UNDERWATERMASK
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
waterlevel = 3;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-05-23 10:21:33 +00:00
|
|
|
|
const sector_t *hsec = Sector->GetHeightSec();
|
|
|
|
|
if (hsec != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2007-01-07 09:43:58 +00:00
|
|
|
|
fh = hsec->floorplane.ZatPoint (x, y);
|
2008-06-10 09:16:01 +00:00
|
|
|
|
//if (hsec->MoreFlags & SECF_UNDERWATERMASK) // also check Boom-style non-swimmable sectors
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
if (z < fh)
|
|
|
|
|
{
|
|
|
|
|
waterlevel = 1;
|
|
|
|
|
if (z + height/2 < fh)
|
|
|
|
|
{
|
|
|
|
|
waterlevel = 2;
|
|
|
|
|
if ((player && z + player->viewheight <= fh) ||
|
|
|
|
|
(z + height <= fh))
|
|
|
|
|
{
|
|
|
|
|
waterlevel = 3;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-18 05:38:14 +00:00
|
|
|
|
else if (!(hsec->MoreFlags & SECF_FAKEFLOORONLY) && (z + height > hsec->ceilingplane.ZatPoint (x, y)))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
waterlevel = 3;
|
|
|
|
|
}
|
2007-01-07 09:43:58 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
2009-12-18 05:38:14 +00:00
|
|
|
|
waterlevel = 0;
|
2007-01-07 09:43:58 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2007-01-07 09:43:58 +00:00
|
|
|
|
// even non-swimmable deep water must be checked here to do the splashes correctly
|
2008-06-10 09:16:01 +00:00
|
|
|
|
// But the water level must be reset when this function returns
|
2009-12-18 05:38:14 +00:00
|
|
|
|
if (!(hsec->MoreFlags&SECF_UNDERWATERMASK))
|
|
|
|
|
{
|
|
|
|
|
reset = true;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-01-04 15:00:29 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Check 3D floors as well!
|
|
|
|
|
for(unsigned int i=0;i<Sector->e->XFloor.ffloors.Size();i++)
|
|
|
|
|
{
|
|
|
|
|
F3DFloor* rover=Sector->e->XFloor.ffloors[i];
|
|
|
|
|
|
|
|
|
|
if (!(rover->flags & FF_EXISTS)) continue;
|
|
|
|
|
if(!(rover->flags & FF_SWIMMABLE) || rover->flags & FF_SOLID) continue;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-01-04 15:00:29 +00:00
|
|
|
|
fixed_t ff_bottom=rover->bottom.plane->ZatPoint(x, y);
|
|
|
|
|
fixed_t ff_top=rover->top.plane->ZatPoint(x, y);
|
|
|
|
|
|
|
|
|
|
if(ff_top <= z || ff_bottom > (z + (height >> 1))) continue;
|
|
|
|
|
|
|
|
|
|
fh=ff_top;
|
|
|
|
|
if (z < fh)
|
|
|
|
|
{
|
|
|
|
|
waterlevel = 1;
|
|
|
|
|
if (z + height/2 < fh)
|
|
|
|
|
{
|
|
|
|
|
waterlevel = 2;
|
|
|
|
|
if ((player && z + player->viewheight <= fh) ||
|
|
|
|
|
(z + height <= fh))
|
|
|
|
|
{
|
|
|
|
|
waterlevel = 3;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-01-07 09:43:58 +00:00
|
|
|
|
|
2008-06-10 09:16:01 +00:00
|
|
|
|
// some additional checks to make deep sectors like Boom's splash without setting
|
2007-01-07 09:43:58 +00:00
|
|
|
|
// the water flags.
|
2009-01-04 12:25:22 +00:00
|
|
|
|
if (boomwaterlevel == 0 && waterlevel != 0 && dosplash)
|
|
|
|
|
{
|
|
|
|
|
P_HitWater(this, Sector, FIXED_MIN, FIXED_MIN, fh, true);
|
|
|
|
|
}
|
2009-12-18 05:38:14 +00:00
|
|
|
|
boomwaterlevel = waterlevel;
|
|
|
|
|
if (reset)
|
|
|
|
|
{
|
|
|
|
|
waterlevel = lastwaterlevel;
|
|
|
|
|
}
|
2009-01-04 15:00:29 +00:00
|
|
|
|
return false; // we did the splash ourselves
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-08-20 12:20:51 +00:00
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// P_SpawnMobj
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
2009-10-25 15:26:19 +00:00
|
|
|
|
AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t iz, replace_t allowreplacement, bool SpawningMapThing)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
if (type == NULL)
|
|
|
|
|
{
|
|
|
|
|
I_Error ("Tried to spawn a class-less actor\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type->ActorInfo == NULL)
|
|
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
|
I_Error ("%s is not an actor\n", type->TypeName.GetChars());
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-07-16 09:10:45 +00:00
|
|
|
|
if (allowreplacement)
|
2010-08-26 20:59:15 +00:00
|
|
|
|
type = type->GetReplacement();
|
2006-07-16 09:10:45 +00:00
|
|
|
|
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
AActor *actor;
|
2009-09-14 19:44:14 +00:00
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
|
actor = static_cast<AActor *>(const_cast<PClass *>(type)->CreateNew ());
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2010-08-20 12:20:51 +00:00
|
|
|
|
// Set default dialogue
|
|
|
|
|
actor->ConversationRoot = GetConversation(actor->GetClass()->TypeName);
|
|
|
|
|
if (actor->ConversationRoot != -1)
|
|
|
|
|
{
|
|
|
|
|
actor->Conversation = StrifeDialogues[actor->ConversationRoot];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
actor->Conversation = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-30 18:53:14 +00:00
|
|
|
|
actor->x = actor->PrevX = ix;
|
|
|
|
|
actor->y = actor->PrevY = iy;
|
|
|
|
|
actor->z = actor->PrevZ = iz;
|
2008-06-15 18:36:26 +00:00
|
|
|
|
actor->picnum.SetInvalid();
|
2009-07-04 18:17:44 +00:00
|
|
|
|
actor->health = actor->SpawnHealth();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-12-16 16:09:48 +00:00
|
|
|
|
// Actors with zero gravity need the NOGRAVITY flag set.
|
|
|
|
|
if (actor->gravity == 0) actor->flags |= MF_NOGRAVITY;
|
|
|
|
|
|
2008-03-28 00:38:17 +00:00
|
|
|
|
FRandom &rng = bglobal.m_Thinking ? pr_botspawnmobj : pr_spawnmobj;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2007-10-29 22:15:46 +00:00
|
|
|
|
if (actor->isFast() && actor->flags3 & MF3_ISMONSTER)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
actor->reactiontime = 0;
|
|
|
|
|
|
|
|
|
|
if (actor->flags3 & MF3_ISMONSTER)
|
|
|
|
|
{
|
2008-03-12 02:56:11 +00:00
|
|
|
|
actor->LastLookPlayerNumber = rng() % MAXPLAYERS;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
actor->TIDtoHate = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the state, but do not use SetState, because action
|
|
|
|
|
// routines can't be called yet. If the spawnstate has an action
|
|
|
|
|
// routine, it will not be called.
|
|
|
|
|
FState *st = actor->SpawnState;
|
|
|
|
|
actor->state = st;
|
|
|
|
|
actor->tics = st->GetTics();
|
2006-11-04 13:06:42 +00:00
|
|
|
|
|
2008-08-10 14:19:47 +00:00
|
|
|
|
actor->sprite = st->sprite;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
actor->frame = st->GetFrame();
|
2015-04-04 16:40:43 +00:00
|
|
|
|
actor->renderflags = (actor->renderflags & ~RF_FULLBRIGHT) | ActorRenderFlags::FromInt (st->GetFullbright());
|
2006-02-24 04:48:15 +00:00
|
|
|
|
actor->touching_sectorlist = NULL; // NULL head of sector list // phares 3/13/98
|
2007-10-29 22:15:46 +00:00
|
|
|
|
if (G_SkillProperty(SKILLP_FastMonsters))
|
2006-11-02 07:23:08 +00:00
|
|
|
|
actor->Speed = actor->GetClass()->Meta.GetMetaFixed(AMETA_FastSpeed, actor->Speed);
|
2014-11-20 05:57:40 +00:00
|
|
|
|
actor->DamageMultiply = FRACUNIT;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-01-18 09:31:49 +00:00
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// set subsector and/or block links
|
|
|
|
|
actor->LinkToWorld (SpawningMapThing);
|
2009-01-18 09:31:49 +00:00
|
|
|
|
|
|
|
|
|
actor->dropoffz = // killough 11/98: for tracking dropoffs
|
|
|
|
|
actor->floorz = actor->Sector->floorplane.ZatPoint (ix, iy);
|
|
|
|
|
actor->ceilingz = actor->Sector->ceilingplane.ZatPoint (ix, iy);
|
|
|
|
|
|
|
|
|
|
// The z-coordinate needs to be set once before calling P_FindFloorCeiling
|
|
|
|
|
// For FLOATRANDZ just use the floor here.
|
|
|
|
|
if (iz == ONFLOORZ || iz == FLOATRANDZ)
|
|
|
|
|
{
|
|
|
|
|
actor->z = actor->floorz;
|
|
|
|
|
}
|
|
|
|
|
else if (iz == ONCEILINGZ)
|
|
|
|
|
{
|
|
|
|
|
actor->z = actor->ceilingz - actor->height;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (SpawningMapThing || !type->IsDescendantOf (RUNTIME_CLASS(APlayerPawn)))
|
|
|
|
|
{
|
2008-05-14 07:45:40 +00:00
|
|
|
|
// Check if there's something solid to stand on between the current position and the
|
2009-01-04 15:51:00 +00:00
|
|
|
|
// current sector's floor. For map spawns this must be delayed until after setting the
|
|
|
|
|
// z-coordinate.
|
2009-01-25 21:59:38 +00:00
|
|
|
|
if (!SpawningMapThing)
|
|
|
|
|
{
|
2012-04-08 05:39:46 +00:00
|
|
|
|
P_FindFloorCeiling(actor, FFCF_ONLYSPAWNPOS);
|
2009-01-25 21:59:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
actor->floorsector = actor->Sector;
|
|
|
|
|
actor->floorpic = actor->floorsector->GetTexture(sector_t::floor);
|
|
|
|
|
actor->ceilingsector = actor->Sector;
|
|
|
|
|
actor->ceilingpic = actor->ceilingsector->GetTexture(sector_t::ceiling);
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
else if (!(actor->flags5 & MF5_NOINTERACTION))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
P_FindFloorCeiling (actor);
|
|
|
|
|
}
|
2008-04-03 10:49:54 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
2008-08-16 20:19:35 +00:00
|
|
|
|
actor->floorpic = actor->Sector->GetTexture(sector_t::floor);
|
2008-04-03 10:49:54 +00:00
|
|
|
|
actor->floorsector = actor->Sector;
|
2008-08-16 20:19:35 +00:00
|
|
|
|
actor->ceilingpic = actor->Sector->GetTexture(sector_t::ceiling);
|
2008-04-03 10:49:54 +00:00
|
|
|
|
actor->ceilingsector = actor->Sector;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-05-08 08:06:26 +00:00
|
|
|
|
actor->SpawnPoint[0] = ix;
|
|
|
|
|
actor->SpawnPoint[1] = iy;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
if (iz == ONFLOORZ)
|
|
|
|
|
{
|
|
|
|
|
actor->z = actor->floorz;
|
|
|
|
|
}
|
|
|
|
|
else if (iz == ONCEILINGZ)
|
|
|
|
|
{
|
|
|
|
|
actor->z = actor->ceilingz - actor->height;
|
|
|
|
|
}
|
|
|
|
|
else if (iz == FLOATRANDZ)
|
|
|
|
|
{
|
|
|
|
|
fixed_t space = actor->ceilingz - actor->height - actor->floorz;
|
|
|
|
|
if (space > 48*FRACUNIT)
|
|
|
|
|
{
|
|
|
|
|
space -= 40*FRACUNIT;
|
|
|
|
|
actor->z = MulScale8 (space, rng()) + actor->floorz + 40*FRACUNIT;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
actor->z = actor->floorz;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2008-05-08 08:06:26 +00:00
|
|
|
|
actor->SpawnPoint[2] = (actor->z - actor->floorz);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-01 12:03:15 +00:00
|
|
|
|
if (actor->FloatBobPhase == (BYTE)-1) actor->FloatBobPhase = rng(); // Don't make everything bob in sync (unless deliberately told to do)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (actor->flags2 & MF2_FLOORCLIP)
|
|
|
|
|
{
|
|
|
|
|
actor->AdjustFloorClip ();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
actor->floorclip = 0;
|
|
|
|
|
}
|
2007-01-07 09:43:58 +00:00
|
|
|
|
actor->UpdateWaterLevel (actor->z, false);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (!SpawningMapThing)
|
|
|
|
|
{
|
|
|
|
|
actor->BeginPlay ();
|
2008-03-12 02:56:11 +00:00
|
|
|
|
if (actor->ObjectFlags & OF_EuthanizeMe)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-07-12 15:15:57 +00:00
|
|
|
|
if (level.flags & LEVEL_NOALLIES && !actor->player)
|
|
|
|
|
{
|
|
|
|
|
actor->flags &= ~MF_FRIENDLY;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// [RH] Count monsters whenever they are spawned.
|
2006-04-16 13:29:50 +00:00
|
|
|
|
if (actor->CountsAsKill())
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
level.total_monsters++;
|
|
|
|
|
}
|
|
|
|
|
// [RH] Same, for items
|
|
|
|
|
if (actor->flags & MF_COUNTITEM)
|
|
|
|
|
{
|
|
|
|
|
level.total_items++;
|
|
|
|
|
}
|
2010-09-19 00:06:45 +00:00
|
|
|
|
// And for secrets
|
|
|
|
|
if (actor->flags5 & MF5_COUNTSECRET)
|
|
|
|
|
{
|
|
|
|
|
level.total_secrets++;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return actor;
|
|
|
|
|
}
|
|
|
|
|
|
2006-07-31 10:22:53 +00:00
|
|
|
|
AActor *Spawn (const char *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement)
|
|
|
|
|
{
|
2010-01-08 03:24:22 +00:00
|
|
|
|
FName classname(type, true);
|
|
|
|
|
if (classname == NAME_None)
|
2006-07-31 10:22:53 +00:00
|
|
|
|
{
|
|
|
|
|
I_Error("Attempt to spawn actor of unknown type '%s'\n", type);
|
|
|
|
|
}
|
2010-01-08 03:24:22 +00:00
|
|
|
|
return Spawn(classname, x, y, z, allowreplacement);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AActor *Spawn (FName classname, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement)
|
|
|
|
|
{
|
|
|
|
|
const PClass *cls = PClass::FindClass(classname);
|
|
|
|
|
if (cls == NULL)
|
|
|
|
|
{
|
|
|
|
|
I_Error("Attempt to spawn actor of unknown type '%s'\n", classname.GetChars());
|
|
|
|
|
}
|
2006-07-31 10:22:53 +00:00
|
|
|
|
return AActor::StaticSpawn (cls, x, y, z, allowreplacement);
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
void AActor::LevelSpawned ()
|
|
|
|
|
{
|
|
|
|
|
if (tics > 0 && !(flags4 & MF4_SYNCHRONIZED))
|
2012-02-28 01:54:35 +00:00
|
|
|
|
{
|
2006-02-24 04:48:15 +00:00
|
|
|
|
tics = 1 + (pr_spawnmapthing() % tics);
|
2012-02-28 01:54:35 +00:00
|
|
|
|
}
|
|
|
|
|
// [RH] Clear MF_DROPPED flag if the default version doesn't have it set.
|
|
|
|
|
// (AInventory::BeginPlay() makes all inventory items spawn with it set.)
|
|
|
|
|
if (!(GetDefault()->flags & MF_DROPPED))
|
|
|
|
|
{
|
|
|
|
|
flags &= ~MF_DROPPED;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
HandleSpawnFlags ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AActor::HandleSpawnFlags ()
|
|
|
|
|
{
|
|
|
|
|
if (SpawnFlags & MTF_AMBUSH)
|
|
|
|
|
{
|
|
|
|
|
flags |= MF_AMBUSH;
|
|
|
|
|
}
|
|
|
|
|
if (SpawnFlags & MTF_DORMANT)
|
|
|
|
|
{
|
|
|
|
|
Deactivate (NULL);
|
|
|
|
|
}
|
|
|
|
|
if (SpawnFlags & MTF_STANDSTILL)
|
|
|
|
|
{
|
|
|
|
|
flags4 |= MF4_STANDSTILL;
|
|
|
|
|
}
|
|
|
|
|
if (SpawnFlags & MTF_FRIENDLY)
|
|
|
|
|
{
|
|
|
|
|
flags |= MF_FRIENDLY;
|
2006-04-17 13:53:34 +00:00
|
|
|
|
// Friendlies don't count as kills!
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (flags & MF_COUNTKILL)
|
|
|
|
|
{
|
|
|
|
|
flags &= ~MF_COUNTKILL;
|
|
|
|
|
level.total_monsters--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (SpawnFlags & MTF_SHADOW)
|
|
|
|
|
{
|
|
|
|
|
flags |= MF_SHADOW;
|
|
|
|
|
RenderStyle = STYLE_Translucent;
|
|
|
|
|
alpha = TRANSLUC25;
|
|
|
|
|
}
|
|
|
|
|
else if (SpawnFlags & MTF_ALTSHADOW)
|
|
|
|
|
{
|
|
|
|
|
RenderStyle = STYLE_None;
|
|
|
|
|
}
|
2010-09-19 00:06:45 +00:00
|
|
|
|
if (SpawnFlags & MTF_SECRET)
|
|
|
|
|
{
|
2013-08-28 09:14:48 +00:00
|
|
|
|
if (!(flags5 & MF5_COUNTSECRET))
|
|
|
|
|
{
|
|
|
|
|
//Printf("Secret %s in sector %i!\n", GetTag(), Sector->sectornum);
|
|
|
|
|
flags5 |= MF5_COUNTSECRET;
|
|
|
|
|
level.total_secrets++;
|
|
|
|
|
}
|
2010-09-19 00:06:45 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AActor::BeginPlay ()
|
|
|
|
|
{
|
2008-01-16 04:43:50 +00:00
|
|
|
|
// If the actor is spawned with the dormant flag set, clear it, and use
|
|
|
|
|
// the normal deactivation logic to make it properly dormant.
|
|
|
|
|
if (flags2 & MF2_DORMANT)
|
|
|
|
|
{
|
|
|
|
|
flags2 &= ~MF2_DORMANT;
|
|
|
|
|
Deactivate (NULL);
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-12-30 12:20:47 +00:00
|
|
|
|
void AActor::PostBeginPlay ()
|
|
|
|
|
{
|
2012-03-07 01:03:56 +00:00
|
|
|
|
if (Renderer != NULL)
|
|
|
|
|
{
|
|
|
|
|
Renderer->StateChanged(this);
|
|
|
|
|
}
|
2009-12-30 12:20:47 +00:00
|
|
|
|
PrevAngle = angle;
|
2013-09-18 01:44:13 +00:00
|
|
|
|
flags7 |= MF7_HANDLENODELAY;
|
2009-12-30 12:20:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-08-22 23:17:49 +00:00
|
|
|
|
void AActor::MarkPrecacheSounds() const
|
|
|
|
|
{
|
|
|
|
|
SeeSound.MarkUsed();
|
|
|
|
|
AttackSound.MarkUsed();
|
|
|
|
|
PainSound.MarkUsed();
|
|
|
|
|
DeathSound.MarkUsed();
|
|
|
|
|
ActiveSound.MarkUsed();
|
|
|
|
|
UseSound.MarkUsed();
|
|
|
|
|
BounceSound.MarkUsed();
|
|
|
|
|
WallBounceSound.MarkUsed();
|
|
|
|
|
CrushPainSound.MarkUsed();
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-29 22:15:46 +00:00
|
|
|
|
bool AActor::isFast()
|
|
|
|
|
{
|
|
|
|
|
if (flags5&MF5_ALWAYSFAST) return true;
|
|
|
|
|
if (flags5&MF5_NEVERFAST) return false;
|
|
|
|
|
return !!G_SkillProperty(SKILLP_FastMonsters);
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-15 19:01:49 +00:00
|
|
|
|
bool AActor::isSlow()
|
|
|
|
|
{
|
|
|
|
|
return !!G_SkillProperty(SKILLP_SlowMonsters);
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
void AActor::Activate (AActor *activator)
|
|
|
|
|
{
|
|
|
|
|
if ((flags3 & MF3_ISMONSTER) && (health > 0 || (flags & MF_ICECORPSE)))
|
|
|
|
|
{
|
|
|
|
|
if (flags2 & MF2_DORMANT)
|
|
|
|
|
{
|
|
|
|
|
flags2 &= ~MF2_DORMANT;
|
2009-10-09 20:35:07 +00:00
|
|
|
|
FState *state = FindState(NAME_Active);
|
2008-08-10 11:29:19 +00:00
|
|
|
|
if (state != NULL)
|
|
|
|
|
{
|
|
|
|
|
SetState(state);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tics = 1;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AActor::Deactivate (AActor *activator)
|
|
|
|
|
{
|
|
|
|
|
if ((flags3 & MF3_ISMONSTER) && (health > 0 || (flags & MF_ICECORPSE)))
|
|
|
|
|
{
|
|
|
|
|
if (!(flags2 & MF2_DORMANT))
|
|
|
|
|
{
|
|
|
|
|
flags2 |= MF2_DORMANT;
|
2009-10-09 20:35:07 +00:00
|
|
|
|
FState *state = FindState(NAME_Inactive);
|
2008-08-10 11:29:19 +00:00
|
|
|
|
if (state != NULL)
|
|
|
|
|
{
|
|
|
|
|
SetState(state);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tics = -1;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// P_RemoveMobj
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
void AActor::Destroy ()
|
|
|
|
|
{
|
|
|
|
|
// [RH] Destroy any inventory this actor is carrying
|
2006-06-18 04:10:47 +00:00
|
|
|
|
DestroyAllInventory ();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// [RH] Unlink from tid chain
|
|
|
|
|
RemoveFromHash ();
|
|
|
|
|
|
|
|
|
|
// unlink from sector and block lists
|
|
|
|
|
UnlinkFromWorld ();
|
|
|
|
|
flags |= MF_NOSECTOR|MF_NOBLOCKMAP;
|
|
|
|
|
|
|
|
|
|
// Delete all nodes on the current sector_list phares 3/16/98
|
2006-05-18 04:25:26 +00:00
|
|
|
|
P_DelSector_List();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2006-06-18 04:10:47 +00:00
|
|
|
|
// Transform any playing sound into positioned, non-actor sounds.
|
2006-02-24 04:48:15 +00:00
|
|
|
|
S_RelinkSound (this, NULL);
|
|
|
|
|
|
|
|
|
|
Super::Destroy ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
// AdjustFloorClip
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
void AActor::AdjustFloorClip ()
|
|
|
|
|
{
|
|
|
|
|
if (flags3 & MF3_SPECIALFLOORCLIP)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fixed_t oldclip = floorclip;
|
|
|
|
|
fixed_t shallowestclip = FIXED_MAX;
|
|
|
|
|
const msecnode_t *m;
|
|
|
|
|
|
2009-01-04 15:00:29 +00:00
|
|
|
|
// possibly standing on a 3D-floor!
|
|
|
|
|
if (Sector->e->XFloor.ffloors.Size() && z>Sector->floorplane.ZatPoint(x,y)) floorclip=0;
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// [RH] clip based on shallowest floor player is standing on
|
|
|
|
|
// If the sector has a deep water effect, then let that effect
|
|
|
|
|
// do the floorclipping instead of the terrain type.
|
|
|
|
|
for (m = touching_sectorlist; m; m = m->m_tnext)
|
|
|
|
|
{
|
2009-05-23 10:21:33 +00:00
|
|
|
|
sector_t *hsec = m->m_sector->GetHeightSec();
|
|
|
|
|
if (hsec == NULL && m->m_sector->floorplane.ZatPoint (x, y) == z)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-08-16 20:19:35 +00:00
|
|
|
|
fixed_t clip = Terrains[TerrainTypes[m->m_sector->GetTexture(sector_t::floor)]].FootClip;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (clip < shallowestclip)
|
|
|
|
|
{
|
|
|
|
|
shallowestclip = clip;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (shallowestclip == FIXED_MAX)
|
|
|
|
|
{
|
|
|
|
|
floorclip = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
floorclip = shallowestclip;
|
|
|
|
|
}
|
|
|
|
|
if (player && player->mo == this && oldclip != floorclip)
|
|
|
|
|
{
|
|
|
|
|
player->viewheight -= oldclip - floorclip;
|
2006-04-11 16:27:41 +00:00
|
|
|
|
player->deltaviewheight = player->GetDeltaViewHeight();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// P_SpawnPlayer
|
|
|
|
|
// Called when a player is spawned on the level.
|
|
|
|
|
// Most of the player structure stays unchanged between levels.
|
|
|
|
|
//
|
|
|
|
|
EXTERN_CVAR (Bool, chasedemo)
|
2007-10-29 22:15:46 +00:00
|
|
|
|
|
2006-04-16 19:09:36 +00:00
|
|
|
|
extern bool demonew;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2013-01-03 02:08:08 +00:00
|
|
|
|
APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
player_t *p;
|
|
|
|
|
APlayerPawn *mobj, *oldactor;
|
|
|
|
|
BYTE state;
|
2012-07-07 03:18:09 +00:00
|
|
|
|
fixed_t spawn_x, spawn_y, spawn_z;
|
2008-01-09 02:53:38 +00:00
|
|
|
|
angle_t spawn_angle;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// not playing?
|
2012-07-07 22:52:37 +00:00
|
|
|
|
if ((unsigned)playernum >= (unsigned)MAXPLAYERS || !playeringame[playernum])
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2014-10-13 03:49:53 +00:00
|
|
|
|
// Old lerp data needs to go
|
|
|
|
|
if (playernum == consoleplayer)
|
|
|
|
|
{
|
|
|
|
|
P_PredictionLerpReset();
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
p = &players[playernum];
|
|
|
|
|
|
|
|
|
|
if (p->cls == NULL)
|
|
|
|
|
{
|
2006-07-13 10:17:56 +00:00
|
|
|
|
// [GRB] Pick a class from player class list
|
|
|
|
|
if (PlayerClasses.Size () > 1)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
int type;
|
|
|
|
|
|
|
|
|
|
if (!deathmatch || !multiplayer)
|
|
|
|
|
{
|
|
|
|
|
type = SinglePlayerClass[playernum];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-05-12 18:27:03 +00:00
|
|
|
|
type = p->userinfo.GetPlayerClassNum();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (type < 0)
|
|
|
|
|
{
|
2006-07-13 10:17:56 +00:00
|
|
|
|
type = pr_multiclasschoice() % PlayerClasses.Size ();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
p->CurrentPlayerClass = type;
|
|
|
|
|
}
|
2006-07-13 10:17:56 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
p->CurrentPlayerClass = 0;
|
|
|
|
|
}
|
|
|
|
|
p->cls = PlayerClasses[p->CurrentPlayerClass].Type;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-02-05 23:32:49 +00:00
|
|
|
|
if (( dmflags2 & DF2_SAME_SPAWN_SPOT ) &&
|
|
|
|
|
( p->playerstate == PST_REBORN ) &&
|
|
|
|
|
( deathmatch == false ) &&
|
|
|
|
|
( gameaction != ga_worlddone ) &&
|
2008-08-02 08:38:07 +00:00
|
|
|
|
( p->mo != NULL ) &&
|
2008-12-14 19:12:41 +00:00
|
|
|
|
( !(p->mo->Sector->Flags & SECF_NORESPAWN) ) &&
|
2016-01-06 11:56:35 +00:00
|
|
|
|
( p->mo->Sector->damageamount < TELEFRAG_DAMAGE ))
|
2008-02-05 23:32:49 +00:00
|
|
|
|
{
|
|
|
|
|
spawn_x = p->mo->x;
|
|
|
|
|
spawn_y = p->mo->y;
|
2014-12-03 06:50:05 +00:00
|
|
|
|
spawn_z = p->mo->z;
|
|
|
|
|
|
2008-02-05 23:32:49 +00:00
|
|
|
|
spawn_angle = p->mo->angle;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2008-05-08 08:06:26 +00:00
|
|
|
|
spawn_x = mthing->x;
|
|
|
|
|
spawn_y = mthing->y;
|
2014-12-03 06:50:05 +00:00
|
|
|
|
|
2010-03-26 16:57:00 +00:00
|
|
|
|
// Allow full angular precision but avoid roundoff errors for multiples of 45 degrees.
|
|
|
|
|
if (mthing->angle % 45 != 0)
|
|
|
|
|
{
|
|
|
|
|
spawn_angle = mthing->angle * (ANG45 / 45);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
spawn_angle = ANG45 * (mthing->angle / 45);
|
|
|
|
|
}
|
2011-11-01 02:58:52 +00:00
|
|
|
|
if (i_compatflags2 & COMPATF2_BADANGLES)
|
|
|
|
|
{
|
|
|
|
|
spawn_angle += 1 << ANGLETOFINESHIFT;
|
|
|
|
|
}
|
2008-01-09 02:53:38 +00:00
|
|
|
|
|
2014-12-03 06:50:05 +00:00
|
|
|
|
if (GetDefaultByType(p->cls)->flags & MF_SPAWNCEILING)
|
|
|
|
|
spawn_z = ONCEILINGZ;
|
|
|
|
|
else if (GetDefaultByType(p->cls)->flags2 & MF2_SPAWNFLOAT)
|
|
|
|
|
spawn_z = FLOATRANDZ;
|
|
|
|
|
else
|
|
|
|
|
spawn_z = ONFLOORZ;
|
|
|
|
|
}
|
2012-07-07 03:18:09 +00:00
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
mobj = static_cast<APlayerPawn *>
|
2012-07-07 03:18:09 +00:00
|
|
|
|
(Spawn (p->cls, spawn_x, spawn_y, spawn_z, NO_REPLACE));
|
|
|
|
|
|
|
|
|
|
if (level.flags & LEVEL_USEPLAYERSTARTZ)
|
|
|
|
|
{
|
|
|
|
|
if (spawn_z == ONFLOORZ)
|
|
|
|
|
mobj->z += mthing->z;
|
|
|
|
|
else if (spawn_z == ONCEILINGZ)
|
|
|
|
|
mobj->z -= mthing->z;
|
|
|
|
|
P_FindFloorCeiling(mobj, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT);
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
mobj->FriendPlayer = playernum + 1; // [RH] players are their own friends
|
|
|
|
|
oldactor = p->mo;
|
|
|
|
|
p->mo = mobj;
|
|
|
|
|
mobj->player = p;
|
|
|
|
|
state = p->playerstate;
|
|
|
|
|
if (state == PST_REBORN || state == PST_ENTER)
|
|
|
|
|
{
|
|
|
|
|
G_PlayerReborn (playernum);
|
|
|
|
|
}
|
2013-01-03 02:08:08 +00:00
|
|
|
|
else if (oldactor != NULL && oldactor->player == p && !(flags & SPF_TEMPPLAYER))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
// Move the voodoo doll's inventory to the new player.
|
|
|
|
|
mobj->ObtainInventory (oldactor);
|
2006-09-17 10:43:51 +00:00
|
|
|
|
FBehavior::StaticStopMyScripts (oldactor); // cancel all ENTER/RESPAWN scripts for the voodoo doll
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
|
// [GRB] Reset skin
|
2013-05-12 18:27:03 +00:00
|
|
|
|
p->userinfo.SkinNumChanged(R_FindSkin (skins[p->userinfo.GetSkin()].name, p->CurrentPlayerClass));
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2010-03-06 09:30:38 +00:00
|
|
|
|
if (!(mobj->flags2 & MF2_DONTTRANSLATE))
|
|
|
|
|
{
|
|
|
|
|
// [RH] Be sure the player has the right translation
|
|
|
|
|
R_BuildPlayerTranslation (playernum);
|
|
|
|
|
|
|
|
|
|
// [RH] set color translations for player sprites
|
|
|
|
|
mobj->Translation = TRANSLATION(TRANSLATION_Players,playernum);
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-01-09 02:53:38 +00:00
|
|
|
|
mobj->angle = spawn_angle;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
mobj->pitch = mobj->roll = 0;
|
|
|
|
|
mobj->health = p->health;
|
|
|
|
|
|
|
|
|
|
// [RH] Set player sprite based on skin
|
2012-04-14 03:07:28 +00:00
|
|
|
|
if (!(mobj->flags4 & MF4_NOSKIN))
|
|
|
|
|
{
|
2013-05-12 18:27:03 +00:00
|
|
|
|
mobj->sprite = skins[p->userinfo.GetSkin()].sprite;
|
2012-04-14 03:07:28 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
p->DesiredFOV = p->FOV = 90.f;
|
|
|
|
|
p->camera = p->mo;
|
|
|
|
|
p->playerstate = PST_LIVE;
|
|
|
|
|
p->refire = 0;
|
|
|
|
|
p->damagecount = 0;
|
|
|
|
|
p->bonuscount = 0;
|
|
|
|
|
p->morphTics = 0;
|
2008-04-06 17:33:43 +00:00
|
|
|
|
p->MorphedPlayerClass = 0;
|
2008-04-08 08:53:42 +00:00
|
|
|
|
p->MorphStyle = 0;
|
|
|
|
|
p->MorphExitFlash = NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
p->extralight = 0;
|
2009-09-20 03:50:05 +00:00
|
|
|
|
p->fixedcolormap = NOFIXEDCOLORMAP;
|
|
|
|
|
p->fixedlightlevel = -1;
|
2006-07-13 10:17:56 +00:00
|
|
|
|
p->viewheight = mobj->ViewHeight;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
p->inconsistant = 0;
|
|
|
|
|
p->attacker = NULL;
|
|
|
|
|
p->spreecount = 0;
|
|
|
|
|
p->multicount = 0;
|
|
|
|
|
p->lastkilltime = 0;
|
|
|
|
|
p->BlendR = p->BlendG = p->BlendB = p->BlendA = 0.f;
|
2008-07-19 12:40:10 +00:00
|
|
|
|
p->mo->ResetAirSupply(false);
|
2006-05-14 14:30:13 +00:00
|
|
|
|
p->Uncrouch();
|
2011-12-06 01:25:37 +00:00
|
|
|
|
p->MinPitch = p->MaxPitch = 0; // will be filled in by PostBeginPlay()/netcode
|
2015-03-27 04:19:05 +00:00
|
|
|
|
p->MUSINFOactor = NULL;
|
|
|
|
|
p->MUSINFOtics = -1;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
|
p->velx = p->vely = 0; // killough 10/98: initialize bobbing to 0.
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2012-03-17 00:52:33 +00:00
|
|
|
|
for (int ii = 0; ii < MAXPLAYERS; ++ii)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2012-03-17 00:52:33 +00:00
|
|
|
|
if (playeringame[ii] && players[ii].camera == oldactor)
|
|
|
|
|
{
|
2012-03-18 01:34:53 +00:00
|
|
|
|
players[ii].camera = mobj;
|
2012-03-17 00:52:33 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [RH] Allow chasecam for demo watching
|
|
|
|
|
if ((demoplayback || demonew) && chasedemo)
|
|
|
|
|
p->cheats = CF_CHASECAM;
|
|
|
|
|
|
|
|
|
|
// setup gun psprite
|
2013-01-03 02:08:08 +00:00
|
|
|
|
if (!(flags & SPF_TEMPPLAYER))
|
|
|
|
|
{ // This can also start a script so don't do it for the dummy player.
|
|
|
|
|
P_SetupPsprites (p, !!(flags & SPF_WEAPONFULLYUP));
|
2006-05-27 10:27:51 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
if (deathmatch)
|
2006-06-18 04:10:47 +00:00
|
|
|
|
{ // Give all cards in death match mode.
|
|
|
|
|
p->mo->GiveDeathmatchInventory ();
|
|
|
|
|
}
|
2009-02-03 19:11:43 +00:00
|
|
|
|
else if ((multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN)) && state == PST_REBORN && oldactor != NULL)
|
2006-06-18 04:10:47 +00:00
|
|
|
|
{ // Special inventory handling for respawning in coop
|
|
|
|
|
p->mo->FilterCoopRespawnInventory (oldactor);
|
|
|
|
|
}
|
|
|
|
|
if (oldactor != NULL)
|
|
|
|
|
{ // Remove any inventory left from the old actor. Coop handles
|
|
|
|
|
// it above, but the other modes don't.
|
|
|
|
|
oldactor->DestroyAllInventory();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-06-15 03:46:04 +00:00
|
|
|
|
// [BC] Handle temporary invulnerability when respawned
|
|
|
|
|
if ((state == PST_REBORN || state == PST_ENTER) &&
|
|
|
|
|
(dmflags2 & DF2_YES_RESPAWN_INVUL) &&
|
|
|
|
|
(multiplayer || alwaysapplydmflags))
|
|
|
|
|
{
|
|
|
|
|
APowerup *invul = static_cast<APowerup*>(p->mo->GiveInventoryType (RUNTIME_CLASS(APowerInvulnerable)));
|
|
|
|
|
invul->EffectTics = 3*TICRATE;
|
2014-12-03 22:04:47 +00:00
|
|
|
|
invul->BlendColor = 0; // don't mess with the view
|
|
|
|
|
invul->ItemFlags |= IF_UNDROPPABLE; // Don't drop this
|
2008-06-15 03:46:04 +00:00
|
|
|
|
p->mo->effects |= FX_RESPAWNINVUL; // [RH] special effect
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
if (StatusBar != NULL && (playernum == consoleplayer || StatusBar->GetPlayer() == playernum))
|
|
|
|
|
{
|
|
|
|
|
StatusBar->AttachToPlayer (p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (multiplayer)
|
|
|
|
|
{
|
2008-05-30 06:56:50 +00:00
|
|
|
|
unsigned an = mobj->angle >> ANGLETOFINESHIFT;
|
2006-07-16 09:10:45 +00:00
|
|
|
|
Spawn ("TeleportFog", mobj->x+20*finecosine[an], mobj->y+20*finesine[an], mobj->z + TELEFOGHEIGHT, ALLOW_REPLACE);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// "Fix" for one of the starts on exec.wad MAP01: If you start inside the ceiling,
|
|
|
|
|
// drop down below it, even if that means sinking into the floor.
|
|
|
|
|
if (mobj->z + mobj->height > mobj->ceilingz)
|
|
|
|
|
{
|
|
|
|
|
mobj->z = mobj->ceilingz - mobj->height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [BC] Do script stuff
|
2013-01-03 02:08:08 +00:00
|
|
|
|
if (!(flags & SPF_TEMPPLAYER))
|
2006-05-17 01:38:07 +00:00
|
|
|
|
{
|
2006-05-27 10:27:51 +00:00
|
|
|
|
if (state == PST_ENTER || (state == PST_LIVE && !savegamerestore))
|
|
|
|
|
{
|
|
|
|
|
FBehavior::StaticStartTypedScripts (SCRIPT_Enter, p->mo, true);
|
|
|
|
|
}
|
|
|
|
|
else if (state == PST_REBORN)
|
|
|
|
|
{
|
2006-10-03 03:14:28 +00:00
|
|
|
|
assert (oldactor != NULL);
|
2008-03-01 13:12:33 +00:00
|
|
|
|
|
|
|
|
|
// before relocating all pointers to the player all sound targets
|
|
|
|
|
// pointing to the old actor have to be NULLed. Otherwise all
|
|
|
|
|
// monsters who last targeted this player will wake up immediately
|
|
|
|
|
// after the player has respawned.
|
|
|
|
|
AActor *th;
|
|
|
|
|
TThinkerIterator<AActor> it;
|
|
|
|
|
while ((th = it.Next()))
|
|
|
|
|
{
|
|
|
|
|
if (th->LastHeard == oldactor) th->LastHeard = NULL;
|
|
|
|
|
}
|
|
|
|
|
for(int i = 0; i < numsectors; i++)
|
|
|
|
|
{
|
|
|
|
|
if (sectors[i].SoundTarget == oldactor) sectors[i].SoundTarget = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-12 16:27:47 +00:00
|
|
|
|
DObject::StaticPointerSubstitution (oldactor, p->mo);
|
2006-11-25 04:26:04 +00:00
|
|
|
|
// PointerSubstitution() will also affect the bodyque, so undo that now.
|
|
|
|
|
for (int ii=0; ii < BODYQUESIZE; ++ii)
|
|
|
|
|
if (bodyque[ii] == p->mo)
|
|
|
|
|
bodyque[ii] = oldactor;
|
2006-05-27 10:27:51 +00:00
|
|
|
|
FBehavior::StaticStartTypedScripts (SCRIPT_Respawn, p->mo, true);
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return mobj;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// P_SpawnMapThing
|
|
|
|
|
// The fields of the mapthing should
|
|
|
|
|
// already be in host byte order.
|
|
|
|
|
//
|
|
|
|
|
// [RH] position is used to weed out unwanted start spots
|
2008-05-08 08:06:26 +00:00
|
|
|
|
AActor *P_SpawnMapThing (FMapThing *mthing, int position)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
|
const PClass *i;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
int mask;
|
|
|
|
|
AActor *mobj;
|
|
|
|
|
fixed_t x, y, z;
|
|
|
|
|
|
2015-04-04 08:25:01 +00:00
|
|
|
|
if (mthing->EdNum == 0 || mthing->EdNum == -1)
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2015-04-03 14:51:45 +00:00
|
|
|
|
// find which type to spawn
|
2015-04-04 08:25:01 +00:00
|
|
|
|
FDoomEdEntry *mentry = mthing->info;
|
2015-04-03 14:51:45 +00:00
|
|
|
|
|
|
|
|
|
if (mentry == NULL)
|
|
|
|
|
{
|
|
|
|
|
// [RH] Don't die if the map tries to spawn an unknown thing
|
|
|
|
|
Printf ("Unknown type %i at (%i, %i)\n",
|
2015-04-04 08:25:01 +00:00
|
|
|
|
mthing->EdNum,
|
2015-04-03 14:51:45 +00:00
|
|
|
|
mthing->x>>FRACBITS, mthing->y>>FRACBITS);
|
|
|
|
|
mentry = DoomEdMap.CheckKey(0);
|
|
|
|
|
if (mentry == NULL) // we need a valid entry for the rest of this function so if we can't find a default, let's exit right away.
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (mentry->Type == NULL && mentry->Special <= 0)
|
|
|
|
|
{
|
|
|
|
|
// has been explicitly set to not spawning anything.
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-03 19:17:10 +00:00
|
|
|
|
// copy args to mapthing so that we have them in one place for the rest of this function
|
2016-01-04 10:52:07 +00:00
|
|
|
|
if (mentry->ArgsDefined > 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-04-07 14:27:57 +00:00
|
|
|
|
if (mentry->Type!= NULL) mthing->special = mentry->Special;
|
2016-01-04 10:52:07 +00:00
|
|
|
|
memcpy(mthing->args, mentry->Args, sizeof(mthing->args[0]) * mentry->ArgsDefined);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-04-03 19:17:10 +00:00
|
|
|
|
int pnum = -1;
|
|
|
|
|
if (mentry->Type == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
|
2015-04-03 19:17:10 +00:00
|
|
|
|
switch (mentry->Special)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-04-04 08:25:01 +00:00
|
|
|
|
case SMT_DeathmatchStart:
|
2015-04-03 19:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
// count deathmatch start positions
|
|
|
|
|
FPlayerStart start(mthing, 0);
|
|
|
|
|
deathmatchstarts.Push(start);
|
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-04-04 08:25:01 +00:00
|
|
|
|
case SMT_PolyAnchor:
|
|
|
|
|
case SMT_PolySpawn:
|
|
|
|
|
case SMT_PolySpawnCrush:
|
|
|
|
|
case SMT_PolySpawnHurt:
|
2015-04-03 19:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
polyspawns_t *polyspawn = new polyspawns_t;
|
|
|
|
|
polyspawn->next = polyspawns;
|
|
|
|
|
polyspawn->x = mthing->x;
|
|
|
|
|
polyspawn->y = mthing->y;
|
|
|
|
|
polyspawn->angle = mthing->angle;
|
|
|
|
|
polyspawn->type = mentry->Special;
|
|
|
|
|
polyspawns = polyspawn;
|
2015-04-04 08:25:01 +00:00
|
|
|
|
if (mentry->Special != SMT_PolyAnchor)
|
2015-04-03 19:17:10 +00:00
|
|
|
|
po_NumPolyobjs++;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2015-04-04 08:25:01 +00:00
|
|
|
|
case SMT_Player1Start:
|
|
|
|
|
case SMT_Player2Start:
|
|
|
|
|
case SMT_Player3Start:
|
|
|
|
|
case SMT_Player4Start:
|
|
|
|
|
case SMT_Player5Start:
|
|
|
|
|
case SMT_Player6Start:
|
|
|
|
|
case SMT_Player7Start:
|
|
|
|
|
case SMT_Player8Start:
|
|
|
|
|
pnum = mentry->Special - SMT_Player1Start;
|
2015-04-03 19:17:10 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Sound sequence override will be handled later
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pnum == -1 || (level.flags & LEVEL_FILTERSTARTS))
|
|
|
|
|
{
|
|
|
|
|
// check for appropriate game type
|
|
|
|
|
if (deathmatch)
|
|
|
|
|
{
|
|
|
|
|
mask = MTF_DEATHMATCH;
|
|
|
|
|
}
|
|
|
|
|
else if (multiplayer)
|
|
|
|
|
{
|
|
|
|
|
mask = MTF_COOPERATIVE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mask = MTF_SINGLE;
|
|
|
|
|
}
|
|
|
|
|
if (!(mthing->flags & mask))
|
|
|
|
|
{
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-10-29 22:15:46 +00:00
|
|
|
|
mask = G_SkillProperty(SKILLP_SpawnFilter);
|
2008-05-11 21:16:32 +00:00
|
|
|
|
if (!(mthing->SkillFilter & mask))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
|
// Check class spawn masks. Now with player classes available
|
|
|
|
|
// this is enabled for all games.
|
|
|
|
|
if (!multiplayer)
|
|
|
|
|
{ // Single player
|
|
|
|
|
int spawnmask = players[consoleplayer].GetSpawnClass();
|
2008-05-11 21:16:32 +00:00
|
|
|
|
if (spawnmask != 0 && (mthing->ClassFilter & spawnmask) == 0)
|
2006-07-13 10:17:56 +00:00
|
|
|
|
{ // Not for current class
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2006-07-13 10:17:56 +00:00
|
|
|
|
}
|
|
|
|
|
else if (!deathmatch)
|
|
|
|
|
{ // Cooperative
|
|
|
|
|
mask = 0;
|
|
|
|
|
for (int i = 0; i < MAXPLAYERS; i++)
|
|
|
|
|
{
|
|
|
|
|
if (playeringame[i])
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2006-07-13 10:17:56 +00:00
|
|
|
|
int spawnmask = players[i].GetSpawnClass();
|
|
|
|
|
if (spawnmask != 0)
|
|
|
|
|
mask |= spawnmask;
|
|
|
|
|
else
|
|
|
|
|
mask = -1;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2008-07-05 10:17:10 +00:00
|
|
|
|
if (mask != -1 && (mthing->ClassFilter & mask) == 0)
|
2006-07-13 10:17:56 +00:00
|
|
|
|
{
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-07-13 10:17:56 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pnum != -1)
|
|
|
|
|
{
|
|
|
|
|
// [RH] Only spawn spots that match position.
|
|
|
|
|
if (mthing->args[0] != position)
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// save spots for respawning in network games
|
2015-04-03 19:17:10 +00:00
|
|
|
|
FPlayerStart start(mthing, pnum+1);
|
2012-07-08 02:18:15 +00:00
|
|
|
|
playerstarts[pnum] = start;
|
|
|
|
|
AllPlayerStarts.Push(start);
|
2012-07-08 01:43:47 +00:00
|
|
|
|
if (!deathmatch && !(level.flags2 & LEVEL2_RANDOMPLAYERSTARTS))
|
|
|
|
|
{
|
2013-01-03 02:08:08 +00:00
|
|
|
|
return P_SpawnPlayer(&start, pnum, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
|
2012-07-08 01:43:47 +00:00
|
|
|
|
}
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [RH] sound sequence overriders
|
2015-04-04 08:25:01 +00:00
|
|
|
|
if (mentry->Type == NULL && mentry->Special == SMT_SSeqOverride)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-04-26 06:48:49 +00:00
|
|
|
|
int type = mthing->args[0];
|
2015-04-03 19:17:10 +00:00
|
|
|
|
if (type == 255) type = -1;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (type > 63)
|
|
|
|
|
{
|
|
|
|
|
Printf ("Sound sequence %d out of range\n", type);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2008-05-08 08:06:26 +00:00
|
|
|
|
P_PointInSector (mthing->x, mthing->y)->seqType = type;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [RH] If the thing's corresponding sprite has no frames, also map
|
|
|
|
|
// it to the unknown thing.
|
2015-04-03 14:51:45 +00:00
|
|
|
|
// Handle decorate replacements explicitly here
|
|
|
|
|
// to check for missing frames in the replacement object.
|
|
|
|
|
i = mentry->Type->GetReplacement();
|
|
|
|
|
|
|
|
|
|
const AActor *defaults = GetDefaultByType (i);
|
|
|
|
|
if (defaults->SpawnState == NULL ||
|
|
|
|
|
sprites[defaults->SpawnState->sprite].numframes == 0)
|
|
|
|
|
{
|
|
|
|
|
// We don't load mods for shareware games so we'll just ignore
|
|
|
|
|
// missing actors. Heretic needs this since the shareware includes
|
|
|
|
|
// the retail weapons in Deathmatch.
|
|
|
|
|
if (gameinfo.flags & GI_SHAREWARE)
|
|
|
|
|
return NULL;
|
2014-01-18 20:09:12 +00:00
|
|
|
|
|
2015-04-03 14:51:45 +00:00
|
|
|
|
Printf ("%s at (%i, %i) has no frames\n",
|
|
|
|
|
i->TypeName.GetChars(), mthing->x>>FRACBITS, mthing->y>>FRACBITS);
|
|
|
|
|
i = PClass::FindClass("Unknown");
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const AActor *info = GetDefaultByType (i);
|
|
|
|
|
|
|
|
|
|
// don't spawn keycards and players in deathmatch
|
|
|
|
|
if (deathmatch && info->flags & MF_NOTDMATCH)
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2006-04-11 16:27:41 +00:00
|
|
|
|
// [RH] don't spawn extra weapons in coop if so desired
|
2006-06-22 02:19:43 +00:00
|
|
|
|
if (multiplayer && !deathmatch && (dmflags & DF_NO_COOP_WEAPON_SPAWN))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2014-07-27 08:07:37 +00:00
|
|
|
|
if (GetDefaultByType(i)->flags7 & MF7_WEAPONSPAWN)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
if ((mthing->flags & (MTF_DEATHMATCH|MTF_SINGLE)) == MTF_DEATHMATCH)
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// don't spawn any monsters if -nomonsters
|
2009-02-03 19:11:43 +00:00
|
|
|
|
if (((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS)) && info->flags3 & MF3_ISMONSTER )
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [RH] Other things that shouldn't be spawned depending on dmflags
|
|
|
|
|
if (deathmatch || alwaysapplydmflags)
|
|
|
|
|
{
|
|
|
|
|
if (dmflags & DF_NO_HEALTH)
|
|
|
|
|
{
|
|
|
|
|
if (i->IsDescendantOf (RUNTIME_CLASS(AHealth)))
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-05-10 02:40:43 +00:00
|
|
|
|
if (i->TypeName == NAME_Berserk)
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-05-10 02:40:43 +00:00
|
|
|
|
if (i->TypeName == NAME_Megasphere)
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
if (dmflags & DF_NO_ITEMS)
|
|
|
|
|
{
|
|
|
|
|
// if (i->IsDescendantOf (RUNTIME_CLASS(AArtifact)))
|
|
|
|
|
// return;
|
|
|
|
|
}
|
|
|
|
|
if (dmflags & DF_NO_ARMOR)
|
|
|
|
|
{
|
|
|
|
|
if (i->IsDescendantOf (RUNTIME_CLASS(AArmor)))
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-05-10 02:40:43 +00:00
|
|
|
|
if (i->TypeName == NAME_Megasphere)
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// spawn it
|
2008-05-08 08:06:26 +00:00
|
|
|
|
x = mthing->x;
|
|
|
|
|
y = mthing->y;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
if (info->flags & MF_SPAWNCEILING)
|
|
|
|
|
z = ONCEILINGZ;
|
|
|
|
|
else if (info->flags2 & MF2_SPAWNFLOAT)
|
|
|
|
|
z = FLOATRANDZ;
|
|
|
|
|
else
|
|
|
|
|
z = ONFLOORZ;
|
|
|
|
|
|
2009-10-25 15:26:19 +00:00
|
|
|
|
mobj = AActor::StaticSpawn (i, x, y, z, NO_REPLACE, true);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
if (z == ONFLOORZ)
|
2015-02-12 17:57:06 +00:00
|
|
|
|
{
|
2008-05-08 08:06:26 +00:00
|
|
|
|
mobj->z += mthing->z;
|
2015-02-12 17:57:06 +00:00
|
|
|
|
if ((mobj->flags2 & MF2_FLOATBOB) && (ib_compatflags & BCOMPATF_FLOATBOB))
|
|
|
|
|
{
|
|
|
|
|
mobj->special1 = mthing->z;
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
else if (z == ONCEILINGZ)
|
2008-05-08 08:06:26 +00:00
|
|
|
|
mobj->z -= mthing->z;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
mobj->SpawnPoint[0] = mthing->x;
|
|
|
|
|
mobj->SpawnPoint[1] = mthing->y;
|
|
|
|
|
mobj->SpawnPoint[2] = mthing->z;
|
|
|
|
|
mobj->SpawnAngle = mthing->angle;
|
|
|
|
|
mobj->SpawnFlags = mthing->flags;
|
2015-11-29 10:28:26 +00:00
|
|
|
|
if (mthing->FloatbobPhase >= 0 && mthing->FloatbobPhase < 64) mobj->FloatBobPhase = mthing->FloatbobPhase;
|
2013-08-09 14:25:16 +00:00
|
|
|
|
if (mthing->gravity < 0) mobj->gravity = -mthing->gravity;
|
|
|
|
|
else if (mthing->gravity > 0) mobj->gravity = FixedMul(mobj->gravity, mthing->gravity);
|
|
|
|
|
else mobj->flags &= ~MF_NOGRAVITY;
|
|
|
|
|
|
2015-02-12 17:57:06 +00:00
|
|
|
|
// For Hexen floatbob 'compatibility' we do not really want to alter the floorz.
|
|
|
|
|
if (mobj->special1 == 0 || !(mobj->flags2 & MF2_FLOATBOB) || !(ib_compatflags & BCOMPATF_FLOATBOB))
|
|
|
|
|
{
|
|
|
|
|
P_FindFloorCeiling(mobj, FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT);
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2015-04-03 14:51:45 +00:00
|
|
|
|
// if the actor got args defined either in DECORATE or MAPINFO we must ignore the map's properties.
|
2015-04-03 19:17:10 +00:00
|
|
|
|
if (!(mobj->flags2 & MF2_ARGSDEFINED))
|
2006-12-02 15:38:50 +00:00
|
|
|
|
{
|
|
|
|
|
// [RH] Set the thing's special
|
|
|
|
|
mobj->special = mthing->special;
|
|
|
|
|
for(int j=0;j<5;j++) mobj->args[j]=mthing->args[j];
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// [RH] Add ThingID to mobj and link it in with the others
|
|
|
|
|
mobj->tid = mthing->thingid;
|
|
|
|
|
mobj->AddToHash ();
|
|
|
|
|
|
2010-12-16 14:39:17 +00:00
|
|
|
|
mobj->PrevAngle = mobj->angle = (DWORD)((mthing->angle * CONST64(0x100000000)) / 360);
|
2010-08-22 17:18:46 +00:00
|
|
|
|
|
|
|
|
|
// Check if this actor's mapthing has a conversation defined
|
|
|
|
|
if (mthing->Conversation > 0)
|
|
|
|
|
{
|
2010-08-24 13:57:17 +00:00
|
|
|
|
// Make sure that this does not partially overwrite the default dialogue settings.
|
|
|
|
|
int root = GetConversation(mthing->Conversation);
|
|
|
|
|
if (root != -1)
|
2010-08-22 17:18:46 +00:00
|
|
|
|
{
|
2010-08-24 13:57:17 +00:00
|
|
|
|
mobj->ConversationRoot = root;
|
2010-08-22 17:18:46 +00:00
|
|
|
|
mobj->Conversation = StrifeDialogues[mobj->ConversationRoot];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-10 22:58:59 +00:00
|
|
|
|
// Set various UDMF options
|
|
|
|
|
if (mthing->alpha != -1)
|
|
|
|
|
mobj->alpha = mthing->alpha;
|
|
|
|
|
if (mthing->RenderStyle != STYLE_Count)
|
|
|
|
|
mobj->RenderStyle = (ERenderStyle)mthing->RenderStyle;
|
|
|
|
|
if (mthing->scaleX)
|
|
|
|
|
mobj->scaleX = FixedMul(mthing->scaleX, mobj->scaleX);
|
|
|
|
|
if (mthing->scaleY)
|
|
|
|
|
mobj->scaleY = FixedMul(mthing->scaleY, mobj->scaleY);
|
|
|
|
|
if (mthing->pitch)
|
|
|
|
|
mobj->pitch = ANGLE_1 * mthing->pitch;
|
|
|
|
|
if (mthing->roll)
|
|
|
|
|
mobj->roll = ANGLE_1 * mthing->roll;
|
|
|
|
|
if (mthing->score)
|
|
|
|
|
mobj->Score = mthing->score;
|
|
|
|
|
if (mthing->fillcolor)
|
|
|
|
|
mobj->fillcolor = mthing->fillcolor;
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
mobj->BeginPlay ();
|
2008-04-05 12:14:33 +00:00
|
|
|
|
if (!(mobj->ObjectFlags & OF_EuthanizeMe))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-05 12:14:33 +00:00
|
|
|
|
mobj->LevelSpawned ();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2014-04-10 22:58:59 +00:00
|
|
|
|
|
|
|
|
|
if (mthing->health > 0)
|
|
|
|
|
mobj->health *= mthing->health;
|
|
|
|
|
else
|
|
|
|
|
mobj->health = -mthing->health;
|
|
|
|
|
if (mthing->health == 0)
|
|
|
|
|
mobj->Die(NULL, NULL);
|
|
|
|
|
else if (mthing->health != 1)
|
|
|
|
|
mobj->StartHealth = mobj->health;
|
|
|
|
|
|
2008-04-04 14:31:20 +00:00
|
|
|
|
return mobj;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// GAME SPAWN FUNCTIONS
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// P_SpawnPuff
|
|
|
|
|
//
|
|
|
|
|
|
2014-12-27 20:15:14 +00:00
|
|
|
|
AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags, AActor *vict)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
AActor *puff;
|
2013-02-27 10:35:44 +00:00
|
|
|
|
|
|
|
|
|
if (!(flags & PF_NORANDOMZ))
|
|
|
|
|
z += pr_spawnpuff.Random2 () << 10;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2006-07-16 09:10:45 +00:00
|
|
|
|
puff = Spawn (pufftype, x, y, z, ALLOW_REPLACE);
|
2008-07-05 10:17:10 +00:00
|
|
|
|
if (puff == NULL) return NULL;
|
2010-01-24 09:46:31 +00:00
|
|
|
|
|
2015-01-13 08:01:00 +00:00
|
|
|
|
if ((puff->flags4 & MF4_RANDOMIZE) && puff->tics > 0)
|
|
|
|
|
{
|
|
|
|
|
puff->tics -= pr_spawnpuff() & 3;
|
|
|
|
|
if (puff->tics < 1)
|
|
|
|
|
puff->tics = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-27 20:15:14 +00:00
|
|
|
|
//Moved puff creation and target/master/tracer setting to here.
|
|
|
|
|
if (puff && vict)
|
|
|
|
|
{
|
|
|
|
|
if (puff->flags7 & MF7_HITTARGET) puff->target = vict;
|
|
|
|
|
if (puff->flags7 & MF7_HITMASTER) puff->master = vict;
|
|
|
|
|
if (puff->flags7 & MF7_HITTRACER) puff->tracer = vict;
|
|
|
|
|
}
|
2010-01-24 09:46:31 +00:00
|
|
|
|
// [BB] If the puff came from a player, set the target of the puff to this player.
|
|
|
|
|
if ( puff && (puff->flags5 & MF5_PUFFGETSOWNER))
|
|
|
|
|
puff->target = source;
|
2014-12-27 20:15:14 +00:00
|
|
|
|
|
2010-01-24 09:46:31 +00:00
|
|
|
|
|
2009-06-06 12:46:35 +00:00
|
|
|
|
if (source != NULL) puff->angle = R_PointToAngle2(x, y, source->x, source->y);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// If a puff has a crash state and an actor was not hit,
|
|
|
|
|
// it will enter the crash state. This is used by the StrifeSpark
|
|
|
|
|
// and BlasterPuff.
|
2006-10-31 14:53:21 +00:00
|
|
|
|
FState *crashstate;
|
2008-04-04 14:31:20 +00:00
|
|
|
|
if (!(flags & PF_HITTHING) && (crashstate = puff->FindState(NAME_Crash)) != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2006-10-31 14:53:21 +00:00
|
|
|
|
puff->SetState (crashstate);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2011-06-13 10:34:46 +00:00
|
|
|
|
else if ((flags & PF_HITTHINGBLEED) && (crashstate = puff->FindState(NAME_Death, NAME_Extreme, true)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
puff->SetState (crashstate);
|
|
|
|
|
}
|
2008-04-04 14:31:20 +00:00
|
|
|
|
else if ((flags & PF_MELEERANGE) && puff->MeleeState != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
// handle the hard coded state jump of Doom's bullet puff
|
|
|
|
|
// in a more flexible manner.
|
|
|
|
|
puff->SetState (puff->MeleeState);
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-04 14:31:20 +00:00
|
|
|
|
if (!(flags & PF_TEMPORARY))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-04-04 14:31:20 +00:00
|
|
|
|
if (cl_pufftype && updown != 3 && (puff->flags4 & MF4_ALLOWPARTICLES))
|
|
|
|
|
{
|
|
|
|
|
P_DrawSplash2 (32, x, y, z, dir, updown, 1);
|
|
|
|
|
puff->renderflags |= RF_INVISIBLE;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-04-04 14:31:20 +00:00
|
|
|
|
if ((flags & PF_HITTHING) && puff->SeeSound)
|
2008-02-12 05:54:03 +00:00
|
|
|
|
{ // Hit thing sound
|
2008-06-15 02:25:09 +00:00
|
|
|
|
S_Sound (puff, CHAN_BODY, puff->SeeSound, 1, ATTN_NORM);
|
2008-02-12 05:54:03 +00:00
|
|
|
|
}
|
|
|
|
|
else if (puff->AttackSound)
|
|
|
|
|
{
|
2008-06-15 02:25:09 +00:00
|
|
|
|
S_Sound (puff, CHAN_BODY, puff->AttackSound, 1, ATTN_NORM);
|
2008-02-12 05:54:03 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return puff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-11-29 10:03:35 +00:00
|
|
|
|
//---------------------------------------------------------------------------
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//
|
|
|
|
|
// P_SpawnBlood
|
|
|
|
|
//
|
2006-11-29 10:03:35 +00:00
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AActor *originator)
|
|
|
|
|
{
|
2006-11-29 10:03:35 +00:00
|
|
|
|
AActor *th;
|
2010-03-21 08:09:45 +00:00
|
|
|
|
PalEntry bloodcolor = originator->GetBloodColor();
|
|
|
|
|
const PClass *bloodcls = originator->GetBloodType();
|
2009-11-24 22:28:38 +00:00
|
|
|
|
|
|
|
|
|
int bloodtype = cl_bloodtype;
|
|
|
|
|
|
|
|
|
|
if (bloodcls != NULL && !(GetDefaultByType(bloodcls)->flags4 & MF4_ALLOWPARTICLES))
|
|
|
|
|
bloodtype = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2013-12-01 09:06:48 +00:00
|
|
|
|
if (bloodcls != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
z += pr_spawnblood.Random2 () << 10;
|
2010-08-21 19:50:07 +00:00
|
|
|
|
th = Spawn (bloodcls, x, y, z, NO_REPLACE); // GetBloodType already performed the replacement
|
2009-06-30 20:57:51 +00:00
|
|
|
|
th->velz = FRACUNIT*2;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
th->angle = dir;
|
2010-11-07 07:29:23 +00:00
|
|
|
|
// [NG] Applying PUFFGETSOWNER to the blood will make it target the owner
|
|
|
|
|
if (th->flags5 & MF5_PUFFGETSOWNER) th->target = originator;
|
2008-08-30 19:44:19 +00:00
|
|
|
|
if (gameinfo.gametype & GAME_DoomChex)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
th->tics -= pr_spawnblood() & 3;
|
|
|
|
|
|
|
|
|
|
if (th->tics < 1)
|
|
|
|
|
th->tics = 1;
|
|
|
|
|
}
|
2006-11-29 10:03:35 +00:00
|
|
|
|
// colorize the blood
|
|
|
|
|
if (bloodcolor != 0 && !(th->flags2 & MF2_DONTTRANSLATE))
|
2006-08-17 00:19:26 +00:00
|
|
|
|
{
|
|
|
|
|
th->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
|
|
|
|
|
}
|
2012-04-22 07:32:09 +00:00
|
|
|
|
|
2012-04-22 07:23:59 +00:00
|
|
|
|
// Moved out of the blood actor so that replacing blood is easier
|
|
|
|
|
if (gameinfo.gametype & GAME_DoomStrifeChex)
|
|
|
|
|
{
|
2012-04-22 07:32:09 +00:00
|
|
|
|
if (gameinfo.gametype == GAME_Strife)
|
|
|
|
|
{
|
|
|
|
|
if (damage > 13)
|
|
|
|
|
{
|
|
|
|
|
FState *state = th->FindState(NAME_Spray);
|
2012-09-01 22:39:11 +00:00
|
|
|
|
if (state != NULL)
|
|
|
|
|
{
|
|
|
|
|
th->SetState (state);
|
|
|
|
|
goto statedone;
|
|
|
|
|
}
|
2012-04-22 07:32:09 +00:00
|
|
|
|
}
|
|
|
|
|
else damage += 2;
|
|
|
|
|
}
|
2012-04-22 02:08:27 +00:00
|
|
|
|
int advance = 0;
|
2006-11-29 10:03:35 +00:00
|
|
|
|
if (damage <= 12 && damage >= 9)
|
|
|
|
|
{
|
2012-04-22 02:08:27 +00:00
|
|
|
|
advance = 1;
|
2006-11-29 10:03:35 +00:00
|
|
|
|
}
|
|
|
|
|
else if (damage < 9)
|
|
|
|
|
{
|
2012-04-22 02:08:27 +00:00
|
|
|
|
advance = 2;
|
|
|
|
|
}
|
2012-04-22 07:23:59 +00:00
|
|
|
|
|
|
|
|
|
PClass *cls = th->GetClass();
|
|
|
|
|
|
|
|
|
|
while (cls != RUNTIME_CLASS(AActor))
|
2012-04-22 02:08:27 +00:00
|
|
|
|
{
|
2012-04-22 07:23:59 +00:00
|
|
|
|
FActorInfo *ai = cls->ActorInfo;
|
2012-09-01 22:34:09 +00:00
|
|
|
|
int checked_advance = advance;
|
2012-04-22 07:23:59 +00:00
|
|
|
|
if (ai->OwnsState(th->SpawnState))
|
2012-04-22 02:08:27 +00:00
|
|
|
|
{
|
2012-09-01 22:34:09 +00:00
|
|
|
|
for (; checked_advance > 0; --checked_advance)
|
2012-04-22 07:23:59 +00:00
|
|
|
|
{
|
|
|
|
|
// [RH] Do not set to a state we do not own.
|
2012-09-01 22:34:09 +00:00
|
|
|
|
if (ai->OwnsState(th->SpawnState + checked_advance))
|
2012-04-22 07:23:59 +00:00
|
|
|
|
{
|
2012-09-01 22:34:09 +00:00
|
|
|
|
th->SetState(th->SpawnState + checked_advance);
|
2012-04-22 07:23:59 +00:00
|
|
|
|
goto statedone;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-22 02:08:27 +00:00
|
|
|
|
}
|
2012-04-22 07:23:59 +00:00
|
|
|
|
cls = cls->ParentClass;
|
2006-11-29 10:03:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-10-25 12:09:19 +00:00
|
|
|
|
|
|
|
|
|
statedone:
|
|
|
|
|
if (!(bloodtype <= 1)) th->renderflags |= RF_INVISIBLE;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-11-24 22:28:38 +00:00
|
|
|
|
if (bloodtype >= 1)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
P_DrawSplash2 (40, x, y, z, dir, 2, bloodcolor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// PROC P_BloodSplatter
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator)
|
|
|
|
|
{
|
2010-03-21 08:09:45 +00:00
|
|
|
|
PalEntry bloodcolor = originator->GetBloodColor();
|
|
|
|
|
const PClass *bloodcls = originator->GetBloodType(1);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-11-24 22:28:38 +00:00
|
|
|
|
int bloodtype = cl_bloodtype;
|
|
|
|
|
|
|
|
|
|
if (bloodcls != NULL && !(GetDefaultByType(bloodcls)->flags4 & MF4_ALLOWPARTICLES))
|
|
|
|
|
bloodtype = 0;
|
|
|
|
|
|
2013-12-01 09:06:48 +00:00
|
|
|
|
if (bloodcls != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
AActor *mo;
|
|
|
|
|
|
2010-08-21 19:50:07 +00:00
|
|
|
|
mo = Spawn(bloodcls, x, y, z, NO_REPLACE); // GetBloodType already performed the replacement
|
2006-02-24 04:48:15 +00:00
|
|
|
|
mo->target = originator;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = pr_splatter.Random2 () << 10;
|
|
|
|
|
mo->vely = pr_splatter.Random2 () << 10;
|
|
|
|
|
mo->velz = 3*FRACUNIT;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// colorize the blood!
|
2006-11-29 10:03:35 +00:00
|
|
|
|
if (bloodcolor!=0 && !(mo->flags2 & MF2_DONTTRANSLATE))
|
|
|
|
|
{
|
|
|
|
|
mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
|
|
|
|
|
}
|
2013-12-01 09:06:48 +00:00
|
|
|
|
|
|
|
|
|
if (!(bloodtype <= 1)) mo->renderflags |= RF_INVISIBLE;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-11-24 22:28:38 +00:00
|
|
|
|
if (bloodtype >= 1)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
P_DrawSplash2 (40, x, y, z, R_PointToAngle2 (x, y, originator->x, originator->y), 2, bloodcolor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-29 10:03:35 +00:00
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
// P_BloodSplatter2
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator)
|
|
|
|
|
{
|
2010-03-21 08:09:45 +00:00
|
|
|
|
PalEntry bloodcolor = originator->GetBloodColor();
|
|
|
|
|
const PClass *bloodcls = originator->GetBloodType(2);
|
2006-11-29 10:03:35 +00:00
|
|
|
|
|
2009-11-24 22:28:38 +00:00
|
|
|
|
int bloodtype = cl_bloodtype;
|
|
|
|
|
|
|
|
|
|
if (bloodcls != NULL && !(GetDefaultByType(bloodcls)->flags4 & MF4_ALLOWPARTICLES))
|
|
|
|
|
bloodtype = 0;
|
|
|
|
|
|
2013-12-01 09:06:48 +00:00
|
|
|
|
if (bloodcls != NULL)
|
2006-11-29 10:03:35 +00:00
|
|
|
|
{
|
|
|
|
|
AActor *mo;
|
|
|
|
|
|
|
|
|
|
x += ((pr_splat()-128)<<11);
|
|
|
|
|
y += ((pr_splat()-128)<<11);
|
|
|
|
|
|
2010-08-21 19:50:07 +00:00
|
|
|
|
mo = Spawn (bloodcls, x, y, z, NO_REPLACE); // GetBloodType already performed the replacement
|
2006-11-29 10:03:35 +00:00
|
|
|
|
mo->target = originator;
|
|
|
|
|
|
|
|
|
|
// colorize the blood!
|
|
|
|
|
if (bloodcolor != 0 && !(mo->flags2 & MF2_DONTTRANSLATE))
|
|
|
|
|
{
|
|
|
|
|
mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
|
|
|
|
|
}
|
2013-12-01 09:06:48 +00:00
|
|
|
|
|
|
|
|
|
if (!(bloodtype <= 1)) mo->renderflags |= RF_INVISIBLE;
|
2006-11-29 10:03:35 +00:00
|
|
|
|
}
|
2009-11-24 22:28:38 +00:00
|
|
|
|
if (bloodtype >= 1)
|
2006-11-29 10:03:35 +00:00
|
|
|
|
{
|
|
|
|
|
P_DrawSplash2 (100, x, y, z, R_PointToAngle2 (0, 0, originator->x - x, originator->y - y), 2, bloodcolor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// PROC P_RipperBlood
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void P_RipperBlood (AActor *mo, AActor *bleeder)
|
|
|
|
|
{
|
|
|
|
|
fixed_t x, y, z;
|
2010-03-21 08:09:45 +00:00
|
|
|
|
PalEntry bloodcolor = bleeder->GetBloodColor();
|
|
|
|
|
const PClass *bloodcls = bleeder->GetBloodType();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
x = mo->x + (pr_ripperblood.Random2 () << 12);
|
|
|
|
|
y = mo->y + (pr_ripperblood.Random2 () << 12);
|
|
|
|
|
z = mo->z + (pr_ripperblood.Random2 () << 12);
|
2009-11-24 22:28:38 +00:00
|
|
|
|
|
|
|
|
|
int bloodtype = cl_bloodtype;
|
|
|
|
|
|
|
|
|
|
if (bloodcls != NULL && !(GetDefaultByType(bloodcls)->flags4 & MF4_ALLOWPARTICLES))
|
|
|
|
|
bloodtype = 0;
|
|
|
|
|
|
2013-12-01 09:06:48 +00:00
|
|
|
|
if (bloodcls != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
AActor *th;
|
2010-08-21 19:50:07 +00:00
|
|
|
|
th = Spawn (bloodcls, x, y, z, NO_REPLACE); // GetBloodType already performed the replacement
|
2010-11-07 07:29:23 +00:00
|
|
|
|
// [NG] Applying PUFFGETSOWNER to the blood will make it target the owner
|
|
|
|
|
if (th->flags5 & MF5_PUFFGETSOWNER) th->target = bleeder;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (gameinfo.gametype == GAME_Heretic)
|
|
|
|
|
th->flags |= MF_NOGRAVITY;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
th->velx = mo->velx >> 1;
|
|
|
|
|
th->vely = mo->vely >> 1;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
th->tics += pr_ripperblood () & 3;
|
|
|
|
|
|
|
|
|
|
// colorize the blood!
|
2006-11-29 10:03:35 +00:00
|
|
|
|
if (bloodcolor!=0 && !(th->flags2 & MF2_DONTTRANSLATE))
|
|
|
|
|
{
|
|
|
|
|
th->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
|
|
|
|
|
}
|
2013-12-01 09:06:48 +00:00
|
|
|
|
|
|
|
|
|
if (!(bloodtype <= 1)) th->renderflags |= RF_INVISIBLE;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-11-24 22:28:38 +00:00
|
|
|
|
if (bloodtype >= 1)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
P_DrawSplash2 (28, x, y, z, 0, 0, bloodcolor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// FUNC P_GetThingFloorType
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
int P_GetThingFloorType (AActor *thing)
|
|
|
|
|
{
|
2008-06-15 18:36:26 +00:00
|
|
|
|
if (thing->floorpic.isValid())
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
return TerrainTypes[thing->floorpic];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2008-08-16 20:19:35 +00:00
|
|
|
|
return TerrainTypes[thing->Sector->GetTexture(sector_t::floor)];
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// FUNC P_HitWater
|
|
|
|
|
//
|
|
|
|
|
// Returns true if hit liquid and splashed, false if not.
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
2009-10-17 11:30:44 +00:00
|
|
|
|
bool P_HitWater (AActor * thing, sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool checkabove, bool alert)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2012-07-06 03:42:03 +00:00
|
|
|
|
if (thing->flags3 & MF3_DONTSPLASH)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (thing->player && (thing->player->cheats & CF_PREDICTING))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
AActor *mo = NULL;
|
|
|
|
|
FSplashDef *splash;
|
|
|
|
|
int terrainnum;
|
2009-06-16 22:04:26 +00:00
|
|
|
|
sector_t *hsec = NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-01-04 12:25:22 +00:00
|
|
|
|
if (x == FIXED_MIN) x = thing->x;
|
|
|
|
|
if (y == FIXED_MIN) y = thing->y;
|
|
|
|
|
if (z == FIXED_MIN) z = thing->z;
|
2007-01-07 09:43:58 +00:00
|
|
|
|
// don't splash above the object
|
2011-09-21 19:39:12 +00:00
|
|
|
|
if (checkabove)
|
|
|
|
|
{
|
|
|
|
|
fixed_t compare_z = thing->z + (thing->height >> 1);
|
|
|
|
|
// Missiles are typically small and fast, so they might
|
|
|
|
|
// end up submerged by the move that calls P_HitWater.
|
|
|
|
|
if (thing->flags & MF_MISSILE)
|
|
|
|
|
compare_z -= thing->velz;
|
|
|
|
|
if (z > compare_z)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2009-01-04 15:00:29 +00:00
|
|
|
|
|
2010-08-27 15:20:05 +00:00
|
|
|
|
#if 0 // needs some rethinking before activation
|
|
|
|
|
|
|
|
|
|
// This avoids spawning splashes on invisible self referencing sectors.
|
|
|
|
|
// For network consistency do this only in single player though because
|
|
|
|
|
// it is not guaranteed that all players have GL nodes loaded.
|
|
|
|
|
if (!multiplayer && thing->subsector->sector != thing->subsector->render_sector)
|
|
|
|
|
{
|
|
|
|
|
fixed_t zs = thing->subsector->sector->floorplane.ZatPoint(x, y);
|
|
|
|
|
fixed_t zr = thing->subsector->render_sector->floorplane.ZatPoint(x, y);
|
|
|
|
|
|
|
|
|
|
if (zs > zr && thing->z >= zs) return false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-01-04 15:00:29 +00:00
|
|
|
|
for(unsigned int i=0;i<sec->e->XFloor.ffloors.Size();i++)
|
|
|
|
|
{
|
|
|
|
|
F3DFloor * rover = sec->e->XFloor.ffloors[i];
|
|
|
|
|
if (!(rover->flags & FF_EXISTS)) continue;
|
|
|
|
|
fixed_t planez = rover->top.plane->ZatPoint(x, y);
|
|
|
|
|
if (z > planez - FRACUNIT/2 && z < planez + FRACUNIT/2) // allow minor imprecisions
|
|
|
|
|
{
|
|
|
|
|
if (rover->flags & (FF_SOLID|FF_SWIMMABLE) )
|
|
|
|
|
{
|
|
|
|
|
terrainnum = TerrainTypes[*rover->top.texture];
|
|
|
|
|
goto foundone;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
planez = rover->bottom.plane->ZatPoint(x, y);
|
2014-02-12 07:30:37 +00:00
|
|
|
|
if (planez < z && !(planez < thing->floorz)) return false;
|
2009-01-04 15:00:29 +00:00
|
|
|
|
}
|
2009-06-16 22:04:26 +00:00
|
|
|
|
hsec = sec->GetHeightSec();
|
2009-05-23 10:21:33 +00:00
|
|
|
|
if (hsec == NULL || !(hsec->MoreFlags & SECF_CLIPFAKEPLANES))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2008-08-16 20:19:35 +00:00
|
|
|
|
terrainnum = TerrainTypes[sec->GetTexture(sector_t::floor)];
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-05-23 10:21:33 +00:00
|
|
|
|
terrainnum = TerrainTypes[hsec->GetTexture(sector_t::floor)];
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-01-04 15:00:29 +00:00
|
|
|
|
foundone:
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
int splashnum = Terrains[terrainnum].Splash;
|
|
|
|
|
bool smallsplash = false;
|
|
|
|
|
const secplane_t *plane;
|
|
|
|
|
|
|
|
|
|
if (splashnum == -1)
|
|
|
|
|
return Terrains[terrainnum].IsLiquid;
|
|
|
|
|
|
2007-01-07 09:43:58 +00:00
|
|
|
|
// don't splash when touching an underwater floor
|
|
|
|
|
if (thing->waterlevel>=1 && z<=thing->floorz) return Terrains[terrainnum].IsLiquid;
|
|
|
|
|
|
2009-05-23 10:21:33 +00:00
|
|
|
|
plane = hsec != NULL? &sec->heightsec->floorplane : &sec->floorplane;
|
2007-01-07 09:43:58 +00:00
|
|
|
|
|
|
|
|
|
// Don't splash for living things with small vertical velocities.
|
|
|
|
|
// There are levels where the constant splashing from the monsters gets extremely annoying
|
2009-06-30 20:57:51 +00:00
|
|
|
|
if ((thing->flags3&MF3_ISMONSTER || thing->player) && thing->velz >= -6*FRACUNIT)
|
|
|
|
|
return Terrains[terrainnum].IsLiquid;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
splash = &Splashes[splashnum];
|
|
|
|
|
|
|
|
|
|
// Small splash for small masses
|
|
|
|
|
if (thing->Mass < 10)
|
|
|
|
|
smallsplash = true;
|
|
|
|
|
|
|
|
|
|
if (smallsplash && splash->SmallSplash)
|
|
|
|
|
{
|
2009-01-04 12:25:22 +00:00
|
|
|
|
mo = Spawn (splash->SmallSplash, x, y, z, ALLOW_REPLACE);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (mo) mo->floorclip += splash->SmallSplashClip;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (splash->SplashChunk)
|
|
|
|
|
{
|
2009-01-04 12:25:22 +00:00
|
|
|
|
mo = Spawn (splash->SplashChunk, x, y, z, ALLOW_REPLACE);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
mo->target = thing;
|
|
|
|
|
if (splash->ChunkXVelShift != 255)
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = pr_chunk.Random2() << splash->ChunkXVelShift;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
if (splash->ChunkYVelShift != 255)
|
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->vely = pr_chunk.Random2() << splash->ChunkYVelShift;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velz = splash->ChunkBaseZVel + (pr_chunk() << splash->ChunkZVelShift);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
if (splash->SplashBase)
|
|
|
|
|
{
|
2009-01-04 12:25:22 +00:00
|
|
|
|
mo = Spawn (splash->SplashBase, x, y, z, ALLOW_REPLACE);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-10-17 11:30:44 +00:00
|
|
|
|
if (thing->player && !splash->NoAlert && alert)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
P_NoiseAlert (thing, thing, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (mo)
|
|
|
|
|
{
|
2008-06-15 02:25:09 +00:00
|
|
|
|
S_Sound (mo, CHAN_ITEM, smallsplash ?
|
2006-02-24 04:48:15 +00:00
|
|
|
|
splash->SmallSplashSound : splash->NormalSplashSound,
|
|
|
|
|
1, ATTN_IDLE);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-01-04 12:25:22 +00:00
|
|
|
|
S_Sound (x, y, z, CHAN_ITEM, smallsplash ?
|
2006-02-24 04:48:15 +00:00
|
|
|
|
splash->SmallSplashSound : splash->NormalSplashSound,
|
|
|
|
|
1, ATTN_IDLE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Don't let deep water eat missiles
|
|
|
|
|
return plane == &sec->floorplane ? Terrains[terrainnum].IsLiquid : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// FUNC P_HitFloor
|
|
|
|
|
//
|
|
|
|
|
// Returns true if hit liquid and splashed, false if not.
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
bool P_HitFloor (AActor *thing)
|
|
|
|
|
{
|
|
|
|
|
const msecnode_t *m;
|
|
|
|
|
|
2009-09-14 20:47:53 +00:00
|
|
|
|
// killough 11/98: touchy objects explode on impact
|
|
|
|
|
// Allow very short drops to be safe, so that a touchy can be summoned without exploding.
|
|
|
|
|
if (thing->flags6 & MF6_TOUCHY && ((thing->flags6 & MF6_ARMED) || thing->IsSentient()) && ((thing->velz) < (-5 * FRACUNIT)))
|
|
|
|
|
{
|
|
|
|
|
thing->flags6 &= ~MF6_ARMED; // Disarm
|
|
|
|
|
P_DamageMobj (thing, NULL, NULL, thing->health, NAME_Crush, DMG_FORCED); // kill object
|
2010-08-27 15:20:05 +00:00
|
|
|
|
return false;
|
2009-09-14 20:47:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-06 03:42:03 +00:00
|
|
|
|
if (thing->flags3 & MF3_DONTSPLASH)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// don't splash if landing on the edge above water/lava/etc....
|
|
|
|
|
for (m = thing->touching_sectorlist; m; m = m->m_tnext)
|
|
|
|
|
{
|
|
|
|
|
if (thing->z == m->m_sector->floorplane.ZatPoint (thing->x, thing->y))
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
2009-01-04 15:00:29 +00:00
|
|
|
|
|
|
|
|
|
// Check 3D floors
|
|
|
|
|
for(unsigned int i=0;i<m->m_sector->e->XFloor.ffloors.Size();i++)
|
|
|
|
|
{
|
|
|
|
|
F3DFloor * rover = m->m_sector->e->XFloor.ffloors[i];
|
|
|
|
|
if (!(rover->flags & FF_EXISTS)) continue;
|
|
|
|
|
if (rover->flags & (FF_SOLID|FF_SWIMMABLE))
|
|
|
|
|
{
|
|
|
|
|
if (rover->top.plane->ZatPoint(thing->x, thing->y) == thing->z)
|
|
|
|
|
{
|
|
|
|
|
return P_HitWater (thing, m->m_sector);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2009-05-23 10:21:33 +00:00
|
|
|
|
if (m == NULL || m->m_sector->GetHeightSec() != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return P_HitWater (thing, m->m_sector);
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-17 11:30:44 +00:00
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// P_CheckSplash
|
|
|
|
|
//
|
|
|
|
|
// Checks for splashes caused by explosions
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void P_CheckSplash(AActor *self, fixed_t distance)
|
|
|
|
|
{
|
2010-11-07 20:18:52 +00:00
|
|
|
|
if (self->z <= self->floorz + (distance<<FRACBITS) && self->floorsector == self->Sector && self->Sector->GetHeightSec() == NULL)
|
2009-10-17 11:30:44 +00:00
|
|
|
|
{
|
|
|
|
|
// Explosion splashes never alert monsters. This is because A_Explode has
|
|
|
|
|
// a separate parameter for that so this would get in the way of proper
|
|
|
|
|
// behavior.
|
|
|
|
|
P_HitWater (self, self->Sector, self->x, self->y, self->floorz, false, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// FUNC P_CheckMissileSpawn
|
|
|
|
|
//
|
|
|
|
|
// Returns true if the missile is at a valid spawn point, otherwise
|
|
|
|
|
// explodes it and returns false.
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
2013-03-21 03:06:04 +00:00
|
|
|
|
bool P_CheckMissileSpawn (AActor* th, fixed_t maxdist)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2006-12-31 11:27:39 +00:00
|
|
|
|
// [RH] Don't decrement tics if they are already less than 1
|
|
|
|
|
if ((th->flags4 & MF4_RANDOMIZE) && th->tics > 0)
|
|
|
|
|
{
|
|
|
|
|
th->tics -= pr_checkmissilespawn() & 3;
|
|
|
|
|
if (th->tics < 1)
|
|
|
|
|
th->tics = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-21 03:06:04 +00:00
|
|
|
|
if (maxdist > 0)
|
2009-01-01 15:09:49 +00:00
|
|
|
|
{
|
2013-03-21 03:06:04 +00:00
|
|
|
|
// move a little forward so an angle can be computed if it immediately explodes
|
|
|
|
|
TVector3<double> advance(FIXED2DBL(th->velx), FIXED2DBL(th->vely), FIXED2DBL(th->velz));
|
|
|
|
|
double maxsquared = FIXED2DBL(maxdist);
|
|
|
|
|
maxsquared *= maxsquared;
|
|
|
|
|
|
|
|
|
|
// Keep halving the advance vector until we get something less than maxdist
|
|
|
|
|
// units away, since we still want to spawn the missile inside the shooter.
|
2013-03-21 22:23:42 +00:00
|
|
|
|
do
|
2009-01-01 15:09:49 +00:00
|
|
|
|
{
|
2013-03-21 03:06:04 +00:00
|
|
|
|
advance *= 0.5f;
|
2009-01-01 15:09:49 +00:00
|
|
|
|
}
|
2013-03-21 22:23:42 +00:00
|
|
|
|
while (TVector2<double>(advance).LengthSquared() >= maxsquared);
|
2013-03-21 03:06:04 +00:00
|
|
|
|
th->x += FLOAT2FIXED(advance.X);
|
|
|
|
|
th->y += FLOAT2FIXED(advance.Y);
|
|
|
|
|
th->z += FLOAT2FIXED(advance.Z);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-01-01 15:09:49 +00:00
|
|
|
|
FCheckPosition tm(!!(th->flags2 & MF2_RIP));
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2013-03-21 03:06:04 +00:00
|
|
|
|
// killough 8/12/98: for non-missile objects (e.g. grenades)
|
|
|
|
|
//
|
|
|
|
|
// [GZ] MBF excludes non-missile objects from the P_TryMove test
|
|
|
|
|
// and subsequent potential P_ExplodeMissile call. That is because
|
|
|
|
|
// in MBF, a projectile is not an actor with the MF_MISSILE flag
|
|
|
|
|
// but an actor with either or both the MF_MISSILE and MF_BOUNCES
|
|
|
|
|
// flags, and a grenade is identified by not having MF_MISSILE.
|
|
|
|
|
// Killough wanted grenades not to explode directly when spawned,
|
|
|
|
|
// therefore they can be fired safely even when humping a wall as
|
|
|
|
|
// they will then just drop on the floor at their shooter's feet.
|
|
|
|
|
//
|
|
|
|
|
// However, ZDoom does allow non-missiles to be shot as well, so
|
|
|
|
|
// Killough's check for non-missiles is inadequate here. So let's
|
|
|
|
|
// replace it by a check for non-missile and MBF bounce type.
|
|
|
|
|
// This should allow MBF behavior where relevant without altering
|
|
|
|
|
// established ZDoom behavior for crazy stuff like a cacodemon cannon.
|
|
|
|
|
bool MBFGrenade = (!(th->flags & MF_MISSILE) || (th->BounceFlags & BOUNCE_MBF));
|
|
|
|
|
|
|
|
|
|
// killough 3/15/98: no dropoff (really = don't care for missiles)
|
|
|
|
|
if (!(P_TryMove (th, th->x, th->y, false, NULL, tm, true)))
|
|
|
|
|
{
|
|
|
|
|
// [RH] Don't explode ripping missiles that spawn inside something
|
|
|
|
|
if (th->BlockingMobj == NULL || !(th->flags2 & MF2_RIP) || (th->BlockingMobj->flags5 & MF5_DONTRIP))
|
|
|
|
|
{
|
|
|
|
|
// If this is a monster spawned by A_CustomMissile subtract it from the counter.
|
|
|
|
|
th->ClearCounters();
|
|
|
|
|
// [RH] Don't explode missiles that spawn on top of horizon lines
|
|
|
|
|
if (th->BlockingLine != NULL && th->BlockingLine->special == Line_Horizon)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2013-03-21 03:06:04 +00:00
|
|
|
|
th->Destroy ();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2013-03-21 03:06:04 +00:00
|
|
|
|
else if (MBFGrenade && th->BlockingLine != NULL)
|
|
|
|
|
{
|
|
|
|
|
P_BounceWall(th);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
P_ExplodeMissile (th, NULL, th->BlockingMobj);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-11-25 12:25:05 +00:00
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// FUNC P_PlaySpawnSound
|
|
|
|
|
//
|
|
|
|
|
// Plays a missiles spawn sound. Location depends on the
|
|
|
|
|
// MF_SPAWNSOUNDSOURCE flag.
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void P_PlaySpawnSound(AActor *missile, AActor *spawner)
|
|
|
|
|
{
|
|
|
|
|
if (missile->SeeSound != 0)
|
|
|
|
|
{
|
|
|
|
|
if (!(missile->flags & MF_SPAWNSOUNDSOURCE))
|
|
|
|
|
{
|
2008-06-15 02:25:09 +00:00
|
|
|
|
S_Sound (missile, CHAN_VOICE, missile->SeeSound, 1, ATTN_NORM);
|
2006-11-25 12:25:05 +00:00
|
|
|
|
}
|
|
|
|
|
else if (spawner != NULL)
|
|
|
|
|
{
|
2008-06-15 02:25:09 +00:00
|
|
|
|
S_Sound (spawner, CHAN_WEAPON, missile->SeeSound, 1, ATTN_NORM);
|
2006-11-25 12:25:05 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// If there is no spawner use the spawn position.
|
|
|
|
|
// But not in a silenced sector.
|
2008-05-11 21:16:32 +00:00
|
|
|
|
if (!(missile->Sector->Flags & SECF_SILENT))
|
2008-07-01 04:06:56 +00:00
|
|
|
|
S_Sound (missile->x, missile->y, missile->z, CHAN_WEAPON, missile->SeeSound, 1, ATTN_NORM);
|
2006-11-25 12:25:05 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-11 22:16:41 +00:00
|
|
|
|
static fixed_t GetDefaultSpeed(const PClass *type)
|
|
|
|
|
{
|
|
|
|
|
if (type == NULL) return 0;
|
|
|
|
|
else if (G_SkillProperty(SKILLP_FastMonsters))
|
|
|
|
|
return type->Meta.GetMetaFixed(AMETA_FastSpeed, GetDefaultByType(type)->Speed);
|
|
|
|
|
else
|
|
|
|
|
return GetDefaultByType(type)->Speed;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// FUNC P_SpawnMissile
|
|
|
|
|
//
|
|
|
|
|
// Returns NULL if the missile exploded immediately, otherwise returns
|
|
|
|
|
// a mobj_t pointer to the missile.
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
2010-01-30 14:35:52 +00:00
|
|
|
|
AActor *P_SpawnMissile (AActor *source, AActor *dest, const PClass *type, AActor *owner)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-02-09 02:39:55 +00:00
|
|
|
|
if (source == NULL)
|
|
|
|
|
{
|
2015-02-08 09:03:49 +00:00
|
|
|
|
return NULL;
|
2015-02-09 02:39:55 +00:00
|
|
|
|
}
|
- Add the bob offset to the missiles spawned by P_SpawnMissile(), P_SpawnMissileAngle(), P_SpawnMissileAngleSpeed(), A_MissileAttack, A_ComboAttack, A_BasicAttack, A_CustomMissile, A_CustomComboAttack, A_ThrowGrenade, A_SpawnDebris, and A_Burst.
- Add the bob offset to the value returned by GetActorZ.
SVN r3795 (trunk)
2012-07-30 00:05:24 +00:00
|
|
|
|
return P_SpawnMissileXYZ (source->x, source->y, source->z + 32*FRACUNIT + source->GetBobOffset(),
|
2010-01-30 14:35:52 +00:00
|
|
|
|
source, dest, type, true, owner);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
|
AActor *P_SpawnMissileZ (AActor *source, fixed_t z, AActor *dest, const PClass *type)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-02-09 02:39:55 +00:00
|
|
|
|
if (source == NULL)
|
|
|
|
|
{
|
2015-02-08 09:03:49 +00:00
|
|
|
|
return NULL;
|
2015-02-09 02:39:55 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return P_SpawnMissileXYZ (source->x, source->y, z, source, dest, type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AActor *P_SpawnMissileXYZ (fixed_t x, fixed_t y, fixed_t z,
|
2010-01-30 14:35:52 +00:00
|
|
|
|
AActor *source, AActor *dest, const PClass *type, bool checkspawn, AActor *owner)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-02-09 02:39:55 +00:00
|
|
|
|
if (source == NULL)
|
|
|
|
|
{
|
2015-02-08 09:03:49 +00:00
|
|
|
|
return NULL;
|
2015-02-09 02:39:55 +00:00
|
|
|
|
}
|
2015-02-08 09:03:49 +00:00
|
|
|
|
|
2006-11-21 05:43:34 +00:00
|
|
|
|
if (dest == NULL)
|
|
|
|
|
{
|
|
|
|
|
Printf ("P_SpawnMissilyXYZ: Tried to shoot %s from %s with no dest\n",
|
|
|
|
|
type->TypeName.GetChars(), source->GetClass()->TypeName.GetChars());
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2009-01-03 18:09:33 +00:00
|
|
|
|
if (z != ONFLOORZ && z != ONCEILINGZ)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
z -= source->floorclip;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-13 22:07:18 +00:00
|
|
|
|
AActor *th = Spawn (type, x, y, z, ALLOW_REPLACE);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2006-11-25 12:25:05 +00:00
|
|
|
|
P_PlaySpawnSound(th, source);
|
2010-01-30 14:35:52 +00:00
|
|
|
|
|
|
|
|
|
// record missile's originator
|
2012-05-17 10:57:57 +00:00
|
|
|
|
if (owner == NULL) owner = source;
|
|
|
|
|
th->target = owner;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
float speed = (float)(th->Speed);
|
|
|
|
|
|
|
|
|
|
// [RH]
|
|
|
|
|
// Hexen calculates the missile velocity based on the source's location.
|
|
|
|
|
// Would it be more useful to base it on the actual position of the
|
2007-03-07 17:31:40 +00:00
|
|
|
|
// missile?
|
|
|
|
|
// Answer: No, because this way, you can set up sets of parallel missiles.
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2007-01-19 02:00:39 +00:00
|
|
|
|
FVector3 velocity(dest->x - source->x, dest->y - source->y, dest->z - source->z);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
// Floor and ceiling huggers should never have a vertical component to their velocity
|
2009-01-03 18:09:33 +00:00
|
|
|
|
if (th->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2007-01-19 02:00:39 +00:00
|
|
|
|
velocity.Z = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2007-03-07 17:31:40 +00:00
|
|
|
|
// [RH] Adjust the trajectory if the missile will go over the target's head.
|
2006-02-24 04:48:15 +00:00
|
|
|
|
else if (z - source->z >= dest->height)
|
|
|
|
|
{
|
2007-01-19 02:00:39 +00:00
|
|
|
|
velocity.Z += dest->height - z + source->z;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2007-01-19 02:00:39 +00:00
|
|
|
|
velocity.Resize (speed);
|
2009-06-30 20:57:51 +00:00
|
|
|
|
th->velx = (fixed_t)(velocity.X);
|
|
|
|
|
th->vely = (fixed_t)(velocity.Y);
|
|
|
|
|
th->velz = (fixed_t)(velocity.Z);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
// invisible target: rotate velocity vector in 2D
|
2011-05-26 23:25:02 +00:00
|
|
|
|
// [RC] Now monsters can aim at invisible player as if they were fully visible.
|
|
|
|
|
if (dest->flags & MF_SHADOW && !(source->flags6 & MF6_SEEINVISIBLE))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
angle_t an = pr_spawnmissile.Random2 () << 20;
|
|
|
|
|
an >>= ANGLETOFINESHIFT;
|
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
|
fixed_t newx = DMulScale16 (th->velx, finecosine[an], -th->vely, finesine[an]);
|
|
|
|
|
fixed_t newy = DMulScale16 (th->velx, finesine[an], th->vely, finecosine[an]);
|
|
|
|
|
th->velx = newx;
|
|
|
|
|
th->vely = newy;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
|
th->angle = R_PointToAngle2 (0, 0, th->velx, th->vely);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2012-05-13 11:17:27 +00:00
|
|
|
|
if (th->flags4 & MF4_SPECTRAL)
|
|
|
|
|
{
|
2012-06-09 04:15:56 +00:00
|
|
|
|
th->SetFriendPlayer(owner->player);
|
2012-05-13 11:17:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-03-21 03:06:04 +00:00
|
|
|
|
return (!checkspawn || P_CheckMissileSpawn (th, source->radius)) ? th : NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-01-30 14:35:52 +00:00
|
|
|
|
AActor * P_OldSpawnMissile(AActor * source, AActor * owner, AActor * dest, const PClass *type)
|
2009-08-02 10:48:58 +00:00
|
|
|
|
{
|
2015-02-09 02:39:55 +00:00
|
|
|
|
if (source == NULL)
|
|
|
|
|
{
|
2015-02-08 09:03:49 +00:00
|
|
|
|
return NULL;
|
2015-02-09 02:39:55 +00:00
|
|
|
|
}
|
2009-08-02 10:48:58 +00:00
|
|
|
|
angle_t an;
|
|
|
|
|
fixed_t dist;
|
|
|
|
|
AActor *th = Spawn (type, source->x, source->y, source->z + 4*8*FRACUNIT, ALLOW_REPLACE);
|
|
|
|
|
|
|
|
|
|
P_PlaySpawnSound(th, source);
|
2010-01-30 14:35:52 +00:00
|
|
|
|
th->target = owner; // record missile's originator
|
2009-08-02 10:48:58 +00:00
|
|
|
|
|
|
|
|
|
th->angle = an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y);
|
|
|
|
|
an >>= ANGLETOFINESHIFT;
|
|
|
|
|
th->velx = FixedMul (th->Speed, finecosine[an]);
|
|
|
|
|
th->vely = FixedMul (th->Speed, finesine[an]);
|
|
|
|
|
|
|
|
|
|
dist = P_AproxDistance (dest->x - source->x, dest->y - source->y);
|
|
|
|
|
if (th->Speed) dist = dist / th->Speed;
|
|
|
|
|
|
|
|
|
|
if (dist < 1)
|
|
|
|
|
dist = 1;
|
|
|
|
|
|
|
|
|
|
th->velz = (dest->z - source->z) / dist;
|
2012-05-13 11:17:27 +00:00
|
|
|
|
|
|
|
|
|
if (th->flags4 & MF4_SPECTRAL)
|
|
|
|
|
{
|
2012-06-09 04:15:56 +00:00
|
|
|
|
th->SetFriendPlayer(owner->player);
|
2012-05-13 11:17:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-03-21 03:06:04 +00:00
|
|
|
|
P_CheckMissileSpawn(th, source->radius);
|
2009-08-02 10:48:58 +00:00
|
|
|
|
return th;
|
|
|
|
|
}
|
2009-09-14 09:41:09 +00:00
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// FUNC P_SpawnMissileAngle
|
|
|
|
|
//
|
|
|
|
|
// Returns NULL if the missile exploded immediately, otherwise returns
|
|
|
|
|
// a mobj_t pointer to the missile.
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
|
AActor *P_SpawnMissileAngle (AActor *source, const PClass *type,
|
2009-06-30 20:57:51 +00:00
|
|
|
|
angle_t angle, fixed_t velz)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-02-09 02:39:55 +00:00
|
|
|
|
if (source == NULL)
|
|
|
|
|
{
|
2015-02-08 09:03:49 +00:00
|
|
|
|
return NULL;
|
2015-02-09 02:39:55 +00:00
|
|
|
|
}
|
- Add the bob offset to the missiles spawned by P_SpawnMissile(), P_SpawnMissileAngle(), P_SpawnMissileAngleSpeed(), A_MissileAttack, A_ComboAttack, A_BasicAttack, A_CustomMissile, A_CustomComboAttack, A_ThrowGrenade, A_SpawnDebris, and A_Burst.
- Add the bob offset to the value returned by GetActorZ.
SVN r3795 (trunk)
2012-07-30 00:05:24 +00:00
|
|
|
|
return P_SpawnMissileAngleZSpeed (source, source->z + 32*FRACUNIT + source->GetBobOffset(),
|
2009-06-30 20:57:51 +00:00
|
|
|
|
type, angle, velz, GetDefaultSpeed (type));
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AActor *P_SpawnMissileAngleZ (AActor *source, fixed_t z,
|
2009-06-30 20:57:51 +00:00
|
|
|
|
const PClass *type, angle_t angle, fixed_t velz)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
|
return P_SpawnMissileAngleZSpeed (source, z, type, angle, velz,
|
2009-05-11 22:16:41 +00:00
|
|
|
|
GetDefaultSpeed (type));
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
|
AActor *P_SpawnMissileZAimed (AActor *source, fixed_t z, AActor *dest, const PClass *type)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-02-09 02:39:55 +00:00
|
|
|
|
if (source == NULL)
|
|
|
|
|
{
|
2015-02-08 09:03:49 +00:00
|
|
|
|
return NULL;
|
2015-02-09 02:39:55 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
angle_t an;
|
|
|
|
|
fixed_t dist;
|
|
|
|
|
fixed_t speed;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
fixed_t velz;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
an = source->angle;
|
|
|
|
|
|
|
|
|
|
if (dest->flags & MF_SHADOW)
|
|
|
|
|
{
|
|
|
|
|
an += pr_spawnmissile.Random2() << 20;
|
|
|
|
|
}
|
|
|
|
|
dist = P_AproxDistance (dest->x - source->x, dest->y - source->y);
|
2009-05-11 22:16:41 +00:00
|
|
|
|
speed = GetDefaultSpeed (type);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
dist /= speed;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
velz = dist != 0 ? (dest->z - source->z)/dist : speed;
|
|
|
|
|
return P_SpawnMissileAngleZSpeed (source, z, type, an, velz, speed);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// FUNC P_SpawnMissileAngleSpeed
|
|
|
|
|
//
|
|
|
|
|
// Returns NULL if the missile exploded immediately, otherwise returns
|
|
|
|
|
// a mobj_t pointer to the missile.
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
|
AActor *P_SpawnMissileAngleSpeed (AActor *source, const PClass *type,
|
2009-06-30 20:57:51 +00:00
|
|
|
|
angle_t angle, fixed_t velz, fixed_t speed)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-02-09 02:39:55 +00:00
|
|
|
|
if (source == NULL)
|
|
|
|
|
{
|
2015-02-08 09:03:49 +00:00
|
|
|
|
return NULL;
|
2015-02-09 02:39:55 +00:00
|
|
|
|
}
|
- Add the bob offset to the missiles spawned by P_SpawnMissile(), P_SpawnMissileAngle(), P_SpawnMissileAngleSpeed(), A_MissileAttack, A_ComboAttack, A_BasicAttack, A_CustomMissile, A_CustomComboAttack, A_ThrowGrenade, A_SpawnDebris, and A_Burst.
- Add the bob offset to the value returned by GetActorZ.
SVN r3795 (trunk)
2012-07-30 00:05:24 +00:00
|
|
|
|
return P_SpawnMissileAngleZSpeed (source, source->z + 32*FRACUNIT + source->GetBobOffset(),
|
2009-06-30 20:57:51 +00:00
|
|
|
|
type, angle, velz, speed);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AActor *P_SpawnMissileAngleZSpeed (AActor *source, fixed_t z,
|
2009-06-30 20:57:51 +00:00
|
|
|
|
const PClass *type, angle_t angle, fixed_t velz, fixed_t speed, AActor *owner, bool checkspawn)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-02-09 02:39:55 +00:00
|
|
|
|
if (source == NULL)
|
|
|
|
|
{
|
2015-02-08 09:03:49 +00:00
|
|
|
|
return NULL;
|
2015-02-09 02:39:55 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
AActor *mo;
|
|
|
|
|
|
2015-02-09 02:39:55 +00:00
|
|
|
|
if (z != ONFLOORZ && z != ONCEILINGZ)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
z -= source->floorclip;
|
|
|
|
|
}
|
2009-01-03 18:09:33 +00:00
|
|
|
|
|
2009-07-13 22:07:18 +00:00
|
|
|
|
mo = Spawn (type, source->x, source->y, z, ALLOW_REPLACE);
|
2009-01-03 18:09:33 +00:00
|
|
|
|
|
2006-11-25 12:25:05 +00:00
|
|
|
|
P_PlaySpawnSound(mo, source);
|
2012-05-17 10:57:57 +00:00
|
|
|
|
if (owner == NULL) owner = source;
|
|
|
|
|
mo->target = owner;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
mo->angle = angle;
|
|
|
|
|
angle >>= ANGLETOFINESHIFT;
|
2009-06-30 20:57:51 +00:00
|
|
|
|
mo->velx = FixedMul (speed, finecosine[angle]);
|
|
|
|
|
mo->vely = FixedMul (speed, finesine[angle]);
|
|
|
|
|
mo->velz = velz;
|
2012-05-13 11:17:27 +00:00
|
|
|
|
|
|
|
|
|
if (mo->flags4 & MF4_SPECTRAL)
|
|
|
|
|
{
|
2012-06-09 04:15:56 +00:00
|
|
|
|
mo->SetFriendPlayer(owner->player);
|
2012-05-13 11:17:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-03-21 03:06:04 +00:00
|
|
|
|
return (!checkspawn || P_CheckMissileSpawn(mo, source->radius)) ? mo : NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
================
|
|
|
|
|
=
|
|
|
|
|
= P_SpawnPlayerMissile
|
|
|
|
|
=
|
|
|
|
|
= Tries to aim at a nearby monster
|
|
|
|
|
================
|
|
|
|
|
*/
|
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
|
AActor *P_SpawnPlayerMissile (AActor *source, const PClass *type)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2015-02-09 02:39:55 +00:00
|
|
|
|
if (source == NULL)
|
|
|
|
|
{
|
2015-02-08 09:03:49 +00:00
|
|
|
|
return NULL;
|
2015-02-09 02:39:55 +00:00
|
|
|
|
}
|
2007-03-07 17:31:40 +00:00
|
|
|
|
return P_SpawnPlayerMissile (source, 0, 0, 0, type, source->angle);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
|
AActor *P_SpawnPlayerMissile (AActor *source, const PClass *type, angle_t angle)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2007-03-07 17:31:40 +00:00
|
|
|
|
return P_SpawnPlayerMissile (source, 0, 0, 0, type, angle);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z,
|
2009-01-10 09:40:47 +00:00
|
|
|
|
const PClass *type, angle_t angle, AActor **pLineTarget, AActor **pMissileActor,
|
2015-09-08 15:40:21 +00:00
|
|
|
|
bool nofreeaim, bool noautoaim)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
static const int angdiff[3] = { -1<<26, 1<<26, 0 };
|
2011-03-29 05:20:33 +00:00
|
|
|
|
angle_t an = angle;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
angle_t pitch;
|
2008-04-10 14:38:43 +00:00
|
|
|
|
AActor *linetarget;
|
2013-03-20 03:19:02 +00:00
|
|
|
|
AActor *defaultobject = GetDefaultByType(type);
|
|
|
|
|
int vrange = nofreeaim ? ANGLE_1*35 : 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2013-05-12 18:36:03 +00:00
|
|
|
|
if (source == NULL)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2015-09-08 15:40:21 +00:00
|
|
|
|
if (source->player && source->player->ReadyWeapon && ((source->player->ReadyWeapon->WeaponFlags & WIF_NOAUTOAIM) || noautoaim))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-06-09 17:13:03 +00:00
|
|
|
|
// Keep exactly the same angle and pitch as the player's own aim
|
2013-03-13 02:56:54 +00:00
|
|
|
|
an = angle;
|
|
|
|
|
pitch = source->pitch;
|
|
|
|
|
linetarget = NULL;
|
2009-06-09 17:13:03 +00:00
|
|
|
|
}
|
|
|
|
|
else // see which target is to be aimed at
|
|
|
|
|
{
|
2013-03-20 03:19:02 +00:00
|
|
|
|
// [XA] If MaxTargetRange is defined in the spawned projectile, use this as the
|
|
|
|
|
// maximum range for the P_AimLineAttack call later; this allows MaxTargetRange
|
|
|
|
|
// to function as a "maximum tracer-acquisition range" for seeker missiles.
|
|
|
|
|
fixed_t linetargetrange = defaultobject->maxtargetrange > 0 ? defaultobject->maxtargetrange*64 : 16*64*FRACUNIT;
|
|
|
|
|
|
|
|
|
|
int i = 2;
|
2009-06-09 17:13:03 +00:00
|
|
|
|
do
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2009-06-09 17:13:03 +00:00
|
|
|
|
an = angle + angdiff[i];
|
2013-03-20 03:19:02 +00:00
|
|
|
|
pitch = P_AimLineAttack (source, an, linetargetrange, &linetarget, vrange);
|
2009-06-09 17:13:03 +00:00
|
|
|
|
|
|
|
|
|
if (source->player != NULL &&
|
|
|
|
|
!nofreeaim &&
|
|
|
|
|
level.IsFreelookAllowed() &&
|
|
|
|
|
source->player->userinfo.GetAimDist() <= ANGLE_1/2)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while (linetarget == NULL && --i >= 0);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2013-03-13 02:56:54 +00:00
|
|
|
|
if (linetarget == NULL)
|
2009-11-12 01:21:25 +00:00
|
|
|
|
{
|
2013-03-13 02:56:54 +00:00
|
|
|
|
an = angle;
|
|
|
|
|
if (nofreeaim || !level.IsFreelookAllowed())
|
|
|
|
|
{
|
|
|
|
|
pitch = 0;
|
|
|
|
|
}
|
2009-11-12 01:21:25 +00:00
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-10 14:38:43 +00:00
|
|
|
|
if (pLineTarget) *pLineTarget = linetarget;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
if (z != ONFLOORZ && z != ONCEILINGZ)
|
|
|
|
|
{
|
2007-03-07 17:31:40 +00:00
|
|
|
|
// Doom spawns missiles 4 units lower than hitscan attacks for players.
|
|
|
|
|
z += source->z + (source->height>>1) - source->floorclip;
|
|
|
|
|
if (source->player != NULL) // Considering this is for player missiles, it better not be NULL.
|
|
|
|
|
{
|
|
|
|
|
z += FixedMul (source->player->mo->AttackZOffset - 4*FRACUNIT, source->player->crouchfactor);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
z += 4*FRACUNIT;
|
|
|
|
|
}
|
|
|
|
|
// Do not fire beneath the floor.
|
|
|
|
|
if (z < source->floorz)
|
|
|
|
|
{
|
|
|
|
|
z = source->floorz;
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2008-04-10 14:38:43 +00:00
|
|
|
|
AActor *MissileActor = Spawn (type, source->x + x, source->y + y, z, ALLOW_REPLACE);
|
|
|
|
|
if (pMissileActor) *pMissileActor = MissileActor;
|
2006-11-25 12:25:05 +00:00
|
|
|
|
P_PlaySpawnSound(MissileActor, source);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
MissileActor->target = source;
|
|
|
|
|
MissileActor->angle = an;
|
|
|
|
|
|
|
|
|
|
fixed_t vx, vy, vz, speed;
|
|
|
|
|
|
|
|
|
|
vx = FixedMul (finecosine[pitch>>ANGLETOFINESHIFT], finecosine[an>>ANGLETOFINESHIFT]);
|
|
|
|
|
vy = FixedMul (finecosine[pitch>>ANGLETOFINESHIFT], finesine[an>>ANGLETOFINESHIFT]);
|
|
|
|
|
vz = -finesine[pitch>>ANGLETOFINESHIFT];
|
|
|
|
|
speed = MissileActor->Speed;
|
|
|
|
|
|
2009-09-27 20:54:46 +00:00
|
|
|
|
FVector3 vec(vx, vy, vz);
|
|
|
|
|
|
2009-09-06 21:24:04 +00:00
|
|
|
|
if (MissileActor->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER))
|
2009-09-06 21:22:07 +00:00
|
|
|
|
{
|
2009-09-27 20:54:46 +00:00
|
|
|
|
vec.Z = 0;
|
2009-09-06 21:24:04 +00:00
|
|
|
|
}
|
2009-09-27 20:54:46 +00:00
|
|
|
|
vec.Resize(speed);
|
|
|
|
|
MissileActor->velx = (fixed_t)vec.X;
|
|
|
|
|
MissileActor->vely = (fixed_t)vec.Y;
|
|
|
|
|
MissileActor->velz = (fixed_t)vec.Z;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
2008-07-12 10:59:36 +00:00
|
|
|
|
if (MissileActor->flags4 & MF4_SPECTRAL)
|
2012-05-13 11:17:27 +00:00
|
|
|
|
{
|
2012-06-09 04:15:56 +00:00
|
|
|
|
MissileActor->SetFriendPlayer(source->player);
|
2012-05-13 11:17:27 +00:00
|
|
|
|
}
|
2013-03-21 03:06:04 +00:00
|
|
|
|
if (P_CheckMissileSpawn (MissileActor, source->radius))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
return MissileActor;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-25 12:59:30 +00:00
|
|
|
|
int AActor::GetTeam()
|
|
|
|
|
{
|
|
|
|
|
if (player)
|
|
|
|
|
{
|
|
|
|
|
return player->userinfo.GetTeam();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int myTeam = DesignatedTeam;
|
|
|
|
|
|
|
|
|
|
// Check for monsters that belong to a player on the team but aren't part of the team themselves.
|
|
|
|
|
if (myTeam == TEAM_NONE && FriendPlayer != 0)
|
|
|
|
|
{
|
|
|
|
|
myTeam = players[FriendPlayer - 1].userinfo.GetTeam();
|
|
|
|
|
}
|
|
|
|
|
return myTeam;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
bool AActor::IsTeammate (AActor *other)
|
|
|
|
|
{
|
2011-01-22 03:35:33 +00:00
|
|
|
|
if (!other)
|
2014-10-25 12:59:30 +00:00
|
|
|
|
{
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return false;
|
2014-10-25 12:59:30 +00:00
|
|
|
|
}
|
2011-01-22 03:35:33 +00:00
|
|
|
|
else if (!deathmatch && player && other->player)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2014-10-25 12:59:30 +00:00
|
|
|
|
else if (teamplay)
|
|
|
|
|
{
|
|
|
|
|
int myTeam = GetTeam();
|
|
|
|
|
int otherTeam = other->GetTeam();
|
|
|
|
|
|
|
|
|
|
return (myTeam != TEAM_NONE && myTeam == otherTeam);
|
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2006-03-03 03:57:01 +00:00
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: GetSpecies
|
|
|
|
|
//
|
|
|
|
|
// Species is defined as the lowest base class that is a monster
|
2009-05-11 15:15:06 +00:00
|
|
|
|
// with no non-monster class in between. If the actor specifies an explicit
|
|
|
|
|
// species (i.e. not 'None'), that is used. This is virtualized, so special
|
2006-03-03 03:57:01 +00:00
|
|
|
|
// monsters can change this behavior if they like.
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
2009-05-11 15:15:06 +00:00
|
|
|
|
FName AActor::GetSpecies()
|
2006-03-03 03:57:01 +00:00
|
|
|
|
{
|
2009-05-11 15:15:06 +00:00
|
|
|
|
if (Species != NAME_None)
|
|
|
|
|
{
|
|
|
|
|
return Species;
|
|
|
|
|
}
|
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
|
const PClass *thistype = GetClass();
|
2006-03-03 03:57:01 +00:00
|
|
|
|
|
|
|
|
|
if (GetDefaultByType(thistype)->flags3 & MF3_ISMONSTER)
|
|
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
|
while (thistype->ParentClass)
|
2006-03-03 03:57:01 +00:00
|
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
|
if (GetDefaultByType(thistype->ParentClass)->flags3 & MF3_ISMONSTER)
|
|
|
|
|
thistype = thistype->ParentClass;
|
2006-03-03 03:57:01 +00:00
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-05-31 20:14:16 +00:00
|
|
|
|
return Species = thistype->TypeName; // [GZ] Speeds up future calls.
|
2006-03-03 03:57:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: IsFriend
|
|
|
|
|
//
|
|
|
|
|
// Checks if two monsters have to be considered friendly.
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
|
|
bool AActor::IsFriend (AActor *other)
|
|
|
|
|
{
|
|
|
|
|
if (flags & other->flags & MF_FRIENDLY)
|
|
|
|
|
{
|
2011-01-22 03:35:33 +00:00
|
|
|
|
if (deathmatch && teamplay)
|
|
|
|
|
return IsTeammate(other) ||
|
|
|
|
|
(FriendPlayer != 0 && other->FriendPlayer != 0 &&
|
|
|
|
|
players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo));
|
|
|
|
|
|
2006-03-03 03:57:01 +00:00
|
|
|
|
return !deathmatch ||
|
|
|
|
|
FriendPlayer == other->FriendPlayer ||
|
|
|
|
|
FriendPlayer == 0 ||
|
2009-08-12 18:57:31 +00:00
|
|
|
|
other->FriendPlayer == 0 ||
|
|
|
|
|
players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo);
|
2006-03-03 03:57:01 +00:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// AActor :: IsHostile
|
|
|
|
|
//
|
|
|
|
|
// Checks if two monsters have to be considered hostile under any circumstances
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
- Fixed: ActorFlagSetOrReset() wasn't receiving the + or - character from
ParseActorProperties().
- Fixed: The decorate FindFlag() function returned flags from ActorFlags
instead of the passed flags set.
- Fixed: The CHT_CHAINSAW, CHT_POWER, CHT_HEALTH, and CHT_RESSURECT needed
NULL player->mo checks.
- Fixed: The "give all" command didn't give the backpack in Doom, and it
must give the backpack before giving ammo.
- Fixed: P_SetPsprite() must not call the action function if the player is
not attached to an actor. This can happen, for instance, if the level is
destroyed while the player is holding a powered-up Phoenix Rod. As part
of its EndPowerup() function, it sets the psprite to the regular version,
but the player actor has already been destroyed.
- Fixed: FinishThingdef() needs to check for valid names, because weapons
could have inherited valid pointers from their superclass.
- Fixed: fuglyname didn't work.
- Fixed: Redefining $ambient sounds leaked memory.
- Added Jim's crashcatcher.c fix for better shell support.
- VC7.1 seems to have no trouble distinguishing between passing a (const
TypeInfo *) reference to operator<< and the generic, templated (object *)
version, so a few places that can benefit from it now use it. I believe
VC6 had problems with this, which is why I didn't do it all along. The
function's implementation was also moved out of dobject.cpp and into
farchive.cpp.
- Fixed: UnpackPixels() unpacked all chunks in a byte, which is wrong for the
last byte in a row if the image width is not an even multiple of the number
pixels per byte.
- Fixed: P_TranslateLineDef() should only clear monster activation for secret
useable lines, not crossable lines.
- Fixed: Some leftover P_IsHostile() calls still needed to be rewritten.
- Fixed: AWeaponHolder::Serialize() wrote the class type in all circumstances.
SVN r20 (trunk)
2006-03-14 06:11:39 +00:00
|
|
|
|
bool AActor::IsHostile (AActor *other)
|
2006-03-03 03:57:01 +00:00
|
|
|
|
{
|
|
|
|
|
// Both monsters are non-friendlies so hostilities depend on infighting settings
|
|
|
|
|
if (!((flags | other->flags) & MF_FRIENDLY)) return false;
|
|
|
|
|
|
|
|
|
|
// Both monsters are friendly and belong to the same player if applicable.
|
|
|
|
|
if (flags & other->flags & MF_FRIENDLY)
|
|
|
|
|
{
|
2011-01-22 03:35:33 +00:00
|
|
|
|
if (deathmatch && teamplay)
|
|
|
|
|
return !IsTeammate(other) &&
|
|
|
|
|
!(FriendPlayer != 0 && other->FriendPlayer != 0 &&
|
|
|
|
|
players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo));
|
|
|
|
|
|
2006-03-03 03:57:01 +00:00
|
|
|
|
return deathmatch &&
|
- Fixed: ActorFlagSetOrReset() wasn't receiving the + or - character from
ParseActorProperties().
- Fixed: The decorate FindFlag() function returned flags from ActorFlags
instead of the passed flags set.
- Fixed: The CHT_CHAINSAW, CHT_POWER, CHT_HEALTH, and CHT_RESSURECT needed
NULL player->mo checks.
- Fixed: The "give all" command didn't give the backpack in Doom, and it
must give the backpack before giving ammo.
- Fixed: P_SetPsprite() must not call the action function if the player is
not attached to an actor. This can happen, for instance, if the level is
destroyed while the player is holding a powered-up Phoenix Rod. As part
of its EndPowerup() function, it sets the psprite to the regular version,
but the player actor has already been destroyed.
- Fixed: FinishThingdef() needs to check for valid names, because weapons
could have inherited valid pointers from their superclass.
- Fixed: fuglyname didn't work.
- Fixed: Redefining $ambient sounds leaked memory.
- Added Jim's crashcatcher.c fix for better shell support.
- VC7.1 seems to have no trouble distinguishing between passing a (const
TypeInfo *) reference to operator<< and the generic, templated (object *)
version, so a few places that can benefit from it now use it. I believe
VC6 had problems with this, which is why I didn't do it all along. The
function's implementation was also moved out of dobject.cpp and into
farchive.cpp.
- Fixed: UnpackPixels() unpacked all chunks in a byte, which is wrong for the
last byte in a row if the image width is not an even multiple of the number
pixels per byte.
- Fixed: P_TranslateLineDef() should only clear monster activation for secret
useable lines, not crossable lines.
- Fixed: Some leftover P_IsHostile() calls still needed to be rewritten.
- Fixed: AWeaponHolder::Serialize() wrote the class type in all circumstances.
SVN r20 (trunk)
2006-03-14 06:11:39 +00:00
|
|
|
|
FriendPlayer != other->FriendPlayer &&
|
|
|
|
|
FriendPlayer !=0 &&
|
2009-08-12 18:57:31 +00:00
|
|
|
|
other->FriendPlayer != 0 &&
|
|
|
|
|
!players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo);
|
2006-03-03 03:57:01 +00:00
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-29 04:21:31 +00:00
|
|
|
|
int AActor::DoSpecialDamage (AActor *target, int damage, FName damagetype)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
if (target->player && target->player->mo == target && damage < 1000 &&
|
2014-11-01 05:00:29 +00:00
|
|
|
|
(target->player->cheats & CF_GODMODE || target->player->cheats & CF_GODMODE2))
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2006-05-07 00:27:22 +00:00
|
|
|
|
if (target->player)
|
|
|
|
|
{
|
2010-07-23 21:36:17 +00:00
|
|
|
|
// Only do this for old style poison damage.
|
|
|
|
|
if (PoisonDamage > 0 && PoisonDuration == INT_MIN)
|
2006-05-07 00:27:22 +00:00
|
|
|
|
{
|
2010-07-23 21:36:17 +00:00
|
|
|
|
P_PoisonPlayer (target->player, this, this->target, PoisonDamage);
|
2006-05-07 00:27:22 +00:00
|
|
|
|
damage >>= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
return damage;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-10-31 14:53:21 +00:00
|
|
|
|
int AActor::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
FState *death;
|
|
|
|
|
|
2006-10-31 14:53:21 +00:00
|
|
|
|
// If the actor does not have a corresponding death state, then it does not take damage.
|
|
|
|
|
// Note that DeathState matches every kind of damagetype, so an actor has that, it can
|
|
|
|
|
// be hurt with any type of damage. Exception: Massacre damage always succeeds, because
|
|
|
|
|
// it needs to work.
|
|
|
|
|
|
|
|
|
|
// Always kill if there is a regular death state or no death states at all.
|
2008-09-22 18:55:29 +00:00
|
|
|
|
if (FindState (NAME_Death) != NULL || !HasSpecialDeathStates() || damagetype == NAME_Massacre)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
|
|
|
|
return damage;
|
|
|
|
|
}
|
2011-06-13 17:15:09 +00:00
|
|
|
|
|
|
|
|
|
if (inflictor && inflictor->DeathType != NAME_None)
|
|
|
|
|
damagetype = inflictor->DeathType;
|
|
|
|
|
|
2006-10-31 14:53:21 +00:00
|
|
|
|
if (damagetype == NAME_Ice)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
{
|
2006-12-16 14:06:21 +00:00
|
|
|
|
death = FindState (NAME_Death, NAME_Ice, true);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
if (death == NULL && !deh.NoAutofreeze && !(flags4 & MF4_NOICEDEATH) &&
|
|
|
|
|
(player || (flags3 & MF3_ISMONSTER)))
|
|
|
|
|
{
|
2008-08-10 14:19:47 +00:00
|
|
|
|
death = FindState(NAME_GenericFreezeDeath);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
2006-10-31 14:53:21 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2006-12-16 14:06:21 +00:00
|
|
|
|
death = FindState (NAME_Death, damagetype);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
}
|
|
|
|
|
return (death == NULL) ? -1 : damage;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-19 10:39:34 +00:00
|
|
|
|
int AActor::GibHealth()
|
|
|
|
|
{
|
|
|
|
|
return -abs(GetClass()->Meta.GetMetaInt (AMETA_GibHealth, FixedMul(SpawnHealth(), gameinfo.gibfactor)));
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-29 10:03:35 +00:00
|
|
|
|
void AActor::Crash()
|
|
|
|
|
{
|
2011-05-26 23:27:58 +00:00
|
|
|
|
// [RC] Weird that this forces the Crash state regardless of flag.
|
|
|
|
|
if(!(flags6 & MF6_DONTCORPSE))
|
|
|
|
|
{
|
2009-12-26 07:29:29 +00:00
|
|
|
|
if (((flags & MF_CORPSE) || (flags6 & MF6_KILLED)) &&
|
2006-11-29 10:03:35 +00:00
|
|
|
|
!(flags3 & MF3_CRASHED) &&
|
|
|
|
|
!(flags & MF_ICECORPSE))
|
|
|
|
|
{
|
2008-03-04 00:56:22 +00:00
|
|
|
|
FState *crashstate = NULL;
|
2006-11-29 10:03:35 +00:00
|
|
|
|
|
|
|
|
|
if (DamageType != NAME_None)
|
|
|
|
|
{
|
2013-07-01 09:02:35 +00:00
|
|
|
|
if (health < GibHealth())
|
|
|
|
|
{ // Extreme death
|
|
|
|
|
FName labels[] = { NAME_Crash, NAME_Extreme, DamageType };
|
|
|
|
|
crashstate = FindState (3, labels, true);
|
|
|
|
|
}
|
|
|
|
|
if (crashstate == NULL)
|
|
|
|
|
{ // Normal death
|
|
|
|
|
crashstate = FindState(NAME_Crash, DamageType, true);
|
|
|
|
|
}
|
2006-11-29 10:03:35 +00:00
|
|
|
|
}
|
|
|
|
|
if (crashstate == NULL)
|
|
|
|
|
{
|
2010-09-19 10:39:34 +00:00
|
|
|
|
if (health < GibHealth())
|
2006-11-29 10:03:35 +00:00
|
|
|
|
{ // Extreme death
|
2006-12-16 14:06:21 +00:00
|
|
|
|
crashstate = FindState (NAME_Crash, NAME_Extreme);
|
2006-11-29 10:03:35 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{ // Normal death
|
|
|
|
|
crashstate = FindState (NAME_Crash);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (crashstate != NULL) SetState(crashstate);
|
|
|
|
|
// Set MF3_CRASHED regardless of the presence of a crash state
|
|
|
|
|
// so this code doesn't have to be executed repeatedly.
|
|
|
|
|
flags3 |= MF3_CRASHED;
|
|
|
|
|
}
|
2011-05-26 23:27:58 +00:00
|
|
|
|
}
|
2006-11-29 10:03:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-04-02 22:06:04 +00:00
|
|
|
|
void AActor::SetIdle(bool nofunction)
|
2008-04-03 10:49:54 +00:00
|
|
|
|
{
|
|
|
|
|
FState *idle = FindState (NAME_Idle);
|
|
|
|
|
if (idle == NULL) idle = SpawnState;
|
2015-04-02 22:06:04 +00:00
|
|
|
|
SetState(idle, nofunction);
|
2008-04-03 10:49:54 +00:00
|
|
|
|
}
|
2008-09-21 18:02:38 +00:00
|
|
|
|
|
2009-07-04 18:17:44 +00:00
|
|
|
|
int AActor::SpawnHealth()
|
|
|
|
|
{
|
2014-04-10 22:58:59 +00:00
|
|
|
|
int defhealth = StartHealth ? StartHealth : GetDefault()->health;
|
|
|
|
|
if (!(flags3 & MF3_ISMONSTER) || defhealth == 0)
|
2009-07-04 18:17:44 +00:00
|
|
|
|
{
|
2014-04-10 22:58:59 +00:00
|
|
|
|
return defhealth;
|
2009-07-04 18:17:44 +00:00
|
|
|
|
}
|
|
|
|
|
else if (flags & MF_FRIENDLY)
|
|
|
|
|
{
|
2014-04-10 22:58:59 +00:00
|
|
|
|
int adj = FixedMul(defhealth, G_SkillProperty(SKILLP_FriendlyHealth));
|
2009-07-04 18:17:44 +00:00
|
|
|
|
return (adj <= 0) ? 1 : adj;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2014-04-10 22:58:59 +00:00
|
|
|
|
int adj = FixedMul(defhealth, G_SkillProperty(SKILLP_MonsterHealth));
|
2009-07-04 18:17:44 +00:00
|
|
|
|
return (adj <= 0) ? 1 : adj;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-09-14 19:44:14 +00:00
|
|
|
|
|
2014-05-05 09:24:20 +00:00
|
|
|
|
FState *AActor::GetRaiseState()
|
|
|
|
|
{
|
|
|
|
|
if (!(flags & MF_CORPSE))
|
|
|
|
|
{
|
|
|
|
|
return NULL; // not a monster
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tics != -1 && // not lying still yet
|
2014-05-07 15:18:44 +00:00
|
|
|
|
!state->GetCanRaise()) // or not ready to be raised yet
|
2014-05-05 09:24:20 +00:00
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsKindOf(RUNTIME_CLASS(APlayerPawn)))
|
|
|
|
|
{
|
|
|
|
|
return NULL; // do not resurrect players
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FindState(NAME_Raise);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AActor::Revive()
|
|
|
|
|
{
|
|
|
|
|
AActor *info = GetDefault();
|
|
|
|
|
flags = info->flags;
|
|
|
|
|
flags2 = info->flags2;
|
|
|
|
|
flags3 = info->flags3;
|
|
|
|
|
flags4 = info->flags4;
|
|
|
|
|
flags5 = info->flags5;
|
|
|
|
|
flags6 = info->flags6;
|
|
|
|
|
flags7 = info->flags7;
|
|
|
|
|
DamageType = info->DamageType;
|
|
|
|
|
health = SpawnHealth();
|
|
|
|
|
target = NULL;
|
|
|
|
|
lastenemy = NULL;
|
|
|
|
|
|
|
|
|
|
// [RH] If it's a monster, it gets to count as another kill
|
|
|
|
|
if (CountsAsKill())
|
|
|
|
|
{
|
|
|
|
|
level.total_monsters++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-21 18:02:38 +00:00
|
|
|
|
FDropItem *AActor::GetDropItems()
|
|
|
|
|
{
|
|
|
|
|
unsigned int index = GetClass()->Meta.GetMetaInt (ACMETA_DropItems) - 1;
|
|
|
|
|
|
2011-02-19 08:59:43 +00:00
|
|
|
|
if (index < DropItemList.Size())
|
2008-09-21 18:02:38 +00:00
|
|
|
|
{
|
|
|
|
|
return DropItemList[index];
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-14 19:44:14 +00:00
|
|
|
|
fixed_t AActor::GetGravity() const
|
|
|
|
|
{
|
|
|
|
|
if (flags & MF_NOGRAVITY) return 0;
|
|
|
|
|
return fixed_t(level.gravity * Sector->gravity * FIXED2FLOAT(gravity) * 81.92);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// killough 11/98:
|
|
|
|
|
// Whether an object is "sentient" or not. Used for environmental influences.
|
|
|
|
|
// (left precisely the same as MBF even though it doesn't make much sense.)
|
|
|
|
|
bool AActor::IsSentient() const
|
|
|
|
|
{
|
2009-09-15 17:19:30 +00:00
|
|
|
|
return health > 0 && SeeState != NULL;
|
2009-09-14 19:44:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-01-12 00:17:13 +00:00
|
|
|
|
FSharedStringArena AActor::mStringPropertyData;
|
|
|
|
|
|
2009-09-14 21:41:44 +00:00
|
|
|
|
const char *AActor::GetTag(const char *def) const
|
|
|
|
|
{
|
2011-01-12 00:17:13 +00:00
|
|
|
|
if (Tag != NULL)
|
2010-05-25 03:53:13 +00:00
|
|
|
|
{
|
2011-01-12 00:17:13 +00:00
|
|
|
|
const char *tag = Tag->GetChars();
|
2010-05-25 03:53:13 +00:00
|
|
|
|
if (tag[0] == '$')
|
|
|
|
|
{
|
|
|
|
|
return GStrings(tag + 1);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return tag;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (def)
|
|
|
|
|
{
|
|
|
|
|
return def;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return GetClass()->TypeName.GetChars();
|
|
|
|
|
}
|
2009-09-14 21:41:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-01-12 00:17:13 +00:00
|
|
|
|
void AActor::SetTag(const char *def)
|
|
|
|
|
{
|
|
|
|
|
if (def == NULL || *def == 0)
|
|
|
|
|
{
|
|
|
|
|
Tag = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Tag = mStringPropertyData.Alloc(def);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-14 21:41:44 +00:00
|
|
|
|
|
2010-09-19 00:06:45 +00:00
|
|
|
|
void AActor::ClearCounters()
|
|
|
|
|
{
|
2010-12-06 21:57:51 +00:00
|
|
|
|
if (CountsAsKill() && health > 0)
|
2010-09-19 00:06:45 +00:00
|
|
|
|
{
|
|
|
|
|
level.total_monsters--;
|
|
|
|
|
flags &= ~MF_COUNTKILL;
|
|
|
|
|
}
|
|
|
|
|
// Same, for items
|
|
|
|
|
if (flags & MF_COUNTITEM)
|
|
|
|
|
{
|
|
|
|
|
level.total_items--;
|
|
|
|
|
flags &= ~MF_COUNTITEM;
|
|
|
|
|
}
|
|
|
|
|
// And finally for secrets
|
|
|
|
|
if (flags5 & MF5_COUNTSECRET)
|
|
|
|
|
{
|
|
|
|
|
level.total_secrets--;
|
|
|
|
|
flags5 &= ~MF5_COUNTSECRET;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-09-21 18:02:38 +00:00
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// DropItem handling
|
|
|
|
|
//
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
FDropItemPtrArray DropItemList;
|
|
|
|
|
|
|
|
|
|
void FreeDropItemChain(FDropItem *chain)
|
|
|
|
|
{
|
|
|
|
|
while (chain != NULL)
|
|
|
|
|
{
|
|
|
|
|
FDropItem *next = chain->Next;
|
|
|
|
|
delete chain;
|
|
|
|
|
chain = next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-14 00:50:02 +00:00
|
|
|
|
void FDropItemPtrArray::Clear()
|
2008-09-21 18:02:38 +00:00
|
|
|
|
{
|
|
|
|
|
for (unsigned int i = 0; i < Size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
FreeDropItemChain ((*this)[i]);
|
|
|
|
|
}
|
2010-12-14 00:50:02 +00:00
|
|
|
|
TArray<FDropItem *>::Clear();
|
2008-09-21 18:02:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int StoreDropItemChain(FDropItem *chain)
|
|
|
|
|
{
|
|
|
|
|
return DropItemList.Push (chain) + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-12 23:59:18 +00:00
|
|
|
|
void PrintMiscActorInfo(AActor *query)
|
2009-09-14 19:44:14 +00:00
|
|
|
|
{
|
|
|
|
|
if (query)
|
|
|
|
|
{
|
|
|
|
|
int flagi;
|
|
|
|
|
int querystyle = STYLE_Count;
|
|
|
|
|
for (int style = STYLE_None; style < STYLE_Count; ++style)
|
|
|
|
|
{ // Check for a legacy render style that matches.
|
|
|
|
|
if (LegacyRenderStyles[style] == query->RenderStyle)
|
|
|
|
|
{
|
|
|
|
|
querystyle = style;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
static const char * renderstyles[]= {"None", "Normal", "Fuzzy", "SoulTrans",
|
2015-03-28 10:13:47 +00:00
|
|
|
|
"OptFuzzy", "Stencil", "Translucent", "Add", "Shaded", "TranslucentStencil",
|
|
|
|
|
"Shadow", "Subtract", "AddStencil", "AddShaded"};
|
2009-09-14 19:44:14 +00:00
|
|
|
|
|
2015-04-30 10:35:29 +00:00
|
|
|
|
Printf("%s @ %p has the following flags:\n flags: %x", query->GetTag(), query, query->flags.GetValue());
|
2013-01-21 22:30:30 +00:00
|
|
|
|
for (flagi = 0; flagi <= 31; flagi++)
|
2015-10-12 22:30:06 +00:00
|
|
|
|
if (query->flags & ActorFlags::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags));
|
2015-04-30 10:35:29 +00:00
|
|
|
|
Printf("\n flags2: %x", query->flags2.GetValue());
|
2013-01-21 22:30:30 +00:00
|
|
|
|
for (flagi = 0; flagi <= 31; flagi++)
|
2015-10-12 22:30:06 +00:00
|
|
|
|
if (query->flags2 & ActorFlags2::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags2));
|
2015-04-30 10:35:29 +00:00
|
|
|
|
Printf("\n flags3: %x", query->flags3.GetValue());
|
2013-01-21 22:30:30 +00:00
|
|
|
|
for (flagi = 0; flagi <= 31; flagi++)
|
2015-10-12 22:30:06 +00:00
|
|
|
|
if (query->flags3 & ActorFlags3::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags3));
|
2015-04-30 10:35:29 +00:00
|
|
|
|
Printf("\n flags4: %x", query->flags4.GetValue());
|
2013-01-21 22:30:30 +00:00
|
|
|
|
for (flagi = 0; flagi <= 31; flagi++)
|
2015-10-12 22:30:06 +00:00
|
|
|
|
if (query->flags4 & ActorFlags4::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags4));
|
2015-04-30 10:35:29 +00:00
|
|
|
|
Printf("\n flags5: %x", query->flags5.GetValue());
|
2013-01-21 22:30:30 +00:00
|
|
|
|
for (flagi = 0; flagi <= 31; flagi++)
|
2015-10-12 22:30:06 +00:00
|
|
|
|
if (query->flags5 & ActorFlags5::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags5));
|
2015-04-30 10:35:29 +00:00
|
|
|
|
Printf("\n flags6: %x", query->flags6.GetValue());
|
2013-01-21 22:30:30 +00:00
|
|
|
|
for (flagi = 0; flagi <= 31; flagi++)
|
2015-10-12 22:30:06 +00:00
|
|
|
|
if (query->flags6 & ActorFlags6::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags6));
|
2015-04-30 10:35:29 +00:00
|
|
|
|
Printf("\n flags7: %x", query->flags7.GetValue());
|
2013-08-12 18:09:21 +00:00
|
|
|
|
for (flagi = 0; flagi <= 31; flagi++)
|
2015-10-12 22:30:06 +00:00
|
|
|
|
if (query->flags7 & ActorFlags7::FromInt(1<<flagi)) Printf(" %s", FLAG_NAME(1<<flagi, flags7));
|
2013-02-14 03:56:34 +00:00
|
|
|
|
Printf("\nBounce flags: %x\nBounce factors: f:%f, w:%f",
|
2015-04-30 10:35:29 +00:00
|
|
|
|
query->BounceFlags.GetValue(), FIXED2FLOAT(query->bouncefactor),
|
2013-02-14 03:56:34 +00:00
|
|
|
|
FIXED2FLOAT(query->wallbouncefactor));
|
2009-09-17 20:54:07 +00:00
|
|
|
|
/*for (flagi = 0; flagi < 31; flagi++)
|
|
|
|
|
if (query->BounceFlags & 1<<flagi) Printf(" %s", flagnamesb[flagi]);*/
|
2012-09-09 02:36:53 +00:00
|
|
|
|
Printf("\nRender style = %i:%s, alpha %f\nRender flags: %x",
|
2009-09-14 19:44:14 +00:00
|
|
|
|
querystyle, (querystyle < STYLE_Count ? renderstyles[querystyle] : "Unknown"),
|
2015-04-30 10:35:29 +00:00
|
|
|
|
FIXED2FLOAT(query->alpha), query->renderflags.GetValue());
|
2009-09-17 20:54:07 +00:00
|
|
|
|
/*for (flagi = 0; flagi < 31; flagi++)
|
|
|
|
|
if (query->renderflags & 1<<flagi) Printf(" %s", flagnamesr[flagi]);*/
|
2012-09-09 02:36:53 +00:00
|
|
|
|
Printf("\nSpecial+args: %s(%i, %i, %i, %i, %i)\nspecial1: %i, special2: %i.",
|
2009-09-17 20:54:07 +00:00
|
|
|
|
(query->special ? LineSpecialsInfo[query->special]->name : "None"),
|
|
|
|
|
query->args[0], query->args[1], query->args[2], query->args[3],
|
|
|
|
|
query->args[4], query->special1, query->special2);
|
2012-09-09 02:36:53 +00:00
|
|
|
|
Printf("\nTID: %d", query->tid);
|
|
|
|
|
Printf("\nCoord= x: %f, y: %f, z:%f, floor:%f, ceiling:%f.",
|
2009-09-14 19:44:14 +00:00
|
|
|
|
FIXED2FLOAT(query->x), FIXED2FLOAT(query->y), FIXED2FLOAT(query->z),
|
|
|
|
|
FIXED2FLOAT(query->floorz), FIXED2FLOAT(query->ceilingz));
|
2012-09-09 02:36:53 +00:00
|
|
|
|
Printf("\nSpeed= %f, velocity= x:%f, y:%f, z:%f, combined:%f.\n",
|
2009-09-14 19:44:14 +00:00
|
|
|
|
FIXED2FLOAT(query->Speed), FIXED2FLOAT(query->velx), FIXED2FLOAT(query->vely), FIXED2FLOAT(query->velz),
|
|
|
|
|
sqrt(pow(FIXED2FLOAT(query->velx), 2) + pow(FIXED2FLOAT(query->vely), 2) + pow(FIXED2FLOAT(query->velz), 2)));
|
|
|
|
|
}
|
|
|
|
|
}
|