mirror of
https://github.com/id-Software/quake2-rerelease-dll.git
synced 2026-03-20 17:09:35 +01:00
Initial commit
This commit is contained in:
263
rerelease/bg_local.h
Normal file
263
rerelease/bg_local.h
Normal file
@@ -0,0 +1,263 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
// g_local.h -- local definitions for game module
|
||||
#pragma once
|
||||
|
||||
#include "q_std.h"
|
||||
|
||||
// define GAME_INCLUDE so that game.h does not define the
|
||||
// short, server-visible gclient_t and edict_t structures,
|
||||
// because we define the full size ones in this file
|
||||
#define GAME_INCLUDE
|
||||
#include "game.h"
|
||||
|
||||
//
|
||||
// p_move.c
|
||||
//
|
||||
struct pm_config_t
|
||||
{
|
||||
int32_t airaccel = 0;
|
||||
bool n64_physics = false;
|
||||
};
|
||||
|
||||
extern pm_config_t pm_config;
|
||||
|
||||
void Pmove(pmove_t *pmove);
|
||||
using pm_trace_func_t = trace_t(const vec3_t &start, const vec3_t &mins, const vec3_t &maxs, const vec3_t &end);
|
||||
using pm_trace_t = std::function<pm_trace_func_t>;
|
||||
void PM_StepSlideMove_Generic(vec3_t &origin, vec3_t &velocity, float frametime, const vec3_t &mins, const vec3_t &maxs, touch_list_t &touch, bool has_time, pm_trace_t trace);
|
||||
|
||||
enum class stuck_result_t
|
||||
{
|
||||
GOOD_POSITION,
|
||||
FIXED,
|
||||
NO_GOOD_POSITION
|
||||
};
|
||||
|
||||
using stuck_object_trace_fn_t = trace_t(const vec3_t &, const vec3_t &, const vec3_t &, const vec3_t &);
|
||||
|
||||
stuck_result_t G_FixStuckObject_Generic(vec3_t &origin, const vec3_t &own_mins, const vec3_t &own_maxs, std::function<stuck_object_trace_fn_t> trace);
|
||||
|
||||
// state for coop respawning; used to select which
|
||||
// message to print for the player this is set on.
|
||||
enum coop_respawn_t
|
||||
{
|
||||
COOP_RESPAWN_NONE, // no messagee
|
||||
COOP_RESPAWN_IN_COMBAT, // player is in combat
|
||||
COOP_RESPAWN_BAD_AREA, // player not in a good spot
|
||||
COOP_RESPAWN_BLOCKED, // spawning was blocked by something
|
||||
COOP_RESPAWN_WAITING, // for players that are waiting to respawn
|
||||
COOP_RESPAWN_NO_LIVES, // out of lives, so need to wait until level switch
|
||||
COOP_RESPAWN_TOTAL
|
||||
};
|
||||
|
||||
// reserved general CS ranges
|
||||
enum
|
||||
{
|
||||
CONFIG_CTF_MATCH = CS_GENERAL,
|
||||
CONFIG_CTF_TEAMINFO,
|
||||
CONFIG_CTF_PLAYER_NAME,
|
||||
CONFIG_CTF_PLAYER_NAME_END = CONFIG_CTF_PLAYER_NAME + MAX_CLIENTS,
|
||||
|
||||
// nb: offset by 1 since NONE is zero
|
||||
CONFIG_COOP_RESPAWN_STRING,
|
||||
CONFIG_COOP_RESPAWN_STRING_END = CONFIG_COOP_RESPAWN_STRING + (COOP_RESPAWN_TOTAL - 1),
|
||||
|
||||
// [Paril-KEX] if 1, n64 player physics apply
|
||||
CONFIG_N64_PHYSICS,
|
||||
CONFIG_HEALTH_BAR_NAME, // active health bar name
|
||||
|
||||
CONFIG_STORY,
|
||||
|
||||
CONFIG_LAST
|
||||
};
|
||||
|
||||
static_assert(CONFIG_LAST <= CS_GENERAL + MAX_GENERAL);
|
||||
|
||||
// ammo IDs
|
||||
enum ammo_t : uint8_t
|
||||
{
|
||||
AMMO_BULLETS,
|
||||
AMMO_SHELLS,
|
||||
AMMO_ROCKETS,
|
||||
AMMO_GRENADES,
|
||||
AMMO_CELLS,
|
||||
AMMO_SLUGS,
|
||||
// RAFAEL
|
||||
AMMO_MAGSLUG,
|
||||
AMMO_TRAP,
|
||||
// RAFAEL
|
||||
// ROGUE
|
||||
AMMO_FLECHETTES,
|
||||
AMMO_TESLA,
|
||||
AMMO_DISRUPTOR,
|
||||
AMMO_PROX,
|
||||
// ROGUE
|
||||
AMMO_MAX
|
||||
};
|
||||
|
||||
// powerup IDs
|
||||
enum powerup_t : uint8_t
|
||||
{
|
||||
POWERUP_SCREEN,
|
||||
POWERUP_SHIELD,
|
||||
|
||||
POWERUP_AM_BOMB,
|
||||
|
||||
POWERUP_QUAD,
|
||||
POWERUP_QUADFIRE,
|
||||
POWERUP_INVULNERABILITY,
|
||||
POWERUP_INVISIBILITY,
|
||||
POWERUP_SILENCER,
|
||||
POWERUP_REBREATHER,
|
||||
POWERUP_ENVIROSUIT,
|
||||
POWERUP_ADRENALINE,
|
||||
POWERUP_IR_GOGGLES,
|
||||
POWERUP_DOUBLE,
|
||||
POWERUP_SPHERE_VENGEANCE,
|
||||
POWERUP_SPHERE_HUNTER,
|
||||
POWERUP_SPHERE_DEFENDER,
|
||||
POWERUP_DOPPELGANGER,
|
||||
|
||||
POWERUP_FLASHLIGHT,
|
||||
POWERUP_COMPASS,
|
||||
POWERUP_TECH1,
|
||||
POWERUP_TECH2,
|
||||
POWERUP_TECH3,
|
||||
POWERUP_TECH4,
|
||||
POWERUP_MAX
|
||||
};
|
||||
|
||||
// ammo stats compressed in 9 bits per entry
|
||||
// since the range is 0-300
|
||||
constexpr size_t BITS_PER_AMMO = 9;
|
||||
|
||||
template<typename TI>
|
||||
constexpr size_t num_of_type_for_bits(size_t num_bits)
|
||||
{
|
||||
return (num_bits + (sizeof(TI) * 8) - 1) / ((sizeof(TI) * 8) + 1);
|
||||
}
|
||||
|
||||
template<size_t bits_per_value>
|
||||
constexpr void set_compressed_integer(uint16_t *start, uint8_t id, uint16_t count)
|
||||
{
|
||||
uint16_t bit_offset = bits_per_value * id;
|
||||
uint16_t byte = bit_offset / 8;
|
||||
uint16_t bit_shift = bit_offset % 8;
|
||||
uint16_t mask = (bit_v<bits_per_value> - 1) << bit_shift;
|
||||
uint16_t *base = (uint16_t *) ((uint8_t *) start + byte);
|
||||
*base = (*base & ~mask) | ((count << bit_shift) & mask);
|
||||
}
|
||||
|
||||
template<size_t bits_per_value>
|
||||
constexpr uint16_t get_compressed_integer(uint16_t *start, uint8_t id)
|
||||
{
|
||||
uint16_t bit_offset = bits_per_value * id;
|
||||
uint16_t byte = bit_offset / 8;
|
||||
uint16_t bit_shift = bit_offset % 8;
|
||||
uint16_t mask = (bit_v<bits_per_value> - 1) << bit_shift;
|
||||
uint16_t *base = (uint16_t *) ((uint8_t *) start + byte);
|
||||
return (*base & mask) >> bit_shift;
|
||||
}
|
||||
|
||||
constexpr size_t NUM_BITS_FOR_AMMO = 9;
|
||||
constexpr size_t NUM_AMMO_STATS = num_of_type_for_bits<uint16_t>(NUM_BITS_FOR_AMMO * AMMO_MAX);
|
||||
// if this value is set on an STAT_AMMO_INFO_xxx, don't render ammo
|
||||
constexpr uint16_t AMMO_VALUE_INFINITE = bit_v<NUM_BITS_FOR_AMMO> - 1;
|
||||
|
||||
constexpr void G_SetAmmoStat(uint16_t *start, uint8_t ammo_id, uint16_t count)
|
||||
{
|
||||
set_compressed_integer<NUM_BITS_FOR_AMMO>(start, ammo_id, count);
|
||||
}
|
||||
|
||||
constexpr uint16_t G_GetAmmoStat(uint16_t *start, uint8_t ammo_id)
|
||||
{
|
||||
return get_compressed_integer<NUM_BITS_FOR_AMMO>(start, ammo_id);
|
||||
}
|
||||
|
||||
// powerup stats compressed in 2 bits per entry;
|
||||
// 3 is the max you'll ever hold, and for some
|
||||
// (flashlight) it's to indicate on/off state
|
||||
constexpr size_t NUM_BITS_PER_POWERUP = 2;
|
||||
constexpr size_t NUM_POWERUP_STATS = num_of_type_for_bits<uint16_t>(NUM_BITS_PER_POWERUP * POWERUP_MAX);
|
||||
|
||||
constexpr void G_SetPowerupStat(uint16_t *start, uint8_t powerup_id, uint16_t count)
|
||||
{
|
||||
set_compressed_integer<NUM_BITS_PER_POWERUP>(start, powerup_id, count);
|
||||
}
|
||||
|
||||
constexpr uint16_t G_GetPowerupStat(uint16_t *start, uint8_t powerup_id)
|
||||
{
|
||||
return get_compressed_integer<NUM_BITS_PER_POWERUP>(start, powerup_id);
|
||||
}
|
||||
|
||||
// player_state->stats[] indexes
|
||||
enum player_stat_t
|
||||
{
|
||||
STAT_HEALTH_ICON = 0,
|
||||
STAT_HEALTH = 1,
|
||||
STAT_AMMO_ICON = 2,
|
||||
STAT_AMMO = 3,
|
||||
STAT_ARMOR_ICON = 4,
|
||||
STAT_ARMOR = 5,
|
||||
STAT_SELECTED_ICON = 6,
|
||||
STAT_PICKUP_ICON = 7,
|
||||
STAT_PICKUP_STRING = 8,
|
||||
STAT_TIMER_ICON = 9,
|
||||
STAT_TIMER = 10,
|
||||
STAT_HELPICON = 11,
|
||||
STAT_SELECTED_ITEM = 12,
|
||||
STAT_LAYOUTS = 13,
|
||||
STAT_FRAGS = 14,
|
||||
STAT_FLASHES = 15, // cleared each frame, 1 = health, 2 = armor
|
||||
STAT_CHASE = 16,
|
||||
STAT_SPECTATOR = 17,
|
||||
|
||||
STAT_CTF_TEAM1_PIC = 18,
|
||||
STAT_CTF_TEAM1_CAPS = 19,
|
||||
STAT_CTF_TEAM2_PIC = 20,
|
||||
STAT_CTF_TEAM2_CAPS = 21,
|
||||
STAT_CTF_FLAG_PIC = 22,
|
||||
STAT_CTF_JOINED_TEAM1_PIC = 23,
|
||||
STAT_CTF_JOINED_TEAM2_PIC = 24,
|
||||
STAT_CTF_TEAM1_HEADER = 25,
|
||||
STAT_CTF_TEAM2_HEADER = 26,
|
||||
STAT_CTF_TECH = 27,
|
||||
STAT_CTF_ID_VIEW = 28,
|
||||
STAT_CTF_MATCH = 29,
|
||||
STAT_CTF_ID_VIEW_COLOR = 30,
|
||||
STAT_CTF_TEAMINFO = 31,
|
||||
|
||||
// [Kex] More stats for weapon wheel
|
||||
STAT_WEAPONS_OWNED_1 = 32,
|
||||
STAT_WEAPONS_OWNED_2 = 33,
|
||||
STAT_AMMO_INFO_START = 34,
|
||||
STAT_AMMO_INFO_END = STAT_AMMO_INFO_START + NUM_AMMO_STATS - 1,
|
||||
STAT_POWERUP_INFO_START,
|
||||
STAT_POWERUP_INFO_END = STAT_POWERUP_INFO_START + NUM_POWERUP_STATS - 1,
|
||||
|
||||
// [Paril-KEX] Key display
|
||||
STAT_KEY_A,
|
||||
STAT_KEY_B,
|
||||
STAT_KEY_C,
|
||||
|
||||
// [Paril-KEX] currently active wheel weapon (or one we're switching to)
|
||||
STAT_ACTIVE_WHEEL_WEAPON,
|
||||
// [Paril-KEX] top of screen coop respawn state
|
||||
STAT_COOP_RESPAWN,
|
||||
// [Paril-KEX] respawns remaining
|
||||
STAT_LIVES,
|
||||
// [Paril-KEX] hit marker; # of damage we successfully landed
|
||||
STAT_HIT_MARKER,
|
||||
// [Paril-KEX]
|
||||
STAT_SELECTED_ITEM_NAME,
|
||||
// [Paril-KEX]
|
||||
STAT_HEALTH_BARS, // two health bar values; 7 bits for value, 1 bit for active
|
||||
// if active,
|
||||
|
||||
// don't use; just for verification
|
||||
STAT_LAST
|
||||
};
|
||||
|
||||
static_assert(STAT_LAST <= MAX_STATS + 1, "stats list overflow");
|
||||
178
rerelease/bots/bot_debug.cpp
Normal file
178
rerelease/bots/bot_debug.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#include "../g_local.h"
|
||||
#include "bot_utils.h"
|
||||
#include "bot_debug.h"
|
||||
|
||||
static const edict_t * escortBot = nullptr;
|
||||
static const edict_t * escortActor = nullptr;
|
||||
|
||||
static const edict_t * moveToPointBot = nullptr;
|
||||
static vec3_t moveToPointPos = vec3_origin;
|
||||
|
||||
// how close the bot will try to get to the move to point goal
|
||||
constexpr float moveToPointTolerance = 16.0f;
|
||||
|
||||
/*
|
||||
================
|
||||
ShowMonsterPathToPlayer
|
||||
================
|
||||
*/
|
||||
void ShowMonsterPathToPlayer( const edict_t * player ) {
|
||||
const edict_t * monster = FindFirstMonster();
|
||||
if ( monster == nullptr ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float moveDist = 8.0f;
|
||||
|
||||
std::array<vec3_t, 512> pathPoints;
|
||||
|
||||
PathRequest request;
|
||||
request.start = monster->s.origin;
|
||||
request.goal = player->s.origin;
|
||||
request.moveDist = moveDist;
|
||||
request.pathFlags = PathFlags::All;
|
||||
request.debugging.drawTime = 0.10f;
|
||||
request.nodeSearch.minHeight = 64.0f;
|
||||
request.nodeSearch.maxHeight = 64.0f;
|
||||
request.nodeSearch.radius = 512.0f;
|
||||
request.pathPoints.array = &pathPoints.front();
|
||||
request.pathPoints.count = pathPoints.size();
|
||||
|
||||
PathInfo info;
|
||||
if ( gi.GetPathToGoal( request, info ) ) {
|
||||
// Do movement stuff....
|
||||
for ( int i = 0; i < info.numPathPoints; ++i ) {
|
||||
const gvec3_t & point = pathPoints[ i ];
|
||||
gi.Draw_Point( point, 8.0f, rgba_yellow, 0.10f, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
UpdateFollowActorDebug
|
||||
|
||||
Set cvar "bot_debug_follow_actor" to 1
|
||||
and then run your cursor over any player/monster to pick
|
||||
that "actor" for the bot to follow.
|
||||
|
||||
When successful, you will see the player/monster highlighted
|
||||
with a yellow box, and the bot will follow them around the map until
|
||||
the actor they're following dies, or the bot is told to do something
|
||||
else by you.
|
||||
|
||||
Check the console for debugging feedback...
|
||||
================
|
||||
*/
|
||||
void UpdateFollowActorDebug( const edict_t * localPlayer ) {
|
||||
if ( bot_debug_follow_actor->integer ) {
|
||||
if ( bot_debug_follow_actor->integer == 1 ) {
|
||||
escortBot = FindFirstBot();
|
||||
escortActor = FindActorUnderCrosshair( localPlayer );
|
||||
|
||||
if ( gi.Bot_FollowActor( escortBot, escortActor ) != GoalReturnCode::Error ) {
|
||||
gi.cvar_set( "bot_debug_follow_actor", "2" );
|
||||
gi.Com_Print( "Follow_Actor: Bot Found Actor To Follow!\n" );
|
||||
} else {
|
||||
gi.Com_Print( "Follow_Actor: Hover Over Monster/Player To Follow...\n" );
|
||||
}
|
||||
} else {
|
||||
if ( gi.Bot_FollowActor( escortBot, escortActor ) != GoalReturnCode::Error ) {
|
||||
gi.Draw_Bounds( escortActor->absmin, escortActor->absmax, rgba_yellow, gi.frame_time_s, false );
|
||||
gi.Draw_Bounds( escortBot->absmin, escortBot->absmax, rgba_cyan, gi.frame_time_s, false );
|
||||
} else {
|
||||
gi.Com_Print( "Follow_Actor: Bot Or Actor Removed...\n" );
|
||||
gi.cvar_set( "bot_debug_follow_actor", "0" );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
escortBot = nullptr;
|
||||
escortActor = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
UpdateMoveToPointDebug
|
||||
|
||||
Set cvar "bot_debug_move_to_point" to 1,
|
||||
look anywhere in world you'd like the bot to move to,
|
||||
and then fire your weapon. The point at the end of your crosshair
|
||||
will be the point in the world the bot will move toward.
|
||||
|
||||
When successful, a point marker will be drawn where the bot will move
|
||||
toward, and the bot itself will have a box drawn around it.
|
||||
|
||||
Once bot reaches the point, it will clear the goal and go about it's
|
||||
business until you give it something else to do.
|
||||
|
||||
Check the console for debugging feedback...
|
||||
================
|
||||
*/
|
||||
void UpdateMoveToPointDebug( const edict_t * localPlayer ) {
|
||||
if ( bot_debug_move_to_point->integer ) {
|
||||
if ( bot_debug_move_to_point->integer == 1 ) {
|
||||
if ( localPlayer->client->buttons & BUTTON_ATTACK ) {
|
||||
vec3_t localPlayerForward, right, up;
|
||||
AngleVectors( localPlayer->client->v_angle, localPlayerForward, right, up );
|
||||
|
||||
const vec3_t localPlayerViewPos = ( localPlayer->s.origin + vec3_t{ 0.0f, 0.0f, (float)localPlayer->viewheight } );
|
||||
const vec3_t end = ( localPlayerViewPos + ( localPlayerForward * 8192.0f ) );
|
||||
const contents_t mask = ( MASK_PROJECTILE & ~CONTENTS_DEADMONSTER );
|
||||
|
||||
trace_t tr = gi.traceline( localPlayerViewPos, end, localPlayer, mask );
|
||||
moveToPointPos = tr.endpos;
|
||||
|
||||
moveToPointBot = FindFirstBot();
|
||||
if ( gi.Bot_MoveToPoint( moveToPointBot, moveToPointPos, moveToPointTolerance ) != GoalReturnCode::Error ) {
|
||||
gi.cvar_set( "bot_debug_move_to_point", "2" );
|
||||
gi.Com_Print( "Move_To_Point: Bot Has Position To Move Toward!\n" );
|
||||
}
|
||||
} else {
|
||||
gi.Com_Print( "Move_To_Point: Fire Weapon To Select Move Point...\n" );
|
||||
}
|
||||
} else {
|
||||
const GoalReturnCode result = gi.Bot_MoveToPoint( moveToPointBot, moveToPointPos, moveToPointTolerance );
|
||||
if ( result == GoalReturnCode::Error ) {
|
||||
gi.cvar_set( "bot_debug_move_to_point", "0" );
|
||||
gi.Com_Print( "Move_To_Point: Bot Can't Reach Goal Position!\n" );
|
||||
} else if ( result == GoalReturnCode::Finished ) {
|
||||
gi.cvar_set( "bot_debug_move_to_point", "0" );
|
||||
gi.Com_Print( "Move_To_Point: Bot Reached Goal Position!\n" );
|
||||
} else {
|
||||
gi.Draw_Point( moveToPointPos, 8.0f, rgba_yellow, gi.frame_time_s, false );
|
||||
gi.Draw_Bounds( moveToPointBot->absmin, moveToPointBot->absmax, rgba_cyan, gi.frame_time_s, false );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
moveToPointBot = nullptr;
|
||||
moveToPointPos = vec3_origin;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Bot_UpdateDebug
|
||||
================
|
||||
*/
|
||||
void Bot_UpdateDebug() {
|
||||
if ( !sv_cheats->integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const edict_t * localPlayer = FindLocalPlayer();
|
||||
if ( localPlayer == nullptr ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( g_debug_monster_paths->integer == 2 ) {
|
||||
ShowMonsterPathToPlayer( localPlayer );
|
||||
}
|
||||
|
||||
UpdateFollowActorDebug( localPlayer );
|
||||
|
||||
UpdateMoveToPointDebug( localPlayer );
|
||||
}
|
||||
6
rerelease/bots/bot_debug.h
Normal file
6
rerelease/bots/bot_debug.h
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#pragma once
|
||||
|
||||
void Bot_UpdateDebug();
|
||||
151
rerelease/bots/bot_exports.cpp
Normal file
151
rerelease/bots/bot_exports.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#include "../g_local.h"
|
||||
#include "bot_exports.h"
|
||||
|
||||
/*
|
||||
================
|
||||
Bot_SetWeapon
|
||||
================
|
||||
*/
|
||||
void Bot_SetWeapon( edict_t * bot, const int weaponIndex, const bool instantSwitch ) {
|
||||
if ( weaponIndex <= IT_NULL || weaponIndex > IT_TOTAL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ( bot->svflags & SVF_BOT ) == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
gclient_t * client = bot->client;
|
||||
if ( client == nullptr || !client->pers.inventory[ weaponIndex ] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const item_id_t weaponItemID = static_cast<item_id_t>( weaponIndex );
|
||||
|
||||
const gitem_t * currentGun = client->pers.weapon;
|
||||
if ( currentGun != nullptr ) {
|
||||
if ( currentGun->id == weaponItemID ) {
|
||||
return;
|
||||
} // already have the gun in hand.
|
||||
}
|
||||
|
||||
const gitem_t * pendingGun = client->newweapon;
|
||||
if ( pendingGun != nullptr ) {
|
||||
if ( pendingGun->id == weaponItemID ) {
|
||||
return;
|
||||
} // already in the process of switching to that gun, just be patient!
|
||||
}
|
||||
|
||||
gitem_t * item = &itemlist[ weaponIndex ];
|
||||
if ( ( item->flags & IF_WEAPON ) == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( item->use == nullptr ) {
|
||||
return;
|
||||
}
|
||||
|
||||
bot->client->no_weapon_chains = true;
|
||||
item->use( bot, item );
|
||||
|
||||
if ( instantSwitch ) {
|
||||
// FIXME: ugly, maybe store in client later
|
||||
const int temp_instant_weapon = g_instant_weapon_switch->integer;
|
||||
g_instant_weapon_switch->integer = 1;
|
||||
ChangeWeapon( bot );
|
||||
g_instant_weapon_switch->integer = temp_instant_weapon;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Bot_TriggerEdict
|
||||
================
|
||||
*/
|
||||
void Bot_TriggerEdict( edict_t * bot, edict_t * edict ) {
|
||||
if ( !bot->inuse || !edict->inuse ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ( bot->svflags & SVF_BOT ) == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( edict->use ) {
|
||||
edict->use( edict, bot, bot );
|
||||
}
|
||||
|
||||
trace_t unUsed;
|
||||
if ( edict->touch ) {
|
||||
edict->touch( edict, bot, unUsed, true );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Bot_UseItem
|
||||
================
|
||||
*/
|
||||
void Bot_UseItem( edict_t * bot, const int32_t itemID ) {
|
||||
if ( !bot->inuse ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ( bot->svflags & SVF_BOT ) == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const item_id_t desiredItemID = item_id_t( itemID );
|
||||
bot->client->pers.selected_item = desiredItemID;
|
||||
|
||||
ValidateSelectedItem( bot );
|
||||
|
||||
if ( bot->client->pers.selected_item == IT_NULL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( bot->client->pers.selected_item != desiredItemID ) {
|
||||
return;
|
||||
} // the itemID changed on us - don't use it!
|
||||
|
||||
gitem_t * item = &itemlist[ bot->client->pers.selected_item ];
|
||||
bot->client->pers.selected_item = IT_NULL;
|
||||
|
||||
if ( item->use == nullptr ) {
|
||||
return;
|
||||
}
|
||||
|
||||
bot->client->no_weapon_chains = true;
|
||||
item->use( bot, item );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Bot_GetItemID
|
||||
================
|
||||
*/
|
||||
int32_t Bot_GetItemID( const char * classname ) {
|
||||
if ( classname == nullptr || classname[ 0 ] == '\0' ) {
|
||||
return Item_Invalid;
|
||||
}
|
||||
|
||||
if ( Q_strcasecmp( classname, "none" ) == 0 ) {
|
||||
return Item_Null;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < IT_TOTAL; ++i ) {
|
||||
const gitem_t * item = itemlist + i;
|
||||
if ( item->classname == nullptr || item->classname[ 0 ] == '\0' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( Q_strcasecmp( item->classname, classname ) == 0 ) {
|
||||
return item->id;
|
||||
}
|
||||
}
|
||||
|
||||
return Item_Invalid;
|
||||
}
|
||||
9
rerelease/bots/bot_exports.h
Normal file
9
rerelease/bots/bot_exports.h
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#pragma once
|
||||
|
||||
void Bot_SetWeapon( edict_t * bot, const int weaponIndex, const bool instantSwitch );
|
||||
void Bot_TriggerEdict( edict_t * bot, edict_t * edict );
|
||||
int32_t Bot_GetItemID( const char * classname );
|
||||
void Bot_UseItem( edict_t * bot, const int32_t itemID );
|
||||
9
rerelease/bots/bot_includes.h
Normal file
9
rerelease/bots/bot_includes.h
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bot_utils.h"
|
||||
#include "bot_think.h"
|
||||
#include "bot_debug.h"
|
||||
#include "bot_exports.h"
|
||||
23
rerelease/bots/bot_think.cpp
Normal file
23
rerelease/bots/bot_think.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#include "../g_local.h"
|
||||
#include "bot_think.h"
|
||||
|
||||
/*
|
||||
================
|
||||
Bot_BeginFrame
|
||||
================
|
||||
*/
|
||||
void Bot_BeginFrame( edict_t * bot ) {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Bot_EndFrame
|
||||
================
|
||||
*/
|
||||
void Bot_EndFrame( edict_t * bot ) {
|
||||
|
||||
}
|
||||
7
rerelease/bots/bot_think.h
Normal file
7
rerelease/bots/bot_think.h
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#pragma once
|
||||
|
||||
void Bot_BeginFrame( edict_t * bot );
|
||||
void Bot_EndFrame( edict_t * bot );
|
||||
532
rerelease/bots/bot_utils.cpp
Normal file
532
rerelease/bots/bot_utils.cpp
Normal file
@@ -0,0 +1,532 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#include "../g_local.h"
|
||||
#include "../m_player.h"
|
||||
#include "bot_utils.h"
|
||||
|
||||
constexpr int Team_Coop_Monster = 0;
|
||||
|
||||
/*
|
||||
================
|
||||
Player_UpdateState
|
||||
================
|
||||
*/
|
||||
void Player_UpdateState( edict_t * player ) {
|
||||
const client_persistant_t & persistant = player->client->pers;
|
||||
|
||||
player->sv.ent_flags = SVFL_NONE;
|
||||
if ( player->groundentity != nullptr || ( player->flags & FL_PARTIALGROUND ) != 0 ) {
|
||||
player->sv.ent_flags |= SVFL_ONGROUND;
|
||||
} else {
|
||||
if ( player->client->ps.pmove.pm_flags & PMF_JUMP_HELD ) {
|
||||
player->sv.ent_flags |= SVFL_IS_JUMPING;
|
||||
}
|
||||
}
|
||||
|
||||
if ( player->client->ps.pmove.pm_flags & PMF_ON_LADDER ) {
|
||||
player->sv.ent_flags |= SVFL_ON_LADDER;
|
||||
}
|
||||
|
||||
if ( ( player->client->ps.pmove.pm_flags & PMF_DUCKED ) != 0 ) {
|
||||
player->sv.ent_flags |= SVFL_IS_CROUCHING;
|
||||
}
|
||||
|
||||
if ( player->client->quad_time > level.time ) {
|
||||
player->sv.ent_flags |= SVFL_HAS_DMG_BOOST;
|
||||
} else if ( player->client->quadfire_time > level.time ) {
|
||||
player->sv.ent_flags |= SVFL_HAS_DMG_BOOST;
|
||||
} else if ( player->client->double_time > level.time ) {
|
||||
player->sv.ent_flags |= SVFL_HAS_DMG_BOOST;
|
||||
}
|
||||
|
||||
if ( player->client->invincible_time > level.time ) {
|
||||
player->sv.ent_flags |= SVFL_HAS_PROTECTION;
|
||||
}
|
||||
|
||||
if ( player->client->invisible_time > level.time ) {
|
||||
player->sv.ent_flags |= SVFL_HAS_INVISIBILITY;
|
||||
}
|
||||
|
||||
if ( ( player->client->ps.pmove.pm_flags & PMF_TIME_TELEPORT ) != 0 ) {
|
||||
player->sv.ent_flags |= SVFL_HAS_TELEPORTED;
|
||||
}
|
||||
|
||||
if ( player->takedamage ) {
|
||||
player->sv.ent_flags |= SVFL_TAKES_DAMAGE;
|
||||
}
|
||||
|
||||
if ( player->solid == SOLID_NOT ) {
|
||||
player->sv.ent_flags |= SVFL_IS_HIDDEN;
|
||||
}
|
||||
|
||||
if ( ( player->flags & FL_INWATER ) != 0 ) {
|
||||
if ( player->waterlevel >= WATER_WAIST ) {
|
||||
player->sv.ent_flags |= SVFL_IN_WATER;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ( player->flags & FL_NOTARGET ) != 0 ) {
|
||||
player->sv.ent_flags |= SVFL_NO_TARGET;
|
||||
}
|
||||
|
||||
if ( ( player->flags & FL_GODMODE ) != 0 ) {
|
||||
player->sv.ent_flags |= SVFL_GOD_MODE;
|
||||
}
|
||||
|
||||
if ( player->movetype == MOVETYPE_NOCLIP ) {
|
||||
player->sv.ent_flags |= SVFL_IS_NOCLIP;
|
||||
}
|
||||
|
||||
if ( player->client->anim_end == FRAME_flip12 ) {
|
||||
player->sv.ent_flags |= SVFL_IS_FLIPPING_OFF;
|
||||
}
|
||||
|
||||
if ( player->client->anim_end == FRAME_salute11 ) {
|
||||
player->sv.ent_flags |= SVFL_IS_SALUTING;
|
||||
}
|
||||
|
||||
if ( player->client->anim_end == FRAME_taunt17 ) {
|
||||
player->sv.ent_flags |= SVFL_IS_TAUNTING;
|
||||
}
|
||||
|
||||
if ( player->client->anim_end == FRAME_wave11 ) {
|
||||
player->sv.ent_flags |= SVFL_IS_WAVING;
|
||||
}
|
||||
|
||||
if ( player->client->anim_end == FRAME_point12 ) {
|
||||
player->sv.ent_flags |= SVFL_IS_POINTING;
|
||||
}
|
||||
|
||||
if ( ( player->client->ps.pmove.pm_flags & PMF_DUCKED ) == 0 && player->client->anim_priority <= ANIM_WAVE ) {
|
||||
player->sv.ent_flags |= SVFL_CAN_GESTURE;
|
||||
}
|
||||
|
||||
if ( player->lastMOD.id == MOD_TELEFRAG || player->lastMOD.id == MOD_TELEFRAG_SPAWN ) {
|
||||
player->sv.ent_flags |= SVFL_WAS_TELEFRAGGED;
|
||||
}
|
||||
|
||||
if ( player->client->resp.spectator ) {
|
||||
player->sv.ent_flags |= SVFL_IS_SPECTATOR;
|
||||
}
|
||||
|
||||
player_skinnum_t pl_skinnum;
|
||||
pl_skinnum.skinnum = player->s.skinnum;
|
||||
player->sv.team = pl_skinnum.team_index;
|
||||
|
||||
player->sv.buttons = player->client->buttons;
|
||||
|
||||
const item_id_t armorType = ArmorIndex( player );
|
||||
player->sv.armor_type = armorType;
|
||||
player->sv.armor_value = persistant.inventory[ armorType ];
|
||||
|
||||
player->sv.health = ( player->deadflag != true ) ? player->health : -1;
|
||||
player->sv.weapon = ( persistant.weapon != nullptr ) ? persistant.weapon->id : IT_NULL;
|
||||
|
||||
player->sv.last_attackertime = static_cast<int32_t>( player->client->last_attacker_time.milliseconds() );
|
||||
player->sv.respawntime = static_cast<int32_t>( player->client->respawn_time.milliseconds() );
|
||||
player->sv.waterlevel = player->waterlevel;
|
||||
player->sv.viewheight = player->viewheight;
|
||||
|
||||
player->sv.viewangles = player->client->v_angle;
|
||||
player->sv.viewforward = player->client->v_forward;
|
||||
player->sv.velocity = player->velocity;
|
||||
|
||||
player->sv.ground_entity = player->groundentity;
|
||||
player->sv.enemy = player->enemy;
|
||||
|
||||
static_assert( sizeof( persistant.inventory ) <= sizeof( player->sv.inventory ) );
|
||||
memcpy( &player->sv.inventory, &persistant.inventory, sizeof( persistant.inventory ) );
|
||||
|
||||
if ( !player->sv.init ) {
|
||||
player->sv.init = true;
|
||||
player->sv.classname = player->classname;
|
||||
player->sv.targetname = player->targetname;
|
||||
player->sv.lobby_usernum = P_GetLobbyUserNum( player );
|
||||
player->sv.starting_health = player->health;
|
||||
player->sv.max_health = player->max_health;
|
||||
|
||||
// NOTE: entries are assumed to be ranked with the first armor assumed
|
||||
// NOTE: to be the "best", and last the "worst". You don't need to add
|
||||
// NOTE: entries for things like armor shards, only actual armors.
|
||||
// NOTE: Check "Max_Armor_Types" to raise/lower the armor count.
|
||||
armorInfo_t * armorInfo = player->sv.armor_info;
|
||||
armorInfo[ 0 ].item_id = IT_ARMOR_BODY;
|
||||
armorInfo[ 0 ].max_count = bodyarmor_info.max_count;
|
||||
armorInfo[ 1 ].item_id = IT_ARMOR_COMBAT;
|
||||
armorInfo[ 1 ].max_count = combatarmor_info.max_count;
|
||||
armorInfo[ 2 ].item_id = IT_ARMOR_JACKET;
|
||||
armorInfo[ 2 ].max_count = jacketarmor_info.max_count;
|
||||
|
||||
gi.Info_ValueForKey( player->client->pers.userinfo, "name", player->sv.netname, sizeof( player->sv.netname ) );
|
||||
|
||||
gi.Bot_RegisterEdict( player );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Monster_UpdateState
|
||||
================
|
||||
*/
|
||||
void Monster_UpdateState( edict_t * monster ) {
|
||||
monster->sv.ent_flags = SVFL_NONE;
|
||||
if ( monster->groundentity != nullptr ) {
|
||||
monster->sv.ent_flags |= SVFL_ONGROUND;
|
||||
}
|
||||
|
||||
if ( monster->takedamage ) {
|
||||
monster->sv.ent_flags |= SVFL_TAKES_DAMAGE;
|
||||
}
|
||||
|
||||
if ( monster->solid == SOLID_NOT || monster->movetype == MOVETYPE_NONE ) {
|
||||
monster->sv.ent_flags |= SVFL_IS_HIDDEN;
|
||||
}
|
||||
|
||||
if ( ( monster->flags & FL_INWATER ) != 0 ) {
|
||||
monster->sv.ent_flags |= SVFL_IN_WATER;
|
||||
}
|
||||
|
||||
if ( coop->integer ) {
|
||||
monster->sv.team = Team_Coop_Monster;
|
||||
} else {
|
||||
monster->sv.team = Team_None; // TODO: CTF/TDM/etc...
|
||||
}
|
||||
|
||||
monster->sv.health = ( monster->deadflag != true ) ? monster->health : -1;
|
||||
monster->sv.waterlevel = monster->waterlevel;
|
||||
monster->sv.enemy = monster->enemy;
|
||||
monster->sv.ground_entity = monster->groundentity;
|
||||
|
||||
int32_t viewHeight = monster->viewheight;
|
||||
if ( ( monster->monsterinfo.aiflags & AI_DUCKED ) != 0 ) {
|
||||
viewHeight = int32_t( monster->maxs[ 2 ] - 4.0f );
|
||||
}
|
||||
monster->sv.viewheight = viewHeight;
|
||||
|
||||
monster->sv.viewangles = monster->s.angles;
|
||||
|
||||
AngleVectors( monster->s.angles, monster->sv.viewforward, nullptr, nullptr );
|
||||
|
||||
monster->sv.velocity = monster->velocity;
|
||||
|
||||
if ( !monster->sv.init ) {
|
||||
monster->sv.init = true;
|
||||
monster->sv.classname = monster->classname;
|
||||
monster->sv.targetname = monster->targetname;
|
||||
monster->sv.starting_health = monster->health;
|
||||
monster->sv.max_health = monster->max_health;
|
||||
|
||||
gi.Bot_RegisterEdict( monster );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Item_UpdateState
|
||||
================
|
||||
*/
|
||||
void Item_UpdateState( edict_t * item ) {
|
||||
item->sv.ent_flags = SVFL_IS_ITEM;
|
||||
item->sv.respawntime = 0;
|
||||
|
||||
if ( item->team != nullptr ) {
|
||||
item->sv.ent_flags |= SVFL_IN_TEAM;
|
||||
} // some DM maps have items chained together in teams...
|
||||
|
||||
if ( item->solid == SOLID_NOT ) {
|
||||
item->sv.ent_flags |= SVFL_IS_HIDDEN;
|
||||
|
||||
if ( item->nextthink.milliseconds() > 0 ) {
|
||||
if ( ( item->svflags & SVF_RESPAWNING ) != 0 ) {
|
||||
const gtime_t pendingRespawnTime = ( item->nextthink - level.time );
|
||||
item->sv.respawntime = static_cast<int32_t>( pendingRespawnTime.milliseconds() );
|
||||
} else {
|
||||
// item will respawn at some unknown time in the future...
|
||||
item->sv.respawntime = Item_UnknownRespawnTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// track who has picked us up so far...
|
||||
item->sv.pickedup_list = item->item_picked_up_by;
|
||||
|
||||
const item_id_t itemID = item->item->id;
|
||||
if ( itemID == IT_FLAG1 || itemID == IT_FLAG2 ) {
|
||||
item->sv.ent_flags |= SVFL_IS_OBJECTIVE;
|
||||
// TODO: figure out if the objective is dropped/carried/home...
|
||||
}
|
||||
|
||||
// always need to update these for items, since random item spawning
|
||||
// could change them at any time...
|
||||
item->sv.classname = item->classname;
|
||||
item->sv.item_id = item->item->id;
|
||||
|
||||
if ( !item->sv.init ) {
|
||||
item->sv.init = true;
|
||||
item->sv.targetname = item->targetname;
|
||||
|
||||
gi.Bot_RegisterEdict( item );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Trap_UpdateState
|
||||
================
|
||||
*/
|
||||
void Trap_UpdateState( edict_t * danger ) {
|
||||
danger->sv.ent_flags = SVFL_TRAP_DANGER;
|
||||
danger->sv.velocity = danger->velocity;
|
||||
|
||||
if ( danger->owner != nullptr && danger->owner->client != nullptr ) {
|
||||
player_skinnum_t pl_skinnum;
|
||||
pl_skinnum.skinnum = danger->owner->s.skinnum;
|
||||
danger->sv.team = pl_skinnum.team_index;
|
||||
}
|
||||
|
||||
if ( danger->groundentity != nullptr ) {
|
||||
danger->sv.ent_flags |= SVFL_ONGROUND;
|
||||
}
|
||||
|
||||
if ( ( danger->flags & FL_TRAP_LASER_FIELD ) == 0 ) {
|
||||
danger->sv.ent_flags |= SVFL_ACTIVE; // non-lasers are always active
|
||||
} else {
|
||||
danger->sv.start_origin = danger->s.origin;
|
||||
danger->sv.end_origin = danger->s.old_origin;
|
||||
if ( ( danger->svflags & SVF_NOCLIENT ) == 0 ) {
|
||||
if ( ( danger->s.renderfx & RF_BEAM ) ) {
|
||||
danger->sv.ent_flags |= SVFL_ACTIVE; // lasers are active!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !danger->sv.init ) {
|
||||
danger->sv.init = true;
|
||||
danger->sv.classname = danger->classname;
|
||||
|
||||
gi.Bot_RegisterEdict( danger );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Edict_UpdateState
|
||||
================
|
||||
*/
|
||||
void Edict_UpdateState( edict_t * edict ) {
|
||||
edict->sv.ent_flags = SVFL_NONE;
|
||||
edict->sv.health = edict->health;
|
||||
|
||||
if ( edict->takedamage ) {
|
||||
edict->sv.ent_flags |= SVFL_TAKES_DAMAGE;
|
||||
}
|
||||
|
||||
// plats, movers, and doors use this to determine move state.
|
||||
const bool isDoor = ( ( edict->svflags & SVF_DOOR ) != 0 );
|
||||
const bool isReversedDoor = ( isDoor && edict->spawnflags.has( SPAWNFLAG_DOOR_REVERSE ) );
|
||||
|
||||
// doors have their top/bottom states reversed from plats
|
||||
// ( unless "reverse" spawnflag is set! )
|
||||
if ( isDoor && !isReversedDoor ) {
|
||||
if ( edict->moveinfo.state == STATE_TOP ) {
|
||||
edict->sv.ent_flags |= SVFL_MOVESTATE_BOTTOM;
|
||||
} else if ( edict->moveinfo.state == STATE_BOTTOM ) {
|
||||
edict->sv.ent_flags |= SVFL_MOVESTATE_TOP;
|
||||
}
|
||||
} else {
|
||||
if ( edict->moveinfo.state == STATE_TOP ) {
|
||||
edict->sv.ent_flags |= SVFL_MOVESTATE_TOP;
|
||||
} else if ( edict->moveinfo.state == STATE_BOTTOM ) {
|
||||
edict->sv.ent_flags |= SVFL_MOVESTATE_BOTTOM;
|
||||
}
|
||||
}
|
||||
|
||||
if ( edict->moveinfo.state == STATE_UP || edict->moveinfo.state == STATE_DOWN ) {
|
||||
edict->sv.ent_flags |= SVFL_MOVESTATE_MOVING;
|
||||
}
|
||||
|
||||
edict->sv.start_origin = edict->moveinfo.start_origin;
|
||||
edict->sv.end_origin = edict->moveinfo.end_origin;
|
||||
|
||||
if ( edict->svflags & SVF_DOOR ) {
|
||||
if ( edict->flags & FL_LOCKED ) {
|
||||
edict->sv.ent_flags |= SVFL_IS_LOCKED_DOOR;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !edict->sv.init ) {
|
||||
edict->sv.init = true;
|
||||
edict->sv.classname = edict->classname;
|
||||
edict->sv.targetname = edict->targetname;
|
||||
edict->sv.spawnflags = edict->spawnflags.value;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Entity_UpdateState
|
||||
================
|
||||
*/
|
||||
void Entity_UpdateState( edict_t * edict ) {
|
||||
if ( edict->svflags & SVF_MONSTER ) {
|
||||
Monster_UpdateState( edict );
|
||||
} else if ( edict->flags & FL_TRAP || edict->flags & FL_TRAP_LASER_FIELD ) {
|
||||
Trap_UpdateState( edict );
|
||||
} else if ( edict->item != nullptr ) {
|
||||
Item_UpdateState( edict );
|
||||
} else if ( edict->client != nullptr ) {
|
||||
Player_UpdateState( edict );
|
||||
} else {
|
||||
Edict_UpdateState( edict );
|
||||
}
|
||||
}
|
||||
|
||||
USE( info_nav_lock_use ) ( edict_t * self, edict_t * other, edict_t * activator ) -> void {
|
||||
edict_t * n = nullptr;
|
||||
|
||||
while ( ( n = G_FindByString<&edict_t::targetname>( n, self->target ) ) ) {
|
||||
if ( !( n->svflags & SVF_DOOR ) ) {
|
||||
gi.Com_PrintFmt( "{} tried targeting {}, a non-SVF_DOOR\n", *self, *n );
|
||||
continue;
|
||||
}
|
||||
|
||||
n->flags ^= FL_LOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
/*QUAKED info_nav_lock (1.0 1.0 0.0) (-16 -16 0) (16 16 32)
|
||||
toggle locked state on linked entity
|
||||
*/
|
||||
void SP_info_nav_lock( edict_t * self ) {
|
||||
if ( !self->targetname ) {
|
||||
gi.Com_PrintFmt( "{} missing targetname\n", *self );
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !self->target ) {
|
||||
gi.Com_PrintFmt( "{} missing target\n", *self );
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
self->use = info_nav_lock_use;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FindLocalPlayer
|
||||
================
|
||||
*/
|
||||
const edict_t * FindLocalPlayer() {
|
||||
const edict_t * localPlayer = nullptr;
|
||||
|
||||
const edict_t * ent = &g_edicts[ 0 ];
|
||||
for ( uint32_t i = 0; i < globals.num_edicts; i++, ent++ ) {
|
||||
if ( !ent->inuse || !( ent->svflags & SVF_PLAYER ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ent->health <= 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
localPlayer = ent;
|
||||
break;
|
||||
}
|
||||
|
||||
return localPlayer;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FindFirstBot
|
||||
================
|
||||
*/
|
||||
const edict_t * FindFirstBot() {
|
||||
const edict_t * firstBot = nullptr;
|
||||
|
||||
const edict_t * ent = &g_edicts[ 0 ];
|
||||
for ( uint32_t i = 0; i < globals.num_edicts; i++, ent++ ) {
|
||||
if ( !ent->inuse || !( ent->svflags & SVF_PLAYER ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ent->health <= 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !( ent->svflags & SVF_BOT ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
firstBot = ent;
|
||||
break;
|
||||
}
|
||||
|
||||
return firstBot;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FindFirstMonster
|
||||
================
|
||||
*/
|
||||
const edict_t * FindFirstMonster() {
|
||||
const edict_t * firstMonster = nullptr;
|
||||
|
||||
const edict_t * ent = &g_edicts[ 0 ];
|
||||
for ( uint32_t i = 0; i < globals.num_edicts; i++, ent++ ) {
|
||||
if ( !ent->inuse || !( ent->svflags & SVF_MONSTER ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ent->health <= 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
firstMonster = ent;
|
||||
break;
|
||||
}
|
||||
|
||||
return firstMonster;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FindFirstMonster
|
||||
|
||||
"Actors" are either players or monsters - i.e. something alive and thinking.
|
||||
================
|
||||
*/
|
||||
const edict_t * FindActorUnderCrosshair( const edict_t * player ) {
|
||||
if ( player == nullptr || !player->inuse ) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
vec3_t forward, right, up;
|
||||
AngleVectors( player->client->v_angle, forward, right, up );
|
||||
|
||||
const vec3_t eye_position = ( player->s.origin + vec3_t{ 0.0f, 0.0f, (float)player->viewheight } );
|
||||
const vec3_t end = ( eye_position + ( forward * 8192.0f ) );
|
||||
const contents_t mask = ( MASK_PROJECTILE & ~CONTENTS_DEADMONSTER );
|
||||
|
||||
trace_t tr = gi.traceline( eye_position, end, player, mask );
|
||||
|
||||
const edict_t * traceEnt = tr.ent;
|
||||
if ( traceEnt == nullptr || !tr.ent->inuse ) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( !( traceEnt->svflags & SVF_PLAYER ) && !( traceEnt->svflags & SVF_MONSTER ) ) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( traceEnt->health <= 0 ) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return traceEnt;
|
||||
}
|
||||
10
rerelease/bots/bot_utils.h
Normal file
10
rerelease/bots/bot_utils.h
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#pragma once
|
||||
|
||||
void Entity_UpdateState( edict_t * edict );
|
||||
const edict_t * FindLocalPlayer();
|
||||
const edict_t * FindFirstBot();
|
||||
const edict_t * FindFirstMonster();
|
||||
const edict_t * FindActorUnderCrosshair( const edict_t * player );
|
||||
14
rerelease/cg_local.h
Normal file
14
rerelease/cg_local.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
// g_local.h -- local definitions for game module
|
||||
#pragma once
|
||||
|
||||
#include "bg_local.h"
|
||||
|
||||
extern cgame_import_t cgi;
|
||||
extern cgame_export_t cglobals;
|
||||
|
||||
#define SERVER_TICK_RATE cgi.tick_rate // in hz
|
||||
#define FRAME_TIME_S cgi.frame_time_s
|
||||
#define FRAME_TIME_MS cgi.frame_time_ms
|
||||
124
rerelease/cg_main.cpp
Normal file
124
rerelease/cg_main.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#include "cg_local.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
cgame_import_t cgi;
|
||||
cgame_export_t cglobals;
|
||||
|
||||
static void *CG_GetExtension(const char *name)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CG_InitScreen();
|
||||
|
||||
uint64_t cgame_init_time = 0;
|
||||
|
||||
static void InitCGame()
|
||||
{
|
||||
CG_InitScreen();
|
||||
|
||||
cgame_init_time = cgi.CL_ClientRealTime();
|
||||
|
||||
pm_config.n64_physics = !!atoi(cgi.get_configstring(CONFIG_N64_PHYSICS));
|
||||
pm_config.airaccel = atoi(cgi.get_configstring(CS_AIRACCEL));
|
||||
}
|
||||
|
||||
static void ShutdownCGame()
|
||||
{
|
||||
}
|
||||
|
||||
void CG_DrawHUD (int32_t isplit, const cg_server_data_t *data, vrect_t hud_vrect, vrect_t hud_safe, int32_t scale, int32_t playernum, const player_state_t *ps);
|
||||
void CG_TouchPics();
|
||||
layout_flags_t CG_LayoutFlags(const player_state_t *ps);
|
||||
|
||||
int32_t CG_GetActiveWeaponWheelWeapon(const player_state_t *ps)
|
||||
{
|
||||
return ps->stats[STAT_ACTIVE_WHEEL_WEAPON];
|
||||
}
|
||||
|
||||
uint32_t CG_GetOwnedWeaponWheelWeapons(const player_state_t *ps)
|
||||
{
|
||||
return ((uint32_t) (uint16_t) ps->stats[STAT_WEAPONS_OWNED_1]) | ((uint32_t) (uint16_t) (ps->stats[STAT_WEAPONS_OWNED_2]) << 16);
|
||||
}
|
||||
|
||||
int16_t CG_GetWeaponWheelAmmoCount(const player_state_t *ps, int32_t ammo_id)
|
||||
{
|
||||
uint16_t ammo = G_GetAmmoStat((uint16_t *) &ps->stats[STAT_AMMO_INFO_START], ammo_id);
|
||||
|
||||
if (ammo == AMMO_VALUE_INFINITE)
|
||||
return -1;
|
||||
|
||||
return ammo;
|
||||
}
|
||||
|
||||
int16_t CG_GetPowerupWheelCount(const player_state_t *ps, int32_t powerup_id)
|
||||
{
|
||||
return G_GetPowerupStat((uint16_t *) &ps->stats[STAT_POWERUP_INFO_START], powerup_id);
|
||||
}
|
||||
|
||||
int16_t CG_GetHitMarkerDamage(const player_state_t *ps)
|
||||
{
|
||||
return ps->stats[STAT_HIT_MARKER];
|
||||
}
|
||||
|
||||
static void CG_ParseConfigString(int32_t i, const char *s)
|
||||
{
|
||||
if (i == CONFIG_N64_PHYSICS)
|
||||
pm_config.n64_physics = !!atoi(s);
|
||||
else if (i == CS_AIRACCEL)
|
||||
pm_config.airaccel = atoi(s);
|
||||
}
|
||||
|
||||
void CG_ParseCenterPrint (const char *str, int isplit, bool instant);
|
||||
void CG_ClearNotify(int32_t isplit);
|
||||
void CG_ClearCenterprint(int32_t isplit);
|
||||
void CG_NotifyMessage(int32_t isplit, const char *msg, bool is_chat);
|
||||
|
||||
void CG_GetMonsterFlashOffset(monster_muzzleflash_id_t id, gvec3_ref_t offset)
|
||||
{
|
||||
if (id >= q_countof(monster_flash_offset))
|
||||
cgi.Com_Error("Bad muzzle flash offset");
|
||||
|
||||
offset = monster_flash_offset[id];
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
GetCGameAPI
|
||||
|
||||
Returns a pointer to the structure with all entry points
|
||||
and global variables
|
||||
=================
|
||||
*/
|
||||
Q2GAME_API cgame_export_t *GetCGameAPI(cgame_import_t *import)
|
||||
{
|
||||
cgi = *import;
|
||||
|
||||
cglobals.apiversion = CGAME_API_VERSION;
|
||||
cglobals.Init = InitCGame;
|
||||
cglobals.Shutdown = ShutdownCGame;
|
||||
|
||||
cglobals.Pmove = Pmove;
|
||||
cglobals.DrawHUD = CG_DrawHUD;
|
||||
cglobals.LayoutFlags = CG_LayoutFlags;
|
||||
cglobals.TouchPics = CG_TouchPics;
|
||||
|
||||
cglobals.GetActiveWeaponWheelWeapon = CG_GetActiveWeaponWheelWeapon;
|
||||
cglobals.GetOwnedWeaponWheelWeapons = CG_GetOwnedWeaponWheelWeapons;
|
||||
cglobals.GetWeaponWheelAmmoCount = CG_GetWeaponWheelAmmoCount;
|
||||
cglobals.GetPowerupWheelCount = CG_GetPowerupWheelCount;
|
||||
cglobals.GetHitMarkerDamage = CG_GetHitMarkerDamage;
|
||||
cglobals.ParseConfigString = CG_ParseConfigString;
|
||||
cglobals.ParseCenterPrint = CG_ParseCenterPrint;
|
||||
cglobals.ClearNotify = CG_ClearNotify;
|
||||
cglobals.ClearCenterprint = CG_ClearCenterprint;
|
||||
cglobals.NotifyMessage = CG_NotifyMessage;
|
||||
cglobals.GetMonsterFlashOffset = CG_GetMonsterFlashOffset;
|
||||
|
||||
cglobals.GetExtension = CG_GetExtension;
|
||||
|
||||
return &cglobals;
|
||||
}
|
||||
1781
rerelease/cg_screen.cpp
Normal file
1781
rerelease/cg_screen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3832
rerelease/ctf/g_ctf.cpp
Normal file
3832
rerelease/ctf/g_ctf.cpp
Normal file
File diff suppressed because it is too large
Load Diff
162
rerelease/ctf/g_ctf.h
Normal file
162
rerelease/ctf/g_ctf.h
Normal file
@@ -0,0 +1,162 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#define CTF_VERSION 1.52
|
||||
#define CTF_VSTRING2(x) #x
|
||||
#define CTF_VSTRING(x) CTF_VSTRING2(x)
|
||||
#define CTF_STRING_VERSION CTF_VSTRING(CTF_VERSION)
|
||||
|
||||
enum ctfteam_t
|
||||
{
|
||||
CTF_NOTEAM,
|
||||
CTF_TEAM1,
|
||||
CTF_TEAM2
|
||||
};
|
||||
|
||||
enum ctfgrapplestate_t
|
||||
{
|
||||
CTF_GRAPPLE_STATE_FLY,
|
||||
CTF_GRAPPLE_STATE_PULL,
|
||||
CTF_GRAPPLE_STATE_HANG
|
||||
};
|
||||
|
||||
struct ghost_t
|
||||
{
|
||||
char netname[MAX_NETNAME];
|
||||
int number;
|
||||
|
||||
// stats
|
||||
int deaths;
|
||||
int kills;
|
||||
int caps;
|
||||
int basedef;
|
||||
int carrierdef;
|
||||
|
||||
int code; // ghost code
|
||||
ctfteam_t team; // team
|
||||
int score; // frags at time of disconnect
|
||||
edict_t *ent;
|
||||
};
|
||||
|
||||
extern cvar_t *ctf;
|
||||
extern cvar_t *g_teamplay_force_join;
|
||||
extern cvar_t *teamplay;
|
||||
|
||||
constexpr const char *CTF_TEAM1_SKIN = "ctf_r";
|
||||
constexpr const char *CTF_TEAM2_SKIN = "ctf_b";
|
||||
|
||||
constexpr int32_t CTF_CAPTURE_BONUS = 15; // what you get for capture
|
||||
constexpr int32_t CTF_TEAM_BONUS = 10; // what your team gets for capture
|
||||
constexpr int32_t CTF_RECOVERY_BONUS = 1; // what you get for recovery
|
||||
constexpr int32_t CTF_FLAG_BONUS = 0; // what you get for picking up enemy flag
|
||||
constexpr int32_t CTF_FRAG_CARRIER_BONUS = 2; // what you get for fragging enemy flag carrier
|
||||
constexpr gtime_t CTF_FLAG_RETURN_TIME = 40_sec; // seconds until auto return
|
||||
|
||||
constexpr int32_t CTF_CARRIER_DANGER_PROTECT_BONUS = 2; // bonus for fraggin someone who has recently hurt your flag carrier
|
||||
constexpr int32_t CTF_CARRIER_PROTECT_BONUS = 1; // bonus for fraggin someone while either you or your target are near your flag carrier
|
||||
constexpr int32_t CTF_FLAG_DEFENSE_BONUS = 1; // bonus for fraggin someone while either you or your target are near your flag
|
||||
constexpr int32_t CTF_RETURN_FLAG_ASSIST_BONUS = 1; // awarded for returning a flag that causes a capture to happen almost immediately
|
||||
constexpr int32_t CTF_FRAG_CARRIER_ASSIST_BONUS = 2; // award for fragging a flag carrier if a capture happens almost immediately
|
||||
|
||||
constexpr float CTF_TARGET_PROTECT_RADIUS = 400; // the radius around an object being defended where a target will be worth extra frags
|
||||
constexpr float CTF_ATTACKER_PROTECT_RADIUS = 400; // the radius around an object being defended where an attacker will get extra frags when making kills
|
||||
|
||||
constexpr gtime_t CTF_CARRIER_DANGER_PROTECT_TIMEOUT = 8_sec;
|
||||
constexpr gtime_t CTF_FRAG_CARRIER_ASSIST_TIMEOUT = 10_sec;
|
||||
constexpr gtime_t CTF_RETURN_FLAG_ASSIST_TIMEOUT = 10_sec;
|
||||
|
||||
constexpr gtime_t CTF_AUTO_FLAG_RETURN_TIMEOUT = 30_sec; // number of seconds before dropped flag auto-returns
|
||||
|
||||
constexpr gtime_t CTF_TECH_TIMEOUT = 60_sec; // seconds before techs spawn again
|
||||
|
||||
constexpr int32_t CTF_DEFAULT_GRAPPLE_SPEED = 650; // speed of grapple in flight
|
||||
constexpr float CTF_DEFAULT_GRAPPLE_PULL_SPEED = 650; // speed player is pulled at
|
||||
|
||||
void CTFInit();
|
||||
void CTFSpawn();
|
||||
void CTFPrecache();
|
||||
bool G_TeamplayEnabled();
|
||||
void G_AdjustTeamScore(ctfteam_t team, int32_t offset);
|
||||
|
||||
void SP_info_player_team1(edict_t *self);
|
||||
void SP_info_player_team2(edict_t *self);
|
||||
|
||||
const char *CTFTeamName(int team);
|
||||
const char *CTFOtherTeamName(int team);
|
||||
void CTFAssignSkin(edict_t *ent, const char *s);
|
||||
void CTFAssignTeam(gclient_t *who);
|
||||
edict_t *SelectCTFSpawnPoint(edict_t *ent, bool force_spawn);
|
||||
bool CTFPickup_Flag(edict_t *ent, edict_t *other);
|
||||
void CTFDrop_Flag(edict_t *ent, gitem_t *item);
|
||||
void CTFEffects(edict_t *player);
|
||||
void CTFCalcScores();
|
||||
void CTFCalcRankings(std::array<uint32_t, MAX_CLIENTS> &player_ranks); // [Paril-KEX]
|
||||
void CheckEndTDMLevel(); // [Paril-KEX]
|
||||
void SetCTFStats(edict_t *ent);
|
||||
void CTFDeadDropFlag(edict_t *self);
|
||||
void CTFScoreboardMessage(edict_t *ent, edict_t *killer);
|
||||
void CTFTeam_f(edict_t *ent);
|
||||
void CTFID_f(edict_t *ent);
|
||||
#ifndef KEX_Q2_GAME
|
||||
void CTFSay_Team(edict_t *who, const char *msg);
|
||||
#endif
|
||||
void CTFFlagSetup(edict_t *ent);
|
||||
void CTFResetFlag(int ctf_team);
|
||||
void CTFFragBonuses(edict_t *targ, edict_t *inflictor, edict_t *attacker);
|
||||
void CTFCheckHurtCarrier(edict_t *targ, edict_t *attacker);
|
||||
void CTFDirtyTeamMenu();
|
||||
|
||||
// GRAPPLE
|
||||
void CTFWeapon_Grapple(edict_t *ent);
|
||||
void CTFPlayerResetGrapple(edict_t *ent);
|
||||
void CTFGrapplePull(edict_t *self);
|
||||
void CTFResetGrapple(edict_t *self);
|
||||
|
||||
// TECH
|
||||
gitem_t *CTFWhat_Tech(edict_t *ent);
|
||||
bool CTFPickup_Tech(edict_t *ent, edict_t *other);
|
||||
void CTFDrop_Tech(edict_t *ent, gitem_t *item);
|
||||
void CTFDeadDropTech(edict_t *ent);
|
||||
void CTFSetupTechSpawn();
|
||||
int CTFApplyResistance(edict_t *ent, int dmg);
|
||||
int CTFApplyStrength(edict_t *ent, int dmg);
|
||||
bool CTFApplyStrengthSound(edict_t *ent);
|
||||
bool CTFApplyHaste(edict_t *ent);
|
||||
void CTFApplyHasteSound(edict_t *ent);
|
||||
void CTFApplyRegeneration(edict_t *ent);
|
||||
bool CTFHasRegeneration(edict_t *ent);
|
||||
void CTFRespawnTech(edict_t *ent);
|
||||
void CTFResetTech();
|
||||
|
||||
void CTFOpenJoinMenu(edict_t *ent);
|
||||
bool CTFStartClient(edict_t *ent);
|
||||
void CTFVoteYes(edict_t *ent);
|
||||
void CTFVoteNo(edict_t *ent);
|
||||
void CTFReady(edict_t *ent);
|
||||
void CTFNotReady(edict_t *ent);
|
||||
bool CTFNextMap();
|
||||
bool CTFMatchSetup();
|
||||
bool CTFMatchOn();
|
||||
void CTFGhost(edict_t *ent);
|
||||
void CTFAdmin(edict_t *ent);
|
||||
bool CTFInMatch();
|
||||
void CTFStats(edict_t *ent);
|
||||
void CTFWarp(edict_t *ent);
|
||||
void CTFBoot(edict_t *ent);
|
||||
void CTFPlayerList(edict_t *ent);
|
||||
|
||||
bool CTFCheckRules();
|
||||
|
||||
void SP_misc_ctf_banner(edict_t *ent);
|
||||
void SP_misc_ctf_small_banner(edict_t *ent);
|
||||
|
||||
void UpdateChaseCam(edict_t *ent);
|
||||
void ChaseNext(edict_t *ent);
|
||||
void ChasePrev(edict_t *ent);
|
||||
|
||||
void CTFObserver(edict_t *ent);
|
||||
|
||||
void SP_trigger_teleport(edict_t *ent);
|
||||
void SP_info_teleport_destination(edict_t *ent);
|
||||
|
||||
void CTFSetPowerUpEffect(edict_t *ent, effects_t def);
|
||||
282
rerelease/ctf/p_ctf_menu.cpp
Normal file
282
rerelease/ctf/p_ctf_menu.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
#include "../g_local.h"
|
||||
|
||||
// Note that the pmenu entries are duplicated
|
||||
// this is so that a static set of pmenu entries can be used
|
||||
// for multiple clients and changed without interference
|
||||
// note that arg will be freed when the menu is closed, it must be allocated memory
|
||||
pmenuhnd_t *PMenu_Open(edict_t *ent, const pmenu_t *entries, int cur, int num, void *arg, UpdateFunc_t UpdateFunc)
|
||||
{
|
||||
pmenuhnd_t *hnd;
|
||||
const pmenu_t *p;
|
||||
int i;
|
||||
|
||||
if (!ent->client)
|
||||
return nullptr;
|
||||
|
||||
if (ent->client->menu)
|
||||
{
|
||||
gi.Com_Print("warning, ent already has a menu\n");
|
||||
PMenu_Close(ent);
|
||||
}
|
||||
|
||||
hnd = (pmenuhnd_t *) gi.TagMalloc(sizeof(*hnd), TAG_LEVEL);
|
||||
hnd->UpdateFunc = UpdateFunc;
|
||||
|
||||
hnd->arg = arg;
|
||||
hnd->entries = (pmenu_t *) gi.TagMalloc(sizeof(pmenu_t) * num, TAG_LEVEL);
|
||||
memcpy(hnd->entries, entries, sizeof(pmenu_t) * num);
|
||||
// duplicate the strings since they may be from static memory
|
||||
for (i = 0; i < num; i++)
|
||||
Q_strlcpy(hnd->entries[i].text, entries[i].text, sizeof(entries[i].text));
|
||||
|
||||
hnd->num = num;
|
||||
|
||||
if (cur < 0 || !entries[cur].SelectFunc)
|
||||
{
|
||||
for (i = 0, p = entries; i < num; i++, p++)
|
||||
if (p->SelectFunc)
|
||||
break;
|
||||
}
|
||||
else
|
||||
i = cur;
|
||||
|
||||
if (i >= num)
|
||||
hnd->cur = -1;
|
||||
else
|
||||
hnd->cur = i;
|
||||
|
||||
ent->client->showscores = true;
|
||||
ent->client->inmenu = true;
|
||||
ent->client->menu = hnd;
|
||||
|
||||
if (UpdateFunc)
|
||||
UpdateFunc(ent);
|
||||
|
||||
PMenu_Do_Update(ent);
|
||||
gi.unicast(ent, true);
|
||||
|
||||
return hnd;
|
||||
}
|
||||
|
||||
void PMenu_Close(edict_t *ent)
|
||||
{
|
||||
pmenuhnd_t *hnd;
|
||||
|
||||
if (!ent->client->menu)
|
||||
return;
|
||||
|
||||
hnd = ent->client->menu;
|
||||
gi.TagFree(hnd->entries);
|
||||
if (hnd->arg)
|
||||
gi.TagFree(hnd->arg);
|
||||
gi.TagFree(hnd);
|
||||
ent->client->menu = nullptr;
|
||||
ent->client->showscores = false;
|
||||
}
|
||||
|
||||
// only use on pmenu's that have been called with PMenu_Open
|
||||
void PMenu_UpdateEntry(pmenu_t *entry, const char *text, int align, SelectFunc_t SelectFunc)
|
||||
{
|
||||
Q_strlcpy(entry->text, text, sizeof(entry->text));
|
||||
entry->align = align;
|
||||
entry->SelectFunc = SelectFunc;
|
||||
}
|
||||
|
||||
#include "../g_statusbar.h"
|
||||
|
||||
void PMenu_Do_Update(edict_t *ent)
|
||||
{
|
||||
int i;
|
||||
pmenu_t *p;
|
||||
int x;
|
||||
pmenuhnd_t *hnd;
|
||||
const char *t;
|
||||
bool alt = false;
|
||||
|
||||
if (!ent->client->menu)
|
||||
{
|
||||
gi.Com_Print("warning: ent has no menu\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hnd = ent->client->menu;
|
||||
|
||||
if (hnd->UpdateFunc)
|
||||
hnd->UpdateFunc(ent);
|
||||
|
||||
statusbar_t sb;
|
||||
|
||||
sb.xv(32).yv(8).picn("inventory");
|
||||
|
||||
for (i = 0, p = hnd->entries; i < hnd->num; i++, p++)
|
||||
{
|
||||
if (!*(p->text))
|
||||
continue; // blank line
|
||||
|
||||
t = p->text;
|
||||
|
||||
if (*t == '*')
|
||||
{
|
||||
alt = true;
|
||||
t++;
|
||||
}
|
||||
|
||||
sb.yv(32 + i * 8);
|
||||
|
||||
const char *loc_func = "loc_string";
|
||||
|
||||
if (p->align == PMENU_ALIGN_CENTER)
|
||||
{
|
||||
x = 0;
|
||||
loc_func = "loc_cstring";
|
||||
}
|
||||
else if (p->align == PMENU_ALIGN_RIGHT)
|
||||
{
|
||||
x = 260;
|
||||
loc_func = "loc_rstring";
|
||||
}
|
||||
else
|
||||
x = 64;
|
||||
|
||||
sb.xv(x);
|
||||
|
||||
sb.sb << loc_func;
|
||||
|
||||
if (hnd->cur == i || alt)
|
||||
sb.sb << '2';
|
||||
|
||||
sb.sb << " 1 \"" << t << "\" \"" << p->text_arg1 << "\" ";
|
||||
|
||||
if (hnd->cur == i)
|
||||
{
|
||||
sb.xv(56);
|
||||
sb.string2("\">\"");
|
||||
}
|
||||
|
||||
alt = false;
|
||||
}
|
||||
|
||||
gi.WriteByte(svc_layout);
|
||||
gi.WriteString(sb.sb.str().c_str());
|
||||
}
|
||||
|
||||
void PMenu_Update(edict_t *ent)
|
||||
{
|
||||
if (!ent->client->menu)
|
||||
{
|
||||
gi.Com_Print("warning: ent has no menu\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (level.time - ent->client->menutime >= 1_sec)
|
||||
{
|
||||
// been a second or more since last update, update now
|
||||
PMenu_Do_Update(ent);
|
||||
gi.unicast(ent, true);
|
||||
ent->client->menutime = level.time + 1_sec;
|
||||
ent->client->menudirty = false;
|
||||
}
|
||||
ent->client->menutime = level.time;
|
||||
ent->client->menudirty = true;
|
||||
}
|
||||
|
||||
void PMenu_Next(edict_t *ent)
|
||||
{
|
||||
pmenuhnd_t *hnd;
|
||||
int i;
|
||||
pmenu_t *p;
|
||||
|
||||
if (!ent->client->menu)
|
||||
{
|
||||
gi.Com_Print("warning: ent has no menu\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hnd = ent->client->menu;
|
||||
|
||||
if (hnd->cur < 0)
|
||||
return; // no selectable entries
|
||||
|
||||
i = hnd->cur;
|
||||
p = hnd->entries + hnd->cur;
|
||||
do
|
||||
{
|
||||
i++;
|
||||
p++;
|
||||
if (i == hnd->num)
|
||||
{
|
||||
i = 0;
|
||||
p = hnd->entries;
|
||||
}
|
||||
if (p->SelectFunc)
|
||||
break;
|
||||
} while (i != hnd->cur);
|
||||
|
||||
hnd->cur = i;
|
||||
|
||||
PMenu_Update(ent);
|
||||
}
|
||||
|
||||
void PMenu_Prev(edict_t *ent)
|
||||
{
|
||||
pmenuhnd_t *hnd;
|
||||
int i;
|
||||
pmenu_t *p;
|
||||
|
||||
if (!ent->client->menu)
|
||||
{
|
||||
gi.Com_Print("warning: ent has no menu\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hnd = ent->client->menu;
|
||||
|
||||
if (hnd->cur < 0)
|
||||
return; // no selectable entries
|
||||
|
||||
i = hnd->cur;
|
||||
p = hnd->entries + hnd->cur;
|
||||
do
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
i = hnd->num - 1;
|
||||
p = hnd->entries + i;
|
||||
}
|
||||
else
|
||||
{
|
||||
i--;
|
||||
p--;
|
||||
}
|
||||
if (p->SelectFunc)
|
||||
break;
|
||||
} while (i != hnd->cur);
|
||||
|
||||
hnd->cur = i;
|
||||
|
||||
PMenu_Update(ent);
|
||||
}
|
||||
|
||||
void PMenu_Select(edict_t *ent)
|
||||
{
|
||||
pmenuhnd_t *hnd;
|
||||
pmenu_t *p;
|
||||
|
||||
if (!ent->client->menu)
|
||||
{
|
||||
gi.Com_Print("warning: ent has no menu\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hnd = ent->client->menu;
|
||||
|
||||
if (hnd->cur < 0)
|
||||
return; // no selectable entries
|
||||
|
||||
p = hnd->entries + hnd->cur;
|
||||
|
||||
if (p->SelectFunc)
|
||||
p->SelectFunc(ent, hnd);
|
||||
}
|
||||
41
rerelease/ctf/p_ctf_menu.h
Normal file
41
rerelease/ctf/p_ctf_menu.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
enum
|
||||
{
|
||||
PMENU_ALIGN_LEFT,
|
||||
PMENU_ALIGN_CENTER,
|
||||
PMENU_ALIGN_RIGHT
|
||||
};
|
||||
|
||||
struct pmenu_t;
|
||||
|
||||
using UpdateFunc_t = void (*)(edict_t *ent);
|
||||
|
||||
struct pmenuhnd_t
|
||||
{
|
||||
pmenu_t *entries;
|
||||
int cur;
|
||||
int num;
|
||||
void *arg;
|
||||
UpdateFunc_t UpdateFunc;
|
||||
};
|
||||
|
||||
using SelectFunc_t = void (*)(edict_t *ent, pmenuhnd_t *hnd);
|
||||
|
||||
struct pmenu_t
|
||||
{
|
||||
char text[64];
|
||||
int align;
|
||||
SelectFunc_t SelectFunc;
|
||||
char text_arg1[64];
|
||||
};
|
||||
|
||||
pmenuhnd_t *PMenu_Open(edict_t *ent, const pmenu_t *entries, int cur, int num, void *arg, UpdateFunc_t UpdateFunc);
|
||||
void PMenu_Close(edict_t *ent);
|
||||
void PMenu_UpdateEntry(pmenu_t *entry, const char *text, int align, SelectFunc_t SelectFunc);
|
||||
void PMenu_Do_Update(edict_t *ent);
|
||||
void PMenu_Update(edict_t *ent);
|
||||
void PMenu_Next(edict_t *ent);
|
||||
void PMenu_Prev(edict_t *ent);
|
||||
void PMenu_Select(edict_t *ent);
|
||||
1769
rerelease/g_ai.cpp
Normal file
1769
rerelease/g_ai.cpp
Normal file
File diff suppressed because it is too large
Load Diff
171
rerelease/g_chase.cpp
Normal file
171
rerelease/g_chase.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
#include "g_local.h"
|
||||
|
||||
void UpdateChaseCam(edict_t *ent)
|
||||
{
|
||||
vec3_t o, ownerv, goal;
|
||||
edict_t *targ;
|
||||
vec3_t forward, right;
|
||||
trace_t trace;
|
||||
vec3_t oldgoal;
|
||||
vec3_t angles;
|
||||
|
||||
// is our chase target gone?
|
||||
if (!ent->client->chase_target->inuse || ent->client->chase_target->client->resp.spectator)
|
||||
{
|
||||
edict_t *old = ent->client->chase_target;
|
||||
ChaseNext(ent);
|
||||
if (ent->client->chase_target == old)
|
||||
{
|
||||
ent->client->chase_target = nullptr;
|
||||
ent->client->ps.pmove.pm_flags &= ~(PMF_NO_POSITIONAL_PREDICTION | PMF_NO_ANGULAR_PREDICTION);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
targ = ent->client->chase_target;
|
||||
|
||||
ownerv = targ->s.origin;
|
||||
oldgoal = ent->s.origin;
|
||||
|
||||
ownerv[2] += targ->viewheight;
|
||||
|
||||
angles = targ->client->v_angle;
|
||||
if (angles[PITCH] > 56)
|
||||
angles[PITCH] = 56;
|
||||
AngleVectors(angles, forward, right, nullptr);
|
||||
forward.normalize();
|
||||
o = ownerv + (forward * -30);
|
||||
|
||||
if (o[2] < targ->s.origin[2] + 20)
|
||||
o[2] = targ->s.origin[2] + 20;
|
||||
|
||||
// jump animation lifts
|
||||
if (!targ->groundentity)
|
||||
o[2] += 16;
|
||||
|
||||
trace = gi.traceline(ownerv, o, targ, MASK_SOLID);
|
||||
|
||||
goal = trace.endpos;
|
||||
|
||||
goal += (forward * 2);
|
||||
|
||||
// pad for floors and ceilings
|
||||
o = goal;
|
||||
o[2] += 6;
|
||||
trace = gi.traceline(goal, o, targ, MASK_SOLID);
|
||||
if (trace.fraction < 1)
|
||||
{
|
||||
goal = trace.endpos;
|
||||
goal[2] -= 6;
|
||||
}
|
||||
|
||||
o = goal;
|
||||
o[2] -= 6;
|
||||
trace = gi.traceline(goal, o, targ, MASK_SOLID);
|
||||
if (trace.fraction < 1)
|
||||
{
|
||||
goal = trace.endpos;
|
||||
goal[2] += 6;
|
||||
}
|
||||
|
||||
if (targ->deadflag)
|
||||
ent->client->ps.pmove.pm_type = PM_DEAD;
|
||||
else
|
||||
ent->client->ps.pmove.pm_type = PM_FREEZE;
|
||||
|
||||
ent->s.origin = goal;
|
||||
ent->client->ps.pmove.delta_angles = targ->client->v_angle - ent->client->resp.cmd_angles;
|
||||
|
||||
if (targ->deadflag)
|
||||
{
|
||||
ent->client->ps.viewangles[ROLL] = 40;
|
||||
ent->client->ps.viewangles[PITCH] = -15;
|
||||
ent->client->ps.viewangles[YAW] = targ->client->killer_yaw;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->client->ps.viewangles = targ->client->v_angle;
|
||||
ent->client->v_angle = targ->client->v_angle;
|
||||
AngleVectors(ent->client->v_angle, ent->client->v_forward, nullptr, nullptr);
|
||||
}
|
||||
|
||||
ent->viewheight = 0;
|
||||
ent->client->ps.pmove.pm_flags |= PMF_NO_POSITIONAL_PREDICTION | PMF_NO_ANGULAR_PREDICTION;
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
void ChaseNext(edict_t *ent)
|
||||
{
|
||||
ptrdiff_t i;
|
||||
edict_t *e;
|
||||
|
||||
if (!ent->client->chase_target)
|
||||
return;
|
||||
|
||||
i = ent->client->chase_target - g_edicts;
|
||||
do
|
||||
{
|
||||
i++;
|
||||
if (i > game.maxclients)
|
||||
i = 1;
|
||||
e = g_edicts + i;
|
||||
if (!e->inuse)
|
||||
continue;
|
||||
if (!e->client->resp.spectator)
|
||||
break;
|
||||
} while (e != ent->client->chase_target);
|
||||
|
||||
ent->client->chase_target = e;
|
||||
ent->client->update_chase = true;
|
||||
}
|
||||
|
||||
void ChasePrev(edict_t *ent)
|
||||
{
|
||||
int i;
|
||||
edict_t *e;
|
||||
|
||||
if (!ent->client->chase_target)
|
||||
return;
|
||||
|
||||
i = ent->client->chase_target - g_edicts;
|
||||
do
|
||||
{
|
||||
i--;
|
||||
if (i < 1)
|
||||
i = game.maxclients;
|
||||
e = g_edicts + i;
|
||||
if (!e->inuse)
|
||||
continue;
|
||||
if (!e->client->resp.spectator)
|
||||
break;
|
||||
} while (e != ent->client->chase_target);
|
||||
|
||||
ent->client->chase_target = e;
|
||||
ent->client->update_chase = true;
|
||||
}
|
||||
|
||||
void GetChaseTarget(edict_t *ent)
|
||||
{
|
||||
uint32_t i;
|
||||
edict_t *other;
|
||||
|
||||
for (i = 1; i <= game.maxclients; i++)
|
||||
{
|
||||
other = g_edicts + i;
|
||||
if (other->inuse && !other->client->resp.spectator)
|
||||
{
|
||||
ent->client->chase_target = other;
|
||||
ent->client->update_chase = true;
|
||||
UpdateChaseCam(ent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ent->client->chase_msg_time <= level.time)
|
||||
{
|
||||
gi.LocCenter_Print(ent, "$g_no_players_chase");
|
||||
ent->client->chase_msg_time = level.time + 5_sec;
|
||||
}
|
||||
}
|
||||
1741
rerelease/g_cmds.cpp
Normal file
1741
rerelease/g_cmds.cpp
Normal file
File diff suppressed because it is too large
Load Diff
911
rerelease/g_combat.cpp
Normal file
911
rerelease/g_combat.cpp
Normal file
@@ -0,0 +1,911 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// g_combat.c
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
/*
|
||||
============
|
||||
CanDamage
|
||||
|
||||
Returns true if the inflictor can directly damage the target. Used for
|
||||
explosions and melee attacks.
|
||||
============
|
||||
*/
|
||||
bool CanDamage(edict_t *targ, edict_t *inflictor)
|
||||
{
|
||||
vec3_t dest;
|
||||
trace_t trace;
|
||||
|
||||
// bmodels need special checking because their origin is 0,0,0
|
||||
vec3_t inflictor_center;
|
||||
|
||||
if (inflictor->linked)
|
||||
inflictor_center = (inflictor->absmin + inflictor->absmax) * 0.5f;
|
||||
else
|
||||
inflictor_center = inflictor->s.origin;
|
||||
|
||||
if (targ->solid == SOLID_BSP)
|
||||
{
|
||||
dest = closest_point_to_box(inflictor_center, targ->absmin, targ->absmax);
|
||||
|
||||
trace = gi.traceline(inflictor_center, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0f)
|
||||
return true;
|
||||
}
|
||||
|
||||
vec3_t targ_center;
|
||||
|
||||
if (targ->linked)
|
||||
targ_center = (targ->absmin + targ->absmax) * 0.5f;
|
||||
else
|
||||
targ_center = targ->s.origin;
|
||||
|
||||
trace = gi.traceline(inflictor_center, targ_center, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0f)
|
||||
return true;
|
||||
|
||||
dest = targ_center;
|
||||
dest[0] += 15.0f;
|
||||
dest[1] += 15.0f;
|
||||
trace = gi.traceline(inflictor_center, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0f)
|
||||
return true;
|
||||
|
||||
dest = targ_center;
|
||||
dest[0] += 15.0f;
|
||||
dest[1] -= 15.0f;
|
||||
trace = gi.traceline(inflictor_center, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0f)
|
||||
return true;
|
||||
|
||||
dest = targ_center;
|
||||
dest[0] -= 15.0f;
|
||||
dest[1] += 15.0f;
|
||||
trace = gi.traceline(inflictor_center, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0f)
|
||||
return true;
|
||||
|
||||
dest = targ_center;
|
||||
dest[0] -= 15.0f;
|
||||
dest[1] -= 15.0f;
|
||||
trace = gi.traceline(inflictor_center, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0f)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Killed
|
||||
============
|
||||
*/
|
||||
void Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, mod_t mod)
|
||||
{
|
||||
if (targ->health < -999)
|
||||
targ->health = -999;
|
||||
|
||||
// [Paril-KEX]
|
||||
if ((targ->svflags & SVF_MONSTER) && targ->monsterinfo.aiflags & AI_MEDIC)
|
||||
{
|
||||
if (targ->enemy && targ->enemy->inuse && (targ->enemy->svflags & SVF_MONSTER)) // god, I hope so
|
||||
{
|
||||
cleanupHealTarget(targ->enemy);
|
||||
}
|
||||
|
||||
// clean up self
|
||||
targ->monsterinfo.aiflags &= ~AI_MEDIC;
|
||||
}
|
||||
|
||||
targ->enemy = attacker;
|
||||
targ->lastMOD = mod;
|
||||
|
||||
// [Paril-KEX] monsters call die in their damage handler
|
||||
if (targ->svflags & SVF_MONSTER)
|
||||
return;
|
||||
|
||||
targ->die(targ, inflictor, attacker, damage, point, mod);
|
||||
|
||||
if (targ->monsterinfo.setskin)
|
||||
targ->monsterinfo.setskin(targ);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SpawnDamage
|
||||
================
|
||||
*/
|
||||
void SpawnDamage(int type, const vec3_t &origin, const vec3_t &normal, int damage)
|
||||
{
|
||||
if (damage > 255)
|
||||
damage = 255;
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(type);
|
||||
// gi.WriteByte (damage);
|
||||
gi.WritePosition(origin);
|
||||
gi.WriteDir(normal);
|
||||
gi.multicast(origin, MULTICAST_PVS, false);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
T_Damage
|
||||
|
||||
targ entity that is being damaged
|
||||
inflictor entity that is causing the damage
|
||||
attacker entity that caused the inflictor to damage targ
|
||||
example: targ=monster, inflictor=rocket, attacker=player
|
||||
|
||||
dir direction of the attack
|
||||
point point at which the damage is being inflicted
|
||||
normal normal vector from that point
|
||||
damage amount of damage being inflicted
|
||||
knockback force to be applied against targ as a result of the damage
|
||||
|
||||
dflags these flags are used to control how T_Damage works
|
||||
DAMAGE_RADIUS damage was indirect (from a nearby explosion)
|
||||
DAMAGE_NO_ARMOR armor does not protect from this damage
|
||||
DAMAGE_ENERGY damage is from an energy based weapon
|
||||
DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles
|
||||
DAMAGE_BULLET damage is from a bullet (used for ricochets)
|
||||
DAMAGE_NO_PROTECTION kills godmode, armor, everything
|
||||
============
|
||||
*/
|
||||
static int CheckPowerArmor(edict_t *ent, const vec3_t &point, const vec3_t &normal, int damage, damageflags_t dflags)
|
||||
{
|
||||
gclient_t *client;
|
||||
int save;
|
||||
item_id_t power_armor_type;
|
||||
int damagePerCell;
|
||||
int pa_te_type;
|
||||
int *power;
|
||||
int power_used;
|
||||
|
||||
if (ent->health <= 0)
|
||||
return 0;
|
||||
|
||||
if (!damage)
|
||||
return 0;
|
||||
|
||||
client = ent->client;
|
||||
|
||||
if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_POWER_ARMOR)) // PGM
|
||||
return 0;
|
||||
|
||||
if (client)
|
||||
{
|
||||
power_armor_type = PowerArmorType(ent);
|
||||
power = &client->pers.inventory[IT_AMMO_CELLS];
|
||||
}
|
||||
else if (ent->svflags & SVF_MONSTER)
|
||||
{
|
||||
power_armor_type = ent->monsterinfo.power_armor_type;
|
||||
power = &ent->monsterinfo.power_armor_power;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
if (power_armor_type == IT_NULL)
|
||||
return 0;
|
||||
if (!*power)
|
||||
return 0;
|
||||
|
||||
if (power_armor_type == IT_ITEM_POWER_SCREEN)
|
||||
{
|
||||
vec3_t vec;
|
||||
float dot;
|
||||
vec3_t forward;
|
||||
|
||||
// only works if damage point is in front
|
||||
AngleVectors(ent->s.angles, forward, nullptr, nullptr);
|
||||
vec = point - ent->s.origin;
|
||||
vec.normalize();
|
||||
dot = vec.dot(forward);
|
||||
if (dot <= 0.3f)
|
||||
return 0;
|
||||
|
||||
damagePerCell = 1;
|
||||
pa_te_type = TE_SCREEN_SPARKS;
|
||||
damage = damage / 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctf->integer)
|
||||
damagePerCell = 1; // power armor is weaker in CTF
|
||||
else
|
||||
damagePerCell = 2;
|
||||
pa_te_type = TE_SCREEN_SPARKS;
|
||||
damage = (2 * damage) / 3;
|
||||
}
|
||||
|
||||
// Paril: fix small amounts of damage not
|
||||
// being absorbed
|
||||
damage = max(1, damage);
|
||||
|
||||
save = *power * damagePerCell;
|
||||
|
||||
if (!save)
|
||||
return 0;
|
||||
|
||||
// [Paril-KEX] energy damage should do more to power armor, not ETF Rifle shots.
|
||||
if (dflags & DAMAGE_ENERGY)
|
||||
save = max(1, save / 2);
|
||||
|
||||
if (save > damage)
|
||||
save = damage;
|
||||
|
||||
// [Paril-KEX] energy damage should do more to power armor, not ETF Rifle shots.
|
||||
if (dflags & DAMAGE_ENERGY)
|
||||
power_used = (save / damagePerCell) * 2;
|
||||
else
|
||||
power_used = save / damagePerCell;
|
||||
|
||||
power_used = max(1, power_used);
|
||||
|
||||
SpawnDamage(pa_te_type, point, normal, save);
|
||||
ent->powerarmor_time = level.time + 200_ms;
|
||||
|
||||
// Paril: adjustment so that power armor
|
||||
// always uses damagePerCell even if it does
|
||||
// only a single point of damage
|
||||
*power = max(0, *power - max(damagePerCell, power_used));
|
||||
|
||||
// check power armor turn-off states
|
||||
if (ent->client)
|
||||
G_CheckPowerArmor(ent);
|
||||
else if (!*power)
|
||||
{
|
||||
gi.sound(ent, CHAN_AUTO, gi.soundindex("misc/mon_power2.wav"), 1.f, ATTN_NORM, 0.f);
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_POWER_SPLASH);
|
||||
gi.WriteEntity(ent);
|
||||
gi.WriteByte((power_armor_type == IT_ITEM_POWER_SCREEN) ? 1 : 0);
|
||||
gi.multicast(ent->s.origin, MULTICAST_PHS, false);
|
||||
}
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
static int CheckArmor(edict_t *ent, const vec3_t &point, const vec3_t &normal, int damage, int te_sparks,
|
||||
damageflags_t dflags)
|
||||
{
|
||||
gclient_t *client;
|
||||
int save;
|
||||
item_id_t index;
|
||||
gitem_t *armor;
|
||||
int *power;
|
||||
|
||||
if (!damage)
|
||||
return 0;
|
||||
|
||||
// ROGUE
|
||||
if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_REG_ARMOR))
|
||||
// ROGUE
|
||||
return 0;
|
||||
|
||||
client = ent->client;
|
||||
index = ArmorIndex(ent);
|
||||
|
||||
if (!index)
|
||||
return 0;
|
||||
|
||||
armor = GetItemByIndex(index);
|
||||
|
||||
if (dflags & DAMAGE_ENERGY)
|
||||
save = (int) ceilf(armor->armor_info->energy_protection * damage);
|
||||
else
|
||||
save = (int) ceilf(armor->armor_info->normal_protection * damage);
|
||||
|
||||
if (client)
|
||||
power = &client->pers.inventory[index];
|
||||
else
|
||||
power = &ent->monsterinfo.armor_power;
|
||||
|
||||
if (save >= *power)
|
||||
save = *power;
|
||||
|
||||
if (!save)
|
||||
return 0;
|
||||
|
||||
*power -= save;
|
||||
|
||||
if (!client && !ent->monsterinfo.armor_power)
|
||||
ent->monsterinfo.armor_type = IT_NULL;
|
||||
|
||||
SpawnDamage(te_sparks, point, normal, save);
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
void M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
|
||||
{
|
||||
// pmm
|
||||
bool new_tesla;
|
||||
|
||||
if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
|
||||
return;
|
||||
|
||||
//=======
|
||||
// ROGUE
|
||||
// logic for tesla - if you are hit by a tesla, and can't see who you should be mad at (attacker)
|
||||
// attack the tesla
|
||||
// also, target the tesla if it's a "new" tesla
|
||||
if ((inflictor) && (!strcmp(inflictor->classname, "tesla_mine")))
|
||||
{
|
||||
new_tesla = MarkTeslaArea(targ, inflictor);
|
||||
if ((new_tesla || brandom()) && (!targ->enemy || !targ->enemy->classname || strcmp(targ->enemy->classname, "tesla_mine")))
|
||||
TargetTesla(targ, inflictor);
|
||||
return;
|
||||
}
|
||||
// ROGUE
|
||||
//=======
|
||||
|
||||
if (attacker == targ || attacker == targ->enemy)
|
||||
return;
|
||||
|
||||
// if we are a good guy monster and our attacker is a player
|
||||
// or another good guy, do not get mad at them
|
||||
if (targ->monsterinfo.aiflags & AI_GOOD_GUY)
|
||||
{
|
||||
if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||
return;
|
||||
}
|
||||
|
||||
// PGM
|
||||
// if we're currently mad at something a target_anger made us mad at, ignore
|
||||
// damage
|
||||
if (targ->enemy && targ->monsterinfo.aiflags & AI_TARGET_ANGER)
|
||||
{
|
||||
float percentHealth;
|
||||
|
||||
// make sure whatever we were pissed at is still around.
|
||||
if (targ->enemy->inuse)
|
||||
{
|
||||
percentHealth = (float) (targ->health) / (float) (targ->max_health);
|
||||
if (targ->enemy->inuse && percentHealth > 0.33f)
|
||||
return;
|
||||
}
|
||||
|
||||
// remove the target anger flag
|
||||
targ->monsterinfo.aiflags &= ~AI_TARGET_ANGER;
|
||||
}
|
||||
// PGM
|
||||
|
||||
// we recently switched from reacting to damage, don't do it
|
||||
if (targ->monsterinfo.react_to_damage_time > level.time)
|
||||
return;
|
||||
|
||||
// PMM
|
||||
// if we're healing someone, do like above and try to stay with them
|
||||
if ((targ->enemy) && (targ->monsterinfo.aiflags & AI_MEDIC))
|
||||
{
|
||||
float percentHealth;
|
||||
|
||||
percentHealth = (float) (targ->health) / (float) (targ->max_health);
|
||||
// ignore it some of the time
|
||||
if (targ->enemy->inuse && percentHealth > 0.25f)
|
||||
return;
|
||||
|
||||
// remove the medic flag
|
||||
cleanupHealTarget(targ->enemy);
|
||||
targ->monsterinfo.aiflags &= ~AI_MEDIC;
|
||||
}
|
||||
// PMM
|
||||
|
||||
// we now know that we are not both good guys
|
||||
targ->monsterinfo.react_to_damage_time = level.time + random_time(3_sec, 5_sec);
|
||||
|
||||
// if attacker is a client, get mad at them because he's good and we're not
|
||||
if (attacker->client)
|
||||
{
|
||||
targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
|
||||
|
||||
// this can only happen in coop (both new and old enemies are clients)
|
||||
// only switch if can't see the current enemy
|
||||
if (targ->enemy != attacker)
|
||||
{
|
||||
if (targ->enemy && targ->enemy->client)
|
||||
{
|
||||
if (visible(targ, targ->enemy))
|
||||
{
|
||||
targ->oldenemy = attacker;
|
||||
return;
|
||||
}
|
||||
targ->oldenemy = targ->enemy;
|
||||
}
|
||||
|
||||
// [Paril-KEX]
|
||||
if ((targ->svflags & SVF_MONSTER) && targ->monsterinfo.aiflags & AI_MEDIC)
|
||||
{
|
||||
if (targ->enemy && targ->enemy->inuse && (targ->enemy->svflags & SVF_MONSTER)) // god, I hope so
|
||||
{
|
||||
cleanupHealTarget(targ->enemy);
|
||||
}
|
||||
|
||||
// clean up self
|
||||
targ->monsterinfo.aiflags &= ~AI_MEDIC;
|
||||
}
|
||||
|
||||
targ->enemy = attacker;
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||
FoundTarget(targ);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (attacker->enemy == targ // if they *meant* to shoot us, then shoot back
|
||||
// it's the same base (walk/swim/fly) type and both don't ignore shots,
|
||||
// get mad at them
|
||||
|| (((targ->flags & (FL_FLY | FL_SWIM)) == (attacker->flags & (FL_FLY | FL_SWIM))) &&
|
||||
(strcmp(targ->classname, attacker->classname) != 0) && !(attacker->monsterinfo.aiflags & AI_IGNORE_SHOTS) &&
|
||||
!(targ->monsterinfo.aiflags & AI_IGNORE_SHOTS)))
|
||||
{
|
||||
if (targ->enemy != attacker)
|
||||
{
|
||||
// [Paril-KEX]
|
||||
if ((targ->svflags & SVF_MONSTER) && targ->monsterinfo.aiflags & AI_MEDIC)
|
||||
{
|
||||
if (targ->enemy && targ->enemy->inuse && (targ->enemy->svflags & SVF_MONSTER)) // god, I hope so
|
||||
{
|
||||
cleanupHealTarget(targ->enemy);
|
||||
}
|
||||
|
||||
// clean up self
|
||||
targ->monsterinfo.aiflags &= ~AI_MEDIC;
|
||||
}
|
||||
|
||||
if (targ->enemy && targ->enemy->client)
|
||||
targ->oldenemy = targ->enemy;
|
||||
targ->enemy = attacker;
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||
FoundTarget(targ);
|
||||
}
|
||||
}
|
||||
// otherwise get mad at whoever they are mad at (help our buddy) unless it is us!
|
||||
else if (attacker->enemy && attacker->enemy != targ && targ->enemy != attacker->enemy)
|
||||
{
|
||||
if (targ->enemy != attacker->enemy)
|
||||
{
|
||||
// [Paril-KEX]
|
||||
if ((targ->svflags & SVF_MONSTER) && targ->monsterinfo.aiflags & AI_MEDIC)
|
||||
{
|
||||
if (targ->enemy && targ->enemy->inuse && (targ->enemy->svflags & SVF_MONSTER)) // god, I hope so
|
||||
{
|
||||
cleanupHealTarget(targ->enemy);
|
||||
}
|
||||
|
||||
// clean up self
|
||||
targ->monsterinfo.aiflags &= ~AI_MEDIC;
|
||||
}
|
||||
|
||||
if (targ->enemy && targ->enemy->client)
|
||||
targ->oldenemy = targ->enemy;
|
||||
targ->enemy = attacker->enemy;
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||
FoundTarget(targ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if the two given entities are on the same team
|
||||
bool OnSameTeam(edict_t *ent1, edict_t *ent2)
|
||||
{
|
||||
// monsters are never on our team atm
|
||||
if (!ent1->client || !ent2->client)
|
||||
return false;
|
||||
// we're never on our own team
|
||||
else if (ent1 == ent2)
|
||||
return false;
|
||||
|
||||
// [Paril-KEX] coop 'team' support
|
||||
if (coop->integer)
|
||||
return ent1->client && ent2->client;
|
||||
// ZOID
|
||||
else if (G_TeamplayEnabled() && ent1->client && ent2->client)
|
||||
{
|
||||
if (ent1->client->resp.ctf_team == ent2->client->resp.ctf_team)
|
||||
return true;
|
||||
}
|
||||
// ZOID
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the two entities are on a team and that
|
||||
// they wouldn't damage each other
|
||||
bool CheckTeamDamage(edict_t *targ, edict_t *attacker)
|
||||
{
|
||||
// always damage teammates if friendly fire is enabled
|
||||
if (g_friendly_fire->integer)
|
||||
return false;
|
||||
|
||||
return OnSameTeam(targ, attacker);
|
||||
}
|
||||
|
||||
void T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, const vec3_t &dir, const vec3_t &point,
|
||||
const vec3_t &normal, int damage, int knockback, damageflags_t dflags, mod_t mod)
|
||||
{
|
||||
gclient_t *client;
|
||||
int take;
|
||||
int save;
|
||||
int asave;
|
||||
int psave;
|
||||
int te_sparks;
|
||||
bool sphere_notified; // PGM
|
||||
|
||||
if (!targ->takedamage)
|
||||
return;
|
||||
|
||||
if (g_instagib->integer && attacker->client && targ->client)
|
||||
{
|
||||
// [Kex] always kill no matter what on instagib
|
||||
damage = 9999;
|
||||
}
|
||||
|
||||
sphere_notified = false; // PGM
|
||||
|
||||
// friendly fire avoidance
|
||||
// if enabled you can't hurt teammates (but you can hurt yourself)
|
||||
// knockback still occurs
|
||||
if ((targ != attacker) && !(dflags & DAMAGE_NO_PROTECTION))
|
||||
{
|
||||
// mark as friendly fire
|
||||
if (OnSameTeam(targ, attacker))
|
||||
{
|
||||
mod.friendly_fire = true;
|
||||
|
||||
// if we're not a nuke & friendly fire is disabled, just kill the damage
|
||||
if (!g_friendly_fire->integer && (mod.id != MOD_NUKE))
|
||||
damage = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ROGUE
|
||||
// allow the deathmatch game to change values
|
||||
if (deathmatch->integer && gamerules->integer)
|
||||
{
|
||||
if (DMGame.ChangeDamage)
|
||||
damage = DMGame.ChangeDamage(targ, attacker, damage, mod);
|
||||
if (DMGame.ChangeKnockback)
|
||||
knockback = DMGame.ChangeKnockback(targ, attacker, knockback, mod);
|
||||
|
||||
if (!damage)
|
||||
return;
|
||||
}
|
||||
// ROGUE
|
||||
|
||||
// easy mode takes half damage
|
||||
if (skill->integer == 0 && deathmatch->integer == 0 && targ->client && damage)
|
||||
{
|
||||
damage /= 2;
|
||||
if (!damage)
|
||||
damage = 1;
|
||||
}
|
||||
|
||||
if ( ( targ->svflags & SVF_MONSTER ) != 0 ) {
|
||||
damage *= ai_damage_scale->integer;
|
||||
} else {
|
||||
damage *= g_damage_scale->integer;
|
||||
} // mal: just for debugging...
|
||||
|
||||
client = targ->client;
|
||||
|
||||
// PMM - defender sphere takes half damage
|
||||
if (damage && (client) && (client->owned_sphere) && (client->owned_sphere->spawnflags == SPHERE_DEFENDER))
|
||||
{
|
||||
damage /= 2;
|
||||
if (!damage)
|
||||
damage = 1;
|
||||
}
|
||||
|
||||
if (dflags & DAMAGE_BULLET)
|
||||
te_sparks = TE_BULLET_SPARKS;
|
||||
else
|
||||
te_sparks = TE_SPARKS;
|
||||
|
||||
// bonus damage for surprising a monster
|
||||
if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) &&
|
||||
(!targ->enemy || targ->monsterinfo.surprise_time == level.time) && (targ->health > 0))
|
||||
{
|
||||
damage *= 2;
|
||||
targ->monsterinfo.surprise_time = level.time;
|
||||
}
|
||||
|
||||
// ZOID
|
||||
// strength tech
|
||||
damage = CTFApplyStrength(attacker, damage);
|
||||
// ZOID
|
||||
|
||||
if ((targ->flags & FL_NO_KNOCKBACK) ||
|
||||
((targ->flags & FL_ALIVE_KNOCKBACK_ONLY) && (!targ->deadflag || targ->dead_time != level.time)))
|
||||
knockback = 0;
|
||||
|
||||
// figure momentum add
|
||||
if (!(dflags & DAMAGE_NO_KNOCKBACK))
|
||||
{
|
||||
if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) &&
|
||||
(targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP))
|
||||
{
|
||||
vec3_t normalized = dir.normalized();
|
||||
vec3_t kvel;
|
||||
float mass;
|
||||
|
||||
if (targ->mass < 50)
|
||||
mass = 50;
|
||||
else
|
||||
mass = (float) targ->mass;
|
||||
|
||||
if (targ->client && attacker == targ)
|
||||
kvel = normalized * (1600.0f * knockback / mass); // the rocket jump hack...
|
||||
else
|
||||
kvel = normalized * (500.0f * knockback / mass);
|
||||
|
||||
targ->velocity += kvel;
|
||||
}
|
||||
}
|
||||
|
||||
take = damage;
|
||||
save = 0;
|
||||
|
||||
// check for godmode
|
||||
if ((targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION))
|
||||
{
|
||||
take = 0;
|
||||
save = damage;
|
||||
SpawnDamage(te_sparks, point, normal, save);
|
||||
}
|
||||
|
||||
// check for invincibility
|
||||
// ROGUE
|
||||
if (!(dflags & DAMAGE_NO_PROTECTION) &&
|
||||
(((client && client->invincible_time > level.time)) ||
|
||||
((targ->svflags & SVF_MONSTER) && targ->monsterinfo.invincible_time > level.time)))
|
||||
// ROGUE
|
||||
{
|
||||
if (targ->pain_debounce_time < level.time)
|
||||
{
|
||||
gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
|
||||
targ->pain_debounce_time = level.time + 2_sec;
|
||||
}
|
||||
take = 0;
|
||||
save = damage;
|
||||
}
|
||||
|
||||
// ZOID
|
||||
// team armor protect
|
||||
if (G_TeamplayEnabled() && targ->client && attacker->client &&
|
||||
targ->client->resp.ctf_team == attacker->client->resp.ctf_team && targ != attacker &&
|
||||
g_teamplay_armor_protect->integer)
|
||||
{
|
||||
psave = asave = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ZOID
|
||||
psave = CheckPowerArmor(targ, point, normal, take, dflags);
|
||||
take -= psave;
|
||||
|
||||
asave = CheckArmor(targ, point, normal, take, te_sparks, dflags);
|
||||
take -= asave;
|
||||
}
|
||||
|
||||
// treat cheat/powerup savings the same as armor
|
||||
asave += save;
|
||||
|
||||
// ZOID
|
||||
// resistance tech
|
||||
take = CTFApplyResistance(targ, take);
|
||||
// ZOID
|
||||
|
||||
// ZOID
|
||||
CTFCheckHurtCarrier(targ, attacker);
|
||||
// ZOID
|
||||
|
||||
// ROGUE - this option will do damage both to the armor and person. originally for DPU rounds
|
||||
if (dflags & DAMAGE_DESTROY_ARMOR)
|
||||
{
|
||||
if (!(targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) &&
|
||||
!(client && client->invincible_time > level.time))
|
||||
{
|
||||
take = damage;
|
||||
}
|
||||
}
|
||||
// ROGUE
|
||||
|
||||
// [Paril-KEX] player hit markers
|
||||
if (targ != attacker && attacker->client && targ->health > 0 && !((targ->svflags & SVF_DEADMONSTER) || (targ->flags & FL_NO_DAMAGE_EFFECTS)) && mod.id != MOD_TARGET_LASER)
|
||||
attacker->client->ps.stats[STAT_HIT_MARKER] += take + psave + asave;
|
||||
|
||||
// do the damage
|
||||
if (take)
|
||||
{
|
||||
if (!(targ->flags & FL_NO_DAMAGE_EFFECTS))
|
||||
{
|
||||
// ROGUE
|
||||
if (targ->flags & FL_MECHANICAL)
|
||||
SpawnDamage(TE_ELECTRIC_SPARKS, point, normal, take);
|
||||
// ROGUE
|
||||
else if ((targ->svflags & SVF_MONSTER) || (client))
|
||||
{
|
||||
// XATRIX
|
||||
if (strcmp(targ->classname, "monster_gekk") == 0)
|
||||
SpawnDamage(TE_GREENBLOOD, point, normal, take);
|
||||
// XATRIX
|
||||
// ROGUE
|
||||
else if (mod.id == MOD_CHAINFIST)
|
||||
SpawnDamage(TE_MOREBLOOD, point, normal, 255);
|
||||
// ROGUE
|
||||
else
|
||||
SpawnDamage(TE_BLOOD, point, normal, take);
|
||||
}
|
||||
else
|
||||
SpawnDamage(te_sparks, point, normal, take);
|
||||
}
|
||||
|
||||
if (!CTFMatchSetup())
|
||||
targ->health = targ->health - take;
|
||||
|
||||
if ((targ->flags & FL_IMMORTAL) && targ->health <= 0)
|
||||
targ->health = 1;
|
||||
|
||||
// PGM - spheres need to know who to shoot at
|
||||
if (client && client->owned_sphere)
|
||||
{
|
||||
sphere_notified = true;
|
||||
if (client->owned_sphere->pain)
|
||||
client->owned_sphere->pain(client->owned_sphere, attacker, 0, 0, mod);
|
||||
}
|
||||
// PGM
|
||||
|
||||
if (targ->health <= 0)
|
||||
{
|
||||
if ((targ->svflags & SVF_MONSTER) || (client))
|
||||
{
|
||||
targ->flags |= FL_ALIVE_KNOCKBACK_ONLY;
|
||||
targ->dead_time = level.time;
|
||||
}
|
||||
targ->monsterinfo.damage_blood += take;
|
||||
targ->monsterinfo.damage_attacker = attacker;
|
||||
targ->monsterinfo.damage_inflictor = inflictor;
|
||||
targ->monsterinfo.damage_from = point;
|
||||
targ->monsterinfo.damage_mod = mod;
|
||||
targ->monsterinfo.damage_knockback += knockback;
|
||||
Killed(targ, inflictor, attacker, take, point, mod);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// PGM - spheres need to know who to shoot at
|
||||
if (!sphere_notified)
|
||||
{
|
||||
if (client && client->owned_sphere)
|
||||
{
|
||||
sphere_notified = true;
|
||||
if (client->owned_sphere->pain)
|
||||
client->owned_sphere->pain(client->owned_sphere, attacker, 0, 0, mod);
|
||||
}
|
||||
}
|
||||
// PGM
|
||||
|
||||
if ( targ->client ) {
|
||||
targ->client->last_attacker_time = level.time;
|
||||
}
|
||||
|
||||
if (targ->svflags & SVF_MONSTER)
|
||||
{
|
||||
if (damage > 0)
|
||||
{
|
||||
M_ReactToDamage(targ, attacker, inflictor);
|
||||
|
||||
targ->monsterinfo.damage_attacker = attacker;
|
||||
targ->monsterinfo.damage_inflictor = inflictor;
|
||||
targ->monsterinfo.damage_blood += take;
|
||||
targ->monsterinfo.damage_from = point;
|
||||
targ->monsterinfo.damage_mod = mod;
|
||||
targ->monsterinfo.damage_knockback += knockback;
|
||||
}
|
||||
|
||||
if (targ->monsterinfo.setskin)
|
||||
targ->monsterinfo.setskin(targ);
|
||||
}
|
||||
else if (take && targ->pain)
|
||||
targ->pain(targ, attacker, (float) knockback, take, mod);
|
||||
|
||||
// add to the damage inflicted on a player this frame
|
||||
// the total will be turned into screen blends and view angle kicks
|
||||
// at the end of the frame
|
||||
if (client)
|
||||
{
|
||||
client->damage_parmor += psave;
|
||||
client->damage_armor += asave;
|
||||
client->damage_blood += take;
|
||||
client->damage_knockback += knockback;
|
||||
client->damage_from = point;
|
||||
client->last_damage_time = level.time + COOP_DAMAGE_RESPAWN_TIME;
|
||||
|
||||
if (!(dflags & DAMAGE_NO_INDICATOR) && inflictor != world && attacker != world && (take || psave || asave))
|
||||
{
|
||||
damage_indicator_t *indicator = nullptr;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < client->num_damage_indicators; i++)
|
||||
{
|
||||
if ((point - client->damage_indicators[i].from).length() < 32.f)
|
||||
{
|
||||
indicator = &client->damage_indicators[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!indicator && i != MAX_DAMAGE_INDICATORS)
|
||||
{
|
||||
indicator = &client->damage_indicators[i];
|
||||
// for projectile direct hits, use the attacker; otherwise
|
||||
// use the inflictor (rocket splash should point to the rocket)
|
||||
indicator->from = (dflags & DAMAGE_RADIUS) ? inflictor->s.origin : attacker->s.origin;
|
||||
indicator->health = indicator->armor = indicator->power = 0;
|
||||
client->num_damage_indicators++;
|
||||
}
|
||||
|
||||
if (indicator)
|
||||
{
|
||||
indicator->health += take;
|
||||
indicator->power += psave;
|
||||
indicator->armor += asave;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
T_RadiusDamage
|
||||
============
|
||||
*/
|
||||
void T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, damageflags_t dflags, mod_t mod)
|
||||
{
|
||||
float points;
|
||||
edict_t *ent = nullptr;
|
||||
vec3_t v;
|
||||
vec3_t dir;
|
||||
vec3_t inflictor_center;
|
||||
|
||||
if (inflictor->linked)
|
||||
inflictor_center = (inflictor->absmax + inflictor->absmin) * 0.5f;
|
||||
else
|
||||
inflictor_center = inflictor->s.origin;
|
||||
|
||||
while ((ent = findradius(ent, inflictor_center, radius)) != nullptr)
|
||||
{
|
||||
if (ent == ignore)
|
||||
continue;
|
||||
if (!ent->takedamage)
|
||||
continue;
|
||||
|
||||
if (ent->solid == SOLID_BSP && ent->linked)
|
||||
v = closest_point_to_box(inflictor_center, ent->absmin, ent->absmax);
|
||||
else
|
||||
{
|
||||
v = ent->mins + ent->maxs;
|
||||
v = ent->s.origin + (v * 0.5f);
|
||||
}
|
||||
v = inflictor_center - v;
|
||||
points = damage - 0.5f * v.length();
|
||||
if (ent == attacker)
|
||||
points = points * 0.5f;
|
||||
if (points > 0)
|
||||
{
|
||||
if (CanDamage(ent, inflictor))
|
||||
{
|
||||
dir = (ent->s.origin - inflictor_center).normalized();
|
||||
// [Paril-KEX] use closest point on bbox to explosion position
|
||||
// to spawn damage effect
|
||||
|
||||
T_Damage(ent, inflictor, attacker, dir, closest_point_to_box(inflictor_center, ent->absmin, ent->absmax), dir, (int) points, (int) points,
|
||||
dflags | DAMAGE_RADIUS, mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2897
rerelease/g_func.cpp
Normal file
2897
rerelease/g_func.cpp
Normal file
File diff suppressed because it is too large
Load Diff
4019
rerelease/g_items.cpp
Normal file
4019
rerelease/g_items.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3500
rerelease/g_local.h
Normal file
3500
rerelease/g_local.h
Normal file
File diff suppressed because it is too large
Load Diff
1051
rerelease/g_main.cpp
Normal file
1051
rerelease/g_main.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2485
rerelease/g_misc.cpp
Normal file
2485
rerelease/g_misc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1630
rerelease/g_monster.cpp
Normal file
1630
rerelease/g_monster.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1044
rerelease/g_phys.cpp
Normal file
1044
rerelease/g_phys.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2473
rerelease/g_save.cpp
Normal file
2473
rerelease/g_save.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1714
rerelease/g_spawn.cpp
Normal file
1714
rerelease/g_spawn.cpp
Normal file
File diff suppressed because it is too large
Load Diff
63
rerelease/g_statusbar.h
Normal file
63
rerelease/g_statusbar.h
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#include <sstream>
|
||||
|
||||
// easy statusbar wrapper
|
||||
struct statusbar_t
|
||||
{
|
||||
std::stringstream sb;
|
||||
|
||||
inline auto &yb(int32_t offset) { sb << "yb " << offset << ' '; return *this; }
|
||||
inline auto &yt(int32_t offset) { sb << "yt " << offset << ' '; return *this; }
|
||||
inline auto &yv(int32_t offset) { sb << "yv " << offset << ' '; return *this; }
|
||||
inline auto &xl(int32_t offset) { sb << "xl " << offset << ' '; return *this; }
|
||||
inline auto &xr(int32_t offset) { sb << "xr " << offset << ' '; return *this; }
|
||||
inline auto &xv(int32_t offset) { sb << "xv " << offset << ' '; return *this; }
|
||||
|
||||
inline auto &ifstat(player_stat_t stat) { sb << "if " << stat << ' '; return *this; }
|
||||
inline auto &endifstat() { sb << "endif "; return *this; }
|
||||
|
||||
inline auto &pic(player_stat_t stat) { sb << "pic " << stat << ' '; return *this; }
|
||||
inline auto &picn(const char *icon) { sb << "picn " << icon << ' '; return *this; }
|
||||
|
||||
inline auto &anum() { sb << "anum "; return *this; }
|
||||
inline auto &rnum() { sb << "rnum "; return *this; }
|
||||
inline auto &hnum() { sb << "hnum "; return *this; }
|
||||
inline auto &num(int32_t width, player_stat_t stat) { sb << "num " << width << ' ' << stat << ' '; return *this; }
|
||||
|
||||
inline auto &loc_stat_string(player_stat_t stat) { sb << "loc_stat_string " << stat << ' '; return *this; }
|
||||
inline auto &loc_stat_rstring(player_stat_t stat) { sb << "loc_stat_rstring " << stat << ' '; return *this; }
|
||||
inline auto &stat_string(player_stat_t stat) { sb << "stat_string " << stat << ' '; return *this; }
|
||||
inline auto &loc_stat_cstring2(player_stat_t stat) { sb << "loc_stat_cstring2 " << stat << ' '; return *this; }
|
||||
inline auto &string2(const char *str)
|
||||
{
|
||||
if (str[0] != '"' && (strchr(str, ' ') || strchr(str, '\n')))
|
||||
sb << "string2 \"" << str << "\" ";
|
||||
else
|
||||
sb << "string2 " << str << ' ';
|
||||
return *this;
|
||||
}
|
||||
inline auto &string(const char *str)
|
||||
{
|
||||
if (str[0] != '"' && (strchr(str, ' ') || strchr(str, '\n')))
|
||||
sb << "string \"" << str << "\" ";
|
||||
else
|
||||
sb << "string " << str << ' ';
|
||||
return *this;
|
||||
}
|
||||
inline auto &loc_rstring(const char *str)
|
||||
{
|
||||
if (str[0] != '"' && (strchr(str, ' ') || strchr(str, '\n')))
|
||||
sb << "loc_rstring 0 \"" << str << "\" ";
|
||||
else
|
||||
sb << "loc_rstring 0 " << str << ' ';
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline auto &lives_num(player_stat_t stat) { sb << "lives_num " << stat << ' '; return *this; }
|
||||
inline auto &stat_pname(player_stat_t stat) { sb << "stat_pname " << stat << ' '; return *this; }
|
||||
|
||||
inline auto &health_bars() { sb << "health_bars "; return *this; }
|
||||
inline auto &story() { sb << "story "; return *this; }
|
||||
};
|
||||
302
rerelease/g_svcmds.cpp
Normal file
302
rerelease/g_svcmds.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
void Svcmd_Test_f()
|
||||
{
|
||||
gi.LocClient_Print(nullptr, PRINT_HIGH, "Svcmd_Test_f()\n");
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
PACKET FILTERING
|
||||
|
||||
|
||||
You can add or remove addresses from the filter list with:
|
||||
|
||||
addip <ip>
|
||||
removeip <ip>
|
||||
|
||||
The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire
|
||||
class C network with "addip 192.246.40".
|
||||
|
||||
Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single
|
||||
host.
|
||||
|
||||
listip
|
||||
Prints the current list of filters.
|
||||
|
||||
writeip
|
||||
Dumps "addip <ip>" commands to listip.cfg so it can be execed at a later date. The filter lists are not saved and
|
||||
restored by default, because I beleive it would cause too much confusion.
|
||||
|
||||
filterban <0 or 1>
|
||||
|
||||
If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the
|
||||
default setting.
|
||||
|
||||
If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that
|
||||
only allows players from your local network.
|
||||
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
struct ipfilter_t
|
||||
{
|
||||
unsigned mask;
|
||||
unsigned compare;
|
||||
};
|
||||
|
||||
constexpr size_t MAX_IPFILTERS = 1024;
|
||||
|
||||
ipfilter_t ipfilters[MAX_IPFILTERS];
|
||||
int numipfilters;
|
||||
|
||||
/*
|
||||
=================
|
||||
StringToFilter
|
||||
=================
|
||||
*/
|
||||
static bool StringToFilter(const char *s, ipfilter_t *f)
|
||||
{
|
||||
char num[128];
|
||||
int i, j;
|
||||
byte b[4];
|
||||
byte m[4];
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
b[i] = 0;
|
||||
m[i] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (*s < '0' || *s > '9')
|
||||
{
|
||||
gi.LocClient_Print(nullptr, PRINT_HIGH, "Bad filter address: {}\n", s);
|
||||
return false;
|
||||
}
|
||||
|
||||
j = 0;
|
||||
while (*s >= '0' && *s <= '9')
|
||||
{
|
||||
num[j++] = *s++;
|
||||
}
|
||||
num[j] = 0;
|
||||
b[i] = atoi(num);
|
||||
if (b[i] != 0)
|
||||
m[i] = 255;
|
||||
|
||||
if (!*s)
|
||||
break;
|
||||
s++;
|
||||
}
|
||||
|
||||
f->mask = *(unsigned *) m;
|
||||
f->compare = *(unsigned *) b;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_FilterPacket
|
||||
=================
|
||||
*/
|
||||
bool SV_FilterPacket(const char *from)
|
||||
{
|
||||
int i;
|
||||
unsigned in;
|
||||
byte m[4];
|
||||
const char *p;
|
||||
|
||||
i = 0;
|
||||
p = from;
|
||||
while (*p && i < 4)
|
||||
{
|
||||
m[i] = 0;
|
||||
while (*p >= '0' && *p <= '9')
|
||||
{
|
||||
m[i] = m[i] * 10 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
if (!*p || *p == ':')
|
||||
break;
|
||||
i++;
|
||||
p++;
|
||||
}
|
||||
|
||||
in = *(unsigned *) m;
|
||||
|
||||
for (i = 0; i < numipfilters; i++)
|
||||
if ((in & ipfilters[i].mask) == ipfilters[i].compare)
|
||||
return filterban->integer;
|
||||
|
||||
return !filterban->integer;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_AddIP_f
|
||||
=================
|
||||
*/
|
||||
void SVCmd_AddIP_f()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (gi.argc() < 3)
|
||||
{
|
||||
gi.LocClient_Print(nullptr, PRINT_HIGH, "Usage: addip <ip-mask>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < numipfilters; i++)
|
||||
if (ipfilters[i].compare == 0xffffffff)
|
||||
break; // free spot
|
||||
if (i == numipfilters)
|
||||
{
|
||||
if (numipfilters == MAX_IPFILTERS)
|
||||
{
|
||||
gi.LocClient_Print(nullptr, PRINT_HIGH, "IP filter list is full\n");
|
||||
return;
|
||||
}
|
||||
numipfilters++;
|
||||
}
|
||||
|
||||
if (!StringToFilter(gi.argv(2), &ipfilters[i]))
|
||||
ipfilters[i].compare = 0xffffffff;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_RemoveIP_f
|
||||
=================
|
||||
*/
|
||||
void SVCmd_RemoveIP_f()
|
||||
{
|
||||
ipfilter_t f;
|
||||
int i, j;
|
||||
|
||||
if (gi.argc() < 3)
|
||||
{
|
||||
gi.LocClient_Print(nullptr, PRINT_HIGH, "Usage: sv removeip <ip-mask>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StringToFilter(gi.argv(2), &f))
|
||||
return;
|
||||
|
||||
for (i = 0; i < numipfilters; i++)
|
||||
if (ipfilters[i].mask == f.mask && ipfilters[i].compare == f.compare)
|
||||
{
|
||||
for (j = i + 1; j < numipfilters; j++)
|
||||
ipfilters[j - 1] = ipfilters[j];
|
||||
numipfilters--;
|
||||
gi.LocClient_Print(nullptr, PRINT_HIGH, "Removed.\n");
|
||||
return;
|
||||
}
|
||||
gi.LocClient_Print(nullptr, PRINT_HIGH, "Didn't find {}.\n", gi.argv(2));
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_ListIP_f
|
||||
=================
|
||||
*/
|
||||
void SVCmd_ListIP_f()
|
||||
{
|
||||
int i;
|
||||
byte b[4];
|
||||
|
||||
gi.LocClient_Print(nullptr, PRINT_HIGH, "Filter list:\n");
|
||||
for (i = 0; i < numipfilters; i++)
|
||||
{
|
||||
*(unsigned *) b = ipfilters[i].compare;
|
||||
gi.LocClient_Print(nullptr, PRINT_HIGH, "{}.{}.{}.{}\n", b[0], b[1], b[2], b[3]);
|
||||
}
|
||||
}
|
||||
|
||||
// [Paril-KEX]
|
||||
void SVCmd_NextMap_f()
|
||||
{
|
||||
gi.LocBroadcast_Print(PRINT_HIGH, "$g_map_ended_by_server");
|
||||
EndDMLevel();
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_WriteIP_f
|
||||
=================
|
||||
*/
|
||||
void SVCmd_WriteIP_f(void)
|
||||
{
|
||||
// KEX_FIXME: Sys_FOpen isn't available atm, just commenting this out since i don't think we even need this functionality - sponge
|
||||
/*
|
||||
FILE* f;
|
||||
|
||||
byte b[4];
|
||||
int i;
|
||||
cvar_t* game;
|
||||
|
||||
game = gi.cvar("game", "", 0);
|
||||
|
||||
std::string name;
|
||||
if (!*game->string)
|
||||
name = std::string(GAMEVERSION) + "/listip.cfg";
|
||||
else
|
||||
name = std::string(game->string) + "/listip.cfg";
|
||||
|
||||
gi.LocClient_Print(nullptr, PRINT_HIGH, "Writing {}.\n", name.c_str());
|
||||
|
||||
f = Sys_FOpen(name.c_str(), "wb");
|
||||
if (!f)
|
||||
{
|
||||
gi.LocClient_Print(nullptr, PRINT_HIGH, "Couldn't open {}\n", name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "set filterban %d\n", filterban->integer);
|
||||
|
||||
for (i = 0; i < numipfilters; i++)
|
||||
{
|
||||
*(unsigned*)b = ipfilters[i].compare;
|
||||
fprintf(f, "sv addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
ServerCommand
|
||||
|
||||
ServerCommand will be called when an "sv" command is issued.
|
||||
The game can issue gi.argc() / gi.argv() commands to get the rest
|
||||
of the parameters
|
||||
=================
|
||||
*/
|
||||
void ServerCommand()
|
||||
{
|
||||
const char *cmd;
|
||||
|
||||
cmd = gi.argv(1);
|
||||
if (Q_strcasecmp(cmd, "test") == 0)
|
||||
Svcmd_Test_f();
|
||||
else if (Q_strcasecmp(cmd, "addip") == 0)
|
||||
SVCmd_AddIP_f();
|
||||
else if (Q_strcasecmp(cmd, "removeip") == 0)
|
||||
SVCmd_RemoveIP_f();
|
||||
else if (Q_strcasecmp(cmd, "listip") == 0)
|
||||
SVCmd_ListIP_f();
|
||||
else if (Q_strcasecmp(cmd, "writeip") == 0)
|
||||
SVCmd_WriteIP_f();
|
||||
else if (Q_strcasecmp(cmd, "nextmap") == 0)
|
||||
SVCmd_NextMap_f();
|
||||
else
|
||||
gi.LocClient_Print(nullptr, PRINT_HIGH, "Unknown server command \"{}\"\n", cmd);
|
||||
}
|
||||
2059
rerelease/g_target.cpp
Normal file
2059
rerelease/g_target.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1333
rerelease/g_trigger.cpp
Normal file
1333
rerelease/g_trigger.cpp
Normal file
File diff suppressed because it is too large
Load Diff
661
rerelease/g_turret.cpp
Normal file
661
rerelease/g_turret.cpp
Normal file
@@ -0,0 +1,661 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// g_turret.c
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_TURRET_BREACH_FIRE = 65536_spawnflag;
|
||||
|
||||
void AnglesNormalize(vec3_t &vec)
|
||||
{
|
||||
while (vec[0] > 360)
|
||||
vec[0] -= 360;
|
||||
while (vec[0] < 0)
|
||||
vec[0] += 360;
|
||||
while (vec[1] > 360)
|
||||
vec[1] -= 360;
|
||||
while (vec[1] < 0)
|
||||
vec[1] += 360;
|
||||
}
|
||||
|
||||
MOVEINFO_BLOCKED(turret_blocked) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
edict_t *attacker;
|
||||
|
||||
if (other->takedamage)
|
||||
{
|
||||
if (self->teammaster->owner)
|
||||
attacker = self->teammaster->owner;
|
||||
else
|
||||
attacker = self->teammaster;
|
||||
T_Damage(other, self, attacker, vec3_origin, other->s.origin, vec3_origin, self->teammaster->dmg, 10, DAMAGE_NONE, MOD_CRUSH);
|
||||
}
|
||||
}
|
||||
|
||||
/*QUAKED turret_breach (0 0 0) ?
|
||||
This portion of the turret can change both pitch and yaw.
|
||||
The model should be made with a flat pitch.
|
||||
It (and the associated base) need to be oriented towards 0.
|
||||
Use "angle" to set the starting angle.
|
||||
|
||||
"speed" default 50
|
||||
"dmg" default 10
|
||||
"angle" point this forward
|
||||
"target" point this at an info_notnull at the muzzle tip
|
||||
"minpitch" min acceptable pitch angle : default -30
|
||||
"maxpitch" max acceptable pitch angle : default 30
|
||||
"minyaw" min acceptable yaw angle : default 0
|
||||
"maxyaw" max acceptable yaw angle : default 360
|
||||
*/
|
||||
|
||||
void turret_breach_fire(edict_t *self)
|
||||
{
|
||||
vec3_t f, r, u;
|
||||
vec3_t start;
|
||||
int damage;
|
||||
int speed;
|
||||
|
||||
AngleVectors(self->s.angles, f, r, u);
|
||||
start = self->s.origin + (f * self->move_origin[0]);
|
||||
start += (r * self->move_origin[1]);
|
||||
start += (u * self->move_origin[2]);
|
||||
|
||||
if (self->count)
|
||||
damage = self->count;
|
||||
else
|
||||
damage = (int) frandom(100, 150);
|
||||
speed = 550 + 50 * skill->integer;
|
||||
edict_t *rocket = fire_rocket(self->teammaster->owner->activator ? self->teammaster->owner->activator : self->teammaster->owner, start, f, damage, speed, 150, damage);
|
||||
rocket->s.scale = self->teammaster->dmg_radius;
|
||||
|
||||
gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
THINK(turret_breach_think) (edict_t *self) -> void
|
||||
{
|
||||
edict_t *ent;
|
||||
vec3_t current_angles;
|
||||
vec3_t delta;
|
||||
|
||||
current_angles = self->s.angles;
|
||||
AnglesNormalize(current_angles);
|
||||
|
||||
AnglesNormalize(self->move_angles);
|
||||
if (self->move_angles[PITCH] > 180)
|
||||
self->move_angles[PITCH] -= 360;
|
||||
|
||||
// clamp angles to mins & maxs
|
||||
if (self->move_angles[PITCH] > self->pos1[PITCH])
|
||||
self->move_angles[PITCH] = self->pos1[PITCH];
|
||||
else if (self->move_angles[PITCH] < self->pos2[PITCH])
|
||||
self->move_angles[PITCH] = self->pos2[PITCH];
|
||||
|
||||
if ((self->move_angles[YAW] < self->pos1[YAW]) || (self->move_angles[YAW] > self->pos2[YAW]))
|
||||
{
|
||||
float dmin, dmax;
|
||||
|
||||
dmin = fabsf(self->pos1[YAW] - self->move_angles[YAW]);
|
||||
if (dmin < -180)
|
||||
dmin += 360;
|
||||
else if (dmin > 180)
|
||||
dmin -= 360;
|
||||
dmax = fabsf(self->pos2[YAW] - self->move_angles[YAW]);
|
||||
if (dmax < -180)
|
||||
dmax += 360;
|
||||
else if (dmax > 180)
|
||||
dmax -= 360;
|
||||
if (fabsf(dmin) < fabsf(dmax))
|
||||
self->move_angles[YAW] = self->pos1[YAW];
|
||||
else
|
||||
self->move_angles[YAW] = self->pos2[YAW];
|
||||
}
|
||||
|
||||
delta = self->move_angles - current_angles;
|
||||
if (delta[0] < -180)
|
||||
delta[0] += 360;
|
||||
else if (delta[0] > 180)
|
||||
delta[0] -= 360;
|
||||
if (delta[1] < -180)
|
||||
delta[1] += 360;
|
||||
else if (delta[1] > 180)
|
||||
delta[1] -= 360;
|
||||
delta[2] = 0;
|
||||
|
||||
if (delta[0] > self->speed * gi.frame_time_s)
|
||||
delta[0] = self->speed * gi.frame_time_s;
|
||||
if (delta[0] < -1 * self->speed * gi.frame_time_s)
|
||||
delta[0] = -1 * self->speed * gi.frame_time_s;
|
||||
if (delta[1] > self->speed * gi.frame_time_s)
|
||||
delta[1] = self->speed * gi.frame_time_s;
|
||||
if (delta[1] < -1 * self->speed * gi.frame_time_s)
|
||||
delta[1] = -1 * self->speed * gi.frame_time_s;
|
||||
|
||||
for (ent = self->teammaster; ent; ent = ent->teamchain)
|
||||
{
|
||||
if (ent->noise_index)
|
||||
{
|
||||
if (delta[0] || delta[1])
|
||||
{
|
||||
ent->s.sound = ent->noise_index;
|
||||
ent->s.loop_attenuation = ATTN_NORM;
|
||||
}
|
||||
else
|
||||
ent->s.sound = 0;
|
||||
}
|
||||
}
|
||||
|
||||
self->avelocity = delta * (1.0f / gi.frame_time_s);
|
||||
|
||||
self->nextthink = level.time + FRAME_TIME_S;
|
||||
|
||||
for (ent = self->teammaster; ent; ent = ent->teamchain)
|
||||
ent->avelocity[1] = self->avelocity[1];
|
||||
|
||||
// if we have a driver, adjust his velocities
|
||||
if (self->owner)
|
||||
{
|
||||
float angle;
|
||||
float target_z;
|
||||
float diff;
|
||||
vec3_t target;
|
||||
vec3_t dir;
|
||||
|
||||
// angular is easy, just copy ours
|
||||
self->owner->avelocity[0] = self->avelocity[0];
|
||||
self->owner->avelocity[1] = self->avelocity[1];
|
||||
|
||||
// x & y
|
||||
angle = self->s.angles[1] + self->owner->move_origin[1];
|
||||
angle *= (float) (PI * 2 / 360);
|
||||
target[0] = self->s.origin[0] + cosf(angle) * self->owner->move_origin[0];
|
||||
target[1] = self->s.origin[1] + sinf(angle) * self->owner->move_origin[0];
|
||||
target[2] = self->owner->s.origin[2];
|
||||
|
||||
dir = target - self->owner->s.origin;
|
||||
self->owner->velocity[0] = dir[0] * 1.0f / gi.frame_time_s;
|
||||
self->owner->velocity[1] = dir[1] * 1.0f / gi.frame_time_s;
|
||||
|
||||
// z
|
||||
angle = self->s.angles[PITCH] * (float) (PI * 2 / 360);
|
||||
target_z = self->s.origin[2] + self->owner->move_origin[0] * tan(angle) + self->owner->move_origin[2];
|
||||
|
||||
diff = target_z - self->owner->s.origin[2];
|
||||
self->owner->velocity[2] = diff * 1.0f / gi.frame_time_s;
|
||||
|
||||
if (self->spawnflags.has(SPAWNFLAG_TURRET_BREACH_FIRE))
|
||||
{
|
||||
turret_breach_fire(self);
|
||||
self->spawnflags &= ~SPAWNFLAG_TURRET_BREACH_FIRE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
THINK(turret_breach_finish_init) (edict_t *self) -> void
|
||||
{
|
||||
// get and save info for muzzle location
|
||||
if (!self->target)
|
||||
{
|
||||
gi.Com_PrintFmt("{}: needs a target\n", *self);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->target_ent = G_PickTarget(self->target);
|
||||
if (self->target_ent)
|
||||
{
|
||||
self->move_origin = self->target_ent->s.origin - self->s.origin;
|
||||
G_FreeEdict(self->target_ent);
|
||||
}
|
||||
else
|
||||
gi.Com_PrintFmt("{}: could not find target entity \"{}\"\n", *self, self->target);
|
||||
}
|
||||
|
||||
self->teammaster->dmg = self->dmg;
|
||||
self->teammaster->dmg_radius = self->dmg_radius; // scale
|
||||
self->think = turret_breach_think;
|
||||
self->think(self);
|
||||
}
|
||||
|
||||
void SP_turret_breach(edict_t *self)
|
||||
{
|
||||
self->solid = SOLID_BSP;
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
|
||||
if (st.noise)
|
||||
self->noise_index = gi.soundindex(st.noise);
|
||||
|
||||
gi.setmodel(self, self->model);
|
||||
|
||||
if (!self->speed)
|
||||
self->speed = 50;
|
||||
if (!self->dmg)
|
||||
self->dmg = 10;
|
||||
|
||||
if (!st.minpitch)
|
||||
st.minpitch = -30;
|
||||
if (!st.maxpitch)
|
||||
st.maxpitch = 30;
|
||||
if (!st.maxyaw)
|
||||
st.maxyaw = 360;
|
||||
|
||||
self->pos1[PITCH] = -1 * st.minpitch;
|
||||
self->pos1[YAW] = st.minyaw;
|
||||
self->pos2[PITCH] = -1 * st.maxpitch;
|
||||
self->pos2[YAW] = st.maxyaw;
|
||||
|
||||
// scale used for rocket scale
|
||||
self->dmg_radius = self->s.scale;
|
||||
self->s.scale = 0;
|
||||
|
||||
self->ideal_yaw = self->s.angles[YAW];
|
||||
self->move_angles[YAW] = self->ideal_yaw;
|
||||
|
||||
self->moveinfo.blocked = turret_blocked;
|
||||
|
||||
self->think = turret_breach_finish_init;
|
||||
self->nextthink = level.time + FRAME_TIME_S;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*QUAKED turret_base (0 0 0) ?
|
||||
This portion of the turret changes yaw only.
|
||||
MUST be teamed with a turret_breach.
|
||||
*/
|
||||
|
||||
void SP_turret_base(edict_t *self)
|
||||
{
|
||||
self->solid = SOLID_BSP;
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
|
||||
if (st.noise)
|
||||
self->noise_index = gi.soundindex(st.noise);
|
||||
|
||||
gi.setmodel(self, self->model);
|
||||
self->moveinfo.blocked = turret_blocked;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32)
|
||||
Must NOT be on the team with the rest of the turret parts.
|
||||
Instead it must target the turret_breach.
|
||||
*/
|
||||
|
||||
void infantry_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod);
|
||||
void infantry_stand(edict_t *self);
|
||||
void infantry_pain(edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod);
|
||||
void infantry_setskin(edict_t *self);
|
||||
|
||||
DIE(turret_driver_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
if (!self->deadflag)
|
||||
{
|
||||
edict_t *ent;
|
||||
|
||||
// level the gun
|
||||
self->target_ent->move_angles[0] = 0;
|
||||
|
||||
// remove the driver from the end of them team chain
|
||||
for (ent = self->target_ent->teammaster; ent->teamchain != self; ent = ent->teamchain)
|
||||
;
|
||||
ent->teamchain = nullptr;
|
||||
self->teammaster = nullptr;
|
||||
self->flags &= ~FL_TEAMSLAVE;
|
||||
|
||||
self->target_ent->owner = nullptr;
|
||||
self->target_ent->teammaster->owner = nullptr;
|
||||
|
||||
self->target_ent->moveinfo.blocked = nullptr;
|
||||
|
||||
// clear pitch
|
||||
self->s.angles[0] = 0;
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
|
||||
self->think = monster_think;
|
||||
}
|
||||
|
||||
infantry_die(self, inflictor, attacker, damage, point, mod);
|
||||
|
||||
G_FixStuckObject(self, self->s.origin);
|
||||
AngleVectors(self->s.angles, self->velocity, nullptr, nullptr);
|
||||
self->velocity *= -50;
|
||||
self->velocity.z += 110.f;
|
||||
}
|
||||
|
||||
bool FindTarget(edict_t *self);
|
||||
|
||||
THINK(turret_driver_think) (edict_t *self) -> void
|
||||
{
|
||||
vec3_t target;
|
||||
vec3_t dir;
|
||||
|
||||
self->nextthink = level.time + FRAME_TIME_S;
|
||||
|
||||
if (self->enemy && (!self->enemy->inuse || self->enemy->health <= 0))
|
||||
self->enemy = nullptr;
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
if (!FindTarget(self))
|
||||
return;
|
||||
self->monsterinfo.trail_time = level.time;
|
||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (visible(self, self->enemy))
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
|
||||
{
|
||||
self->monsterinfo.trail_time = level.time;
|
||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.aiflags |= AI_LOST_SIGHT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// let the turret know where we want it to aim
|
||||
target = self->enemy->s.origin;
|
||||
target[2] += self->enemy->viewheight;
|
||||
dir = target - self->target_ent->s.origin;
|
||||
self->target_ent->move_angles = vectoangles(dir);
|
||||
|
||||
// decide if we should shoot
|
||||
if (level.time < self->monsterinfo.attack_finished)
|
||||
return;
|
||||
|
||||
gtime_t reaction_time = gtime_t::from_sec(3 - skill->integer);
|
||||
if ((level.time - self->monsterinfo.trail_time) < reaction_time)
|
||||
return;
|
||||
|
||||
self->monsterinfo.attack_finished = level.time + reaction_time + 1_sec;
|
||||
// FIXME how do we really want to pass this along?
|
||||
self->target_ent->spawnflags |= SPAWNFLAG_TURRET_BREACH_FIRE;
|
||||
}
|
||||
|
||||
THINK(turret_driver_link) (edict_t *self) -> void
|
||||
{
|
||||
vec3_t vec;
|
||||
edict_t *ent;
|
||||
|
||||
self->think = turret_driver_think;
|
||||
self->nextthink = level.time + FRAME_TIME_S;
|
||||
|
||||
self->target_ent = G_PickTarget(self->target);
|
||||
self->target_ent->owner = self;
|
||||
self->target_ent->teammaster->owner = self;
|
||||
self->s.angles = self->target_ent->s.angles;
|
||||
|
||||
vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
|
||||
vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
|
||||
vec[2] = 0;
|
||||
self->move_origin[0] = vec.length();
|
||||
|
||||
vec = self->s.origin - self->target_ent->s.origin;
|
||||
vec = vectoangles(vec);
|
||||
AnglesNormalize(vec);
|
||||
self->move_origin[1] = vec[1];
|
||||
|
||||
self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];
|
||||
|
||||
// add the driver to the end of them team chain
|
||||
for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain)
|
||||
;
|
||||
ent->teamchain = self;
|
||||
self->teammaster = self->target_ent->teammaster;
|
||||
self->flags |= FL_TEAMSLAVE;
|
||||
}
|
||||
|
||||
void InfantryPrecache();
|
||||
|
||||
void SP_turret_driver(edict_t *self)
|
||||
{
|
||||
if (deathmatch->integer)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
InfantryPrecache();
|
||||
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, 32 };
|
||||
|
||||
self->health = self->max_health = 100;
|
||||
self->gib_health = -40;
|
||||
self->mass = 200;
|
||||
self->viewheight = 24;
|
||||
|
||||
self->pain = infantry_pain;
|
||||
self->die = turret_driver_die;
|
||||
self->monsterinfo.stand = infantry_stand;
|
||||
|
||||
self->flags |= FL_NO_KNOCKBACK;
|
||||
|
||||
if (g_debug_monster_kills->integer)
|
||||
level.monsters_registered[level.total_monsters] = self;
|
||||
level.total_monsters++;
|
||||
|
||||
self->svflags |= SVF_MONSTER;
|
||||
self->takedamage = true;
|
||||
self->use = monster_use;
|
||||
self->clipmask = MASK_MONSTERSOLID;
|
||||
self->s.old_origin = self->s.origin;
|
||||
self->monsterinfo.aiflags |= AI_STAND_GROUND;
|
||||
self->monsterinfo.setskin = infantry_setskin;
|
||||
|
||||
if (st.item)
|
||||
{
|
||||
self->item = FindItemByClassname(st.item);
|
||||
if (!self->item)
|
||||
gi.Com_PrintFmt("{}: bad item: {}\n", *self, st.item);
|
||||
}
|
||||
|
||||
self->think = turret_driver_link;
|
||||
self->nextthink = level.time + FRAME_TIME_S;
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
//============
|
||||
// ROGUE
|
||||
|
||||
// invisible turret drivers so we can have unmanned turrets.
|
||||
// originally designed to shoot at func_trains and such, so they
|
||||
// fire at the center of the bounding box, rather than the entity's
|
||||
// origin.
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_TURRET_BRAIN_IGNORE_SIGHT = 1_spawnflag;
|
||||
|
||||
THINK(turret_brain_think) (edict_t *self) -> void
|
||||
{
|
||||
vec3_t target;
|
||||
vec3_t dir;
|
||||
vec3_t endpos;
|
||||
trace_t trace;
|
||||
|
||||
self->nextthink = level.time + FRAME_TIME_S;
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
if (!self->enemy->inuse)
|
||||
self->enemy = nullptr;
|
||||
else if (self->enemy->takedamage && self->enemy->health <= 0)
|
||||
self->enemy = nullptr;
|
||||
}
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
if (!FindTarget(self))
|
||||
return;
|
||||
self->monsterinfo.trail_time = level.time;
|
||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
||||
}
|
||||
|
||||
endpos = self->enemy->absmax + self->enemy->absmin;
|
||||
endpos *= 0.5f;
|
||||
|
||||
if (!self->spawnflags.has(SPAWNFLAG_TURRET_BRAIN_IGNORE_SIGHT))
|
||||
{
|
||||
trace = gi.traceline(self->target_ent->s.origin, endpos, self->target_ent, MASK_SHOT);
|
||||
if (trace.fraction == 1 || trace.ent == self->enemy)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
|
||||
{
|
||||
self->monsterinfo.trail_time = level.time;
|
||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.aiflags |= AI_LOST_SIGHT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// let the turret know where we want it to aim
|
||||
target = endpos;
|
||||
dir = target - self->target_ent->s.origin;
|
||||
self->target_ent->move_angles = vectoangles(dir);
|
||||
|
||||
// decide if we should shoot
|
||||
if (level.time < self->monsterinfo.attack_finished)
|
||||
return;
|
||||
|
||||
gtime_t reaction_time;
|
||||
|
||||
if (self->delay)
|
||||
reaction_time = gtime_t::from_sec(self->delay);
|
||||
else
|
||||
reaction_time = gtime_t::from_sec(3 - skill->integer);
|
||||
|
||||
if ((level.time - self->monsterinfo.trail_time) < reaction_time)
|
||||
return;
|
||||
|
||||
self->monsterinfo.attack_finished = level.time + reaction_time + 1_sec;
|
||||
// FIXME how do we really want to pass this along?
|
||||
self->target_ent->spawnflags |= SPAWNFLAG_TURRET_BREACH_FIRE;
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
THINK(turret_brain_link) (edict_t *self) -> void
|
||||
{
|
||||
vec3_t vec;
|
||||
edict_t *ent;
|
||||
|
||||
if (self->killtarget)
|
||||
{
|
||||
self->enemy = G_PickTarget(self->killtarget);
|
||||
}
|
||||
|
||||
self->think = turret_brain_think;
|
||||
self->nextthink = level.time + FRAME_TIME_S;
|
||||
|
||||
self->target_ent = G_PickTarget(self->target);
|
||||
self->target_ent->owner = self;
|
||||
self->target_ent->teammaster->owner = self;
|
||||
self->s.angles = self->target_ent->s.angles;
|
||||
|
||||
vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
|
||||
vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
|
||||
vec[2] = 0;
|
||||
self->move_origin[0] = vec.length();
|
||||
|
||||
vec = self->s.origin - self->target_ent->s.origin;
|
||||
vec = vectoangles(vec);
|
||||
AnglesNormalize(vec);
|
||||
self->move_origin[1] = vec[1];
|
||||
|
||||
self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];
|
||||
|
||||
// add the driver to the end of them team chain
|
||||
for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain)
|
||||
ent->activator = self->activator; // pass along activator to breach, etc
|
||||
|
||||
ent->teamchain = self;
|
||||
self->teammaster = self->target_ent->teammaster;
|
||||
self->flags |= FL_TEAMSLAVE;
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
USE(turret_brain_deactivate) (edict_t *self, edict_t *other, edict_t *activator) -> void
|
||||
{
|
||||
self->think = nullptr;
|
||||
self->nextthink = 0_ms;
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
USE(turret_brain_activate) (edict_t *self, edict_t *other, edict_t *activator) -> void
|
||||
{
|
||||
if (!self->enemy)
|
||||
self->enemy = activator;
|
||||
|
||||
// wait at least 3 seconds to fire.
|
||||
if (self->wait)
|
||||
self->monsterinfo.attack_finished = level.time + gtime_t::from_sec(self->wait);
|
||||
else
|
||||
self->monsterinfo.attack_finished = level.time + 3_sec;
|
||||
self->use = turret_brain_deactivate;
|
||||
|
||||
// Paril NOTE: rhangar1 has a turret_invisible_brain that breaks the
|
||||
// hangar ceiling; once the final rocket explodes the barrier,
|
||||
// it attempts to print "Barrier neutralized." to the rocket owner
|
||||
// who happens to be this brain rather than the player that activated
|
||||
// the turret. this resolves this by passing it along to fire_rocket.
|
||||
self->activator = activator;
|
||||
|
||||
self->think = turret_brain_link;
|
||||
self->nextthink = level.time + FRAME_TIME_S;
|
||||
}
|
||||
|
||||
/*QUAKED turret_invisible_brain (1 .5 0) (-16 -16 -16) (16 16 16)
|
||||
Invisible brain to drive the turret.
|
||||
|
||||
Does not search for targets. If targeted, can only be turned on once
|
||||
and then off once. After that they are completely disabled.
|
||||
|
||||
"delay" the delay between firing (default ramps for skill level)
|
||||
"Target" the turret breach
|
||||
"Killtarget" the item you want it to attack.
|
||||
Target the brain if you want it activated later, instead of immediately. It will wait 3 seconds
|
||||
before firing to acquire the target.
|
||||
*/
|
||||
void SP_turret_invisible_brain(edict_t *self)
|
||||
{
|
||||
if (!self->killtarget)
|
||||
{
|
||||
gi.Com_Print("turret_invisible_brain with no killtarget!\n");
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
if (!self->target)
|
||||
{
|
||||
gi.Com_Print("turret_invisible_brain with no target!\n");
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->targetname)
|
||||
{
|
||||
self->use = turret_brain_activate;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->think = turret_brain_link;
|
||||
self->nextthink = level.time + FRAME_TIME_S;
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
// ROGUE
|
||||
//============
|
||||
546
rerelease/g_utils.cpp
Normal file
546
rerelease/g_utils.cpp
Normal file
@@ -0,0 +1,546 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// g_utils.c -- misc utility functions for game module
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
/*
|
||||
=============
|
||||
G_Find
|
||||
|
||||
Searches all active entities for the next one that validates the given callback.
|
||||
|
||||
Searches beginning at the edict after from, or the beginning if nullptr
|
||||
nullptr will be returned if the end of the list is reached.
|
||||
=============
|
||||
*/
|
||||
edict_t *G_Find(edict_t *from, std::function<bool(edict_t *e)> matcher)
|
||||
{
|
||||
if (!from)
|
||||
from = g_edicts;
|
||||
else
|
||||
from++;
|
||||
|
||||
for (; from < &g_edicts[globals.num_edicts]; from++)
|
||||
{
|
||||
if (!from->inuse)
|
||||
continue;
|
||||
if (matcher(from))
|
||||
return from;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
findradius
|
||||
|
||||
Returns entities that have origins within a spherical area
|
||||
|
||||
findradius (origin, radius)
|
||||
=================
|
||||
*/
|
||||
edict_t *findradius(edict_t *from, const vec3_t &org, float rad)
|
||||
{
|
||||
vec3_t eorg;
|
||||
int j;
|
||||
|
||||
if (!from)
|
||||
from = g_edicts;
|
||||
else
|
||||
from++;
|
||||
for (; from < &g_edicts[globals.num_edicts]; from++)
|
||||
{
|
||||
if (!from->inuse)
|
||||
continue;
|
||||
if (from->solid == SOLID_NOT)
|
||||
continue;
|
||||
for (j = 0; j < 3; j++)
|
||||
eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j]) * 0.5f);
|
||||
if (eorg.length() > rad)
|
||||
continue;
|
||||
return from;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
G_PickTarget
|
||||
|
||||
Searches all active entities for the next one that holds
|
||||
the matching string at fieldofs in the structure.
|
||||
|
||||
Searches beginning at the edict after from, or the beginning if nullptr
|
||||
nullptr will be returned if the end of the list is reached.
|
||||
|
||||
=============
|
||||
*/
|
||||
constexpr size_t MAXCHOICES = 8;
|
||||
|
||||
edict_t *G_PickTarget(const char *targetname)
|
||||
{
|
||||
edict_t *ent = nullptr;
|
||||
int num_choices = 0;
|
||||
edict_t *choice[MAXCHOICES];
|
||||
|
||||
if (!targetname)
|
||||
{
|
||||
gi.Com_Print("G_PickTarget called with nullptr targetname\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
ent = G_FindByString<&edict_t::targetname>(ent, targetname);
|
||||
if (!ent)
|
||||
break;
|
||||
choice[num_choices++] = ent;
|
||||
if (num_choices == MAXCHOICES)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!num_choices)
|
||||
{
|
||||
gi.Com_PrintFmt("G_PickTarget: target {} not found\n", targetname);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return choice[irandom(num_choices)];
|
||||
}
|
||||
|
||||
THINK(Think_Delay) (edict_t *ent) -> void
|
||||
{
|
||||
G_UseTargets(ent, ent->activator);
|
||||
G_FreeEdict(ent);
|
||||
}
|
||||
|
||||
void G_PrintActivationMessage(edict_t *ent, edict_t *activator, bool coop_global)
|
||||
{
|
||||
//
|
||||
// print the message
|
||||
//
|
||||
if ((ent->message) && !(activator->svflags & SVF_MONSTER))
|
||||
{
|
||||
if (coop_global && coop->integer)
|
||||
gi.LocBroadcast_Print(PRINT_CENTER, "{}", ent->message);
|
||||
else
|
||||
gi.LocCenter_Print(activator, "{}", ent->message);
|
||||
|
||||
// [Paril-KEX] allow non-noisy centerprints
|
||||
if (ent->noise_index >= 0)
|
||||
{
|
||||
if (ent->noise_index)
|
||||
gi.sound(activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void G_MonsterKilled(edict_t *self);
|
||||
|
||||
/*
|
||||
==============================
|
||||
G_UseTargets
|
||||
|
||||
the global "activator" should be set to the entity that initiated the firing.
|
||||
|
||||
If self.delay is set, a DelayedUse entity will be created that will actually
|
||||
do the SUB_UseTargets after that many seconds have passed.
|
||||
|
||||
Centerprints any self.message to the activator.
|
||||
|
||||
Search for (string)targetname in all entities that
|
||||
match (string)self.target and call their .use function
|
||||
|
||||
==============================
|
||||
*/
|
||||
void G_UseTargets(edict_t *ent, edict_t *activator)
|
||||
{
|
||||
edict_t *t;
|
||||
|
||||
//
|
||||
// check for a delay
|
||||
//
|
||||
if (ent->delay)
|
||||
{
|
||||
// create a temp object to fire at a later time
|
||||
t = G_Spawn();
|
||||
t->classname = "DelayedUse";
|
||||
t->nextthink = level.time + gtime_t::from_sec(ent->delay);
|
||||
t->think = Think_Delay;
|
||||
t->activator = activator;
|
||||
if (!activator)
|
||||
gi.Com_Print("Think_Delay with no activator\n");
|
||||
t->message = ent->message;
|
||||
t->target = ent->target;
|
||||
t->killtarget = ent->killtarget;
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// print the message
|
||||
//
|
||||
G_PrintActivationMessage(ent, activator, true);
|
||||
|
||||
//
|
||||
// kill killtargets
|
||||
//
|
||||
if (ent->killtarget)
|
||||
{
|
||||
t = nullptr;
|
||||
while ((t = G_FindByString<&edict_t::targetname>(t, ent->killtarget)))
|
||||
{
|
||||
if (t->teammaster)
|
||||
{
|
||||
// PMM - if this entity is part of a chain, cleanly remove it
|
||||
if (t->flags & FL_TEAMSLAVE)
|
||||
{
|
||||
for (edict_t *master = t->teammaster; master; master = master->teamchain)
|
||||
{
|
||||
if (master->teamchain == t)
|
||||
{
|
||||
master->teamchain = t->teamchain;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// [Paril-KEX] remove teammaster too
|
||||
else if (t->flags & FL_TEAMMASTER)
|
||||
{
|
||||
t->teammaster->flags &= ~FL_TEAMMASTER;
|
||||
|
||||
edict_t *new_master = t->teammaster->teamchain;
|
||||
|
||||
if (new_master)
|
||||
{
|
||||
new_master->flags |= FL_TEAMMASTER;
|
||||
new_master->flags &= ~FL_TEAMSLAVE;
|
||||
|
||||
for (edict_t *m = new_master; m; m = m->teamchain)
|
||||
m->teammaster = new_master;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [Paril-KEX] if we killtarget a monster, clean up properly
|
||||
if (t->svflags & SVF_MONSTER)
|
||||
{
|
||||
if (!t->deadflag && !(t->monsterinfo.aiflags & AI_DO_NOT_COUNT) && !(t->spawnflags & SPAWNFLAG_MONSTER_DEAD))
|
||||
G_MonsterKilled(t);
|
||||
}
|
||||
|
||||
// PMM
|
||||
G_FreeEdict(t);
|
||||
|
||||
if (!ent->inuse)
|
||||
{
|
||||
gi.Com_Print("entity was removed while using killtargets\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// fire targets
|
||||
//
|
||||
if (ent->target)
|
||||
{
|
||||
t = nullptr;
|
||||
while ((t = G_FindByString<&edict_t::targetname>(t, ent->target)))
|
||||
{
|
||||
// doors fire area portals in a specific way
|
||||
if (!Q_strcasecmp(t->classname, "func_areaportal") &&
|
||||
(!Q_strcasecmp(ent->classname, "func_door") || !Q_strcasecmp(ent->classname, "func_door_rotating")
|
||||
|| !Q_strcasecmp(ent->classname, "func_door_secret") || !Q_strcasecmp(ent->classname, "func_water")))
|
||||
continue;
|
||||
|
||||
if (t == ent)
|
||||
{
|
||||
gi.Com_Print("WARNING: Entity used itself.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t->use)
|
||||
t->use(t, ent, activator);
|
||||
}
|
||||
if (!ent->inuse)
|
||||
{
|
||||
gi.Com_Print("entity was removed while using targets\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr vec3_t VEC_UP = { 0, -1, 0 };
|
||||
constexpr vec3_t MOVEDIR_UP = { 0, 0, 1 };
|
||||
constexpr vec3_t VEC_DOWN = { 0, -2, 0 };
|
||||
constexpr vec3_t MOVEDIR_DOWN = { 0, 0, -1 };
|
||||
|
||||
void G_SetMovedir(vec3_t &angles, vec3_t &movedir)
|
||||
{
|
||||
if (angles == VEC_UP)
|
||||
{
|
||||
movedir = MOVEDIR_UP;
|
||||
}
|
||||
else if (angles == VEC_DOWN)
|
||||
{
|
||||
movedir = MOVEDIR_DOWN;
|
||||
}
|
||||
else
|
||||
{
|
||||
AngleVectors(angles, movedir, nullptr, nullptr);
|
||||
}
|
||||
|
||||
angles = {};
|
||||
}
|
||||
|
||||
char *G_CopyString(const char *in, int32_t tag)
|
||||
{
|
||||
if(!in)
|
||||
return nullptr;
|
||||
const size_t amt = strlen(in) + 1;
|
||||
char *const out = static_cast<char *>(gi.TagMalloc(amt, tag));
|
||||
Q_strlcpy(out, in, amt);
|
||||
return out;
|
||||
}
|
||||
|
||||
void G_InitEdict(edict_t *e)
|
||||
{
|
||||
// ROGUE
|
||||
// FIXME -
|
||||
// this fixes a bug somewhere that is setting "nextthink" for an entity that has
|
||||
// already been released. nextthink is being set to FRAME_TIME_S after level.time,
|
||||
// since freetime = nextthink - FRAME_TIME_S
|
||||
if (e->nextthink)
|
||||
e->nextthink = 0_ms;
|
||||
// ROGUE
|
||||
|
||||
e->inuse = true;
|
||||
e->sv.init = false;
|
||||
e->classname = "noclass";
|
||||
e->gravity = 1.0;
|
||||
e->s.number = e - g_edicts;
|
||||
|
||||
// PGM - do this before calling the spawn function so it can be overridden.
|
||||
e->gravityVector[0] = 0.0;
|
||||
e->gravityVector[1] = 0.0;
|
||||
e->gravityVector[2] = -1.0;
|
||||
// PGM
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
G_Spawn
|
||||
|
||||
Either finds a free edict, or allocates a new one.
|
||||
Try to avoid reusing an entity that was recently freed, because it
|
||||
can cause the client to think the entity morphed into something else
|
||||
instead of being removed and recreated, which can cause interpolated
|
||||
angles and bad trails.
|
||||
=================
|
||||
*/
|
||||
edict_t *G_Spawn()
|
||||
{
|
||||
uint32_t i;
|
||||
edict_t *e;
|
||||
|
||||
e = &g_edicts[game.maxclients + 1];
|
||||
for (i = game.maxclients + 1; i < globals.num_edicts; i++, e++)
|
||||
{
|
||||
// the first couple seconds of server time can involve a lot of
|
||||
// freeing and allocating, so relax the replacement policy
|
||||
if (!e->inuse && (e->freetime < 2_sec || level.time - e->freetime > 500_ms))
|
||||
{
|
||||
G_InitEdict(e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == game.maxentities)
|
||||
gi.Com_Error("ED_Alloc: no free edicts");
|
||||
|
||||
globals.num_edicts++;
|
||||
G_InitEdict(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
G_FreeEdict
|
||||
|
||||
Marks the edict as free
|
||||
=================
|
||||
*/
|
||||
THINK(G_FreeEdict) (edict_t *ed) -> void
|
||||
{
|
||||
// already freed
|
||||
if (!ed->inuse)
|
||||
return;
|
||||
|
||||
gi.unlinkentity(ed); // unlink from world
|
||||
|
||||
if ((ed - g_edicts) <= (ptrdiff_t) (game.maxclients + BODY_QUEUE_SIZE))
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
gi.Com_Print("tried to free special edict\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
gi.Bot_UnRegisterEdict( ed );
|
||||
|
||||
int32_t id = ed->spawn_count + 1;
|
||||
memset(ed, 0, sizeof(*ed));
|
||||
ed->s.number = ed - g_edicts;
|
||||
ed->classname = "freed";
|
||||
ed->freetime = level.time;
|
||||
ed->inuse = false;
|
||||
ed->spawn_count = id;
|
||||
ed->sv.init = false;
|
||||
}
|
||||
|
||||
BoxEdictsResult_t G_TouchTriggers_BoxFilter(edict_t *hit, void *)
|
||||
{
|
||||
if (!hit->touch)
|
||||
return BoxEdictsResult_t::Skip;
|
||||
|
||||
return BoxEdictsResult_t::Keep;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
G_TouchTriggers
|
||||
|
||||
============
|
||||
*/
|
||||
void G_TouchTriggers(edict_t *ent)
|
||||
{
|
||||
int i, num;
|
||||
static edict_t *touch[MAX_EDICTS];
|
||||
edict_t *hit;
|
||||
|
||||
// dead things don't activate triggers!
|
||||
if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
|
||||
return;
|
||||
|
||||
num = gi.BoxEdicts(ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_TRIGGERS, G_TouchTriggers_BoxFilter, nullptr);
|
||||
|
||||
// be careful, it is possible to have an entity in this
|
||||
// list removed before we get to it (killtriggered)
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
hit = touch[i];
|
||||
if (!hit->inuse)
|
||||
continue;
|
||||
if (!hit->touch)
|
||||
continue;
|
||||
hit->touch(hit, ent, null_trace, true);
|
||||
}
|
||||
}
|
||||
|
||||
// [Paril-KEX] scan for projectiles between our movement positions
|
||||
// to see if we need to collide against them
|
||||
void G_TouchProjectiles(edict_t *ent, vec3_t previous_origin)
|
||||
{
|
||||
// a bit ugly, but we'll store projectiles we are ignoring here.
|
||||
static std::vector<edict_t *> skipped;
|
||||
|
||||
while (true)
|
||||
{
|
||||
trace_t tr = gi.trace(previous_origin, ent->mins, ent->maxs, ent->s.origin, ent, ent->clipmask | CONTENTS_PROJECTILE);
|
||||
|
||||
if (tr.fraction == 1.0f)
|
||||
break;
|
||||
else if (!(tr.ent->svflags & SVF_PROJECTILE))
|
||||
break;
|
||||
|
||||
// always skip this projectile since certain conditions may cause the projectile
|
||||
// to not disappear immediately
|
||||
tr.ent->svflags &= ~SVF_PROJECTILE;
|
||||
skipped.push_back(tr.ent);
|
||||
|
||||
// if we're both players and it's coop, allow the projectile to "pass" through
|
||||
if (ent->client && tr.ent->owner && tr.ent->owner->client && !G_ShouldPlayersCollide(true))
|
||||
continue;
|
||||
|
||||
G_Impact(ent, tr);
|
||||
}
|
||||
|
||||
for (auto &skip : skipped)
|
||||
skip->svflags |= SVF_PROJECTILE;
|
||||
|
||||
skipped.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
Kill box
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
=================
|
||||
KillBox
|
||||
|
||||
Kills all entities that would touch the proposed new positioning
|
||||
of ent.
|
||||
=================
|
||||
*/
|
||||
|
||||
BoxEdictsResult_t KillBox_BoxFilter(edict_t *hit, void *)
|
||||
{
|
||||
if (!hit->solid || !hit->takedamage || hit->solid == SOLID_TRIGGER)
|
||||
return BoxEdictsResult_t::Skip;
|
||||
|
||||
return BoxEdictsResult_t::Keep;
|
||||
}
|
||||
|
||||
bool KillBox(edict_t *ent, bool from_spawning, mod_id_t mod, bool bsp_clipping)
|
||||
{
|
||||
// don't telefrag as spectator...
|
||||
if (ent->movetype == MOVETYPE_NOCLIP)
|
||||
return true;
|
||||
|
||||
contents_t mask = CONTENTS_MONSTER | CONTENTS_PLAYER;
|
||||
|
||||
// [Paril-KEX] don't gib other players in coop if we're not colliding
|
||||
if (from_spawning && ent->client && coop->integer && !G_ShouldPlayersCollide(false))
|
||||
mask &= ~CONTENTS_PLAYER;
|
||||
|
||||
int i, num;
|
||||
static edict_t *touch[MAX_EDICTS];
|
||||
edict_t *hit;
|
||||
|
||||
num = gi.BoxEdicts(ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_SOLID, KillBox_BoxFilter, nullptr);
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
hit = touch[i];
|
||||
|
||||
if (hit == ent)
|
||||
continue;
|
||||
else if (!hit->inuse || !hit->takedamage || !hit->solid || hit->solid == SOLID_TRIGGER)
|
||||
continue;
|
||||
else if (hit->client && !(mask & CONTENTS_PLAYER))
|
||||
continue;
|
||||
|
||||
if ((ent->solid == SOLID_BSP || (ent->svflags & SVF_HULL)) && bsp_clipping)
|
||||
{
|
||||
trace_t clip = gi.clip(ent, hit->s.origin, hit->mins, hit->maxs, hit->s.origin, G_GetClipMask(hit));
|
||||
|
||||
if (clip.fraction == 1.0f)
|
||||
continue;
|
||||
}
|
||||
|
||||
T_Damage(hit, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, mod);
|
||||
}
|
||||
|
||||
return true; // all clear
|
||||
}
|
||||
1217
rerelease/g_weapon.cpp
Normal file
1217
rerelease/g_weapon.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2315
rerelease/game.h
Normal file
2315
rerelease/game.h
Normal file
File diff suppressed because it is too large
Load Diff
25
rerelease/game.sln
Normal file
25
rerelease/game.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.7.33808.371
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "game", "game.vcxproj", "{C994B5EA-3058-403C-953D-3673C2C4D64E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C994B5EA-3058-403C-953D-3673C2C4D64E}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{C994B5EA-3058-403C-953D-3673C2C4D64E}.Debug|x64.Build.0 = Debug|x64
|
||||
{C994B5EA-3058-403C-953D-3673C2C4D64E}.Release|x64.ActiveCfg = Release|x64
|
||||
{C994B5EA-3058-403C-953D-3673C2C4D64E}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {5E7547B5-6A49-45CD-97AA-956B8460495D}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
246
rerelease/game.vcxproj
Normal file
246
rerelease/game.vcxproj
Normal file
@@ -0,0 +1,246 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{c994b5ea-3058-403c-953d-3673c2c4d64e}</ProjectGuid>
|
||||
<RootNamespace>game</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>../</OutDir>
|
||||
<TargetName>$(ProjectName)_x64</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>../</OutDir>
|
||||
<TargetName>$(ProjectName)_x64</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<VcpkgUseStatic>true</VcpkgUseStatic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<VcpkgUseStatic>true</VcpkgUseStatic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg">
|
||||
<VcpkgEnableManifest>true</VcpkgEnableManifest>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>KEX_Q2_GAME;KEX_Q2GAME_EXPORTS;NO_FMT_SOURCE;KEX_Q2GAME_DYNAMIC;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<DisableSpecificWarnings>4267;4244</DisableSpecificWarnings>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>NotSet</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>KEX_Q2_GAME;KEX_Q2GAME_EXPORTS;NO_FMT_SOURCE;KEX_Q2GAME_DYNAMIC;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<DisableSpecificWarnings>4267;4244</DisableSpecificWarnings>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>NotSet</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="bg_local.h" />
|
||||
<ClInclude Include="bots\bot_debug.h" />
|
||||
<ClInclude Include="bots\bot_exports.h" />
|
||||
<ClInclude Include="bots\bot_includes.h" />
|
||||
<ClInclude Include="bots\bot_think.h" />
|
||||
<ClInclude Include="bots\bot_utils.h" />
|
||||
<ClInclude Include="cg_local.h" />
|
||||
<ClInclude Include="ctf\g_ctf.h" />
|
||||
<ClInclude Include="ctf\p_ctf_menu.h" />
|
||||
<ClInclude Include="game.h" />
|
||||
<ClInclude Include="g_local.h" />
|
||||
<ClInclude Include="g_statusbar.h" />
|
||||
<ClInclude Include="m_actor.h" />
|
||||
<ClInclude Include="m_arachnid.h" />
|
||||
<ClInclude Include="m_berserk.h" />
|
||||
<ClInclude Include="m_boss2.h" />
|
||||
<ClInclude Include="m_boss31.h" />
|
||||
<ClInclude Include="m_boss32.h" />
|
||||
<ClInclude Include="m_brain.h" />
|
||||
<ClInclude Include="m_chick.h" />
|
||||
<ClInclude Include="m_flash.h" />
|
||||
<ClInclude Include="m_flipper.h" />
|
||||
<ClInclude Include="m_float.h" />
|
||||
<ClInclude Include="m_flyer.h" />
|
||||
<ClInclude Include="m_gladiator.h" />
|
||||
<ClInclude Include="m_guardian.h" />
|
||||
<ClInclude Include="m_gunner.h" />
|
||||
<ClInclude Include="m_hover.h" />
|
||||
<ClInclude Include="m_infantry.h" />
|
||||
<ClInclude Include="m_insane.h" />
|
||||
<ClInclude Include="m_medic.h" />
|
||||
<ClInclude Include="m_mutant.h" />
|
||||
<ClInclude Include="m_parasite.h" />
|
||||
<ClInclude Include="m_player.h" />
|
||||
<ClInclude Include="m_rider.h" />
|
||||
<ClInclude Include="m_shambler.h" />
|
||||
<ClInclude Include="m_soldier.h" />
|
||||
<ClInclude Include="m_supertank.h" />
|
||||
<ClInclude Include="m_tank.h" />
|
||||
<ClInclude Include="q_std.h" />
|
||||
<ClInclude Include="q_vec3.h" />
|
||||
<ClInclude Include="rogue\m_rogue_carrier.h" />
|
||||
<ClInclude Include="rogue\m_rogue_stalker.h" />
|
||||
<ClInclude Include="rogue\m_rogue_turret.h" />
|
||||
<ClInclude Include="rogue\m_rogue_widow.h" />
|
||||
<ClInclude Include="rogue\m_rogue_widow2.h" />
|
||||
<ClInclude Include="xatrix\m_xatrix_fixbot.h" />
|
||||
<ClInclude Include="xatrix\m_xatrix_gekk.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="bots\bot_debug.cpp" />
|
||||
<ClCompile Include="bots\bot_exports.cpp" />
|
||||
<ClCompile Include="bots\bot_think.cpp" />
|
||||
<ClCompile Include="bots\bot_utils.cpp" />
|
||||
<ClCompile Include="cg_main.cpp" />
|
||||
<ClCompile Include="cg_screen.cpp" />
|
||||
<ClCompile Include="ctf\g_ctf.cpp" />
|
||||
<ClCompile Include="ctf\p_ctf_menu.cpp" />
|
||||
<ClCompile Include="g_ai.cpp" />
|
||||
<ClCompile Include="g_chase.cpp" />
|
||||
<ClCompile Include="g_cmds.cpp" />
|
||||
<ClCompile Include="g_combat.cpp" />
|
||||
<ClCompile Include="g_func.cpp" />
|
||||
<ClCompile Include="g_items.cpp" />
|
||||
<ClCompile Include="g_main.cpp" />
|
||||
<ClCompile Include="g_misc.cpp" />
|
||||
<ClCompile Include="g_monster.cpp" />
|
||||
<ClCompile Include="g_phys.cpp" />
|
||||
<ClCompile Include="g_save.cpp" />
|
||||
<ClCompile Include="g_spawn.cpp" />
|
||||
<ClCompile Include="g_svcmds.cpp" />
|
||||
<ClCompile Include="g_target.cpp" />
|
||||
<ClCompile Include="g_trigger.cpp" />
|
||||
<ClCompile Include="g_turret.cpp" />
|
||||
<ClCompile Include="g_utils.cpp" />
|
||||
<ClCompile Include="g_weapon.cpp" />
|
||||
<ClCompile Include="m_actor.cpp" />
|
||||
<ClCompile Include="m_arachnid.cpp" />
|
||||
<ClCompile Include="m_berserk.cpp" />
|
||||
<ClCompile Include="m_boss2.cpp" />
|
||||
<ClCompile Include="m_boss3.cpp" />
|
||||
<ClCompile Include="m_boss31.cpp" />
|
||||
<ClCompile Include="m_boss32.cpp" />
|
||||
<ClCompile Include="m_brain.cpp" />
|
||||
<ClCompile Include="m_chick.cpp" />
|
||||
<ClCompile Include="m_flipper.cpp" />
|
||||
<ClCompile Include="m_float.cpp" />
|
||||
<ClCompile Include="m_flyer.cpp" />
|
||||
<ClCompile Include="m_gladiator.cpp" />
|
||||
<ClCompile Include="m_guardian.cpp" />
|
||||
<ClCompile Include="m_guncmdr.cpp" />
|
||||
<ClCompile Include="m_gunner.cpp" />
|
||||
<ClCompile Include="m_hover.cpp" />
|
||||
<ClCompile Include="m_infantry.cpp" />
|
||||
<ClCompile Include="m_insane.cpp" />
|
||||
<ClCompile Include="m_medic.cpp" />
|
||||
<ClCompile Include="m_move.cpp" />
|
||||
<ClCompile Include="m_mutant.cpp" />
|
||||
<ClCompile Include="m_parasite.cpp" />
|
||||
<ClCompile Include="m_shambler.cpp" />
|
||||
<ClCompile Include="m_soldier.cpp" />
|
||||
<ClCompile Include="m_supertank.cpp" />
|
||||
<ClCompile Include="m_tank.cpp" />
|
||||
<ClCompile Include="p_client.cpp" />
|
||||
<ClCompile Include="p_hud.cpp" />
|
||||
<ClCompile Include="p_move.cpp" />
|
||||
<ClCompile Include="p_trail.cpp" />
|
||||
<ClCompile Include="p_view.cpp" />
|
||||
<ClCompile Include="p_weapon.cpp" />
|
||||
<ClCompile Include="q_std.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_combat.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_func.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_items.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_misc.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_monster.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_newai.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_newdm.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_newfnc.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_newtarg.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_newtrig.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_newweap.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_phys.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_spawn.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_sphere.cpp" />
|
||||
<ClCompile Include="rogue\g_rogue_utils.cpp" />
|
||||
<ClCompile Include="rogue\m_rogue_carrier.cpp" />
|
||||
<ClCompile Include="rogue\m_rogue_stalker.cpp" />
|
||||
<ClCompile Include="rogue\m_rogue_turret.cpp" />
|
||||
<ClCompile Include="rogue\m_rogue_widow.cpp" />
|
||||
<ClCompile Include="rogue\m_rogue_widow2.cpp" />
|
||||
<ClCompile Include="rogue\p_rogue_weapon.cpp" />
|
||||
<ClCompile Include="rogue\rogue_dm_ball.cpp" />
|
||||
<ClCompile Include="rogue\rogue_dm_tag.cpp" />
|
||||
<ClCompile Include="xatrix\g_xatrix_func.cpp" />
|
||||
<ClCompile Include="xatrix\g_xatrix_items.cpp" />
|
||||
<ClCompile Include="xatrix\g_xatrix_misc.cpp" />
|
||||
<ClCompile Include="xatrix\g_xatrix_monster.cpp" />
|
||||
<ClCompile Include="xatrix\g_xatrix_target.cpp" />
|
||||
<ClCompile Include="xatrix\g_xatrix_weapon.cpp" />
|
||||
<ClCompile Include="xatrix\m_xatrix_fixbot.cpp" />
|
||||
<ClCompile Include="xatrix\m_xatrix_gekk.cpp" />
|
||||
<ClCompile Include="xatrix\p_xatrix_weapon.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
265
rerelease/game.vcxproj.filters
Normal file
265
rerelease/game.vcxproj.filters
Normal file
@@ -0,0 +1,265 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClInclude Include="bg_local.h" />
|
||||
<ClInclude Include="cg_local.h" />
|
||||
<ClInclude Include="g_local.h" />
|
||||
<ClInclude Include="g_statusbar.h" />
|
||||
<ClInclude Include="game.h" />
|
||||
<ClInclude Include="m_actor.h" />
|
||||
<ClInclude Include="m_arachnid.h" />
|
||||
<ClInclude Include="m_berserk.h" />
|
||||
<ClInclude Include="m_boss2.h" />
|
||||
<ClInclude Include="m_boss31.h" />
|
||||
<ClInclude Include="m_boss32.h" />
|
||||
<ClInclude Include="m_brain.h" />
|
||||
<ClInclude Include="m_chick.h" />
|
||||
<ClInclude Include="m_flash.h" />
|
||||
<ClInclude Include="m_flipper.h" />
|
||||
<ClInclude Include="m_float.h" />
|
||||
<ClInclude Include="m_flyer.h" />
|
||||
<ClInclude Include="m_gladiator.h" />
|
||||
<ClInclude Include="m_guardian.h" />
|
||||
<ClInclude Include="m_gunner.h" />
|
||||
<ClInclude Include="m_hover.h" />
|
||||
<ClInclude Include="m_infantry.h" />
|
||||
<ClInclude Include="m_insane.h" />
|
||||
<ClInclude Include="m_medic.h" />
|
||||
<ClInclude Include="m_mutant.h" />
|
||||
<ClInclude Include="m_parasite.h" />
|
||||
<ClInclude Include="m_player.h" />
|
||||
<ClInclude Include="m_rider.h" />
|
||||
<ClInclude Include="m_shambler.h" />
|
||||
<ClInclude Include="m_soldier.h" />
|
||||
<ClInclude Include="m_supertank.h" />
|
||||
<ClInclude Include="m_tank.h" />
|
||||
<ClInclude Include="q_std.h" />
|
||||
<ClInclude Include="q_vec3.h" />
|
||||
<ClInclude Include="bots\bot_debug.h">
|
||||
<Filter>bots</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bots\bot_exports.h">
|
||||
<Filter>bots</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bots\bot_includes.h">
|
||||
<Filter>bots</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bots\bot_think.h">
|
||||
<Filter>bots</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bots\bot_utils.h">
|
||||
<Filter>bots</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ctf\g_ctf.h">
|
||||
<Filter>ctf</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ctf\p_ctf_menu.h">
|
||||
<Filter>ctf</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rogue\m_rogue_carrier.h">
|
||||
<Filter>rogue</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rogue\m_rogue_stalker.h">
|
||||
<Filter>rogue</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rogue\m_rogue_turret.h">
|
||||
<Filter>rogue</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rogue\m_rogue_widow.h">
|
||||
<Filter>rogue</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rogue\m_rogue_widow2.h">
|
||||
<Filter>rogue</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="xatrix\m_xatrix_fixbot.h">
|
||||
<Filter>xatrix</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="xatrix\m_xatrix_gekk.h">
|
||||
<Filter>xatrix</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="cg_main.cpp" />
|
||||
<ClCompile Include="cg_screen.cpp" />
|
||||
<ClCompile Include="g_ai.cpp" />
|
||||
<ClCompile Include="g_chase.cpp" />
|
||||
<ClCompile Include="g_cmds.cpp" />
|
||||
<ClCompile Include="g_combat.cpp" />
|
||||
<ClCompile Include="g_func.cpp" />
|
||||
<ClCompile Include="g_items.cpp" />
|
||||
<ClCompile Include="g_main.cpp" />
|
||||
<ClCompile Include="g_misc.cpp" />
|
||||
<ClCompile Include="g_monster.cpp" />
|
||||
<ClCompile Include="g_phys.cpp" />
|
||||
<ClCompile Include="g_save.cpp" />
|
||||
<ClCompile Include="g_spawn.cpp" />
|
||||
<ClCompile Include="g_svcmds.cpp" />
|
||||
<ClCompile Include="g_target.cpp" />
|
||||
<ClCompile Include="g_trigger.cpp" />
|
||||
<ClCompile Include="g_turret.cpp" />
|
||||
<ClCompile Include="g_utils.cpp" />
|
||||
<ClCompile Include="g_weapon.cpp" />
|
||||
<ClCompile Include="m_actor.cpp" />
|
||||
<ClCompile Include="m_arachnid.cpp" />
|
||||
<ClCompile Include="m_berserk.cpp" />
|
||||
<ClCompile Include="m_boss2.cpp" />
|
||||
<ClCompile Include="m_boss3.cpp" />
|
||||
<ClCompile Include="m_boss31.cpp" />
|
||||
<ClCompile Include="m_boss32.cpp" />
|
||||
<ClCompile Include="m_brain.cpp" />
|
||||
<ClCompile Include="m_chick.cpp" />
|
||||
<ClCompile Include="m_flipper.cpp" />
|
||||
<ClCompile Include="m_float.cpp" />
|
||||
<ClCompile Include="m_flyer.cpp" />
|
||||
<ClCompile Include="m_gladiator.cpp" />
|
||||
<ClCompile Include="m_guardian.cpp" />
|
||||
<ClCompile Include="m_guncmdr.cpp" />
|
||||
<ClCompile Include="m_gunner.cpp" />
|
||||
<ClCompile Include="m_hover.cpp" />
|
||||
<ClCompile Include="m_infantry.cpp" />
|
||||
<ClCompile Include="m_insane.cpp" />
|
||||
<ClCompile Include="m_medic.cpp" />
|
||||
<ClCompile Include="m_move.cpp" />
|
||||
<ClCompile Include="m_mutant.cpp" />
|
||||
<ClCompile Include="m_parasite.cpp" />
|
||||
<ClCompile Include="m_shambler.cpp" />
|
||||
<ClCompile Include="m_soldier.cpp" />
|
||||
<ClCompile Include="m_supertank.cpp" />
|
||||
<ClCompile Include="m_tank.cpp" />
|
||||
<ClCompile Include="p_client.cpp" />
|
||||
<ClCompile Include="p_hud.cpp" />
|
||||
<ClCompile Include="p_move.cpp" />
|
||||
<ClCompile Include="p_trail.cpp" />
|
||||
<ClCompile Include="p_view.cpp" />
|
||||
<ClCompile Include="p_weapon.cpp" />
|
||||
<ClCompile Include="q_std.cpp" />
|
||||
<ClCompile Include="bots\bot_debug.cpp">
|
||||
<Filter>bots</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="bots\bot_exports.cpp">
|
||||
<Filter>bots</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="bots\bot_think.cpp">
|
||||
<Filter>bots</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="bots\bot_utils.cpp">
|
||||
<Filter>bots</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ctf\g_ctf.cpp">
|
||||
<Filter>ctf</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ctf\p_ctf_menu.cpp">
|
||||
<Filter>ctf</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_combat.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_func.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_items.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_misc.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_monster.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_newai.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_newdm.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_newfnc.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_newtarg.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_newtrig.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_newweap.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_phys.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_spawn.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_sphere.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\g_rogue_utils.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\m_rogue_carrier.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\m_rogue_stalker.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\m_rogue_turret.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\m_rogue_widow.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\m_rogue_widow2.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\p_rogue_weapon.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\rogue_dm_ball.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rogue\rogue_dm_tag.cpp">
|
||||
<Filter>rogue</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xatrix\g_xatrix_func.cpp">
|
||||
<Filter>xatrix</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xatrix\g_xatrix_items.cpp">
|
||||
<Filter>xatrix</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xatrix\g_xatrix_misc.cpp">
|
||||
<Filter>xatrix</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xatrix\g_xatrix_monster.cpp">
|
||||
<Filter>xatrix</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xatrix\g_xatrix_target.cpp">
|
||||
<Filter>xatrix</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xatrix\g_xatrix_weapon.cpp">
|
||||
<Filter>xatrix</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xatrix\m_xatrix_fixbot.cpp">
|
||||
<Filter>xatrix</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xatrix\m_xatrix_gekk.cpp">
|
||||
<Filter>xatrix</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xatrix\p_xatrix_weapon.cpp">
|
||||
<Filter>xatrix</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="bots">
|
||||
<UniqueIdentifier>{185665ce-b604-4d9a-b22b-02a83797d112}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ctf">
|
||||
<UniqueIdentifier>{2b0fdaa0-3de9-4bbd-9bc6-3cadf798c291}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="rogue">
|
||||
<UniqueIdentifier>{76ef0be8-1c1d-472a-ada1-3aa1b354e022}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="xatrix">
|
||||
<UniqueIdentifier>{6565427e-a805-4dc7-ba57-3ce0b62e4336}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
564
rerelease/m_actor.cpp
Normal file
564
rerelease/m_actor.cpp
Normal file
@@ -0,0 +1,564 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// g_actor.c
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_actor.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
constexpr const char *actor_names[] = {
|
||||
"Hellrot",
|
||||
"Tokay",
|
||||
"Killme",
|
||||
"Disruptor",
|
||||
"Adrianator",
|
||||
"Rambear",
|
||||
"Titus",
|
||||
"Bitterman"
|
||||
};
|
||||
|
||||
mframe_t actor_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(actor_move_stand) = { FRAME_stand101, FRAME_stand140, actor_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(actor_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &actor_move_stand);
|
||||
|
||||
// randomize on startup
|
||||
if (level.time < 1_sec)
|
||||
self->s.frame = irandom(self->monsterinfo.active_move->firstframe, self->monsterinfo.active_move->lastframe + 1);
|
||||
}
|
||||
|
||||
mframe_t actor_frames_walk[] = {
|
||||
{ ai_walk },
|
||||
{ ai_walk, 6 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 3 },
|
||||
{ ai_walk, 2 },
|
||||
{ ai_walk, 7 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 1 }
|
||||
};
|
||||
MMOVE_T(actor_move_walk) = { FRAME_walk01, FRAME_walk08, actor_frames_walk, nullptr };
|
||||
|
||||
MONSTERINFO_WALK(actor_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &actor_move_walk);
|
||||
}
|
||||
|
||||
mframe_t actor_frames_run[] = {
|
||||
{ ai_run, 4 },
|
||||
{ ai_run, 15 },
|
||||
{ ai_run, 15 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 20 },
|
||||
{ ai_run, 15 }
|
||||
};
|
||||
MMOVE_T(actor_move_run) = { FRAME_run02, FRAME_run07, actor_frames_run, nullptr };
|
||||
|
||||
MONSTERINFO_RUN(actor_run) (edict_t *self) -> void
|
||||
{
|
||||
if ((level.time < self->pain_debounce_time) && (!self->enemy))
|
||||
{
|
||||
if (self->movetarget)
|
||||
actor_walk(self);
|
||||
else
|
||||
actor_stand(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
actor_stand(self);
|
||||
return;
|
||||
}
|
||||
|
||||
M_SetAnimation(self, &actor_move_run);
|
||||
}
|
||||
|
||||
mframe_t actor_frames_pain1[] = {
|
||||
{ ai_move, -5 },
|
||||
{ ai_move, 4 },
|
||||
{ ai_move, 1 }
|
||||
};
|
||||
MMOVE_T(actor_move_pain1) = { FRAME_pain101, FRAME_pain103, actor_frames_pain1, actor_run };
|
||||
|
||||
mframe_t actor_frames_pain2[] = {
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, 4 },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(actor_move_pain2) = { FRAME_pain201, FRAME_pain203, actor_frames_pain2, actor_run };
|
||||
|
||||
mframe_t actor_frames_pain3[] = {
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 0 }
|
||||
};
|
||||
MMOVE_T(actor_move_pain3) = { FRAME_pain301, FRAME_pain303, actor_frames_pain3, actor_run };
|
||||
|
||||
mframe_t actor_frames_flipoff[] = {
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn }
|
||||
};
|
||||
MMOVE_T(actor_move_flipoff) = { FRAME_flip01, FRAME_flip14, actor_frames_flipoff, actor_run };
|
||||
|
||||
mframe_t actor_frames_taunt[] = {
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn },
|
||||
{ ai_turn }
|
||||
};
|
||||
MMOVE_T(actor_move_taunt) = { FRAME_taunt01, FRAME_taunt17, actor_frames_taunt, actor_run };
|
||||
|
||||
const char *messages[] = {
|
||||
"Watch it",
|
||||
"#$@*&",
|
||||
"Idiot",
|
||||
"Check your targets"
|
||||
};
|
||||
|
||||
PAIN(actor_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
int n;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
// gi.sound (self, CHAN_VOICE, actor.sound_pain, 1, ATTN_NORM, 0);
|
||||
|
||||
if ((other->client) && (frandom() < 0.4f))
|
||||
{
|
||||
vec3_t v;
|
||||
const char *name;
|
||||
|
||||
v = other->s.origin - self->s.origin;
|
||||
self->ideal_yaw = vectoyaw(v);
|
||||
if (frandom() < 0.5f)
|
||||
M_SetAnimation(self, &actor_move_flipoff);
|
||||
else
|
||||
M_SetAnimation(self, &actor_move_taunt);
|
||||
name = actor_names[(self - g_edicts) % q_countof(actor_names)];
|
||||
gi.LocClient_Print(other, PRINT_CHAT, "{}: {}!\n", name, random_element(messages));
|
||||
return;
|
||||
}
|
||||
|
||||
n = irandom(3);
|
||||
if (n == 0)
|
||||
M_SetAnimation(self, &actor_move_pain1);
|
||||
else if (n == 1)
|
||||
M_SetAnimation(self, &actor_move_pain2);
|
||||
else
|
||||
M_SetAnimation(self, &actor_move_pain3);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(actor_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
void actorMachineGun(edict_t *self)
|
||||
{
|
||||
vec3_t start, target;
|
||||
vec3_t forward, right;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_ACTOR_MACHINEGUN_1], forward, right);
|
||||
if (self->enemy)
|
||||
{
|
||||
if (self->enemy->health > 0)
|
||||
{
|
||||
target = self->enemy->s.origin + (self->enemy->velocity * -0.2f);
|
||||
target[2] += self->enemy->viewheight;
|
||||
}
|
||||
else
|
||||
{
|
||||
target = self->enemy->absmin;
|
||||
target[2] += (self->enemy->size[2] / 2) + 1;
|
||||
}
|
||||
forward = target - start;
|
||||
forward.normalize();
|
||||
}
|
||||
else
|
||||
{
|
||||
AngleVectors(self->s.angles, forward, nullptr, nullptr);
|
||||
}
|
||||
monster_fire_bullet(self, start, forward, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_ACTOR_MACHINEGUN_1);
|
||||
}
|
||||
|
||||
void actor_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, -8 };
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0_ms;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t actor_frames_death1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, -13 },
|
||||
{ ai_move, 14 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, 1 }
|
||||
};
|
||||
MMOVE_T(actor_move_death1) = { FRAME_death101, FRAME_death107, actor_frames_death1, actor_dead };
|
||||
|
||||
mframe_t actor_frames_death2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move, 7 },
|
||||
{ ai_move, -6 },
|
||||
{ ai_move, -5 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, -9 },
|
||||
{ ai_move, -13 },
|
||||
{ ai_move, -13 },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(actor_move_death2) = { FRAME_death201, FRAME_death213, actor_frames_death2, actor_dead };
|
||||
|
||||
DIE(actor_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
// check for gib
|
||||
if (self->health <= -80)
|
||||
{
|
||||
// gi.sound (self, CHAN_VOICE, actor.sound_gib, 1, ATTN_NORM, 0);
|
||||
ThrowGibs(self, damage, {
|
||||
{ 2, "models/objects/gibs/bone/tris.md2" },
|
||||
{ 4, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ "models/objects/gibs/head2/tris.md2", GIB_HEAD }
|
||||
});
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
// gi.sound (self, CHAN_VOICE, actor.sound_die, 1, ATTN_NORM, 0);
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
|
||||
if (brandom())
|
||||
M_SetAnimation(self, &actor_move_death1);
|
||||
else
|
||||
M_SetAnimation(self, &actor_move_death2);
|
||||
}
|
||||
|
||||
void actor_fire(edict_t *self)
|
||||
{
|
||||
actorMachineGun(self);
|
||||
|
||||
if (level.time >= self->monsterinfo.fire_wait)
|
||||
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
|
||||
else
|
||||
self->monsterinfo.aiflags |= AI_HOLD_FRAME;
|
||||
}
|
||||
|
||||
mframe_t actor_frames_attack[] = {
|
||||
{ ai_charge, -2, actor_fire },
|
||||
{ ai_charge, -2 },
|
||||
{ ai_charge, 3 },
|
||||
{ ai_charge, 2 }
|
||||
};
|
||||
MMOVE_T(actor_move_attack) = { FRAME_attak01, FRAME_attak04, actor_frames_attack, actor_run };
|
||||
|
||||
MONSTERINFO_ATTACK(actor_attack) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &actor_move_attack);
|
||||
self->monsterinfo.fire_wait = level.time + random_time(1_sec, 2.6_sec);
|
||||
}
|
||||
|
||||
USE(actor_use) (edict_t *self, edict_t *other, edict_t *activator) -> void
|
||||
{
|
||||
vec3_t v;
|
||||
|
||||
self->goalentity = self->movetarget = G_PickTarget(self->target);
|
||||
if ((!self->movetarget) || (strcmp(self->movetarget->classname, "target_actor") != 0))
|
||||
{
|
||||
gi.Com_PrintFmt("{}: bad target {}\n", *self, self->target);
|
||||
self->target = nullptr;
|
||||
self->monsterinfo.pausetime = HOLD_FOREVER;
|
||||
self->monsterinfo.stand(self);
|
||||
return;
|
||||
}
|
||||
|
||||
v = self->goalentity->s.origin - self->s.origin;
|
||||
self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
|
||||
self->monsterinfo.walk(self);
|
||||
self->target = nullptr;
|
||||
}
|
||||
|
||||
/*QUAKED misc_actor (1 .5 0) (-16 -16 -24) (16 16 32)
|
||||
*/
|
||||
|
||||
void SP_misc_actor(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->targetname)
|
||||
{
|
||||
gi.Com_PrintFmt("{}: no targetname\n", *self);
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->target)
|
||||
{
|
||||
gi.Com_PrintFmt("{}: no target\n", *self);
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("players/male/tris.md2");
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, 32 };
|
||||
|
||||
if (!self->health)
|
||||
self->health = 100;
|
||||
self->mass = 200;
|
||||
|
||||
self->pain = actor_pain;
|
||||
self->die = actor_die;
|
||||
|
||||
self->monsterinfo.stand = actor_stand;
|
||||
self->monsterinfo.walk = actor_walk;
|
||||
self->monsterinfo.run = actor_run;
|
||||
self->monsterinfo.attack = actor_attack;
|
||||
self->monsterinfo.melee = nullptr;
|
||||
self->monsterinfo.sight = nullptr;
|
||||
self->monsterinfo.setskin = actor_setskin;
|
||||
|
||||
self->monsterinfo.aiflags |= AI_GOOD_GUY;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &actor_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
|
||||
// actors always start in a dormant state, they *must* be used to get going
|
||||
self->use = actor_use;
|
||||
}
|
||||
|
||||
/*QUAKED target_actor (.5 .3 0) (-8 -8 -8) (8 8 8) JUMP SHOOT ATTACK x HOLD BRUTAL
|
||||
JUMP jump in set direction upon reaching this target
|
||||
SHOOT take a single shot at the pathtarget
|
||||
ATTACK attack pathtarget until it or actor is dead
|
||||
|
||||
"target" next target_actor
|
||||
"pathtarget" target of any action to be taken at this point
|
||||
"wait" amount of time actor should pause at this point
|
||||
"message" actor will "say" this to the player
|
||||
|
||||
for JUMP only:
|
||||
"speed" speed thrown forward (default 200)
|
||||
"height" speed thrown upwards (default 200)
|
||||
*/
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_TARGET_ACTOR_JUMP = 1_spawnflag;
|
||||
constexpr spawnflags_t SPAWNFLAG_TARGET_ACTOR_SHOOT = 2_spawnflag;
|
||||
constexpr spawnflags_t SPAWNFLAG_TARGET_ACTOR_ATTACK = 4_spawnflag;
|
||||
constexpr spawnflags_t SPAWNFLAG_TARGET_ACTOR_HOLD = 16_spawnflag;
|
||||
constexpr spawnflags_t SPAWNFLAG_TARGET_ACTOR_BRUTAL = 32_spawnflag;
|
||||
|
||||
TOUCH(target_actor_touch) (edict_t *self, edict_t *other, const trace_t &tr, bool other_touching_self) -> void
|
||||
{
|
||||
vec3_t v;
|
||||
|
||||
if (other->movetarget != self)
|
||||
return;
|
||||
|
||||
if (other->enemy)
|
||||
return;
|
||||
|
||||
other->goalentity = other->movetarget = nullptr;
|
||||
|
||||
if (self->message)
|
||||
{
|
||||
edict_t *ent;
|
||||
|
||||
for (uint32_t n = 1; n <= game.maxclients; n++)
|
||||
{
|
||||
ent = &g_edicts[n];
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
gi.LocClient_Print(ent, PRINT_CHAT, "{}: {}\n", actor_names[(other - g_edicts) % q_countof(actor_names)], self->message);
|
||||
}
|
||||
}
|
||||
|
||||
if (self->spawnflags.has(SPAWNFLAG_TARGET_ACTOR_JUMP)) // jump
|
||||
{
|
||||
other->velocity[0] = self->movedir[0] * self->speed;
|
||||
other->velocity[1] = self->movedir[1] * self->speed;
|
||||
|
||||
if (other->groundentity)
|
||||
{
|
||||
other->groundentity = nullptr;
|
||||
other->velocity[2] = self->movedir[2];
|
||||
gi.sound(other, CHAN_VOICE, gi.soundindex("player/male/jump1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (self->spawnflags.has(SPAWNFLAG_TARGET_ACTOR_SHOOT)) // shoot
|
||||
{
|
||||
}
|
||||
else if (self->spawnflags.has(SPAWNFLAG_TARGET_ACTOR_ATTACK)) // attack
|
||||
{
|
||||
other->enemy = G_PickTarget(self->pathtarget);
|
||||
if (other->enemy)
|
||||
{
|
||||
other->goalentity = other->enemy;
|
||||
if (self->spawnflags.has(SPAWNFLAG_TARGET_ACTOR_BRUTAL))
|
||||
other->monsterinfo.aiflags |= AI_BRUTAL;
|
||||
if (self->spawnflags.has(SPAWNFLAG_TARGET_ACTOR_HOLD))
|
||||
{
|
||||
other->monsterinfo.aiflags |= AI_STAND_GROUND;
|
||||
actor_stand(other);
|
||||
}
|
||||
else
|
||||
{
|
||||
actor_run(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!self->spawnflags.has((SPAWNFLAG_TARGET_ACTOR_ATTACK | SPAWNFLAG_TARGET_ACTOR_SHOOT)) && (self->pathtarget))
|
||||
{
|
||||
const char *savetarget;
|
||||
|
||||
savetarget = self->target;
|
||||
self->target = self->pathtarget;
|
||||
G_UseTargets(self, other);
|
||||
self->target = savetarget;
|
||||
}
|
||||
|
||||
other->movetarget = G_PickTarget(self->target);
|
||||
|
||||
if (!other->goalentity)
|
||||
other->goalentity = other->movetarget;
|
||||
|
||||
if (!other->movetarget && !other->enemy)
|
||||
{
|
||||
other->monsterinfo.pausetime = HOLD_FOREVER;
|
||||
other->monsterinfo.stand(other);
|
||||
}
|
||||
else if (other->movetarget == other->goalentity)
|
||||
{
|
||||
v = other->movetarget->s.origin - other->s.origin;
|
||||
other->ideal_yaw = vectoyaw(v);
|
||||
}
|
||||
}
|
||||
|
||||
void SP_target_actor(edict_t *self)
|
||||
{
|
||||
if (!self->targetname)
|
||||
gi.Com_PrintFmt("{}: no targetname\n", *self);
|
||||
|
||||
self->solid = SOLID_TRIGGER;
|
||||
self->touch = target_actor_touch;
|
||||
self->mins = { -8, -8, -8 };
|
||||
self->maxs = { 8, 8, 8 };
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
|
||||
if (self->spawnflags.has(SPAWNFLAG_TARGET_ACTOR_JUMP))
|
||||
{
|
||||
if (!self->speed)
|
||||
self->speed = 200;
|
||||
if (!st.height)
|
||||
st.height = 200;
|
||||
if (self->s.angles[YAW] == 0)
|
||||
self->s.angles[YAW] = 360;
|
||||
G_SetMovedir(self->s.angles, self->movedir);
|
||||
self->movedir[2] = (float) st.height;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
492
rerelease/m_actor.h
Normal file
492
rerelease/m_actor.h
Normal file
@@ -0,0 +1,492 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/player_y
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_attak01,
|
||||
FRAME_attak02,
|
||||
FRAME_attak03,
|
||||
FRAME_attak04,
|
||||
FRAME_death101,
|
||||
FRAME_death102,
|
||||
FRAME_death103,
|
||||
FRAME_death104,
|
||||
FRAME_death105,
|
||||
FRAME_death106,
|
||||
FRAME_death107,
|
||||
FRAME_death201,
|
||||
FRAME_death202,
|
||||
FRAME_death203,
|
||||
FRAME_death204,
|
||||
FRAME_death205,
|
||||
FRAME_death206,
|
||||
FRAME_death207,
|
||||
FRAME_death208,
|
||||
FRAME_death209,
|
||||
FRAME_death210,
|
||||
FRAME_death211,
|
||||
FRAME_death212,
|
||||
FRAME_death213,
|
||||
FRAME_death301,
|
||||
FRAME_death302,
|
||||
FRAME_death303,
|
||||
FRAME_death304,
|
||||
FRAME_death305,
|
||||
FRAME_death306,
|
||||
FRAME_death307,
|
||||
FRAME_death308,
|
||||
FRAME_death309,
|
||||
FRAME_death310,
|
||||
FRAME_death311,
|
||||
FRAME_death312,
|
||||
FRAME_death313,
|
||||
FRAME_death314,
|
||||
FRAME_death315,
|
||||
FRAME_flip01,
|
||||
FRAME_flip02,
|
||||
FRAME_flip03,
|
||||
FRAME_flip04,
|
||||
FRAME_flip05,
|
||||
FRAME_flip06,
|
||||
FRAME_flip07,
|
||||
FRAME_flip08,
|
||||
FRAME_flip09,
|
||||
FRAME_flip10,
|
||||
FRAME_flip11,
|
||||
FRAME_flip12,
|
||||
FRAME_flip13,
|
||||
FRAME_flip14,
|
||||
FRAME_grenad01,
|
||||
FRAME_grenad02,
|
||||
FRAME_grenad03,
|
||||
FRAME_grenad04,
|
||||
FRAME_grenad05,
|
||||
FRAME_grenad06,
|
||||
FRAME_grenad07,
|
||||
FRAME_grenad08,
|
||||
FRAME_grenad09,
|
||||
FRAME_grenad10,
|
||||
FRAME_grenad11,
|
||||
FRAME_grenad12,
|
||||
FRAME_grenad13,
|
||||
FRAME_grenad14,
|
||||
FRAME_grenad15,
|
||||
FRAME_jump01,
|
||||
FRAME_jump02,
|
||||
FRAME_jump03,
|
||||
FRAME_jump04,
|
||||
FRAME_jump05,
|
||||
FRAME_jump06,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_push01,
|
||||
FRAME_push02,
|
||||
FRAME_push03,
|
||||
FRAME_push04,
|
||||
FRAME_push05,
|
||||
FRAME_push06,
|
||||
FRAME_push07,
|
||||
FRAME_push08,
|
||||
FRAME_push09,
|
||||
FRAME_run01,
|
||||
FRAME_run02,
|
||||
FRAME_run03,
|
||||
FRAME_run04,
|
||||
FRAME_run05,
|
||||
FRAME_run06,
|
||||
FRAME_run07,
|
||||
FRAME_run08,
|
||||
FRAME_run09,
|
||||
FRAME_run10,
|
||||
FRAME_run11,
|
||||
FRAME_run12,
|
||||
FRAME_runs01,
|
||||
FRAME_runs02,
|
||||
FRAME_runs03,
|
||||
FRAME_runs04,
|
||||
FRAME_runs05,
|
||||
FRAME_runs06,
|
||||
FRAME_runs07,
|
||||
FRAME_runs08,
|
||||
FRAME_runs09,
|
||||
FRAME_runs10,
|
||||
FRAME_runs11,
|
||||
FRAME_runs12,
|
||||
FRAME_salute01,
|
||||
FRAME_salute02,
|
||||
FRAME_salute03,
|
||||
FRAME_salute04,
|
||||
FRAME_salute05,
|
||||
FRAME_salute06,
|
||||
FRAME_salute07,
|
||||
FRAME_salute08,
|
||||
FRAME_salute09,
|
||||
FRAME_salute10,
|
||||
FRAME_salute11,
|
||||
FRAME_salute12,
|
||||
FRAME_stand101,
|
||||
FRAME_stand102,
|
||||
FRAME_stand103,
|
||||
FRAME_stand104,
|
||||
FRAME_stand105,
|
||||
FRAME_stand106,
|
||||
FRAME_stand107,
|
||||
FRAME_stand108,
|
||||
FRAME_stand109,
|
||||
FRAME_stand110,
|
||||
FRAME_stand111,
|
||||
FRAME_stand112,
|
||||
FRAME_stand113,
|
||||
FRAME_stand114,
|
||||
FRAME_stand115,
|
||||
FRAME_stand116,
|
||||
FRAME_stand117,
|
||||
FRAME_stand118,
|
||||
FRAME_stand119,
|
||||
FRAME_stand120,
|
||||
FRAME_stand121,
|
||||
FRAME_stand122,
|
||||
FRAME_stand123,
|
||||
FRAME_stand124,
|
||||
FRAME_stand125,
|
||||
FRAME_stand126,
|
||||
FRAME_stand127,
|
||||
FRAME_stand128,
|
||||
FRAME_stand129,
|
||||
FRAME_stand130,
|
||||
FRAME_stand131,
|
||||
FRAME_stand132,
|
||||
FRAME_stand133,
|
||||
FRAME_stand134,
|
||||
FRAME_stand135,
|
||||
FRAME_stand136,
|
||||
FRAME_stand137,
|
||||
FRAME_stand138,
|
||||
FRAME_stand139,
|
||||
FRAME_stand140,
|
||||
FRAME_stand201,
|
||||
FRAME_stand202,
|
||||
FRAME_stand203,
|
||||
FRAME_stand204,
|
||||
FRAME_stand205,
|
||||
FRAME_stand206,
|
||||
FRAME_stand207,
|
||||
FRAME_stand208,
|
||||
FRAME_stand209,
|
||||
FRAME_stand210,
|
||||
FRAME_stand211,
|
||||
FRAME_stand212,
|
||||
FRAME_stand213,
|
||||
FRAME_stand214,
|
||||
FRAME_stand215,
|
||||
FRAME_stand216,
|
||||
FRAME_stand217,
|
||||
FRAME_stand218,
|
||||
FRAME_stand219,
|
||||
FRAME_stand220,
|
||||
FRAME_stand221,
|
||||
FRAME_stand222,
|
||||
FRAME_stand223,
|
||||
FRAME_swim01,
|
||||
FRAME_swim02,
|
||||
FRAME_swim03,
|
||||
FRAME_swim04,
|
||||
FRAME_swim05,
|
||||
FRAME_swim06,
|
||||
FRAME_swim07,
|
||||
FRAME_swim08,
|
||||
FRAME_swim09,
|
||||
FRAME_swim10,
|
||||
FRAME_swim11,
|
||||
FRAME_swim12,
|
||||
FRAME_sw_atk01,
|
||||
FRAME_sw_atk02,
|
||||
FRAME_sw_atk03,
|
||||
FRAME_sw_atk04,
|
||||
FRAME_sw_atk05,
|
||||
FRAME_sw_atk06,
|
||||
FRAME_sw_pan01,
|
||||
FRAME_sw_pan02,
|
||||
FRAME_sw_pan03,
|
||||
FRAME_sw_pan04,
|
||||
FRAME_sw_pan05,
|
||||
FRAME_sw_std01,
|
||||
FRAME_sw_std02,
|
||||
FRAME_sw_std03,
|
||||
FRAME_sw_std04,
|
||||
FRAME_sw_std05,
|
||||
FRAME_sw_std06,
|
||||
FRAME_sw_std07,
|
||||
FRAME_sw_std08,
|
||||
FRAME_sw_std09,
|
||||
FRAME_sw_std10,
|
||||
FRAME_sw_std11,
|
||||
FRAME_sw_std12,
|
||||
FRAME_sw_std13,
|
||||
FRAME_sw_std14,
|
||||
FRAME_sw_std15,
|
||||
FRAME_sw_std16,
|
||||
FRAME_sw_std17,
|
||||
FRAME_sw_std18,
|
||||
FRAME_sw_std19,
|
||||
FRAME_sw_std20,
|
||||
FRAME_taunt01,
|
||||
FRAME_taunt02,
|
||||
FRAME_taunt03,
|
||||
FRAME_taunt04,
|
||||
FRAME_taunt05,
|
||||
FRAME_taunt06,
|
||||
FRAME_taunt07,
|
||||
FRAME_taunt08,
|
||||
FRAME_taunt09,
|
||||
FRAME_taunt10,
|
||||
FRAME_taunt11,
|
||||
FRAME_taunt12,
|
||||
FRAME_taunt13,
|
||||
FRAME_taunt14,
|
||||
FRAME_taunt15,
|
||||
FRAME_taunt16,
|
||||
FRAME_taunt17,
|
||||
FRAME_walk01,
|
||||
FRAME_walk02,
|
||||
FRAME_walk03,
|
||||
FRAME_walk04,
|
||||
FRAME_walk05,
|
||||
FRAME_walk06,
|
||||
FRAME_walk07,
|
||||
FRAME_walk08,
|
||||
FRAME_walk09,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_wave01,
|
||||
FRAME_wave02,
|
||||
FRAME_wave03,
|
||||
FRAME_wave04,
|
||||
FRAME_wave05,
|
||||
FRAME_wave06,
|
||||
FRAME_wave07,
|
||||
FRAME_wave08,
|
||||
FRAME_wave09,
|
||||
FRAME_wave10,
|
||||
FRAME_wave11,
|
||||
FRAME_wave12,
|
||||
FRAME_wave13,
|
||||
FRAME_wave14,
|
||||
FRAME_wave15,
|
||||
FRAME_wave16,
|
||||
FRAME_wave17,
|
||||
FRAME_wave18,
|
||||
FRAME_wave19,
|
||||
FRAME_wave20,
|
||||
FRAME_wave21,
|
||||
FRAME_bl_atk01,
|
||||
FRAME_bl_atk02,
|
||||
FRAME_bl_atk03,
|
||||
FRAME_bl_atk04,
|
||||
FRAME_bl_atk05,
|
||||
FRAME_bl_atk06,
|
||||
FRAME_bl_flp01,
|
||||
FRAME_bl_flp02,
|
||||
FRAME_bl_flp13,
|
||||
FRAME_bl_flp14,
|
||||
FRAME_bl_flp15,
|
||||
FRAME_bl_jmp01,
|
||||
FRAME_bl_jmp02,
|
||||
FRAME_bl_jmp03,
|
||||
FRAME_bl_jmp04,
|
||||
FRAME_bl_jmp05,
|
||||
FRAME_bl_jmp06,
|
||||
FRAME_bl_pn101,
|
||||
FRAME_bl_pn102,
|
||||
FRAME_bl_pn103,
|
||||
FRAME_bl_pn201,
|
||||
FRAME_bl_pn202,
|
||||
FRAME_bl_pn203,
|
||||
FRAME_bl_pn301,
|
||||
FRAME_bl_pn302,
|
||||
FRAME_bl_pn303,
|
||||
FRAME_bl_psh08,
|
||||
FRAME_bl_psh09,
|
||||
FRAME_bl_run01,
|
||||
FRAME_bl_run02,
|
||||
FRAME_bl_run03,
|
||||
FRAME_bl_run04,
|
||||
FRAME_bl_run05,
|
||||
FRAME_bl_run06,
|
||||
FRAME_bl_run07,
|
||||
FRAME_bl_run08,
|
||||
FRAME_bl_run09,
|
||||
FRAME_bl_run10,
|
||||
FRAME_bl_run11,
|
||||
FRAME_bl_run12,
|
||||
FRAME_bl_rns03,
|
||||
FRAME_bl_rns04,
|
||||
FRAME_bl_rns05,
|
||||
FRAME_bl_rns06,
|
||||
FRAME_bl_rns07,
|
||||
FRAME_bl_rns08,
|
||||
FRAME_bl_rns09,
|
||||
FRAME_bl_sal10,
|
||||
FRAME_bl_sal11,
|
||||
FRAME_bl_sal12,
|
||||
FRAME_bl_std01,
|
||||
FRAME_bl_std02,
|
||||
FRAME_bl_std03,
|
||||
FRAME_bl_std04,
|
||||
FRAME_bl_std05,
|
||||
FRAME_bl_std06,
|
||||
FRAME_bl_std07,
|
||||
FRAME_bl_std08,
|
||||
FRAME_bl_std09,
|
||||
FRAME_bl_std10,
|
||||
FRAME_bl_std11,
|
||||
FRAME_bl_std12,
|
||||
FRAME_bl_std13,
|
||||
FRAME_bl_std14,
|
||||
FRAME_bl_std15,
|
||||
FRAME_bl_std16,
|
||||
FRAME_bl_std17,
|
||||
FRAME_bl_std18,
|
||||
FRAME_bl_std19,
|
||||
FRAME_bl_std20,
|
||||
FRAME_bl_std21,
|
||||
FRAME_bl_std22,
|
||||
FRAME_bl_std23,
|
||||
FRAME_bl_std24,
|
||||
FRAME_bl_std25,
|
||||
FRAME_bl_std26,
|
||||
FRAME_bl_std27,
|
||||
FRAME_bl_std28,
|
||||
FRAME_bl_std29,
|
||||
FRAME_bl_std30,
|
||||
FRAME_bl_std31,
|
||||
FRAME_bl_std32,
|
||||
FRAME_bl_std33,
|
||||
FRAME_bl_std34,
|
||||
FRAME_bl_std35,
|
||||
FRAME_bl_std36,
|
||||
FRAME_bl_std37,
|
||||
FRAME_bl_std38,
|
||||
FRAME_bl_std39,
|
||||
FRAME_bl_std40,
|
||||
FRAME_bl_swm01,
|
||||
FRAME_bl_swm02,
|
||||
FRAME_bl_swm03,
|
||||
FRAME_bl_swm04,
|
||||
FRAME_bl_swm05,
|
||||
FRAME_bl_swm06,
|
||||
FRAME_bl_swm07,
|
||||
FRAME_bl_swm08,
|
||||
FRAME_bl_swm09,
|
||||
FRAME_bl_swm10,
|
||||
FRAME_bl_swm11,
|
||||
FRAME_bl_swm12,
|
||||
FRAME_bl_swk01,
|
||||
FRAME_bl_swk02,
|
||||
FRAME_bl_swk03,
|
||||
FRAME_bl_swk04,
|
||||
FRAME_bl_swk05,
|
||||
FRAME_bl_swk06,
|
||||
FRAME_bl_swp01,
|
||||
FRAME_bl_swp02,
|
||||
FRAME_bl_swp03,
|
||||
FRAME_bl_swp04,
|
||||
FRAME_bl_swp05,
|
||||
FRAME_bl_sws01,
|
||||
FRAME_bl_sws02,
|
||||
FRAME_bl_sws03,
|
||||
FRAME_bl_sws04,
|
||||
FRAME_bl_sws05,
|
||||
FRAME_bl_sws06,
|
||||
FRAME_bl_sws07,
|
||||
FRAME_bl_sws08,
|
||||
FRAME_bl_sws09,
|
||||
FRAME_bl_sws10,
|
||||
FRAME_bl_sws11,
|
||||
FRAME_bl_sws12,
|
||||
FRAME_bl_sws13,
|
||||
FRAME_bl_sws14,
|
||||
FRAME_bl_tau14,
|
||||
FRAME_bl_tau15,
|
||||
FRAME_bl_tau16,
|
||||
FRAME_bl_tau17,
|
||||
FRAME_bl_wlk01,
|
||||
FRAME_bl_wlk02,
|
||||
FRAME_bl_wlk03,
|
||||
FRAME_bl_wlk04,
|
||||
FRAME_bl_wlk05,
|
||||
FRAME_bl_wlk06,
|
||||
FRAME_bl_wlk07,
|
||||
FRAME_bl_wlk08,
|
||||
FRAME_bl_wlk09,
|
||||
FRAME_bl_wlk10,
|
||||
FRAME_bl_wlk11,
|
||||
FRAME_bl_wav19,
|
||||
FRAME_bl_wav20,
|
||||
FRAME_bl_wav21,
|
||||
FRAME_cr_atk01,
|
||||
FRAME_cr_atk02,
|
||||
FRAME_cr_atk03,
|
||||
FRAME_cr_atk04,
|
||||
FRAME_cr_atk05,
|
||||
FRAME_cr_atk06,
|
||||
FRAME_cr_atk07,
|
||||
FRAME_cr_atk08,
|
||||
FRAME_cr_pan01,
|
||||
FRAME_cr_pan02,
|
||||
FRAME_cr_pan03,
|
||||
FRAME_cr_pan04,
|
||||
FRAME_cr_std01,
|
||||
FRAME_cr_std02,
|
||||
FRAME_cr_std03,
|
||||
FRAME_cr_std04,
|
||||
FRAME_cr_std05,
|
||||
FRAME_cr_std06,
|
||||
FRAME_cr_std07,
|
||||
FRAME_cr_std08,
|
||||
FRAME_cr_wlk01,
|
||||
FRAME_cr_wlk02,
|
||||
FRAME_cr_wlk03,
|
||||
FRAME_cr_wlk04,
|
||||
FRAME_cr_wlk05,
|
||||
FRAME_cr_wlk06,
|
||||
FRAME_cr_wlk07,
|
||||
FRAME_crbl_a01,
|
||||
FRAME_crbl_a02,
|
||||
FRAME_crbl_a03,
|
||||
FRAME_crbl_a04,
|
||||
FRAME_crbl_a05,
|
||||
FRAME_crbl_a06,
|
||||
FRAME_crbl_a07,
|
||||
FRAME_crbl_p01,
|
||||
FRAME_crbl_p02,
|
||||
FRAME_crbl_p03,
|
||||
FRAME_crbl_p04,
|
||||
FRAME_crbl_s01,
|
||||
FRAME_crbl_s02,
|
||||
FRAME_crbl_s03,
|
||||
FRAME_crbl_s04,
|
||||
FRAME_crbl_s05,
|
||||
FRAME_crbl_s06,
|
||||
FRAME_crbl_s07,
|
||||
FRAME_crbl_s08,
|
||||
FRAME_crbl_w01,
|
||||
FRAME_crbl_w02,
|
||||
FRAME_crbl_w03,
|
||||
FRAME_crbl_w04,
|
||||
FRAME_crbl_w05,
|
||||
FRAME_crbl_w06,
|
||||
FRAME_crbl_w07
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
385
rerelease/m_arachnid.cpp
Normal file
385
rerelease/m_arachnid.cpp
Normal file
@@ -0,0 +1,385 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
TANK
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_arachnid.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
static int sound_pain;
|
||||
static int sound_death;
|
||||
static int sound_sight;
|
||||
|
||||
MONSTERINFO_SIGHT(arachnid_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
//
|
||||
// stand
|
||||
//
|
||||
|
||||
mframe_t arachnid_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(arachnid_move_stand) = { FRAME_idle1, FRAME_idle13, arachnid_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(arachnid_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &arachnid_move_stand);
|
||||
}
|
||||
|
||||
//
|
||||
// walk
|
||||
//
|
||||
|
||||
static int sound_step;
|
||||
|
||||
void arachnid_footstep(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_step, 0.5f, ATTN_IDLE, 0.0f);
|
||||
}
|
||||
|
||||
mframe_t arachnid_frames_walk[] = {
|
||||
{ ai_walk, 8, arachnid_footstep },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8, arachnid_footstep },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 }
|
||||
};
|
||||
MMOVE_T(arachnid_move_walk) = { FRAME_walk1, FRAME_walk10, arachnid_frames_walk, nullptr };
|
||||
|
||||
MONSTERINFO_WALK(arachnid_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &arachnid_move_walk);
|
||||
}
|
||||
|
||||
//
|
||||
// run
|
||||
//
|
||||
|
||||
mframe_t arachnid_frames_run[] = {
|
||||
{ ai_run, 8, arachnid_footstep },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8, arachnid_footstep },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 }
|
||||
};
|
||||
MMOVE_T(arachnid_move_run) = { FRAME_walk1, FRAME_walk10, arachnid_frames_run, nullptr };
|
||||
|
||||
MONSTERINFO_RUN(arachnid_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
M_SetAnimation(self, &arachnid_move_stand);
|
||||
return;
|
||||
}
|
||||
|
||||
M_SetAnimation(self, &arachnid_move_run);
|
||||
}
|
||||
|
||||
//
|
||||
// pain
|
||||
//
|
||||
|
||||
mframe_t arachnid_frames_pain1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(arachnid_move_pain1) = { FRAME_pain11, FRAME_pain15, arachnid_frames_pain1, arachnid_run };
|
||||
|
||||
mframe_t arachnid_frames_pain2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(arachnid_move_pain2) = { FRAME_pain21, FRAME_pain26, arachnid_frames_pain2, arachnid_run };
|
||||
|
||||
PAIN(arachnid_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
float r = frandom();
|
||||
|
||||
if (r < 0.5f)
|
||||
M_SetAnimation(self, &arachnid_move_pain1);
|
||||
else
|
||||
M_SetAnimation(self, &arachnid_move_pain2);
|
||||
}
|
||||
|
||||
static int sound_charge;
|
||||
|
||||
void arachnid_charge_rail(edict_t *self)
|
||||
{
|
||||
if (!self->enemy || !self->enemy->inuse)
|
||||
return;
|
||||
|
||||
gi.sound(self, CHAN_WEAPON, sound_charge, 1.f, ATTN_NORM, 0.f);
|
||||
self->pos1 = self->enemy->s.origin;
|
||||
self->pos1[2] += self->enemy->viewheight;
|
||||
}
|
||||
|
||||
void arachnid_rail(edict_t *self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t forward, right;
|
||||
monster_muzzleflash_id_t id;
|
||||
|
||||
switch (self->s.frame)
|
||||
{
|
||||
case FRAME_rails4:
|
||||
default:
|
||||
id = MZ2_ARACHNID_RAIL1;
|
||||
break;
|
||||
case FRAME_rails8:
|
||||
id = MZ2_ARACHNID_RAIL2;
|
||||
break;
|
||||
case FRAME_rails_up7:
|
||||
id = MZ2_ARACHNID_RAIL_UP1;
|
||||
break;
|
||||
case FRAME_rails_up11:
|
||||
id = MZ2_ARACHNID_RAIL_UP2;
|
||||
break;
|
||||
}
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[id], forward, right);
|
||||
|
||||
// calc direction to where we targeted
|
||||
dir = self->pos1 - start;
|
||||
dir.normalize();
|
||||
|
||||
monster_fire_railgun(self, start, dir, 35, 100, id);
|
||||
}
|
||||
|
||||
mframe_t arachnid_frames_attack1[] = {
|
||||
{ ai_charge, 0, arachnid_charge_rail },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, arachnid_rail },
|
||||
{ ai_charge, 0, arachnid_charge_rail },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, arachnid_rail },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(arachnid_attack1) = { FRAME_rails1, FRAME_rails11, arachnid_frames_attack1, arachnid_run };
|
||||
|
||||
mframe_t arachnid_frames_attack_up1[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, arachnid_charge_rail },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, arachnid_rail },
|
||||
{ ai_charge, 0, arachnid_charge_rail },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, arachnid_rail },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
};
|
||||
MMOVE_T(arachnid_attack_up1) = { FRAME_rails_up1, FRAME_rails_up16, arachnid_frames_attack_up1, arachnid_run };
|
||||
|
||||
static int sound_melee, sound_melee_hit;
|
||||
|
||||
void arachnid_melee_charge(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_melee, 1.f, ATTN_NORM, 0.f);
|
||||
}
|
||||
|
||||
void arachnid_melee_hit(edict_t *self)
|
||||
{
|
||||
if (!fire_hit(self, { MELEE_DISTANCE, 0, 0 }, 15, 50))
|
||||
self->monsterinfo.melee_debounce_time = level.time + 1000_ms;
|
||||
}
|
||||
|
||||
mframe_t arachnid_frames_melee[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, arachnid_melee_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, arachnid_melee_hit },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, arachnid_melee_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, arachnid_melee_hit },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(arachnid_melee) = { FRAME_melee_atk1, FRAME_melee_atk12, arachnid_frames_melee, arachnid_run };
|
||||
|
||||
MONSTERINFO_ATTACK(arachnid_attack) (edict_t *self) -> void
|
||||
{
|
||||
if (!self->enemy || !self->enemy->inuse)
|
||||
return;
|
||||
|
||||
if (self->monsterinfo.melee_debounce_time < level.time && range_to(self, self->enemy) < MELEE_DISTANCE)
|
||||
M_SetAnimation(self, &arachnid_melee);
|
||||
else if ((self->enemy->s.origin[2] - self->s.origin[2]) > 150.f)
|
||||
M_SetAnimation(self, &arachnid_attack_up1);
|
||||
else
|
||||
M_SetAnimation(self, &arachnid_attack1);
|
||||
}
|
||||
|
||||
//
|
||||
// death
|
||||
//
|
||||
|
||||
void arachnid_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, -8 };
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0_ms;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t arachnid_frames_death1[] = {
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, -1.23f },
|
||||
{ ai_move, -1.23f },
|
||||
{ ai_move, -1.23f },
|
||||
{ ai_move, -1.23f },
|
||||
{ ai_move, -1.64f },
|
||||
{ ai_move, -1.64f },
|
||||
{ ai_move, -2.45f },
|
||||
{ ai_move, -8.63f },
|
||||
{ ai_move, -4.0f },
|
||||
{ ai_move, -4.5f },
|
||||
{ ai_move, -6.8f },
|
||||
{ ai_move, -8.0f },
|
||||
{ ai_move, -5.4f },
|
||||
{ ai_move, -3.4f },
|
||||
{ ai_move, -1.9f },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(arachnid_move_death) = { FRAME_death1, FRAME_death20, arachnid_frames_death1, arachnid_dead };
|
||||
|
||||
DIE(arachnid_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
// check for gib
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
ThrowGibs(self, damage, {
|
||||
{ 2, "models/objects/gibs/bone/tris.md2" },
|
||||
{ 4, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ "models/objects/gibs/head2/tris.md2", GIB_HEAD }
|
||||
});
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
|
||||
M_SetAnimation(self, &arachnid_move_death);
|
||||
}
|
||||
|
||||
//
|
||||
// monster_arachnid
|
||||
//
|
||||
|
||||
/*QUAKED monster_arachnid (1 .5 0) (-48 -48 -20) (48 48 48) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_arachnid(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_step = gi.soundindex("insane/insane11.wav");
|
||||
sound_charge = gi.soundindex("gladiator/railgun.wav");
|
||||
sound_melee = gi.soundindex("gladiator/melee3.wav");
|
||||
sound_melee_hit = gi.soundindex("gladiator/melee2.wav");
|
||||
sound_pain = gi.soundindex("arachnid/pain.wav");
|
||||
sound_death = gi.soundindex("arachnid/death.wav");
|
||||
sound_sight = gi.soundindex("arachnid/sight.wav");
|
||||
|
||||
self->s.modelindex = gi.modelindex("models/monsters/arachnid/tris.md2");
|
||||
self->mins = { -48, -48, -20 };
|
||||
self->maxs = { 48, 48, 48 };
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
|
||||
self->health = 1000 * st.health_multiplier;
|
||||
self->gib_health = -200;
|
||||
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
self->mass = 450;
|
||||
|
||||
self->pain = arachnid_pain;
|
||||
self->die = arachnid_die;
|
||||
self->monsterinfo.stand = arachnid_stand;
|
||||
self->monsterinfo.walk = arachnid_walk;
|
||||
self->monsterinfo.run = arachnid_run;
|
||||
self->monsterinfo.attack = arachnid_attack;
|
||||
self->monsterinfo.sight = arachnid_sight;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &arachnid_move_stand);
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
142
rerelease/m_arachnid.h
Normal file
142
rerelease/m_arachnid.h
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/arachnid
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_rails1,
|
||||
FRAME_rails2,
|
||||
FRAME_rails3,
|
||||
FRAME_rails4,
|
||||
FRAME_rails5,
|
||||
FRAME_rails6,
|
||||
FRAME_rails7,
|
||||
FRAME_rails8,
|
||||
FRAME_rails9,
|
||||
FRAME_rails10,
|
||||
FRAME_rails11,
|
||||
FRAME_death1,
|
||||
FRAME_death2,
|
||||
FRAME_death3,
|
||||
FRAME_death4,
|
||||
FRAME_death5,
|
||||
FRAME_death6,
|
||||
FRAME_death7,
|
||||
FRAME_death8,
|
||||
FRAME_death9,
|
||||
FRAME_death10,
|
||||
FRAME_death11,
|
||||
FRAME_death12,
|
||||
FRAME_death13,
|
||||
FRAME_death14,
|
||||
FRAME_death15,
|
||||
FRAME_death16,
|
||||
FRAME_death17,
|
||||
FRAME_death18,
|
||||
FRAME_death19,
|
||||
FRAME_death20,
|
||||
FRAME_melee_atk1,
|
||||
FRAME_melee_atk2,
|
||||
FRAME_melee_atk3,
|
||||
FRAME_melee_atk4,
|
||||
FRAME_melee_atk5,
|
||||
FRAME_melee_atk6,
|
||||
FRAME_melee_atk7,
|
||||
FRAME_melee_atk8,
|
||||
FRAME_melee_atk9,
|
||||
FRAME_melee_atk10,
|
||||
FRAME_melee_atk11,
|
||||
FRAME_melee_atk12,
|
||||
FRAME_pain11,
|
||||
FRAME_pain12,
|
||||
FRAME_pain13,
|
||||
FRAME_pain14,
|
||||
FRAME_pain15,
|
||||
FRAME_idle1,
|
||||
FRAME_idle2,
|
||||
FRAME_idle3,
|
||||
FRAME_idle4,
|
||||
FRAME_idle5,
|
||||
FRAME_idle6,
|
||||
FRAME_idle7,
|
||||
FRAME_idle8,
|
||||
FRAME_idle9,
|
||||
FRAME_idle10,
|
||||
FRAME_idle11,
|
||||
FRAME_idle12,
|
||||
FRAME_idle13,
|
||||
FRAME_walk1,
|
||||
FRAME_walk2,
|
||||
FRAME_walk3,
|
||||
FRAME_walk4,
|
||||
FRAME_walk5,
|
||||
FRAME_walk6,
|
||||
FRAME_walk7,
|
||||
FRAME_walk8,
|
||||
FRAME_walk9,
|
||||
FRAME_walk10,
|
||||
FRAME_turn1,
|
||||
FRAME_turn2,
|
||||
FRAME_turn3,
|
||||
FRAME_melee_out1,
|
||||
FRAME_melee_out2,
|
||||
FRAME_melee_out3,
|
||||
FRAME_pain21,
|
||||
FRAME_pain22,
|
||||
FRAME_pain23,
|
||||
FRAME_pain24,
|
||||
FRAME_pain25,
|
||||
FRAME_pain26,
|
||||
FRAME_melee_pain1,
|
||||
FRAME_melee_pain2,
|
||||
FRAME_melee_pain3,
|
||||
FRAME_melee_pain4,
|
||||
FRAME_melee_pain5,
|
||||
FRAME_melee_pain6,
|
||||
FRAME_melee_pain7,
|
||||
FRAME_melee_pain8,
|
||||
FRAME_melee_pain9,
|
||||
FRAME_melee_pain10,
|
||||
FRAME_melee_pain11,
|
||||
FRAME_melee_pain12,
|
||||
FRAME_melee_pain13,
|
||||
FRAME_melee_pain14,
|
||||
FRAME_melee_pain15,
|
||||
FRAME_melee_pain16,
|
||||
FRAME_melee_in1,
|
||||
FRAME_melee_in2,
|
||||
FRAME_melee_in3,
|
||||
FRAME_melee_in4,
|
||||
FRAME_melee_in5,
|
||||
FRAME_melee_in6,
|
||||
FRAME_melee_in7,
|
||||
FRAME_melee_in8,
|
||||
FRAME_melee_in9,
|
||||
FRAME_melee_in10,
|
||||
FRAME_melee_in11,
|
||||
FRAME_melee_in12,
|
||||
FRAME_melee_in13,
|
||||
FRAME_melee_in14,
|
||||
FRAME_melee_in15,
|
||||
FRAME_melee_in16,
|
||||
FRAME_rails_up1,
|
||||
FRAME_rails_up2,
|
||||
FRAME_rails_up3,
|
||||
FRAME_rails_up4,
|
||||
FRAME_rails_up5,
|
||||
FRAME_rails_up6,
|
||||
FRAME_rails_up7,
|
||||
FRAME_rails_up8,
|
||||
FRAME_rails_up9,
|
||||
FRAME_rails_up10,
|
||||
FRAME_rails_up11,
|
||||
FRAME_rails_up12,
|
||||
FRAME_rails_up13,
|
||||
FRAME_rails_up14,
|
||||
FRAME_rails_up15,
|
||||
FRAME_rails_up16
|
||||
};
|
||||
|
||||
#define MODEL_SCALE 1.000000f
|
||||
822
rerelease/m_berserk.cpp
Normal file
822
rerelease/m_berserk.cpp
Normal file
@@ -0,0 +1,822 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
BERSERK
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_berserk.h"
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_BERSERK_NOJUMPING = 8_spawnflag;
|
||||
|
||||
static int sound_pain;
|
||||
static int sound_die;
|
||||
static int sound_idle;
|
||||
static int sound_idle2;
|
||||
static int sound_punch;
|
||||
static int sound_sight;
|
||||
static int sound_search;
|
||||
static int sound_thud;
|
||||
static int sound_jump;
|
||||
|
||||
MONSTERINFO_SIGHT(berserk_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_SEARCH(berserk_search) (edict_t *self) -> void
|
||||
{
|
||||
if (brandom())
|
||||
gi.sound(self, CHAN_VOICE, sound_idle2, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void berserk_fidget(edict_t *self);
|
||||
|
||||
mframe_t berserk_frames_stand[] = {
|
||||
{ ai_stand, 0, berserk_fidget },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(berserk_move_stand) = { FRAME_stand1, FRAME_stand5, berserk_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(berserk_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &berserk_move_stand);
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_stand_fidget[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(berserk_move_stand_fidget) = { FRAME_standb1, FRAME_standb20, berserk_frames_stand_fidget, berserk_stand };
|
||||
|
||||
void berserk_fidget(edict_t *self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
return;
|
||||
else if (self->enemy)
|
||||
return;
|
||||
if (frandom() > 0.15f)
|
||||
return;
|
||||
|
||||
M_SetAnimation(self, &berserk_move_stand_fidget);
|
||||
gi.sound(self, CHAN_WEAPON, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_walk[] = {
|
||||
{ ai_walk, 9.1f },
|
||||
{ ai_walk, 6.3f },
|
||||
{ ai_walk, 4.9f },
|
||||
{ ai_walk, 6.7f, monster_footstep },
|
||||
{ ai_walk, 6.0f },
|
||||
{ ai_walk, 8.2f },
|
||||
{ ai_walk, 7.2f },
|
||||
{ ai_walk, 6.1f },
|
||||
{ ai_walk, 4.9f },
|
||||
{ ai_walk, 4.7f, monster_footstep },
|
||||
{ ai_walk, 4.7f }
|
||||
};
|
||||
MMOVE_T(berserk_move_walk) = { FRAME_walkc1, FRAME_walkc11, berserk_frames_walk, nullptr };
|
||||
|
||||
MONSTERINFO_WALK(berserk_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &berserk_move_walk);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
*****************************
|
||||
SKIPPED THIS FOR NOW!
|
||||
*****************************
|
||||
|
||||
Running -> Arm raised in air
|
||||
|
||||
void() berserk_runb1 =[ $r_att1 , berserk_runb2 ] {ai_run(21);};
|
||||
void() berserk_runb2 =[ $r_att2 , berserk_runb3 ] {ai_run(11);};
|
||||
void() berserk_runb3 =[ $r_att3 , berserk_runb4 ] {ai_run(21);};
|
||||
void() berserk_runb4 =[ $r_att4 , berserk_runb5 ] {ai_run(25);};
|
||||
void() berserk_runb5 =[ $r_att5 , berserk_runb6 ] {ai_run(18);};
|
||||
void() berserk_runb6 =[ $r_att6 , berserk_runb7 ] {ai_run(19);};
|
||||
// running with arm in air : start loop
|
||||
void() berserk_runb7 =[ $r_att7 , berserk_runb8 ] {ai_run(21);};
|
||||
void() berserk_runb8 =[ $r_att8 , berserk_runb9 ] {ai_run(11);};
|
||||
void() berserk_runb9 =[ $r_att9 , berserk_runb10 ] {ai_run(21);};
|
||||
void() berserk_runb10 =[ $r_att10 , berserk_runb11 ] {ai_run(25);};
|
||||
void() berserk_runb11 =[ $r_att11 , berserk_runb12 ] {ai_run(18);};
|
||||
void() berserk_runb12 =[ $r_att12 , berserk_runb7 ] {ai_run(19);};
|
||||
// running with arm in air : end loop
|
||||
*/
|
||||
|
||||
mframe_t berserk_frames_run1[] = {
|
||||
{ ai_run, 21 },
|
||||
{ ai_run, 11, monster_footstep },
|
||||
{ ai_run, 21 },
|
||||
{ ai_run, 25, monster_done_dodge },
|
||||
{ ai_run, 18, monster_footstep },
|
||||
{ ai_run, 19 }
|
||||
};
|
||||
MMOVE_T(berserk_move_run1) = { FRAME_run1, FRAME_run6, berserk_frames_run1, nullptr };
|
||||
|
||||
MONSTERINFO_RUN(berserk_run) (edict_t *self) -> void
|
||||
{
|
||||
monster_done_dodge(self);
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &berserk_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, &berserk_move_run1);
|
||||
}
|
||||
|
||||
void berserk_attack_spike(edict_t *self)
|
||||
{
|
||||
constexpr vec3_t aim = { MELEE_DISTANCE, 0, -24 };
|
||||
|
||||
if (!fire_hit(self, aim, irandom(5, 11), 80)) // Faster attack -- upwards and backwards
|
||||
self->monsterinfo.melee_debounce_time = level.time + 1.2_sec;
|
||||
}
|
||||
|
||||
void berserk_swing(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_punch, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_attack_spike[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, berserk_swing },
|
||||
{ ai_charge, 0, berserk_attack_spike },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(berserk_move_attack_spike) = { FRAME_att_c1, FRAME_att_c8, berserk_frames_attack_spike, berserk_run };
|
||||
|
||||
void berserk_attack_club(edict_t *self)
|
||||
{
|
||||
vec3_t aim = { MELEE_DISTANCE, self->mins[0], -4 };
|
||||
|
||||
if (!fire_hit(self, aim, irandom(15, 21), 400)) // Slower attack
|
||||
self->monsterinfo.melee_debounce_time = level.time + 2.5_sec;
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_attack_club[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, monster_footstep },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, berserk_swing },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, berserk_attack_club },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(berserk_move_attack_club) = { FRAME_att_c9, FRAME_att_c20, berserk_frames_attack_club, berserk_run };
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
T_RadiusDamage
|
||||
============
|
||||
*/
|
||||
void T_SlamRadiusDamage(vec3_t point, edict_t *inflictor, edict_t *attacker, float damage, float kick, edict_t *ignore, float radius, mod_t mod)
|
||||
{
|
||||
float points;
|
||||
edict_t *ent = nullptr;
|
||||
vec3_t v;
|
||||
vec3_t dir;
|
||||
|
||||
while ((ent = findradius(ent, inflictor->s.origin, radius)) != nullptr)
|
||||
{
|
||||
if (ent == ignore)
|
||||
continue;
|
||||
if (!ent->takedamage)
|
||||
continue;
|
||||
if (!CanDamage(ent, inflictor))
|
||||
continue;
|
||||
|
||||
v = closest_point_to_box(point, ent->s.origin + ent->mins, ent->s.origin + ent->maxs) - point;
|
||||
points = damage - 0.5f * v.length();
|
||||
if (ent == attacker)
|
||||
points = points * 0.5f;
|
||||
points = max(1.f, points);
|
||||
|
||||
dir = (ent->s.origin - point).normalized();
|
||||
|
||||
// keep the point at their feet so they always get knocked up
|
||||
point[2] = ent->absmin[2];
|
||||
T_Damage(ent, inflictor, attacker, dir, point, dir, (int) points, (int) kick,
|
||||
DAMAGE_RADIUS, mod);
|
||||
|
||||
if (ent->client)
|
||||
ent->velocity.z = max(270.f, ent->velocity.z);
|
||||
}
|
||||
}
|
||||
|
||||
static void berserk_attack_slam(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_thud, 1, ATTN_NORM, 0);
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_BERSERK_SLAM);
|
||||
vec3_t f, r, start;
|
||||
AngleVectors(self->s.angles, f, r, nullptr);
|
||||
start = M_ProjectFlashSource(self, { 20.f, -14.3f, -21.f }, f, r);
|
||||
trace_t tr = gi.traceline(self->s.origin, start, self, MASK_SOLID);
|
||||
gi.WritePosition(tr.endpos);
|
||||
gi.WriteDir({ 0.f, 0.f, 1.f });
|
||||
gi.multicast(tr.endpos, MULTICAST_PHS, false);
|
||||
self->gravity = 1.0f;
|
||||
self->velocity = {};
|
||||
self->flags |= FL_KILL_VELOCITY;
|
||||
|
||||
T_SlamRadiusDamage(tr.endpos, self, self, 35, 150.f, self, 275, MOD_UNKNOWN);
|
||||
}
|
||||
|
||||
TOUCH(berserk_jump_touch) (edict_t *self, edict_t *other, const trace_t &tr, bool other_touching_self) -> void
|
||||
{
|
||||
if (self->health <= 0)
|
||||
{
|
||||
self->touch = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->groundentity)
|
||||
{
|
||||
self->s.frame = FRAME_slam18;
|
||||
|
||||
if (self->touch)
|
||||
berserk_attack_slam(self);
|
||||
|
||||
self->touch = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void berserk_high_gravity(edict_t *self)
|
||||
{
|
||||
if (self->velocity[2] < 0)
|
||||
self->gravity = 2.25f * (800.f / level.gravity);
|
||||
else
|
||||
self->gravity = 5.25f * (800.f / level.gravity);
|
||||
}
|
||||
|
||||
void berserk_jump_takeoff(edict_t *self)
|
||||
{
|
||||
vec3_t forward;
|
||||
|
||||
if (!self->enemy)
|
||||
return;
|
||||
|
||||
// immediately turn to where we need to go
|
||||
float length = (self->s.origin - self->enemy->s.origin).length();
|
||||
float fwd_speed = length * 1.95f;
|
||||
vec3_t dir;
|
||||
PredictAim(self, self->enemy, self->s.origin, fwd_speed, false, 0.f, &dir, nullptr);
|
||||
self->s.angles[1] = vectoyaw(dir);
|
||||
AngleVectors(self->s.angles, forward, nullptr, nullptr);
|
||||
self->s.origin[2] += 1;
|
||||
self->velocity = forward * fwd_speed;
|
||||
self->velocity[2] = 450;
|
||||
self->groundentity = nullptr;
|
||||
self->monsterinfo.aiflags |= AI_DUCKED;
|
||||
self->monsterinfo.attack_finished = level.time + 3_sec;
|
||||
self->touch = berserk_jump_touch;
|
||||
gi.sound(self, CHAN_WEAPON, sound_jump, 1, ATTN_NORM, 0);
|
||||
berserk_high_gravity(self);
|
||||
}
|
||||
|
||||
void berserk_check_landing(edict_t *self)
|
||||
{
|
||||
berserk_high_gravity(self);
|
||||
|
||||
if (self->groundentity)
|
||||
{
|
||||
self->monsterinfo.attack_finished = 0_ms;
|
||||
self->monsterinfo.unduck(self);
|
||||
self->s.frame = FRAME_slam18;
|
||||
if (self->touch)
|
||||
{
|
||||
berserk_attack_slam(self);
|
||||
self->touch = nullptr;
|
||||
}
|
||||
self->flags &= ~FL_KILL_VELOCITY;
|
||||
return;
|
||||
}
|
||||
|
||||
if (level.time > self->monsterinfo.attack_finished)
|
||||
self->monsterinfo.nextframe = FRAME_slam2;
|
||||
else
|
||||
self->monsterinfo.nextframe = FRAME_slam5;
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_attack_strike[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, berserk_jump_takeoff },
|
||||
{ ai_move, 0, berserk_high_gravity },
|
||||
{ ai_move, 0, berserk_high_gravity },
|
||||
{ ai_move, 0, berserk_check_landing },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep }
|
||||
};
|
||||
MMOVE_T(berserk_move_attack_strike) = { FRAME_slam1, FRAME_slam23, berserk_frames_attack_strike, berserk_run };
|
||||
|
||||
extern const mmove_t berserk_move_run_attack1;
|
||||
|
||||
MONSTERINFO_MELEE(berserk_melee) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.melee_debounce_time > level.time)
|
||||
return;
|
||||
// if we're *almost* ready to land down the hammer from run-attack
|
||||
// don't switch us
|
||||
else if (self->monsterinfo.active_move == &berserk_move_run_attack1 && self->s.frame >= FRAME_r_att13)
|
||||
{
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
self->monsterinfo.attack_finished = 0_ms;
|
||||
return;
|
||||
}
|
||||
|
||||
monster_done_dodge(self);
|
||||
|
||||
if (brandom())
|
||||
M_SetAnimation(self, &berserk_move_attack_spike);
|
||||
else
|
||||
M_SetAnimation(self, &berserk_move_attack_club);
|
||||
}
|
||||
|
||||
static void berserk_run_attack_speed(edict_t *self)
|
||||
{
|
||||
if (self->enemy && range_to(self, self->enemy) < MELEE_DISTANCE)
|
||||
{
|
||||
self->monsterinfo.nextframe = self->s.frame + 6;
|
||||
monster_done_dodge(self);
|
||||
}
|
||||
}
|
||||
|
||||
static void berserk_run_swing(edict_t *self)
|
||||
{
|
||||
berserk_swing(self);
|
||||
self->monsterinfo.melee_debounce_time = level.time + 0.6_sec;
|
||||
|
||||
if (self->monsterinfo.attack_state == AS_SLIDING)
|
||||
{
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
monster_done_dodge(self);
|
||||
}
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_run_attack1[] = {
|
||||
{ ai_run, 21, berserk_run_attack_speed },
|
||||
{ ai_run, 11, [](edict_t *self) { berserk_run_attack_speed(self); monster_footstep(self); } },
|
||||
{ ai_run, 21, berserk_run_attack_speed },
|
||||
{ ai_run, 25, [](edict_t *self) { berserk_run_attack_speed(self); monster_done_dodge(self); } },
|
||||
{ ai_run, 18, [](edict_t *self) { berserk_run_attack_speed(self); monster_footstep(self); } },
|
||||
{ ai_run, 19, berserk_run_attack_speed },
|
||||
{ ai_run, 21 },
|
||||
{ ai_run, 11, monster_footstep },
|
||||
{ ai_run, 21 },
|
||||
{ ai_run, 25 },
|
||||
{ ai_run, 18, monster_footstep },
|
||||
{ ai_run, 19 },
|
||||
{ ai_run, 21, berserk_run_swing },
|
||||
{ ai_run, 11, monster_footstep },
|
||||
{ ai_run, 21 },
|
||||
{ ai_run, 25 },
|
||||
{ ai_run, 18, monster_footstep },
|
||||
{ ai_run, 19, berserk_attack_club }
|
||||
};
|
||||
MMOVE_T(berserk_move_run_attack1) = { FRAME_r_att1, FRAME_r_att18, berserk_frames_run_attack1, berserk_run };
|
||||
|
||||
MONSTERINFO_ATTACK(berserk_attack) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.melee_debounce_time <= level.time && (range_to(self, self->enemy) < MELEE_DISTANCE))
|
||||
berserk_melee(self);
|
||||
// only jump if they are far enough away for it to make sense (otherwise
|
||||
// it gets annoying to have them keep hopping over and over again)
|
||||
else if (!self->spawnflags.has(SPAWNFLAG_BERSERK_NOJUMPING) && (self->timestamp < level.time && brandom()) && range_to(self, self->enemy) > 150.f)
|
||||
{
|
||||
M_SetAnimation(self, &berserk_move_attack_strike);
|
||||
// don't do this for a while, otherwise we just keep doing it
|
||||
self->timestamp = level.time + 5_sec;
|
||||
}
|
||||
else if (self->monsterinfo.active_move == &berserk_move_run1 && (range_to(self, self->enemy) <= RANGE_NEAR))
|
||||
{
|
||||
M_SetAnimation(self, &berserk_move_run_attack1);
|
||||
self->monsterinfo.nextframe = FRAME_r_att1 + (self->s.frame - FRAME_run1) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_pain1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(berserk_move_pain1) = { FRAME_painc1, FRAME_painc4, berserk_frames_pain1, berserk_run };
|
||||
|
||||
mframe_t berserk_frames_pain2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep }
|
||||
};
|
||||
MMOVE_T(berserk_move_pain2) = { FRAME_painb1, FRAME_painb20, berserk_frames_pain2, berserk_run };
|
||||
|
||||
extern const mmove_t berserk_move_jump, berserk_move_jump2;
|
||||
|
||||
PAIN(berserk_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
// if we're jumping, don't pain
|
||||
if ((self->monsterinfo.active_move == &berserk_move_jump) ||
|
||||
(self->monsterinfo.active_move == &berserk_move_jump2) ||
|
||||
(self->monsterinfo.active_move == &berserk_move_attack_strike))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
monster_done_dodge(self);
|
||||
|
||||
if ((damage <= 50) || (frandom() < 0.5f))
|
||||
M_SetAnimation(self, &berserk_move_pain1);
|
||||
else
|
||||
M_SetAnimation(self, &berserk_move_pain2);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(berserk_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
void berserk_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, -8 };
|
||||
monster_dead(self);
|
||||
}
|
||||
|
||||
static void berserk_shrink(edict_t *self)
|
||||
{
|
||||
self->maxs[2] = 0;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_death1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, berserk_shrink },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(berserk_move_death1) = { FRAME_death1, FRAME_death13, berserk_frames_death1, berserk_dead };
|
||||
|
||||
mframe_t berserk_frames_death2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, berserk_shrink },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(berserk_move_death2) = { FRAME_deathc1, FRAME_deathc8, berserk_frames_death2, berserk_dead };
|
||||
|
||||
DIE(berserk_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
|
||||
self->s.skinnum = 0;
|
||||
|
||||
ThrowGibs(self, damage, {
|
||||
{ 2, "models/objects/gibs/bone/tris.md2" },
|
||||
{ 3, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ 1, "models/objects/gibs/gear/tris.md2" },
|
||||
{ "models/monsters/berserk/gibs/chest.md2", GIB_SKINNED },
|
||||
{ "models/monsters/berserk/gibs/hammer.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/berserk/gibs/thigh.md2", GIB_SKINNED },
|
||||
{ "models/monsters/berserk/gibs/head.md2", GIB_HEAD | GIB_SKINNED }
|
||||
});
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
|
||||
if (damage >= 50)
|
||||
M_SetAnimation(self, &berserk_move_death1);
|
||||
else
|
||||
M_SetAnimation(self, &berserk_move_death2);
|
||||
}
|
||||
|
||||
//===========
|
||||
// PGM
|
||||
void berserk_jump_now(edict_t *self)
|
||||
{
|
||||
vec3_t forward, up;
|
||||
|
||||
AngleVectors(self->s.angles, forward, nullptr, up);
|
||||
self->velocity += (forward * 100);
|
||||
self->velocity += (up * 300);
|
||||
}
|
||||
|
||||
void berserk_jump2_now(edict_t *self)
|
||||
{
|
||||
vec3_t forward, up;
|
||||
|
||||
AngleVectors(self->s.angles, forward, nullptr, up);
|
||||
self->velocity += (forward * 150);
|
||||
self->velocity += (up * 400);
|
||||
}
|
||||
|
||||
void berserk_jump_wait_land(edict_t *self)
|
||||
{
|
||||
if (self->groundentity == nullptr)
|
||||
{
|
||||
self->monsterinfo.nextframe = self->s.frame;
|
||||
|
||||
if (monster_jump_finished(self))
|
||||
self->monsterinfo.nextframe = self->s.frame + 1;
|
||||
}
|
||||
else
|
||||
self->monsterinfo.nextframe = self->s.frame + 1;
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_jump[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, berserk_jump_now },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, berserk_jump_wait_land },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(berserk_move_jump) = { FRAME_jump1, FRAME_jump9, berserk_frames_jump, berserk_run };
|
||||
|
||||
mframe_t berserk_frames_jump2[] = {
|
||||
{ ai_move, -8 },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, 0, berserk_jump2_now },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, berserk_jump_wait_land },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(berserk_move_jump2) = { FRAME_jump1, FRAME_jump9, berserk_frames_jump2, berserk_run };
|
||||
|
||||
void berserk_jump(edict_t *self, blocked_jump_result_t result)
|
||||
{
|
||||
if (!self->enemy)
|
||||
return;
|
||||
|
||||
if (result == blocked_jump_result_t::JUMP_JUMP_UP)
|
||||
M_SetAnimation(self, &berserk_move_jump2);
|
||||
else
|
||||
M_SetAnimation(self, &berserk_move_jump);
|
||||
}
|
||||
|
||||
MONSTERINFO_BLOCKED(berserk_blocked) (edict_t *self, float dist) -> bool
|
||||
{
|
||||
if (auto result = blocked_checkjump(self, dist); result != blocked_jump_result_t::NO_JUMP)
|
||||
{
|
||||
if (result != blocked_jump_result_t::JUMP_TURN)
|
||||
berserk_jump(self, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (blocked_checkplat(self, dist))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
// PGM
|
||||
//===========
|
||||
|
||||
MONSTERINFO_SIDESTEP(berserk_sidestep) (edict_t *self) -> bool
|
||||
{
|
||||
// if we're jumping or in long pain, don't dodge
|
||||
if ((self->monsterinfo.active_move == &berserk_move_jump) ||
|
||||
(self->monsterinfo.active_move == &berserk_move_jump2) ||
|
||||
(self->monsterinfo.active_move == &berserk_move_attack_strike) ||
|
||||
(self->monsterinfo.active_move == &berserk_move_pain2))
|
||||
return false;
|
||||
|
||||
if (self->monsterinfo.active_move != &berserk_move_run1)
|
||||
M_SetAnimation(self, &berserk_move_run1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_duck[] = {
|
||||
{ ai_move, 0, monster_duck_down },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_duck_hold },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_duck_up },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(berserk_move_duck) = { FRAME_duck1, FRAME_duck10, berserk_frames_duck, berserk_run };
|
||||
|
||||
mframe_t berserk_frames_duck2[] = {
|
||||
{ ai_move, 21, monster_duck_down },
|
||||
{ ai_move, 28 },
|
||||
{ ai_move, 20 },
|
||||
{ ai_move, 12, monster_footstep },
|
||||
{ ai_move, 7 },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_duck_hold },
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move, 0, monster_duck_up },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
};
|
||||
MMOVE_T(berserk_move_duck2) = { FRAME_fall2, FRAME_fall18, berserk_frames_duck2, berserk_run };
|
||||
|
||||
MONSTERINFO_DUCK(berserk_duck) (edict_t *self, gtime_t eta) -> bool
|
||||
{
|
||||
// berserk only dives forward, and very rarely
|
||||
if (frandom() >= 0.05f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we're jumping, don't dodge
|
||||
if ((self->monsterinfo.active_move == &berserk_move_jump) ||
|
||||
(self->monsterinfo.active_move == &berserk_move_jump2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
M_SetAnimation(self, &berserk_move_duck2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*QUAKED monster_berserk (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_berserk(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
// pre-caches
|
||||
sound_pain = gi.soundindex("berserk/berpain2.wav");
|
||||
sound_die = gi.soundindex("berserk/berdeth2.wav");
|
||||
sound_idle = gi.soundindex("berserk/beridle1.wav");
|
||||
sound_idle2 = gi.soundindex("berserk/idle.wav");
|
||||
sound_punch = gi.soundindex("berserk/attack.wav");
|
||||
sound_search = gi.soundindex("berserk/bersrch1.wav");
|
||||
sound_sight = gi.soundindex("berserk/sight.wav");
|
||||
sound_thud = gi.soundindex("mutant/thud1.wav");
|
||||
sound_jump = gi.soundindex("berserk/jump.wav");
|
||||
|
||||
self->s.modelindex = gi.modelindex("models/monsters/berserk/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/berserk/gibs/head.md2");
|
||||
gi.modelindex("models/monsters/berserk/gibs/chest.md2");
|
||||
gi.modelindex("models/monsters/berserk/gibs/hammer.md2");
|
||||
gi.modelindex("models/monsters/berserk/gibs/thigh.md2");
|
||||
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, 32 };
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
|
||||
self->health = 240 * st.health_multiplier;
|
||||
self->gib_health = -60;
|
||||
self->mass = 250;
|
||||
|
||||
self->pain = berserk_pain;
|
||||
self->die = berserk_die;
|
||||
|
||||
self->monsterinfo.stand = berserk_stand;
|
||||
self->monsterinfo.walk = berserk_walk;
|
||||
self->monsterinfo.run = berserk_run;
|
||||
// pmm
|
||||
self->monsterinfo.dodge = M_MonsterDodge;
|
||||
self->monsterinfo.duck = berserk_duck;
|
||||
self->monsterinfo.unduck = monster_duck_up;
|
||||
self->monsterinfo.sidestep = berserk_sidestep;
|
||||
self->monsterinfo.blocked = berserk_blocked; // PGM
|
||||
// pmm
|
||||
self->monsterinfo.attack = berserk_attack;
|
||||
self->monsterinfo.melee = berserk_melee;
|
||||
self->monsterinfo.sight = berserk_sight;
|
||||
self->monsterinfo.search = berserk_search;
|
||||
self->monsterinfo.setskin = berserk_setskin;
|
||||
|
||||
M_SetAnimation(self, &berserk_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
self->monsterinfo.combat_style = COMBAT_MELEE;
|
||||
self->monsterinfo.can_jump = !self->spawnflags.has(SPAWNFLAG_BERSERK_NOJUMPING);
|
||||
self->monsterinfo.drop_height = 256;
|
||||
self->monsterinfo.jump_height = 40;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
266
rerelease/m_berserk.h
Normal file
266
rerelease/m_berserk.h
Normal file
@@ -0,0 +1,266 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/berserk
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_stand1,
|
||||
FRAME_stand2,
|
||||
FRAME_stand3,
|
||||
FRAME_stand4,
|
||||
FRAME_stand5,
|
||||
FRAME_standb1,
|
||||
FRAME_standb2,
|
||||
FRAME_standb3,
|
||||
FRAME_standb4,
|
||||
FRAME_standb5,
|
||||
FRAME_standb6,
|
||||
FRAME_standb7,
|
||||
FRAME_standb8,
|
||||
FRAME_standb9,
|
||||
FRAME_standb10,
|
||||
FRAME_standb11,
|
||||
FRAME_standb12,
|
||||
FRAME_standb13,
|
||||
FRAME_standb14,
|
||||
FRAME_standb15,
|
||||
FRAME_standb16,
|
||||
FRAME_standb17,
|
||||
FRAME_standb18,
|
||||
FRAME_standb19,
|
||||
FRAME_standb20,
|
||||
FRAME_walkc1,
|
||||
FRAME_walkc2,
|
||||
FRAME_walkc3,
|
||||
FRAME_walkc4,
|
||||
FRAME_walkc5,
|
||||
FRAME_walkc6,
|
||||
FRAME_walkc7,
|
||||
FRAME_walkc8,
|
||||
FRAME_walkc9,
|
||||
FRAME_walkc10,
|
||||
FRAME_walkc11,
|
||||
FRAME_run1,
|
||||
FRAME_run2,
|
||||
FRAME_run3,
|
||||
FRAME_run4,
|
||||
FRAME_run5,
|
||||
FRAME_run6,
|
||||
FRAME_att_a1,
|
||||
FRAME_att_a2,
|
||||
FRAME_att_a3,
|
||||
FRAME_att_a4,
|
||||
FRAME_att_a5,
|
||||
FRAME_att_a6,
|
||||
FRAME_att_a7,
|
||||
FRAME_att_a8,
|
||||
FRAME_att_a9,
|
||||
FRAME_att_a10,
|
||||
FRAME_att_a11,
|
||||
FRAME_att_a12,
|
||||
FRAME_att_a13,
|
||||
FRAME_att_b1,
|
||||
FRAME_att_b2,
|
||||
FRAME_att_b3,
|
||||
FRAME_att_b4,
|
||||
FRAME_att_b5,
|
||||
FRAME_att_b6,
|
||||
FRAME_att_b7,
|
||||
FRAME_att_b8,
|
||||
FRAME_att_b9,
|
||||
FRAME_att_b10,
|
||||
FRAME_att_b11,
|
||||
FRAME_att_b12,
|
||||
FRAME_att_b13,
|
||||
FRAME_att_b14,
|
||||
FRAME_att_b15,
|
||||
FRAME_att_b16,
|
||||
FRAME_att_b17,
|
||||
FRAME_att_b18,
|
||||
FRAME_att_b19,
|
||||
FRAME_att_b20,
|
||||
FRAME_att_b21,
|
||||
FRAME_att_c1,
|
||||
FRAME_att_c2,
|
||||
FRAME_att_c3,
|
||||
FRAME_att_c4,
|
||||
FRAME_att_c5,
|
||||
FRAME_att_c6,
|
||||
FRAME_att_c7,
|
||||
FRAME_att_c8,
|
||||
FRAME_att_c9,
|
||||
FRAME_att_c10,
|
||||
FRAME_att_c11,
|
||||
FRAME_att_c12,
|
||||
FRAME_att_c13,
|
||||
FRAME_att_c14,
|
||||
FRAME_att_c15,
|
||||
FRAME_att_c16,
|
||||
FRAME_att_c17,
|
||||
FRAME_att_c18,
|
||||
FRAME_att_c19,
|
||||
FRAME_att_c20,
|
||||
FRAME_att_c21,
|
||||
FRAME_att_c22,
|
||||
FRAME_att_c23,
|
||||
FRAME_att_c24,
|
||||
FRAME_att_c25,
|
||||
FRAME_att_c26,
|
||||
FRAME_att_c27,
|
||||
FRAME_att_c28,
|
||||
FRAME_att_c29,
|
||||
FRAME_att_c30,
|
||||
FRAME_att_c31,
|
||||
FRAME_att_c32,
|
||||
FRAME_att_c33,
|
||||
FRAME_att_c34,
|
||||
FRAME_r_att1,
|
||||
FRAME_r_att2,
|
||||
FRAME_r_att3,
|
||||
FRAME_r_att4,
|
||||
FRAME_r_att5,
|
||||
FRAME_r_att6,
|
||||
FRAME_r_att7,
|
||||
FRAME_r_att8,
|
||||
FRAME_r_att9,
|
||||
FRAME_r_att10,
|
||||
FRAME_r_att11,
|
||||
FRAME_r_att12,
|
||||
FRAME_r_att13,
|
||||
FRAME_r_att14,
|
||||
FRAME_r_att15,
|
||||
FRAME_r_att16,
|
||||
FRAME_r_att17,
|
||||
FRAME_r_att18,
|
||||
FRAME_r_attb1,
|
||||
FRAME_r_attb2,
|
||||
FRAME_r_attb3,
|
||||
FRAME_r_attb4,
|
||||
FRAME_r_attb5,
|
||||
FRAME_r_attb6,
|
||||
FRAME_r_attb7,
|
||||
FRAME_r_attb8,
|
||||
FRAME_r_attb9,
|
||||
FRAME_r_attb10,
|
||||
FRAME_r_attb11,
|
||||
FRAME_r_attb12,
|
||||
FRAME_r_attb13,
|
||||
FRAME_r_attb14,
|
||||
FRAME_r_attb15,
|
||||
FRAME_r_attb16,
|
||||
FRAME_r_attb17,
|
||||
FRAME_r_attb18,
|
||||
FRAME_slam1,
|
||||
FRAME_slam2,
|
||||
FRAME_slam3,
|
||||
FRAME_slam4,
|
||||
FRAME_slam5,
|
||||
FRAME_slam6,
|
||||
FRAME_slam7,
|
||||
FRAME_slam8,
|
||||
FRAME_slam9,
|
||||
FRAME_slam10,
|
||||
FRAME_slam11,
|
||||
FRAME_slam12,
|
||||
FRAME_slam13,
|
||||
FRAME_slam14,
|
||||
FRAME_slam15,
|
||||
FRAME_slam16,
|
||||
FRAME_slam17,
|
||||
FRAME_slam18,
|
||||
FRAME_slam19,
|
||||
FRAME_slam20,
|
||||
FRAME_slam21,
|
||||
FRAME_slam22,
|
||||
FRAME_slam23,
|
||||
FRAME_duck1,
|
||||
FRAME_duck2,
|
||||
FRAME_duck3,
|
||||
FRAME_duck4,
|
||||
FRAME_duck5,
|
||||
FRAME_duck6,
|
||||
FRAME_duck7,
|
||||
FRAME_duck8,
|
||||
FRAME_duck9,
|
||||
FRAME_duck10,
|
||||
FRAME_fall1,
|
||||
FRAME_fall2,
|
||||
FRAME_fall3,
|
||||
FRAME_fall4,
|
||||
FRAME_fall5,
|
||||
FRAME_fall6,
|
||||
FRAME_fall7,
|
||||
FRAME_fall8,
|
||||
FRAME_fall9,
|
||||
FRAME_fall10,
|
||||
FRAME_fall11,
|
||||
FRAME_fall12,
|
||||
FRAME_fall13,
|
||||
FRAME_fall14,
|
||||
FRAME_fall15,
|
||||
FRAME_fall16,
|
||||
FRAME_fall17,
|
||||
FRAME_fall18,
|
||||
FRAME_fall19,
|
||||
FRAME_fall20,
|
||||
FRAME_painc1,
|
||||
FRAME_painc2,
|
||||
FRAME_painc3,
|
||||
FRAME_painc4,
|
||||
FRAME_painb1,
|
||||
FRAME_painb2,
|
||||
FRAME_painb3,
|
||||
FRAME_painb4,
|
||||
FRAME_painb5,
|
||||
FRAME_painb6,
|
||||
FRAME_painb7,
|
||||
FRAME_painb8,
|
||||
FRAME_painb9,
|
||||
FRAME_painb10,
|
||||
FRAME_painb11,
|
||||
FRAME_painb12,
|
||||
FRAME_painb13,
|
||||
FRAME_painb14,
|
||||
FRAME_painb15,
|
||||
FRAME_painb16,
|
||||
FRAME_painb17,
|
||||
FRAME_painb18,
|
||||
FRAME_painb19,
|
||||
FRAME_painb20,
|
||||
FRAME_death1,
|
||||
FRAME_death2,
|
||||
FRAME_death3,
|
||||
FRAME_death4,
|
||||
FRAME_death5,
|
||||
FRAME_death6,
|
||||
FRAME_death7,
|
||||
FRAME_death8,
|
||||
FRAME_death9,
|
||||
FRAME_death10,
|
||||
FRAME_death11,
|
||||
FRAME_death12,
|
||||
FRAME_death13,
|
||||
FRAME_deathc1,
|
||||
FRAME_deathc2,
|
||||
FRAME_deathc3,
|
||||
FRAME_deathc4,
|
||||
FRAME_deathc5,
|
||||
FRAME_deathc6,
|
||||
FRAME_deathc7,
|
||||
FRAME_deathc8,
|
||||
// PGM
|
||||
FRAME_jump1,
|
||||
FRAME_jump2,
|
||||
FRAME_jump3,
|
||||
FRAME_jump4,
|
||||
FRAME_jump5,
|
||||
FRAME_jump6,
|
||||
FRAME_jump7,
|
||||
FRAME_jump8,
|
||||
FRAME_jump9
|
||||
// PGM
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
768
rerelease/m_boss2.cpp
Normal file
768
rerelease/m_boss2.cpp
Normal file
@@ -0,0 +1,768 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
boss2
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_boss2.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
// [Paril-KEX]
|
||||
constexpr spawnflags_t SPAWNFLAG_BOSS2_N64 = 8_spawnflag;
|
||||
|
||||
bool infront(edict_t *self, edict_t *other);
|
||||
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_pain3;
|
||||
static int sound_death;
|
||||
static int sound_search1;
|
||||
|
||||
MONSTERINFO_SEARCH(boss2_search) (edict_t *self) -> void
|
||||
{
|
||||
if (frandom() < 0.5f)
|
||||
gi.sound(self, CHAN_VOICE, sound_search1, 1, ATTN_NONE, 0);
|
||||
}
|
||||
|
||||
void boss2_run(edict_t *self);
|
||||
void boss2_dead(edict_t *self);
|
||||
void boss2_attack_mg(edict_t *self);
|
||||
void boss2_reattack_mg(edict_t *self);
|
||||
|
||||
constexpr int32_t BOSS2_ROCKET_SPEED = 750;
|
||||
|
||||
void Boss2PredictiveRocket(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
|
||||
// 1
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_BOSS2_ROCKET_1], forward, right);
|
||||
PredictAim(self, self->enemy, start, BOSS2_ROCKET_SPEED, false, -0.10f, &dir, nullptr);
|
||||
monster_fire_rocket(self, start, dir, 50, BOSS2_ROCKET_SPEED, MZ2_BOSS2_ROCKET_1);
|
||||
|
||||
// 2
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_BOSS2_ROCKET_2], forward, right);
|
||||
PredictAim(self, self->enemy, start, BOSS2_ROCKET_SPEED, false, -0.05f, &dir, nullptr);
|
||||
monster_fire_rocket(self, start, dir, 50, BOSS2_ROCKET_SPEED, MZ2_BOSS2_ROCKET_2);
|
||||
|
||||
// 3
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_BOSS2_ROCKET_3], forward, right);
|
||||
PredictAim(self, self->enemy, start, BOSS2_ROCKET_SPEED, false, 0.05f, &dir, nullptr);
|
||||
monster_fire_rocket(self, start, dir, 50, BOSS2_ROCKET_SPEED, MZ2_BOSS2_ROCKET_3);
|
||||
|
||||
// 4
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_BOSS2_ROCKET_4], forward, right);
|
||||
PredictAim(self, self->enemy, start, BOSS2_ROCKET_SPEED, false, 0.10f, &dir, nullptr);
|
||||
monster_fire_rocket(self, start, dir, 50, BOSS2_ROCKET_SPEED, MZ2_BOSS2_ROCKET_4);
|
||||
}
|
||||
|
||||
void Boss2Rocket(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
if (self->enemy->client && frandom() < 0.9f)
|
||||
{
|
||||
Boss2PredictiveRocket(self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
|
||||
// 1
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_BOSS2_ROCKET_1], forward, right);
|
||||
vec = self->enemy->s.origin;
|
||||
vec[2] -= 15;
|
||||
dir = vec - start;
|
||||
dir.normalize();
|
||||
dir += (right * 0.4f);
|
||||
dir.normalize();
|
||||
monster_fire_rocket(self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_1);
|
||||
|
||||
// 2
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_BOSS2_ROCKET_2], forward, right);
|
||||
vec = self->enemy->s.origin;
|
||||
dir = vec - start;
|
||||
dir.normalize();
|
||||
dir += (right * 0.025f);
|
||||
dir.normalize();
|
||||
monster_fire_rocket(self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_2);
|
||||
|
||||
// 3
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_BOSS2_ROCKET_3], forward, right);
|
||||
vec = self->enemy->s.origin;
|
||||
dir = vec - start;
|
||||
dir.normalize();
|
||||
dir += (right * -0.025f);
|
||||
dir.normalize();
|
||||
monster_fire_rocket(self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_3);
|
||||
|
||||
// 4
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_BOSS2_ROCKET_4], forward, right);
|
||||
vec = self->enemy->s.origin;
|
||||
vec[2] -= 15;
|
||||
dir = vec - start;
|
||||
dir.normalize();
|
||||
dir += (right * -0.4f);
|
||||
dir.normalize();
|
||||
monster_fire_rocket(self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_4);
|
||||
}
|
||||
|
||||
void Boss2Rocket64(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
float time, dist;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_BOSS2_ROCKET_1], forward, right);
|
||||
|
||||
float scale = self->s.scale ? self->s.scale : 1;
|
||||
|
||||
start[2] += 10.f * scale;
|
||||
start -= right * 2.f * scale;
|
||||
start -= right * ((self->count++ % 4) * 8.f * scale);
|
||||
|
||||
if (self->enemy && self->enemy->client && frandom() < 0.9f)
|
||||
{
|
||||
// 1
|
||||
dir = self->enemy->s.origin - start;
|
||||
dist = dir.length();
|
||||
time = dist / BOSS2_ROCKET_SPEED;
|
||||
vec = self->enemy->s.origin + (self->enemy->velocity * (time - 0.3f));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1
|
||||
vec = self->enemy->s.origin;
|
||||
vec[2] -= 15;
|
||||
}
|
||||
|
||||
dir = vec - start;
|
||||
dir.normalize();
|
||||
|
||||
monster_fire_rocket(self, start, dir, 35, BOSS2_ROCKET_SPEED, MZ2_BOSS2_ROCKET_1);
|
||||
}
|
||||
|
||||
void boss2_firebullet_right(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right, start;
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_BOSS2_MACHINEGUN_R1], forward, right);
|
||||
PredictAim(self, self->enemy, start, 0, true, -0.2f, &forward, nullptr);
|
||||
monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD * 3, DEFAULT_BULLET_VSPREAD, MZ2_BOSS2_MACHINEGUN_R1);
|
||||
}
|
||||
|
||||
void boss2_firebullet_left(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right, start;
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_BOSS2_MACHINEGUN_L1], forward, right);
|
||||
PredictAim(self, self->enemy, start, 0, true, -0.2f, &forward, nullptr);
|
||||
monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD * 3, DEFAULT_BULLET_VSPREAD, MZ2_BOSS2_MACHINEGUN_L1);
|
||||
}
|
||||
|
||||
void Boss2MachineGun(edict_t *self)
|
||||
{
|
||||
boss2_firebullet_left(self);
|
||||
boss2_firebullet_right(self);
|
||||
}
|
||||
|
||||
mframe_t boss2_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(boss2_move_stand) = { FRAME_stand30, FRAME_stand50, boss2_frames_stand, nullptr };
|
||||
|
||||
mframe_t boss2_frames_walk[] = {
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 10 }
|
||||
};
|
||||
MMOVE_T(boss2_move_walk) = { FRAME_walk1, FRAME_walk20, boss2_frames_walk, nullptr };
|
||||
|
||||
mframe_t boss2_frames_run[] = {
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 }
|
||||
};
|
||||
MMOVE_T(boss2_move_run) = { FRAME_walk1, FRAME_walk20, boss2_frames_run, nullptr };
|
||||
|
||||
mframe_t boss2_frames_attack_pre_mg[] = {
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2, boss2_attack_mg }
|
||||
};
|
||||
MMOVE_T(boss2_move_attack_pre_mg) = { FRAME_attack1, FRAME_attack9, boss2_frames_attack_pre_mg, nullptr };
|
||||
|
||||
// Loop this
|
||||
mframe_t boss2_frames_attack_mg[] = {
|
||||
{ ai_charge, 2, Boss2MachineGun },
|
||||
{ ai_charge, 2, Boss2MachineGun },
|
||||
{ ai_charge, 2, Boss2MachineGun },
|
||||
{ ai_charge, 2, Boss2MachineGun },
|
||||
{ ai_charge, 2, Boss2MachineGun },
|
||||
{ ai_charge, 2, boss2_reattack_mg }
|
||||
};
|
||||
MMOVE_T(boss2_move_attack_mg) = { FRAME_attack10, FRAME_attack15, boss2_frames_attack_mg, nullptr };
|
||||
|
||||
// [Paril-KEX]
|
||||
void Boss2HyperBlaster(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right, target;
|
||||
vec3_t start;
|
||||
monster_muzzleflash_id_t id = (self->s.frame & 1) ? MZ2_BOSS2_MACHINEGUN_L2 : MZ2_BOSS2_MACHINEGUN_R2;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[id], forward, right);
|
||||
target = self->enemy->s.origin;
|
||||
target[2] += self->enemy->viewheight;
|
||||
forward = target - start;
|
||||
forward.normalize();
|
||||
|
||||
monster_fire_blaster(self, start, forward, 2, 1000, id, (self->s.frame % 4) ? EF_NONE : EF_HYPERBLASTER);
|
||||
}
|
||||
|
||||
mframe_t boss2_frames_attack_hb[] = {
|
||||
{ ai_charge, 2, Boss2HyperBlaster },
|
||||
{ ai_charge, 2, Boss2HyperBlaster },
|
||||
{ ai_charge, 2, Boss2HyperBlaster },
|
||||
{ ai_charge, 2, Boss2HyperBlaster },
|
||||
{ ai_charge, 2, Boss2HyperBlaster },
|
||||
{ ai_charge, 2, [](edict_t *self) -> void { Boss2HyperBlaster(self); boss2_reattack_mg(self); } }
|
||||
};
|
||||
MMOVE_T(boss2_move_attack_hb) = { FRAME_attack10, FRAME_attack15, boss2_frames_attack_hb, nullptr };
|
||||
|
||||
mframe_t boss2_frames_attack_post_mg[] = {
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 }
|
||||
};
|
||||
MMOVE_T(boss2_move_attack_post_mg) = { FRAME_attack16, FRAME_attack19, boss2_frames_attack_post_mg, boss2_run };
|
||||
|
||||
mframe_t boss2_frames_attack_rocket[] = {
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_move, -5, Boss2Rocket },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 }
|
||||
};
|
||||
MMOVE_T(boss2_move_attack_rocket) = { FRAME_attack20, FRAME_attack40, boss2_frames_attack_rocket, boss2_run };
|
||||
|
||||
// [Paril-KEX] n64 rocket behavior
|
||||
mframe_t boss2_frames_attack_rocket2[] = {
|
||||
{ ai_charge, 2, Boss2Rocket64 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2, Boss2Rocket64 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2, Boss2Rocket64 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2, Boss2Rocket64 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2, Boss2Rocket64 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 2 }
|
||||
};
|
||||
MMOVE_T(boss2_move_attack_rocket2) = { FRAME_attack20, FRAME_attack39, boss2_frames_attack_rocket2, boss2_run };
|
||||
|
||||
mframe_t boss2_frames_pain_heavy[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(boss2_move_pain_heavy) = { FRAME_pain2, FRAME_pain19, boss2_frames_pain_heavy, boss2_run };
|
||||
|
||||
mframe_t boss2_frames_pain_light[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(boss2_move_pain_light) = { FRAME_pain20, FRAME_pain23, boss2_frames_pain_light, boss2_run };
|
||||
|
||||
static void boss2_shrink(edict_t *self)
|
||||
{
|
||||
self->maxs.z = 50.f;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t boss2_frames_death[] = {
|
||||
{ ai_move, 0, BossExplode },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, boss2_shrink },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(boss2_move_death) = { FRAME_death2, FRAME_death50, boss2_frames_death, boss2_dead };
|
||||
|
||||
MONSTERINFO_STAND(boss2_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &boss2_move_stand);
|
||||
}
|
||||
|
||||
MONSTERINFO_RUN(boss2_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &boss2_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, &boss2_move_run);
|
||||
}
|
||||
|
||||
MONSTERINFO_WALK(boss2_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &boss2_move_walk);
|
||||
}
|
||||
|
||||
MONSTERINFO_ATTACK(boss2_attack) (edict_t *self) -> void
|
||||
{
|
||||
vec3_t vec;
|
||||
float range;
|
||||
|
||||
vec = self->enemy->s.origin - self->s.origin;
|
||||
range = vec.length();
|
||||
|
||||
if (range <= 125 || frandom() <= 0.6f)
|
||||
M_SetAnimation(self, self->spawnflags.has(SPAWNFLAG_BOSS2_N64) ? &boss2_move_attack_hb : &boss2_move_attack_pre_mg);
|
||||
else
|
||||
M_SetAnimation(self, self->spawnflags.has(SPAWNFLAG_BOSS2_N64) ? &boss2_move_attack_rocket2 : &boss2_move_attack_rocket);
|
||||
}
|
||||
|
||||
void boss2_attack_mg(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, self->spawnflags.has(SPAWNFLAG_BOSS2_N64) ? &boss2_move_attack_hb : &boss2_move_attack_mg);
|
||||
}
|
||||
|
||||
void boss2_reattack_mg(edict_t *self)
|
||||
{
|
||||
if (infront(self, self->enemy) && frandom() <= 0.7f)
|
||||
boss2_attack_mg(self);
|
||||
else
|
||||
M_SetAnimation(self, &boss2_move_attack_post_mg);
|
||||
}
|
||||
|
||||
PAIN(boss2_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
// American wanted these at no attenuation
|
||||
if (damage < 10)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain3, 1, ATTN_NONE, 0);
|
||||
else if (damage < 30)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NONE, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NONE, 0);
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (damage < 10)
|
||||
M_SetAnimation(self, &boss2_move_pain_light);
|
||||
else if (damage < 30)
|
||||
M_SetAnimation(self, &boss2_move_pain_light);
|
||||
else
|
||||
M_SetAnimation(self, &boss2_move_pain_heavy);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(boss2_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
static void boss2_gib(edict_t *self)
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1_BIG);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.multicast(self->s.origin, MULTICAST_PHS, false);
|
||||
|
||||
self->s.sound = 0;
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
self->gravityVector.z = -1.0f;
|
||||
|
||||
ThrowGibs(self, 500, {
|
||||
{ 2, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ 2, "models/objects/gibs/sm_metal/tris.md2", GIB_METALLIC },
|
||||
{ "models/monsters/boss2/gibs/chest.md2", GIB_SKINNED },
|
||||
{ 2, "models/monsters/boss2/gibs/chaingun.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss2/gibs/cpu.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss2/gibs/engine.md2", GIB_SKINNED },
|
||||
{ "models/monsters/boss2/gibs/rocket.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss2/gibs/spine.md2", GIB_SKINNED },
|
||||
{ 2, "models/monsters/boss2/gibs/wing.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss2/gibs/larm.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss2/gibs/rarm.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss2/gibs/larm.md2", 2.0f, GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss2/gibs/rarm.md2", 2.0f, GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss2/gibs/larm.md2", 1.35f, GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss2/gibs/rarm.md2", 1.35f, GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss2/gibs/head.md2", GIB_SKINNED | GIB_METALLIC | GIB_HEAD }
|
||||
});
|
||||
}
|
||||
|
||||
void boss2_dead(edict_t *self)
|
||||
{
|
||||
// no blowy on deady
|
||||
if (self->spawnflags.has(SPAWNFLAG_MONSTER_DEAD))
|
||||
{
|
||||
self->deadflag = false;
|
||||
self->takedamage = true;
|
||||
return;
|
||||
}
|
||||
|
||||
boss2_gib(self);
|
||||
}
|
||||
|
||||
DIE(boss2_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
if (self->spawnflags.has(SPAWNFLAG_MONSTER_DEAD))
|
||||
{
|
||||
// check for gib
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
boss2_gib(self);
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0);
|
||||
self->deadflag = true;
|
||||
self->takedamage = false;
|
||||
self->count = 0;
|
||||
self->velocity = {};
|
||||
self->gravityVector.z *= 0.30f;
|
||||
}
|
||||
|
||||
M_SetAnimation(self, &boss2_move_death);
|
||||
}
|
||||
|
||||
MONSTERINFO_CHECKATTACK(Boss2_CheckAttack) (edict_t *self) -> bool
|
||||
{
|
||||
vec3_t spot1, spot2;
|
||||
vec3_t temp;
|
||||
float chance;
|
||||
trace_t tr;
|
||||
float enemy_yaw;
|
||||
|
||||
if (self->enemy->health > 0)
|
||||
{
|
||||
// see if any entities are in the way of the shot
|
||||
spot1 = self->s.origin;
|
||||
spot1[2] += self->viewheight;
|
||||
spot2 = self->enemy->s.origin;
|
||||
spot2[2] += self->enemy->viewheight;
|
||||
|
||||
tr = gi.traceline(spot1, spot2, self, CONTENTS_SOLID | CONTENTS_PLAYER | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA);
|
||||
|
||||
// do we have a clear shot?
|
||||
if (tr.ent != self->enemy && !(tr.ent->svflags & SVF_PLAYER))
|
||||
{
|
||||
// PGM - we want them to go ahead and shoot at info_notnulls if they can.
|
||||
if (self->enemy->solid != SOLID_NOT || tr.fraction < 1.0f) // PGM
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
float enemy_range = range_to(self, self->enemy);
|
||||
temp = self->enemy->s.origin - self->s.origin;
|
||||
enemy_yaw = vectoyaw(temp);
|
||||
|
||||
self->ideal_yaw = enemy_yaw;
|
||||
|
||||
// melee attack
|
||||
if (enemy_range <= RANGE_MELEE)
|
||||
{
|
||||
if (self->monsterinfo.melee)
|
||||
self->monsterinfo.attack_state = AS_MELEE;
|
||||
else
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// missile attack
|
||||
if (!self->monsterinfo.attack)
|
||||
return false;
|
||||
|
||||
if (level.time < self->monsterinfo.attack_finished)
|
||||
return false;
|
||||
|
||||
if (enemy_range > RANGE_MID)
|
||||
return false;
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
chance = 0.4f;
|
||||
}
|
||||
else if (enemy_range <= RANGE_MELEE)
|
||||
{
|
||||
chance = 0.8f;
|
||||
}
|
||||
else if (enemy_range <= RANGE_NEAR)
|
||||
{
|
||||
chance = 0.8f;
|
||||
}
|
||||
else
|
||||
{
|
||||
chance = 0.8f;
|
||||
}
|
||||
|
||||
// PGM - go ahead and shoot every time if it's a info_notnull
|
||||
if ((frandom() < chance) || (self->enemy->solid == SOLID_NOT))
|
||||
{
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
self->monsterinfo.attack_finished = level.time + random_time(2_sec);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (self->flags & FL_FLY)
|
||||
{
|
||||
if (frandom() < 0.3f)
|
||||
self->monsterinfo.attack_state = AS_SLIDING;
|
||||
else
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*QUAKED monster_boss2 (1 .5 0) (-56 -56 0) (56 56 80) Ambush Trigger_Spawn Sight Hyperblaster
|
||||
*/
|
||||
void SP_monster_boss2(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_pain1 = gi.soundindex("bosshovr/bhvpain1.wav");
|
||||
sound_pain2 = gi.soundindex("bosshovr/bhvpain2.wav");
|
||||
sound_pain3 = gi.soundindex("bosshovr/bhvpain3.wav");
|
||||
sound_death = gi.soundindex("bosshovr/bhvdeth1.wav");
|
||||
sound_search1 = gi.soundindex("bosshovr/bhvunqv1.wav");
|
||||
|
||||
gi.soundindex("tank/rocket.wav");
|
||||
|
||||
if (self->spawnflags.has(SPAWNFLAG_BOSS2_N64))
|
||||
gi.soundindex("flyer/flyatck3.wav");
|
||||
else
|
||||
gi.soundindex("infantry/infatck1.wav");
|
||||
|
||||
self->monsterinfo.weapon_sound = gi.soundindex("bosshovr/bhvengn1.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/boss2/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/boss2/gibs/chaingun.md2");
|
||||
gi.modelindex("models/monsters/boss2/gibs/chest.md2");
|
||||
gi.modelindex("models/monsters/boss2/gibs/cpu.md2");
|
||||
gi.modelindex("models/monsters/boss2/gibs/engine.md2");
|
||||
gi.modelindex("models/monsters/boss2/gibs/head.md2");
|
||||
gi.modelindex("models/monsters/boss2/gibs/larm.md2");
|
||||
gi.modelindex("models/monsters/boss2/gibs/rarm.md2");
|
||||
gi.modelindex("models/monsters/boss2/gibs/rocket.md2");
|
||||
gi.modelindex("models/monsters/boss2/gibs/spine.md2");
|
||||
gi.modelindex("models/monsters/boss2/gibs/wing.md2");
|
||||
|
||||
self->mins = { -56, -56, 0 };
|
||||
self->maxs = { 56, 56, 80 };
|
||||
|
||||
self->health = 2000 * st.health_multiplier;
|
||||
self->gib_health = -200;
|
||||
self->mass = 1000;
|
||||
|
||||
self->yaw_speed = 50;
|
||||
|
||||
self->flags |= FL_IMMUNE_LASER;
|
||||
|
||||
self->pain = boss2_pain;
|
||||
self->die = boss2_die;
|
||||
|
||||
self->monsterinfo.stand = boss2_stand;
|
||||
self->monsterinfo.walk = boss2_walk;
|
||||
self->monsterinfo.run = boss2_run;
|
||||
self->monsterinfo.attack = boss2_attack;
|
||||
self->monsterinfo.search = boss2_search;
|
||||
self->monsterinfo.checkattack = Boss2_CheckAttack;
|
||||
self->monsterinfo.setskin = boss2_setskin;
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &boss2_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
// [Paril-KEX]
|
||||
self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
|
||||
|
||||
flymonster_start(self);
|
||||
}
|
||||
192
rerelease/m_boss2.h
Normal file
192
rerelease/m_boss2.h
Normal file
@@ -0,0 +1,192 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/boss2
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_stand30,
|
||||
FRAME_stand31,
|
||||
FRAME_stand32,
|
||||
FRAME_stand33,
|
||||
FRAME_stand34,
|
||||
FRAME_stand35,
|
||||
FRAME_stand36,
|
||||
FRAME_stand37,
|
||||
FRAME_stand38,
|
||||
FRAME_stand39,
|
||||
FRAME_stand40,
|
||||
FRAME_stand41,
|
||||
FRAME_stand42,
|
||||
FRAME_stand43,
|
||||
FRAME_stand44,
|
||||
FRAME_stand45,
|
||||
FRAME_stand46,
|
||||
FRAME_stand47,
|
||||
FRAME_stand48,
|
||||
FRAME_stand49,
|
||||
FRAME_stand50,
|
||||
FRAME_stand1,
|
||||
FRAME_stand2,
|
||||
FRAME_stand3,
|
||||
FRAME_stand4,
|
||||
FRAME_stand5,
|
||||
FRAME_stand6,
|
||||
FRAME_stand7,
|
||||
FRAME_stand8,
|
||||
FRAME_stand9,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_stand18,
|
||||
FRAME_stand19,
|
||||
FRAME_stand20,
|
||||
FRAME_stand21,
|
||||
FRAME_stand22,
|
||||
FRAME_stand23,
|
||||
FRAME_stand24,
|
||||
FRAME_stand25,
|
||||
FRAME_stand26,
|
||||
FRAME_stand27,
|
||||
FRAME_stand28,
|
||||
FRAME_stand29,
|
||||
FRAME_walk1,
|
||||
FRAME_walk2,
|
||||
FRAME_walk3,
|
||||
FRAME_walk4,
|
||||
FRAME_walk5,
|
||||
FRAME_walk6,
|
||||
FRAME_walk7,
|
||||
FRAME_walk8,
|
||||
FRAME_walk9,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_walk13,
|
||||
FRAME_walk14,
|
||||
FRAME_walk15,
|
||||
FRAME_walk16,
|
||||
FRAME_walk17,
|
||||
FRAME_walk18,
|
||||
FRAME_walk19,
|
||||
FRAME_walk20,
|
||||
FRAME_attack1,
|
||||
FRAME_attack2,
|
||||
FRAME_attack3,
|
||||
FRAME_attack4,
|
||||
FRAME_attack5,
|
||||
FRAME_attack6,
|
||||
FRAME_attack7,
|
||||
FRAME_attack8,
|
||||
FRAME_attack9,
|
||||
FRAME_attack10,
|
||||
FRAME_attack11,
|
||||
FRAME_attack12,
|
||||
FRAME_attack13,
|
||||
FRAME_attack14,
|
||||
FRAME_attack15,
|
||||
FRAME_attack16,
|
||||
FRAME_attack17,
|
||||
FRAME_attack18,
|
||||
FRAME_attack19,
|
||||
FRAME_attack20,
|
||||
FRAME_attack21,
|
||||
FRAME_attack22,
|
||||
FRAME_attack23,
|
||||
FRAME_attack24,
|
||||
FRAME_attack25,
|
||||
FRAME_attack26,
|
||||
FRAME_attack27,
|
||||
FRAME_attack28,
|
||||
FRAME_attack29,
|
||||
FRAME_attack30,
|
||||
FRAME_attack31,
|
||||
FRAME_attack32,
|
||||
FRAME_attack33,
|
||||
FRAME_attack34,
|
||||
FRAME_attack35,
|
||||
FRAME_attack36,
|
||||
FRAME_attack37,
|
||||
FRAME_attack38,
|
||||
FRAME_attack39,
|
||||
FRAME_attack40,
|
||||
FRAME_pain2,
|
||||
FRAME_pain3,
|
||||
FRAME_pain4,
|
||||
FRAME_pain5,
|
||||
FRAME_pain6,
|
||||
FRAME_pain7,
|
||||
FRAME_pain8,
|
||||
FRAME_pain9,
|
||||
FRAME_pain10,
|
||||
FRAME_pain11,
|
||||
FRAME_pain12,
|
||||
FRAME_pain13,
|
||||
FRAME_pain14,
|
||||
FRAME_pain15,
|
||||
FRAME_pain16,
|
||||
FRAME_pain17,
|
||||
FRAME_pain18,
|
||||
FRAME_pain19,
|
||||
FRAME_pain20,
|
||||
FRAME_pain21,
|
||||
FRAME_pain22,
|
||||
FRAME_pain23,
|
||||
FRAME_death2,
|
||||
FRAME_death3,
|
||||
FRAME_death4,
|
||||
FRAME_death5,
|
||||
FRAME_death6,
|
||||
FRAME_death7,
|
||||
FRAME_death8,
|
||||
FRAME_death9,
|
||||
FRAME_death10,
|
||||
FRAME_death11,
|
||||
FRAME_death12,
|
||||
FRAME_death13,
|
||||
FRAME_death14,
|
||||
FRAME_death15,
|
||||
FRAME_death16,
|
||||
FRAME_death17,
|
||||
FRAME_death18,
|
||||
FRAME_death19,
|
||||
FRAME_death20,
|
||||
FRAME_death21,
|
||||
FRAME_death22,
|
||||
FRAME_death23,
|
||||
FRAME_death24,
|
||||
FRAME_death25,
|
||||
FRAME_death26,
|
||||
FRAME_death27,
|
||||
FRAME_death28,
|
||||
FRAME_death29,
|
||||
FRAME_death30,
|
||||
FRAME_death31,
|
||||
FRAME_death32,
|
||||
FRAME_death33,
|
||||
FRAME_death34,
|
||||
FRAME_death35,
|
||||
FRAME_death36,
|
||||
FRAME_death37,
|
||||
FRAME_death38,
|
||||
FRAME_death39,
|
||||
FRAME_death40,
|
||||
FRAME_death41,
|
||||
FRAME_death42,
|
||||
FRAME_death43,
|
||||
FRAME_death44,
|
||||
FRAME_death45,
|
||||
FRAME_death46,
|
||||
FRAME_death47,
|
||||
FRAME_death48,
|
||||
FRAME_death49,
|
||||
FRAME_death50
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
61
rerelease/m_boss3.cpp
Normal file
61
rerelease/m_boss3.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
boss3
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_boss32.h"
|
||||
|
||||
USE(Use_Boss3) (edict_t *self, edict_t *other, edict_t *activator) -> void
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_BOSSTPORT);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.multicast(self->s.origin, MULTICAST_PHS, false);
|
||||
|
||||
// just hide, don't kill ent so we can trigger it again
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
|
||||
THINK(Think_Boss3Stand) (edict_t *self) -> void
|
||||
{
|
||||
if (self->s.frame == FRAME_stand260)
|
||||
self->s.frame = FRAME_stand201;
|
||||
else
|
||||
self->s.frame++;
|
||||
self->nextthink = level.time + 10_hz;
|
||||
}
|
||||
|
||||
/*QUAKED monster_boss3_stand (1 .5 0) (-32 -32 0) (32 32 90)
|
||||
|
||||
Just stands and cycles in one place until targeted, then teleports away.
|
||||
*/
|
||||
void SP_monster_boss3_stand(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->model = "models/monsters/boss3/rider/tris.md2";
|
||||
self->s.modelindex = gi.modelindex(self->model);
|
||||
self->s.frame = FRAME_stand201;
|
||||
|
||||
gi.soundindex("misc/bigtele.wav");
|
||||
|
||||
self->mins = { -32, -32, 0 };
|
||||
self->maxs = { 32, 32, 90 };
|
||||
|
||||
self->use = Use_Boss3;
|
||||
self->think = Think_Boss3Stand;
|
||||
self->nextthink = level.time + FRAME_TIME_S;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
720
rerelease/m_boss31.cpp
Normal file
720
rerelease/m_boss31.cpp
Normal file
@@ -0,0 +1,720 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
jorg
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_boss31.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
void SP_monster_makron(edict_t *self);
|
||||
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_pain3;
|
||||
static int sound_idle;
|
||||
static int sound_death;
|
||||
static int sound_search1;
|
||||
static int sound_search2;
|
||||
static int sound_search3;
|
||||
static int sound_attack1, sound_attack1_loop, sound_attack1_end;
|
||||
static int sound_attack2, sound_bfg_fire;
|
||||
static int sound_firegun;
|
||||
static int sound_step_left;
|
||||
static int sound_step_right;
|
||||
static int sound_death_hit;
|
||||
|
||||
void MakronToss(edict_t *self);
|
||||
|
||||
void jorg_attack1_end_sound(edict_t *self)
|
||||
{
|
||||
if (self->monsterinfo.weapon_sound)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_attack1_end, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.weapon_sound = 0;
|
||||
}
|
||||
}
|
||||
|
||||
MONSTERINFO_SEARCH(jorg_search) (edict_t *self) -> void
|
||||
{
|
||||
float r;
|
||||
|
||||
r = frandom();
|
||||
|
||||
if (r <= 0.3f)
|
||||
gi.sound(self, CHAN_VOICE, sound_search1, 1, ATTN_NORM, 0);
|
||||
else if (r <= 0.6f)
|
||||
gi.sound(self, CHAN_VOICE, sound_search2, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_search3, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void jorg_dead(edict_t *self);
|
||||
void jorgBFG(edict_t *self);
|
||||
void jorg_firebullet(edict_t *self);
|
||||
void jorg_reattack1(edict_t *self);
|
||||
void jorg_attack1(edict_t *self);
|
||||
void jorg_idle(edict_t *self);
|
||||
void jorg_step_left(edict_t *self);
|
||||
void jorg_step_right(edict_t *self);
|
||||
void jorg_death_hit(edict_t *self);
|
||||
|
||||
//
|
||||
// stand
|
||||
//
|
||||
|
||||
mframe_t jorg_frames_stand[] = {
|
||||
{ ai_stand, 0, jorg_idle },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 10
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 20
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 30
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 19 },
|
||||
{ ai_stand, 11, jorg_step_left },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 6 },
|
||||
{ ai_stand, 9, jorg_step_right },
|
||||
{ ai_stand }, // 40
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, -2, nullptr },
|
||||
{ ai_stand, -17, jorg_step_left },
|
||||
{ ai_stand },
|
||||
{ ai_stand, -12 }, // 50
|
||||
{ ai_stand, -14, jorg_step_right } // 51
|
||||
};
|
||||
MMOVE_T(jorg_move_stand) = { FRAME_stand01, FRAME_stand51, jorg_frames_stand, nullptr };
|
||||
|
||||
void jorg_idle (edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void jorg_death_hit(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_death_hit, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void jorg_step_left(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_step_left, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void jorg_step_right(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_step_right, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_STAND(jorg_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &jorg_move_stand);
|
||||
|
||||
jorg_attack1_end_sound(self);
|
||||
}
|
||||
|
||||
mframe_t jorg_frames_run[] = {
|
||||
{ ai_run, 17, jorg_step_left },
|
||||
{ ai_run },
|
||||
{ ai_run },
|
||||
{ ai_run },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 33, jorg_step_right },
|
||||
{ ai_run },
|
||||
{ ai_run },
|
||||
{ ai_run },
|
||||
{ ai_run, 9 },
|
||||
{ ai_run, 9 },
|
||||
{ ai_run, 9 }
|
||||
};
|
||||
MMOVE_T(jorg_move_run) = { FRAME_walk06, FRAME_walk19, jorg_frames_run, nullptr };
|
||||
|
||||
//
|
||||
// walk
|
||||
//
|
||||
#if 0
|
||||
mframe_t jorg_frames_start_walk[] = {
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 6 },
|
||||
{ ai_walk, 7 },
|
||||
{ ai_walk, 9 },
|
||||
{ ai_walk, 15 }
|
||||
};
|
||||
MMOVE_T(jorg_move_start_walk) = { FRAME_walk01, FRAME_walk05, jorg_frames_start_walk, nullptr };
|
||||
#endif
|
||||
|
||||
mframe_t jorg_frames_walk[] = {
|
||||
{ ai_walk, 17 },
|
||||
{ ai_walk },
|
||||
{ ai_walk },
|
||||
{ ai_walk },
|
||||
{ ai_walk, 12 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 33 },
|
||||
{ ai_walk },
|
||||
{ ai_walk },
|
||||
{ ai_walk },
|
||||
{ ai_walk, 9 },
|
||||
{ ai_walk, 9 },
|
||||
{ ai_walk, 9 }
|
||||
};
|
||||
MMOVE_T(jorg_move_walk) = { FRAME_walk06, FRAME_walk19, jorg_frames_walk, nullptr };
|
||||
|
||||
#if 0
|
||||
mframe_t jorg_frames_end_walk[] = {
|
||||
{ ai_walk, 11 },
|
||||
{ ai_walk },
|
||||
{ ai_walk },
|
||||
{ ai_walk },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, -8 }
|
||||
};
|
||||
MMOVE_T(jorg_move_end_walk) = { FRAME_walk20, FRAME_walk25, jorg_frames_end_walk, nullptr };
|
||||
#endif
|
||||
|
||||
MONSTERINFO_WALK(jorg_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &jorg_move_walk);
|
||||
}
|
||||
|
||||
MONSTERINFO_RUN(jorg_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &jorg_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, &jorg_move_run);
|
||||
|
||||
jorg_attack1_end_sound(self);
|
||||
}
|
||||
|
||||
mframe_t jorg_frames_pain3[] = {
|
||||
{ ai_move, -28 },
|
||||
{ ai_move, -6 },
|
||||
{ ai_move, -3, jorg_step_left },
|
||||
{ ai_move, -9 },
|
||||
{ ai_move, 0, jorg_step_right },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, -7 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, -11 },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 10 },
|
||||
{ ai_move, 11 },
|
||||
{ ai_move },
|
||||
{ ai_move, 10 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 10 },
|
||||
{ ai_move, 7, jorg_step_left },
|
||||
{ ai_move, 17 },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, jorg_step_right }
|
||||
};
|
||||
MMOVE_T(jorg_move_pain3) = { FRAME_pain301, FRAME_pain325, jorg_frames_pain3, jorg_run };
|
||||
|
||||
mframe_t jorg_frames_pain2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(jorg_move_pain2) = { FRAME_pain201, FRAME_pain203, jorg_frames_pain2, jorg_run };
|
||||
|
||||
mframe_t jorg_frames_pain1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(jorg_move_pain1) = { FRAME_pain101, FRAME_pain103, jorg_frames_pain1, jorg_run };
|
||||
|
||||
mframe_t jorg_frames_death1[] = {
|
||||
{ ai_move, 0, BossExplode },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, -5 },
|
||||
{ ai_move, -8 },
|
||||
{ ai_move, -15, jorg_step_left },
|
||||
{ ai_move }, // 10
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, -11 },
|
||||
{ ai_move, -25 },
|
||||
{ ai_move, -10, jorg_step_right },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 20
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, -21 },
|
||||
{ ai_move, -10 },
|
||||
{ ai_move, -16, jorg_step_left },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 30
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 22 },
|
||||
{ ai_move, 33, jorg_step_left },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 28 },
|
||||
{ ai_move, 28, jorg_step_right },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 40
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, -19 },
|
||||
{ ai_move, 0, jorg_death_hit },
|
||||
{ ai_move },
|
||||
{ ai_move } // 50
|
||||
};
|
||||
MMOVE_T(jorg_move_death) = { FRAME_death01, FRAME_death50, jorg_frames_death1, jorg_dead };
|
||||
|
||||
mframe_t jorg_frames_attack2[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, jorgBFG },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(jorg_move_attack2) = { FRAME_attak201, FRAME_attak213, jorg_frames_attack2, jorg_run };
|
||||
|
||||
mframe_t jorg_frames_start_attack1[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(jorg_move_start_attack1) = { FRAME_attak101, FRAME_attak108, jorg_frames_start_attack1, jorg_attack1 };
|
||||
|
||||
mframe_t jorg_frames_attack1[] = {
|
||||
{ ai_charge, 0, jorg_firebullet },
|
||||
{ ai_charge, 0, jorg_firebullet },
|
||||
{ ai_charge, 0, jorg_firebullet },
|
||||
{ ai_charge, 0, jorg_firebullet },
|
||||
{ ai_charge, 0, jorg_firebullet },
|
||||
{ ai_charge, 0, jorg_firebullet }
|
||||
};
|
||||
MMOVE_T(jorg_move_attack1) = { FRAME_attak109, FRAME_attak114, jorg_frames_attack1, jorg_reattack1 };
|
||||
|
||||
mframe_t jorg_frames_end_attack1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(jorg_move_end_attack1) = { FRAME_attak115, FRAME_attak118, jorg_frames_end_attack1, jorg_run };
|
||||
|
||||
void jorg_reattack1(edict_t *self)
|
||||
{
|
||||
if (visible(self, self->enemy))
|
||||
{
|
||||
if (frandom() < 0.9f)
|
||||
M_SetAnimation(self, &jorg_move_attack1);
|
||||
else
|
||||
{
|
||||
M_SetAnimation(self, &jorg_move_end_attack1);
|
||||
jorg_attack1_end_sound(self);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
M_SetAnimation(self, &jorg_move_end_attack1);
|
||||
jorg_attack1_end_sound(self);
|
||||
}
|
||||
}
|
||||
|
||||
void jorg_attack1(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &jorg_move_attack1);
|
||||
}
|
||||
|
||||
PAIN(jorg_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
// Lessen the chance of him going into his pain frames if he takes little damage
|
||||
if (mod.id != MOD_CHAINFIST)
|
||||
{
|
||||
if (damage <= 40)
|
||||
if (frandom() <= 0.6f)
|
||||
return;
|
||||
|
||||
/*
|
||||
If he's entering his attack1 or using attack1, lessen the chance of him
|
||||
going into pain
|
||||
*/
|
||||
|
||||
if ((self->s.frame >= FRAME_attak101) && (self->s.frame <= FRAME_attak108))
|
||||
if (frandom() <= 0.005f)
|
||||
return;
|
||||
|
||||
if ((self->s.frame >= FRAME_attak109) && (self->s.frame <= FRAME_attak114))
|
||||
if (frandom() <= 0.00005f)
|
||||
return;
|
||||
|
||||
if ((self->s.frame >= FRAME_attak201) && (self->s.frame <= FRAME_attak208))
|
||||
if (frandom() <= 0.005f)
|
||||
return;
|
||||
}
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
bool do_pain3 = false;
|
||||
|
||||
if (damage > 50)
|
||||
{
|
||||
if (damage <= 100)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (frandom() <= 0.3f)
|
||||
{
|
||||
do_pain3 = true;
|
||||
gi.sound(self, CHAN_VOICE, sound_pain3, 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
jorg_attack1_end_sound(self);
|
||||
|
||||
if (damage <= 50)
|
||||
M_SetAnimation(self, &jorg_move_pain1);
|
||||
else if (damage <= 100)
|
||||
M_SetAnimation(self, &jorg_move_pain2);
|
||||
else if (do_pain3)
|
||||
M_SetAnimation(self, &jorg_move_pain3);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(jorg_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
void jorgBFG(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_JORG_BFG_1], forward, right);
|
||||
|
||||
vec = self->enemy->s.origin;
|
||||
vec[2] += self->enemy->viewheight;
|
||||
dir = vec - start;
|
||||
dir.normalize();
|
||||
gi.sound(self, CHAN_WEAPON, sound_bfg_fire, 1, ATTN_NORM, 0);
|
||||
monster_fire_bfg(self, start, dir, 50, 300, 100, 200, MZ2_JORG_BFG_1);
|
||||
}
|
||||
|
||||
void jorg_firebullet_right(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right, start;
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_JORG_MACHINEGUN_R1], forward, right);
|
||||
PredictAim(self, self->enemy, start, 0, false, -0.2f, &forward, nullptr);
|
||||
monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_JORG_MACHINEGUN_R1);
|
||||
}
|
||||
|
||||
void jorg_firebullet_left(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right, start;
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_JORG_MACHINEGUN_L1], forward, right);
|
||||
PredictAim(self, self->enemy, start, 0, false, 0.2f, &forward, nullptr);
|
||||
monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_JORG_MACHINEGUN_L1);
|
||||
}
|
||||
|
||||
void jorg_firebullet(edict_t *self)
|
||||
{
|
||||
jorg_firebullet_left(self);
|
||||
jorg_firebullet_right(self);
|
||||
};
|
||||
|
||||
MONSTERINFO_ATTACK(jorg_attack) (edict_t *self) -> void
|
||||
{
|
||||
if (frandom() <= 0.75f)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_attack1, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.weapon_sound = gi.soundindex("boss3/w_loop.wav");
|
||||
M_SetAnimation(self, &jorg_move_start_attack1);
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_attack2, 1, ATTN_NORM, 0);
|
||||
M_SetAnimation(self, &jorg_move_attack2);
|
||||
}
|
||||
}
|
||||
|
||||
void jorg_dead(edict_t *self)
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1_BIG);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.multicast(self->s.origin, MULTICAST_PHS, false);
|
||||
|
||||
self->s.sound = 0;
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
ThrowGibs(self, 500, {
|
||||
{ 2, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ 2, "models/objects/gibs/sm_metal/tris.md2", GIB_METALLIC },
|
||||
{ "models/monsters/boss3/jorg/gibs/chest.md2", GIB_SKINNED },
|
||||
{ 2, "models/monsters/boss3/jorg/gibs/foot.md2", GIB_SKINNED },
|
||||
{ 2, "models/monsters/boss3/jorg/gibs/gun.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ 2, "models/monsters/boss3/jorg/gibs/thigh.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss3/jorg/gibs/spine.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ 4, "models/monsters/boss3/jorg/gibs/tube.md2", GIB_SKINNED },
|
||||
{ 6, "models/monsters/boss3/jorg/gibs/spike.md2", GIB_SKINNED },
|
||||
{ "models/monsters/boss3/jorg/gibs/head.md2", GIB_SKINNED | GIB_METALLIC | GIB_HEAD }
|
||||
});
|
||||
|
||||
MakronToss(self);
|
||||
}
|
||||
|
||||
DIE(jorg_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
|
||||
jorg_attack1_end_sound(self);
|
||||
self->deadflag = true;
|
||||
self->takedamage = false;
|
||||
self->count = 0;
|
||||
M_SetAnimation(self, &jorg_move_death);
|
||||
}
|
||||
|
||||
MONSTERINFO_CHECKATTACK(Jorg_CheckAttack) (edict_t *self) -> bool
|
||||
{
|
||||
vec3_t spot1, spot2;
|
||||
vec3_t temp;
|
||||
float chance;
|
||||
trace_t tr;
|
||||
float enemy_yaw;
|
||||
|
||||
if (self->enemy->health > 0)
|
||||
{
|
||||
// see if any entities are in the way of the shot
|
||||
spot1 = self->s.origin;
|
||||
spot1[2] += self->viewheight;
|
||||
spot2 = self->enemy->s.origin;
|
||||
spot2[2] += self->enemy->viewheight;
|
||||
|
||||
tr = gi.traceline(spot1, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_PLAYER | CONTENTS_SLIME | CONTENTS_LAVA);
|
||||
|
||||
// do we have a clear shot?
|
||||
if (tr.ent != self->enemy && !(tr.ent->svflags & SVF_PLAYER))
|
||||
return false;
|
||||
}
|
||||
|
||||
float enemy_range = range_to(self, self->enemy);
|
||||
temp = self->enemy->s.origin - self->s.origin;
|
||||
enemy_yaw = vectoyaw(temp);
|
||||
|
||||
self->ideal_yaw = enemy_yaw;
|
||||
|
||||
// melee attack
|
||||
if (enemy_range <= RANGE_MELEE)
|
||||
{
|
||||
if (self->monsterinfo.melee)
|
||||
self->monsterinfo.attack_state = AS_MELEE;
|
||||
else
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// missile attack
|
||||
if (!self->monsterinfo.attack)
|
||||
return false;
|
||||
|
||||
if (level.time < self->monsterinfo.attack_finished)
|
||||
return false;
|
||||
|
||||
if (enemy_range > RANGE_MID)
|
||||
return false;
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
chance = 0.4f;
|
||||
}
|
||||
else if (enemy_range <= RANGE_MELEE)
|
||||
{
|
||||
chance = 0.8f;
|
||||
}
|
||||
else if (enemy_range <= RANGE_NEAR)
|
||||
{
|
||||
chance = 0.4f;
|
||||
}
|
||||
else
|
||||
{
|
||||
chance = 0.2f;
|
||||
}
|
||||
|
||||
if (frandom() < chance)
|
||||
{
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
self->monsterinfo.attack_finished = level.time + random_time(2_sec);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (self->flags & FL_FLY)
|
||||
{
|
||||
if (frandom() < 0.3f)
|
||||
self->monsterinfo.attack_state = AS_SLIDING;
|
||||
else
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MakronPrecache();
|
||||
|
||||
/*QUAKED monster_jorg (1 .5 0) (-80 -80 0) (90 90 140) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_jorg(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_pain1 = gi.soundindex("boss3/bs3pain1.wav");
|
||||
sound_pain2 = gi.soundindex("boss3/bs3pain2.wav");
|
||||
sound_pain3 = gi.soundindex("boss3/bs3pain3.wav");
|
||||
sound_death = gi.soundindex("boss3/bs3deth1.wav");
|
||||
sound_attack1 = gi.soundindex("boss3/bs3atck1.wav");
|
||||
sound_attack1_loop = gi.soundindex("boss3/bs3atck1_loop.wav");
|
||||
sound_attack1_end = gi.soundindex("boss3/bs3atck1_end.wav");
|
||||
sound_attack2 = gi.soundindex("boss3/bs3atck2.wav");
|
||||
sound_search1 = gi.soundindex("boss3/bs3srch1.wav");
|
||||
sound_search2 = gi.soundindex("boss3/bs3srch2.wav");
|
||||
sound_search3 = gi.soundindex("boss3/bs3srch3.wav");
|
||||
sound_idle = gi.soundindex("boss3/bs3idle1.wav");
|
||||
sound_step_left = gi.soundindex("boss3/step1.wav");
|
||||
sound_step_right = gi.soundindex("boss3/step2.wav");
|
||||
sound_firegun = gi.soundindex("boss3/xfire.wav");
|
||||
sound_death_hit = gi.soundindex("boss3/d_hit.wav");
|
||||
sound_bfg_fire = gi.soundindex("makron/bfg_fire.wav");
|
||||
|
||||
MakronPrecache();
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/boss3/jorg/tris.md2");
|
||||
self->s.modelindex2 = gi.modelindex("models/monsters/boss3/rider/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/boss3/jorg/gibs/chest.md2");
|
||||
gi.modelindex("models/monsters/boss3/jorg/gibs/foot.md2");
|
||||
gi.modelindex("models/monsters/boss3/jorg/gibs/gun.md2");
|
||||
gi.modelindex("models/monsters/boss3/jorg/gibs/head.md2");
|
||||
gi.modelindex("models/monsters/boss3/jorg/gibs/spike.md2");
|
||||
gi.modelindex("models/monsters/boss3/jorg/gibs/spine.md2");
|
||||
gi.modelindex("models/monsters/boss3/jorg/gibs/thigh.md2");
|
||||
gi.modelindex("models/monsters/boss3/jorg/gibs/tube.md2");
|
||||
|
||||
self->mins = { -80, -80, 0 };
|
||||
self->maxs = { 80, 80, 140 };
|
||||
|
||||
self->health = 8000 * st.health_multiplier;
|
||||
self->gib_health = -2000;
|
||||
self->mass = 1000;
|
||||
|
||||
self->pain = jorg_pain;
|
||||
self->die = jorg_die;
|
||||
self->monsterinfo.stand = jorg_stand;
|
||||
self->monsterinfo.walk = jorg_walk;
|
||||
self->monsterinfo.run = jorg_run;
|
||||
self->monsterinfo.dodge = nullptr;
|
||||
self->monsterinfo.attack = jorg_attack;
|
||||
self->monsterinfo.search = jorg_search;
|
||||
self->monsterinfo.melee = nullptr;
|
||||
self->monsterinfo.sight = nullptr;
|
||||
self->monsterinfo.checkattack = Jorg_CheckAttack;
|
||||
self->monsterinfo.setskin = jorg_setskin;
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &jorg_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
// PMM
|
||||
self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
|
||||
// pmm
|
||||
self->monsterinfo.aiflags |= AI_DOUBLE_TROUBLE;
|
||||
}
|
||||
199
rerelease/m_boss31.h
Normal file
199
rerelease/m_boss31.h
Normal file
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/boss3/jorg
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_attak101,
|
||||
FRAME_attak102,
|
||||
FRAME_attak103,
|
||||
FRAME_attak104,
|
||||
FRAME_attak105,
|
||||
FRAME_attak106,
|
||||
FRAME_attak107,
|
||||
FRAME_attak108,
|
||||
FRAME_attak109,
|
||||
FRAME_attak110,
|
||||
FRAME_attak111,
|
||||
FRAME_attak112,
|
||||
FRAME_attak113,
|
||||
FRAME_attak114,
|
||||
FRAME_attak115,
|
||||
FRAME_attak116,
|
||||
FRAME_attak117,
|
||||
FRAME_attak118,
|
||||
FRAME_attak201,
|
||||
FRAME_attak202,
|
||||
FRAME_attak203,
|
||||
FRAME_attak204,
|
||||
FRAME_attak205,
|
||||
FRAME_attak206,
|
||||
FRAME_attak207,
|
||||
FRAME_attak208,
|
||||
FRAME_attak209,
|
||||
FRAME_attak210,
|
||||
FRAME_attak211,
|
||||
FRAME_attak212,
|
||||
FRAME_attak213,
|
||||
FRAME_death01,
|
||||
FRAME_death02,
|
||||
FRAME_death03,
|
||||
FRAME_death04,
|
||||
FRAME_death05,
|
||||
FRAME_death06,
|
||||
FRAME_death07,
|
||||
FRAME_death08,
|
||||
FRAME_death09,
|
||||
FRAME_death10,
|
||||
FRAME_death11,
|
||||
FRAME_death12,
|
||||
FRAME_death13,
|
||||
FRAME_death14,
|
||||
FRAME_death15,
|
||||
FRAME_death16,
|
||||
FRAME_death17,
|
||||
FRAME_death18,
|
||||
FRAME_death19,
|
||||
FRAME_death20,
|
||||
FRAME_death21,
|
||||
FRAME_death22,
|
||||
FRAME_death23,
|
||||
FRAME_death24,
|
||||
FRAME_death25,
|
||||
FRAME_death26,
|
||||
FRAME_death27,
|
||||
FRAME_death28,
|
||||
FRAME_death29,
|
||||
FRAME_death30,
|
||||
FRAME_death31,
|
||||
FRAME_death32,
|
||||
FRAME_death33,
|
||||
FRAME_death34,
|
||||
FRAME_death35,
|
||||
FRAME_death36,
|
||||
FRAME_death37,
|
||||
FRAME_death38,
|
||||
FRAME_death39,
|
||||
FRAME_death40,
|
||||
FRAME_death41,
|
||||
FRAME_death42,
|
||||
FRAME_death43,
|
||||
FRAME_death44,
|
||||
FRAME_death45,
|
||||
FRAME_death46,
|
||||
FRAME_death47,
|
||||
FRAME_death48,
|
||||
FRAME_death49,
|
||||
FRAME_death50,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_pain304,
|
||||
FRAME_pain305,
|
||||
FRAME_pain306,
|
||||
FRAME_pain307,
|
||||
FRAME_pain308,
|
||||
FRAME_pain309,
|
||||
FRAME_pain310,
|
||||
FRAME_pain311,
|
||||
FRAME_pain312,
|
||||
FRAME_pain313,
|
||||
FRAME_pain314,
|
||||
FRAME_pain315,
|
||||
FRAME_pain316,
|
||||
FRAME_pain317,
|
||||
FRAME_pain318,
|
||||
FRAME_pain319,
|
||||
FRAME_pain320,
|
||||
FRAME_pain321,
|
||||
FRAME_pain322,
|
||||
FRAME_pain323,
|
||||
FRAME_pain324,
|
||||
FRAME_pain325,
|
||||
FRAME_stand01,
|
||||
FRAME_stand02,
|
||||
FRAME_stand03,
|
||||
FRAME_stand04,
|
||||
FRAME_stand05,
|
||||
FRAME_stand06,
|
||||
FRAME_stand07,
|
||||
FRAME_stand08,
|
||||
FRAME_stand09,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_stand18,
|
||||
FRAME_stand19,
|
||||
FRAME_stand20,
|
||||
FRAME_stand21,
|
||||
FRAME_stand22,
|
||||
FRAME_stand23,
|
||||
FRAME_stand24,
|
||||
FRAME_stand25,
|
||||
FRAME_stand26,
|
||||
FRAME_stand27,
|
||||
FRAME_stand28,
|
||||
FRAME_stand29,
|
||||
FRAME_stand30,
|
||||
FRAME_stand31,
|
||||
FRAME_stand32,
|
||||
FRAME_stand33,
|
||||
FRAME_stand34,
|
||||
FRAME_stand35,
|
||||
FRAME_stand36,
|
||||
FRAME_stand37,
|
||||
FRAME_stand38,
|
||||
FRAME_stand39,
|
||||
FRAME_stand40,
|
||||
FRAME_stand41,
|
||||
FRAME_stand42,
|
||||
FRAME_stand43,
|
||||
FRAME_stand44,
|
||||
FRAME_stand45,
|
||||
FRAME_stand46,
|
||||
FRAME_stand47,
|
||||
FRAME_stand48,
|
||||
FRAME_stand49,
|
||||
FRAME_stand50,
|
||||
FRAME_stand51,
|
||||
FRAME_walk01,
|
||||
FRAME_walk02,
|
||||
FRAME_walk03,
|
||||
FRAME_walk04,
|
||||
FRAME_walk05,
|
||||
FRAME_walk06,
|
||||
FRAME_walk07,
|
||||
FRAME_walk08,
|
||||
FRAME_walk09,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_walk13,
|
||||
FRAME_walk14,
|
||||
FRAME_walk15,
|
||||
FRAME_walk16,
|
||||
FRAME_walk17,
|
||||
FRAME_walk18,
|
||||
FRAME_walk19,
|
||||
FRAME_walk20,
|
||||
FRAME_walk21,
|
||||
FRAME_walk22,
|
||||
FRAME_walk23,
|
||||
FRAME_walk24,
|
||||
FRAME_walk25
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
905
rerelease/m_boss32.cpp
Normal file
905
rerelease/m_boss32.cpp
Normal file
@@ -0,0 +1,905 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
Makron -- Final Boss
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_boss32.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
void MakronRailgun(edict_t *self);
|
||||
void MakronSaveloc(edict_t *self);
|
||||
void MakronHyperblaster(edict_t *self);
|
||||
void makron_step_left(edict_t *self);
|
||||
void makron_step_right(edict_t *self);
|
||||
void makronBFG(edict_t *self);
|
||||
void makron_dead(edict_t *self);
|
||||
|
||||
static int sound_pain4;
|
||||
static int sound_pain5;
|
||||
static int sound_pain6;
|
||||
static int sound_death;
|
||||
static int sound_step_left;
|
||||
static int sound_step_right;
|
||||
static int sound_attack_bfg;
|
||||
static int sound_brainsplorch;
|
||||
static int sound_prerailgun;
|
||||
static int sound_popup;
|
||||
static int sound_taunt1;
|
||||
static int sound_taunt2;
|
||||
static int sound_taunt3;
|
||||
static int sound_hit;
|
||||
|
||||
void makron_taunt(edict_t *self)
|
||||
{
|
||||
float r;
|
||||
|
||||
r = frandom();
|
||||
if (r <= 0.3f)
|
||||
gi.sound(self, CHAN_AUTO, sound_taunt1, 1, ATTN_NONE, 0);
|
||||
else if (r <= 0.6f)
|
||||
gi.sound(self, CHAN_AUTO, sound_taunt2, 1, ATTN_NONE, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_AUTO, sound_taunt3, 1, ATTN_NONE, 0);
|
||||
}
|
||||
|
||||
//
|
||||
// stand
|
||||
//
|
||||
|
||||
mframe_t makron_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 10
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 20
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 30
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 40
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 50
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand } // 60
|
||||
};
|
||||
MMOVE_T(makron_move_stand) = { FRAME_stand201, FRAME_stand260, makron_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(makron_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &makron_move_stand);
|
||||
}
|
||||
|
||||
mframe_t makron_frames_run[] = {
|
||||
{ ai_run, 3, makron_step_left },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8, makron_step_right },
|
||||
{ ai_run, 6 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 9 },
|
||||
{ ai_run, 6 },
|
||||
{ ai_run, 12 }
|
||||
};
|
||||
MMOVE_T(makron_move_run) = { FRAME_walk204, FRAME_walk213, makron_frames_run, nullptr };
|
||||
|
||||
void makron_hit(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_AUTO, sound_hit, 1, ATTN_NONE, 0);
|
||||
}
|
||||
|
||||
void makron_popup(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_popup, 1, ATTN_NONE, 0);
|
||||
}
|
||||
|
||||
void makron_step_left(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_step_left, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void makron_step_right(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_step_right, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void makron_brainsplorch(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_brainsplorch, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void makron_prerailgun(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_prerailgun, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t makron_frames_walk[] = {
|
||||
{ ai_walk, 3, makron_step_left },
|
||||
{ ai_walk, 12 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8, makron_step_right },
|
||||
{ ai_walk, 6 },
|
||||
{ ai_walk, 12 },
|
||||
{ ai_walk, 9 },
|
||||
{ ai_walk, 6 },
|
||||
{ ai_walk, 12 }
|
||||
};
|
||||
MMOVE_T(makron_move_walk) = { FRAME_walk204, FRAME_walk213, makron_frames_run, nullptr };
|
||||
|
||||
MONSTERINFO_WALK(makron_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &makron_move_walk);
|
||||
}
|
||||
|
||||
MONSTERINFO_RUN(makron_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &makron_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, &makron_move_run);
|
||||
}
|
||||
|
||||
mframe_t makron_frames_pain6[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 10
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, makron_popup },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 20
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, makron_taunt },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(makron_move_pain6) = { FRAME_pain601, FRAME_pain627, makron_frames_pain6, makron_run };
|
||||
|
||||
mframe_t makron_frames_pain5[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(makron_move_pain5) = { FRAME_pain501, FRAME_pain504, makron_frames_pain5, makron_run };
|
||||
|
||||
mframe_t makron_frames_pain4[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(makron_move_pain4) = { FRAME_pain401, FRAME_pain404, makron_frames_pain4, makron_run };
|
||||
|
||||
/*
|
||||
---
|
||||
Makron Torso. This needs to be spawned in
|
||||
---
|
||||
*/
|
||||
|
||||
THINK(makron_torso_think) (edict_t *self) -> void
|
||||
{
|
||||
if (++self->s.frame >= 365)
|
||||
self->s.frame = 346;
|
||||
|
||||
self->nextthink = level.time + 10_hz;
|
||||
|
||||
if (self->s.angles[0] > 0)
|
||||
self->s.angles[0] = max(0.f, self->s.angles[0] - 15);
|
||||
}
|
||||
|
||||
void makron_torso(edict_t *ent)
|
||||
{
|
||||
ent->s.frame = 346;
|
||||
ent->s.modelindex = gi.modelindex("models/monsters/boss3/rider/tris.md2");
|
||||
ent->s.skinnum = 1;
|
||||
ent->think = makron_torso_think;
|
||||
ent->nextthink = level.time + 10_hz;
|
||||
ent->s.sound = gi.soundindex("makron/spine.wav");
|
||||
ent->movetype = MOVETYPE_TOSS;
|
||||
ent->s.effects = EF_GIB;
|
||||
vec3_t forward, up;
|
||||
AngleVectors(ent->s.angles, forward, nullptr, up);
|
||||
ent->velocity += (up * 120);
|
||||
ent->velocity += (forward * -120);
|
||||
ent->s.origin += (forward * -10);
|
||||
ent->s.angles[0] = 90;
|
||||
ent->avelocity = {};
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
void makron_spawn_torso(edict_t *self)
|
||||
{
|
||||
edict_t *tempent = ThrowGib(self, "models/monsters/boss3/rider/tris.md2", 0, GIB_NONE, self->s.scale);
|
||||
tempent->s.origin = self->s.origin;
|
||||
tempent->s.angles = self->s.angles;
|
||||
self->maxs[2] -= tempent->maxs[2];
|
||||
tempent->s.origin[2] += self->maxs[2] - 15;
|
||||
makron_torso(tempent);
|
||||
}
|
||||
|
||||
mframe_t makron_frames_death2[] = {
|
||||
{ ai_move, -15 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, -12 },
|
||||
{ ai_move, 0, makron_step_left },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 10
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 11 },
|
||||
{ ai_move, 12 },
|
||||
{ ai_move, 11, makron_step_right },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 20
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 30
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 5 },
|
||||
{ ai_move, 7 },
|
||||
{ ai_move, 6, makron_step_left },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, 2 }, // 40
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 50
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, -6 },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, -6, makron_step_right },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, -4, makron_step_left },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 60
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, -5 },
|
||||
{ ai_move, -3, makron_step_right },
|
||||
{ ai_move, -8 },
|
||||
{ ai_move, -3, makron_step_left },
|
||||
{ ai_move, -7 },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, -4, makron_step_right }, // 70
|
||||
{ ai_move, -6 },
|
||||
{ ai_move, -7 },
|
||||
{ ai_move, 0, makron_step_left },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 80
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move }, // 90
|
||||
{ ai_move, 27, makron_hit },
|
||||
{ ai_move, 26 },
|
||||
{ ai_move, 0, makron_brainsplorch },
|
||||
{ ai_move },
|
||||
{ ai_move } // 95
|
||||
};
|
||||
MMOVE_T(makron_move_death2) = { FRAME_death201, FRAME_death295, makron_frames_death2, makron_dead };
|
||||
|
||||
#if 0
|
||||
mframe_t makron_frames_death3[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(makron_move_death3) = { FRAME_death301, FRAME_death320, makron_frames_death3, nullptr };
|
||||
#endif
|
||||
|
||||
mframe_t makron_frames_sight[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(makron_move_sight) = { FRAME_active01, FRAME_active13, makron_frames_sight, makron_run };
|
||||
|
||||
void makronBFG(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_MAKRON_BFG], forward, right);
|
||||
|
||||
vec = self->enemy->s.origin;
|
||||
vec[2] += self->enemy->viewheight;
|
||||
dir = vec - start;
|
||||
dir.normalize();
|
||||
gi.sound(self, CHAN_VOICE, sound_attack_bfg, 1, ATTN_NORM, 0);
|
||||
monster_fire_bfg(self, start, dir, 50, 300, 100, 300, MZ2_MAKRON_BFG);
|
||||
}
|
||||
|
||||
mframe_t makron_frames_attack3[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, makronBFG },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(makron_move_attack3) = { FRAME_attak301, FRAME_attak308, makron_frames_attack3, makron_run };
|
||||
|
||||
mframe_t makron_frames_attack4[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move, 0, MakronHyperblaster }, // fire
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(makron_move_attack4) = { FRAME_attak401, FRAME_attak426, makron_frames_attack4, makron_run };
|
||||
|
||||
mframe_t makron_frames_attack5[] = {
|
||||
{ ai_charge, 0, makron_prerailgun },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, MakronSaveloc },
|
||||
{ ai_move, 0, MakronRailgun }, // Fire railgun
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(makron_move_attack5) = { FRAME_attak501, FRAME_attak516, makron_frames_attack5, makron_run };
|
||||
|
||||
void MakronSaveloc(edict_t *self)
|
||||
{
|
||||
self->pos1 = self->enemy->s.origin; // save for aiming the shot
|
||||
self->pos1[2] += self->enemy->viewheight;
|
||||
};
|
||||
|
||||
void MakronRailgun(edict_t *self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t forward, right;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_MAKRON_RAILGUN_1], forward, right);
|
||||
|
||||
// calc direction to where we targted
|
||||
dir = self->pos1 - start;
|
||||
dir.normalize();
|
||||
|
||||
monster_fire_railgun(self, start, dir, 50, 100, MZ2_MAKRON_RAILGUN_1);
|
||||
}
|
||||
|
||||
void MakronHyperblaster(edict_t *self)
|
||||
{
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
|
||||
monster_muzzleflash_id_t flash_number = (monster_muzzleflash_id_t) (MZ2_MAKRON_BLASTER_1 + (self->s.frame - FRAME_attak405));
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[flash_number], forward, right);
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
vec = self->enemy->s.origin;
|
||||
vec[2] += self->enemy->viewheight;
|
||||
vec -= start;
|
||||
vec = vectoangles(vec);
|
||||
dir[0] = vec[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
dir[0] = 0;
|
||||
}
|
||||
if (self->s.frame <= FRAME_attak413)
|
||||
dir[1] = self->s.angles[1] - 10 * (self->s.frame - FRAME_attak413);
|
||||
else
|
||||
dir[1] = self->s.angles[1] + 10 * (self->s.frame - FRAME_attak421);
|
||||
dir[2] = 0;
|
||||
|
||||
AngleVectors(dir, forward, nullptr, nullptr);
|
||||
|
||||
monster_fire_blaster(self, start, forward, 15, 1000, flash_number, EF_BLASTER);
|
||||
}
|
||||
|
||||
PAIN(makron_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
if (self->monsterinfo.active_move == &makron_move_sight)
|
||||
return;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
// Lessen the chance of him going into his pain frames
|
||||
if (mod.id != MOD_CHAINFIST && damage <= 25)
|
||||
if (frandom() < 0.2f)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
bool do_pain6 = false;
|
||||
|
||||
if (damage <= 40)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain4, 1, ATTN_NONE, 0);
|
||||
else if (damage <= 110)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain5, 1, ATTN_NONE, 0);
|
||||
else
|
||||
{
|
||||
if (damage <= 150)
|
||||
{
|
||||
if (frandom() <= 0.45f)
|
||||
{
|
||||
do_pain6 = true;
|
||||
gi.sound(self, CHAN_VOICE, sound_pain6, 1, ATTN_NONE, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (frandom() <= 0.35f)
|
||||
{
|
||||
do_pain6 = true;
|
||||
gi.sound(self, CHAN_VOICE, sound_pain6, 1, ATTN_NONE, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (damage <= 40)
|
||||
M_SetAnimation(self, &makron_move_pain4);
|
||||
else if (damage <= 110)
|
||||
M_SetAnimation(self, &makron_move_pain5);
|
||||
else if (do_pain6)
|
||||
M_SetAnimation(self, &makron_move_pain6);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(makron_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
MONSTERINFO_SIGHT(makron_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
M_SetAnimation(self, &makron_move_sight);
|
||||
}
|
||||
|
||||
MONSTERINFO_ATTACK(makron_attack) (edict_t *self) -> void
|
||||
{
|
||||
float r;
|
||||
|
||||
r = frandom();
|
||||
|
||||
if (r <= 0.3f)
|
||||
M_SetAnimation(self, &makron_move_attack3);
|
||||
else if (r <= 0.6f)
|
||||
M_SetAnimation(self, &makron_move_attack4);
|
||||
else
|
||||
M_SetAnimation(self, &makron_move_attack5);
|
||||
}
|
||||
|
||||
//
|
||||
// death
|
||||
//
|
||||
|
||||
void makron_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -60, -60, 0 };
|
||||
self->maxs = { 60, 60, 24 };
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
gi.linkentity(self);
|
||||
monster_dead(self);
|
||||
}
|
||||
|
||||
DIE(makron_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
self->s.sound = 0;
|
||||
|
||||
// check for gib
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
ThrowGibs(self, damage, {
|
||||
{ "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ 4, "models/objects/gibs/sm_metal/tris.md2", GIB_METALLIC },
|
||||
{ "models/objects/gibs/gear/tris.md2", GIB_METALLIC | GIB_HEAD }
|
||||
});
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0);
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
|
||||
M_SetAnimation(self, &makron_move_death2);
|
||||
|
||||
makron_spawn_torso(self);
|
||||
|
||||
self->mins = { -60, -60, 0 };
|
||||
self->maxs = { 60, 60, 48 };
|
||||
}
|
||||
|
||||
MONSTERINFO_CHECKATTACK(Makron_CheckAttack) (edict_t *self) -> bool
|
||||
{
|
||||
vec3_t spot1, spot2;
|
||||
vec3_t temp;
|
||||
float chance;
|
||||
trace_t tr;
|
||||
float enemy_yaw;
|
||||
|
||||
if (self->enemy->health > 0)
|
||||
{
|
||||
// see if any entities are in the way of the shot
|
||||
spot1 = self->s.origin;
|
||||
spot1[2] += self->viewheight;
|
||||
spot2 = self->enemy->s.origin;
|
||||
spot2[2] += self->enemy->viewheight;
|
||||
|
||||
tr = gi.traceline(spot1, spot2, self, CONTENTS_SOLID | CONTENTS_PLAYER | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA);
|
||||
|
||||
// do we have a clear shot?
|
||||
if (tr.ent != self->enemy && !(tr.ent->svflags & SVF_PLAYER))
|
||||
return false;
|
||||
}
|
||||
|
||||
float enemy_range = range_to(self, self->enemy);
|
||||
temp = self->enemy->s.origin - self->s.origin;
|
||||
enemy_yaw = vectoyaw(temp);
|
||||
|
||||
self->ideal_yaw = enemy_yaw;
|
||||
|
||||
// melee attack
|
||||
if (enemy_range <= RANGE_MELEE)
|
||||
{
|
||||
if (self->monsterinfo.melee)
|
||||
self->monsterinfo.attack_state = AS_MELEE;
|
||||
else
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// missile attack
|
||||
if (!self->monsterinfo.attack)
|
||||
return false;
|
||||
|
||||
if (level.time < self->monsterinfo.attack_finished)
|
||||
return false;
|
||||
|
||||
if (enemy_range > RANGE_MID)
|
||||
return false;
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
chance = 0.4f;
|
||||
}
|
||||
else if (enemy_range <= RANGE_MELEE)
|
||||
{
|
||||
chance = 0.8f;
|
||||
}
|
||||
else if (enemy_range <= RANGE_NEAR)
|
||||
{
|
||||
chance = 0.4f;
|
||||
}
|
||||
else
|
||||
{
|
||||
chance = 0.2f;
|
||||
}
|
||||
|
||||
if (frandom() < chance)
|
||||
{
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
self->monsterinfo.attack_finished = level.time + random_time(2_sec);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (self->flags & FL_FLY)
|
||||
{
|
||||
if (frandom() < 0.3f)
|
||||
self->monsterinfo.attack_state = AS_SLIDING;
|
||||
else
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// monster_makron
|
||||
//
|
||||
|
||||
void MakronPrecache()
|
||||
{
|
||||
sound_pain4 = gi.soundindex("makron/pain3.wav");
|
||||
sound_pain5 = gi.soundindex("makron/pain2.wav");
|
||||
sound_pain6 = gi.soundindex("makron/pain1.wav");
|
||||
sound_death = gi.soundindex("makron/death.wav");
|
||||
sound_step_left = gi.soundindex("makron/step1.wav");
|
||||
sound_step_right = gi.soundindex("makron/step2.wav");
|
||||
sound_attack_bfg = gi.soundindex("makron/bfg_fire.wav");
|
||||
sound_brainsplorch = gi.soundindex("makron/brain1.wav");
|
||||
sound_prerailgun = gi.soundindex("makron/rail_up.wav");
|
||||
sound_popup = gi.soundindex("makron/popup.wav");
|
||||
sound_taunt1 = gi.soundindex("makron/voice4.wav");
|
||||
sound_taunt2 = gi.soundindex("makron/voice3.wav");
|
||||
sound_taunt3 = gi.soundindex("makron/voice.wav");
|
||||
sound_hit = gi.soundindex("makron/bhit.wav");
|
||||
|
||||
gi.modelindex("models/monsters/boss3/rider/tris.md2");
|
||||
}
|
||||
|
||||
/*QUAKED monster_makron (1 .5 0) (-30 -30 0) (30 30 90) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_makron(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
MakronPrecache();
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/boss3/rider/tris.md2");
|
||||
self->mins = { -30, -30, 0 };
|
||||
self->maxs = { 30, 30, 90 };
|
||||
|
||||
self->health = 3000 * st.health_multiplier;
|
||||
self->gib_health = -2000;
|
||||
self->mass = 500;
|
||||
|
||||
self->pain = makron_pain;
|
||||
self->die = makron_die;
|
||||
self->monsterinfo.stand = makron_stand;
|
||||
self->monsterinfo.walk = makron_walk;
|
||||
self->monsterinfo.run = makron_run;
|
||||
self->monsterinfo.dodge = nullptr;
|
||||
self->monsterinfo.attack = makron_attack;
|
||||
self->monsterinfo.melee = nullptr;
|
||||
self->monsterinfo.sight = makron_sight;
|
||||
self->monsterinfo.checkattack = Makron_CheckAttack;
|
||||
self->monsterinfo.setskin = makron_setskin;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
// M_SetAnimation(self, &makron_move_stand);
|
||||
M_SetAnimation(self, &makron_move_sight);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
|
||||
// PMM
|
||||
self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
|
||||
// pmm
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
MakronSpawn
|
||||
|
||||
=================
|
||||
*/
|
||||
THINK(MakronSpawn) (edict_t *self) -> void
|
||||
{
|
||||
vec3_t vec;
|
||||
edict_t *player;
|
||||
|
||||
SP_monster_makron(self);
|
||||
self->think(self);
|
||||
|
||||
// jump at player
|
||||
if (self->enemy && self->enemy->inuse && self->enemy->health > 0)
|
||||
player = self->enemy;
|
||||
else
|
||||
player = AI_GetSightClient(self);
|
||||
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
vec = player->s.origin - self->s.origin;
|
||||
self->s.angles[YAW] = vectoyaw(vec);
|
||||
vec.normalize();
|
||||
self->velocity = vec * 400;
|
||||
self->velocity[2] = 200;
|
||||
self->groundentity = nullptr;
|
||||
self->enemy = player;
|
||||
FoundTarget(self);
|
||||
self->monsterinfo.sight(self, self->enemy);
|
||||
self->s.frame = self->monsterinfo.nextframe = FRAME_active01; // FIXME: why????
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
MakronToss
|
||||
|
||||
Jorg is just about dead, so set up to launch Makron out
|
||||
=================
|
||||
*/
|
||||
void MakronToss(edict_t *self)
|
||||
{
|
||||
edict_t *ent = G_Spawn();
|
||||
ent->classname = "monster_makron";
|
||||
ent->target = self->target;
|
||||
ent->s.origin = self->s.origin;
|
||||
ent->enemy = self->enemy;
|
||||
|
||||
MakronSpawn(ent);
|
||||
|
||||
// [Paril-KEX] set health bar over to Makron when we throw him out
|
||||
for (size_t i = 0; i < 2; i++)
|
||||
if (level.health_bar_entities[i] && level.health_bar_entities[i]->enemy == self)
|
||||
level.health_bar_entities[i]->enemy = ent;
|
||||
}
|
||||
502
rerelease/m_boss32.h
Normal file
502
rerelease/m_boss32.h
Normal file
@@ -0,0 +1,502 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/boss3/rider
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_attak101,
|
||||
FRAME_attak102,
|
||||
FRAME_attak103,
|
||||
FRAME_attak104,
|
||||
FRAME_attak105,
|
||||
FRAME_attak106,
|
||||
FRAME_attak107,
|
||||
FRAME_attak108,
|
||||
FRAME_attak109,
|
||||
FRAME_attak110,
|
||||
FRAME_attak111,
|
||||
FRAME_attak112,
|
||||
FRAME_attak113,
|
||||
FRAME_attak114,
|
||||
FRAME_attak115,
|
||||
FRAME_attak116,
|
||||
FRAME_attak117,
|
||||
FRAME_attak118,
|
||||
FRAME_attak201,
|
||||
FRAME_attak202,
|
||||
FRAME_attak203,
|
||||
FRAME_attak204,
|
||||
FRAME_attak205,
|
||||
FRAME_attak206,
|
||||
FRAME_attak207,
|
||||
FRAME_attak208,
|
||||
FRAME_attak209,
|
||||
FRAME_attak210,
|
||||
FRAME_attak211,
|
||||
FRAME_attak212,
|
||||
FRAME_attak213,
|
||||
FRAME_death01,
|
||||
FRAME_death02,
|
||||
FRAME_death03,
|
||||
FRAME_death04,
|
||||
FRAME_death05,
|
||||
FRAME_death06,
|
||||
FRAME_death07,
|
||||
FRAME_death08,
|
||||
FRAME_death09,
|
||||
FRAME_death10,
|
||||
FRAME_death11,
|
||||
FRAME_death12,
|
||||
FRAME_death13,
|
||||
FRAME_death14,
|
||||
FRAME_death15,
|
||||
FRAME_death16,
|
||||
FRAME_death17,
|
||||
FRAME_death18,
|
||||
FRAME_death19,
|
||||
FRAME_death20,
|
||||
FRAME_death21,
|
||||
FRAME_death22,
|
||||
FRAME_death23,
|
||||
FRAME_death24,
|
||||
FRAME_death25,
|
||||
FRAME_death26,
|
||||
FRAME_death27,
|
||||
FRAME_death28,
|
||||
FRAME_death29,
|
||||
FRAME_death30,
|
||||
FRAME_death31,
|
||||
FRAME_death32,
|
||||
FRAME_death33,
|
||||
FRAME_death34,
|
||||
FRAME_death35,
|
||||
FRAME_death36,
|
||||
FRAME_death37,
|
||||
FRAME_death38,
|
||||
FRAME_death39,
|
||||
FRAME_death40,
|
||||
FRAME_death41,
|
||||
FRAME_death42,
|
||||
FRAME_death43,
|
||||
FRAME_death44,
|
||||
FRAME_death45,
|
||||
FRAME_death46,
|
||||
FRAME_death47,
|
||||
FRAME_death48,
|
||||
FRAME_death49,
|
||||
FRAME_death50,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_pain304,
|
||||
FRAME_pain305,
|
||||
FRAME_pain306,
|
||||
FRAME_pain307,
|
||||
FRAME_pain308,
|
||||
FRAME_pain309,
|
||||
FRAME_pain310,
|
||||
FRAME_pain311,
|
||||
FRAME_pain312,
|
||||
FRAME_pain313,
|
||||
FRAME_pain314,
|
||||
FRAME_pain315,
|
||||
FRAME_pain316,
|
||||
FRAME_pain317,
|
||||
FRAME_pain318,
|
||||
FRAME_pain319,
|
||||
FRAME_pain320,
|
||||
FRAME_pain321,
|
||||
FRAME_pain322,
|
||||
FRAME_pain323,
|
||||
FRAME_pain324,
|
||||
FRAME_pain325,
|
||||
FRAME_stand01,
|
||||
FRAME_stand02,
|
||||
FRAME_stand03,
|
||||
FRAME_stand04,
|
||||
FRAME_stand05,
|
||||
FRAME_stand06,
|
||||
FRAME_stand07,
|
||||
FRAME_stand08,
|
||||
FRAME_stand09,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_stand18,
|
||||
FRAME_stand19,
|
||||
FRAME_stand20,
|
||||
FRAME_stand21,
|
||||
FRAME_stand22,
|
||||
FRAME_stand23,
|
||||
FRAME_stand24,
|
||||
FRAME_stand25,
|
||||
FRAME_stand26,
|
||||
FRAME_stand27,
|
||||
FRAME_stand28,
|
||||
FRAME_stand29,
|
||||
FRAME_stand30,
|
||||
FRAME_stand31,
|
||||
FRAME_stand32,
|
||||
FRAME_stand33,
|
||||
FRAME_stand34,
|
||||
FRAME_stand35,
|
||||
FRAME_stand36,
|
||||
FRAME_stand37,
|
||||
FRAME_stand38,
|
||||
FRAME_stand39,
|
||||
FRAME_stand40,
|
||||
FRAME_stand41,
|
||||
FRAME_stand42,
|
||||
FRAME_stand43,
|
||||
FRAME_stand44,
|
||||
FRAME_stand45,
|
||||
FRAME_stand46,
|
||||
FRAME_stand47,
|
||||
FRAME_stand48,
|
||||
FRAME_stand49,
|
||||
FRAME_stand50,
|
||||
FRAME_stand51,
|
||||
FRAME_walk01,
|
||||
FRAME_walk02,
|
||||
FRAME_walk03,
|
||||
FRAME_walk04,
|
||||
FRAME_walk05,
|
||||
FRAME_walk06,
|
||||
FRAME_walk07,
|
||||
FRAME_walk08,
|
||||
FRAME_walk09,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_walk13,
|
||||
FRAME_walk14,
|
||||
FRAME_walk15,
|
||||
FRAME_walk16,
|
||||
FRAME_walk17,
|
||||
FRAME_walk18,
|
||||
FRAME_walk19,
|
||||
FRAME_walk20,
|
||||
FRAME_walk21,
|
||||
FRAME_walk22,
|
||||
FRAME_walk23,
|
||||
FRAME_walk24,
|
||||
FRAME_walk25,
|
||||
FRAME_active01,
|
||||
FRAME_active02,
|
||||
FRAME_active03,
|
||||
FRAME_active04,
|
||||
FRAME_active05,
|
||||
FRAME_active06,
|
||||
FRAME_active07,
|
||||
FRAME_active08,
|
||||
FRAME_active09,
|
||||
FRAME_active10,
|
||||
FRAME_active11,
|
||||
FRAME_active12,
|
||||
FRAME_active13,
|
||||
FRAME_attak301,
|
||||
FRAME_attak302,
|
||||
FRAME_attak303,
|
||||
FRAME_attak304,
|
||||
FRAME_attak305,
|
||||
FRAME_attak306,
|
||||
FRAME_attak307,
|
||||
FRAME_attak308,
|
||||
FRAME_attak401,
|
||||
FRAME_attak402,
|
||||
FRAME_attak403,
|
||||
FRAME_attak404,
|
||||
FRAME_attak405,
|
||||
FRAME_attak406,
|
||||
FRAME_attak407,
|
||||
FRAME_attak408,
|
||||
FRAME_attak409,
|
||||
FRAME_attak410,
|
||||
FRAME_attak411,
|
||||
FRAME_attak412,
|
||||
FRAME_attak413,
|
||||
FRAME_attak414,
|
||||
FRAME_attak415,
|
||||
FRAME_attak416,
|
||||
FRAME_attak417,
|
||||
FRAME_attak418,
|
||||
FRAME_attak419,
|
||||
FRAME_attak420,
|
||||
FRAME_attak421,
|
||||
FRAME_attak422,
|
||||
FRAME_attak423,
|
||||
FRAME_attak424,
|
||||
FRAME_attak425,
|
||||
FRAME_attak426,
|
||||
FRAME_attak501,
|
||||
FRAME_attak502,
|
||||
FRAME_attak503,
|
||||
FRAME_attak504,
|
||||
FRAME_attak505,
|
||||
FRAME_attak506,
|
||||
FRAME_attak507,
|
||||
FRAME_attak508,
|
||||
FRAME_attak509,
|
||||
FRAME_attak510,
|
||||
FRAME_attak511,
|
||||
FRAME_attak512,
|
||||
FRAME_attak513,
|
||||
FRAME_attak514,
|
||||
FRAME_attak515,
|
||||
FRAME_attak516,
|
||||
FRAME_death201,
|
||||
FRAME_death202,
|
||||
FRAME_death203,
|
||||
FRAME_death204,
|
||||
FRAME_death205,
|
||||
FRAME_death206,
|
||||
FRAME_death207,
|
||||
FRAME_death208,
|
||||
FRAME_death209,
|
||||
FRAME_death210,
|
||||
FRAME_death211,
|
||||
FRAME_death212,
|
||||
FRAME_death213,
|
||||
FRAME_death214,
|
||||
FRAME_death215,
|
||||
FRAME_death216,
|
||||
FRAME_death217,
|
||||
FRAME_death218,
|
||||
FRAME_death219,
|
||||
FRAME_death220,
|
||||
FRAME_death221,
|
||||
FRAME_death222,
|
||||
FRAME_death223,
|
||||
FRAME_death224,
|
||||
FRAME_death225,
|
||||
FRAME_death226,
|
||||
FRAME_death227,
|
||||
FRAME_death228,
|
||||
FRAME_death229,
|
||||
FRAME_death230,
|
||||
FRAME_death231,
|
||||
FRAME_death232,
|
||||
FRAME_death233,
|
||||
FRAME_death234,
|
||||
FRAME_death235,
|
||||
FRAME_death236,
|
||||
FRAME_death237,
|
||||
FRAME_death238,
|
||||
FRAME_death239,
|
||||
FRAME_death240,
|
||||
FRAME_death241,
|
||||
FRAME_death242,
|
||||
FRAME_death243,
|
||||
FRAME_death244,
|
||||
FRAME_death245,
|
||||
FRAME_death246,
|
||||
FRAME_death247,
|
||||
FRAME_death248,
|
||||
FRAME_death249,
|
||||
FRAME_death250,
|
||||
FRAME_death251,
|
||||
FRAME_death252,
|
||||
FRAME_death253,
|
||||
FRAME_death254,
|
||||
FRAME_death255,
|
||||
FRAME_death256,
|
||||
FRAME_death257,
|
||||
FRAME_death258,
|
||||
FRAME_death259,
|
||||
FRAME_death260,
|
||||
FRAME_death261,
|
||||
FRAME_death262,
|
||||
FRAME_death263,
|
||||
FRAME_death264,
|
||||
FRAME_death265,
|
||||
FRAME_death266,
|
||||
FRAME_death267,
|
||||
FRAME_death268,
|
||||
FRAME_death269,
|
||||
FRAME_death270,
|
||||
FRAME_death271,
|
||||
FRAME_death272,
|
||||
FRAME_death273,
|
||||
FRAME_death274,
|
||||
FRAME_death275,
|
||||
FRAME_death276,
|
||||
FRAME_death277,
|
||||
FRAME_death278,
|
||||
FRAME_death279,
|
||||
FRAME_death280,
|
||||
FRAME_death281,
|
||||
FRAME_death282,
|
||||
FRAME_death283,
|
||||
FRAME_death284,
|
||||
FRAME_death285,
|
||||
FRAME_death286,
|
||||
FRAME_death287,
|
||||
FRAME_death288,
|
||||
FRAME_death289,
|
||||
FRAME_death290,
|
||||
FRAME_death291,
|
||||
FRAME_death292,
|
||||
FRAME_death293,
|
||||
FRAME_death294,
|
||||
FRAME_death295,
|
||||
FRAME_death301,
|
||||
FRAME_death302,
|
||||
FRAME_death303,
|
||||
FRAME_death304,
|
||||
FRAME_death305,
|
||||
FRAME_death306,
|
||||
FRAME_death307,
|
||||
FRAME_death308,
|
||||
FRAME_death309,
|
||||
FRAME_death310,
|
||||
FRAME_death311,
|
||||
FRAME_death312,
|
||||
FRAME_death313,
|
||||
FRAME_death314,
|
||||
FRAME_death315,
|
||||
FRAME_death316,
|
||||
FRAME_death317,
|
||||
FRAME_death318,
|
||||
FRAME_death319,
|
||||
FRAME_death320,
|
||||
FRAME_jump01,
|
||||
FRAME_jump02,
|
||||
FRAME_jump03,
|
||||
FRAME_jump04,
|
||||
FRAME_jump05,
|
||||
FRAME_jump06,
|
||||
FRAME_jump07,
|
||||
FRAME_jump08,
|
||||
FRAME_jump09,
|
||||
FRAME_jump10,
|
||||
FRAME_jump11,
|
||||
FRAME_jump12,
|
||||
FRAME_jump13,
|
||||
FRAME_pain401,
|
||||
FRAME_pain402,
|
||||
FRAME_pain403,
|
||||
FRAME_pain404,
|
||||
FRAME_pain501,
|
||||
FRAME_pain502,
|
||||
FRAME_pain503,
|
||||
FRAME_pain504,
|
||||
FRAME_pain601,
|
||||
FRAME_pain602,
|
||||
FRAME_pain603,
|
||||
FRAME_pain604,
|
||||
FRAME_pain605,
|
||||
FRAME_pain606,
|
||||
FRAME_pain607,
|
||||
FRAME_pain608,
|
||||
FRAME_pain609,
|
||||
FRAME_pain610,
|
||||
FRAME_pain611,
|
||||
FRAME_pain612,
|
||||
FRAME_pain613,
|
||||
FRAME_pain614,
|
||||
FRAME_pain615,
|
||||
FRAME_pain616,
|
||||
FRAME_pain617,
|
||||
FRAME_pain618,
|
||||
FRAME_pain619,
|
||||
FRAME_pain620,
|
||||
FRAME_pain621,
|
||||
FRAME_pain622,
|
||||
FRAME_pain623,
|
||||
FRAME_pain624,
|
||||
FRAME_pain625,
|
||||
FRAME_pain626,
|
||||
FRAME_pain627,
|
||||
FRAME_stand201,
|
||||
FRAME_stand202,
|
||||
FRAME_stand203,
|
||||
FRAME_stand204,
|
||||
FRAME_stand205,
|
||||
FRAME_stand206,
|
||||
FRAME_stand207,
|
||||
FRAME_stand208,
|
||||
FRAME_stand209,
|
||||
FRAME_stand210,
|
||||
FRAME_stand211,
|
||||
FRAME_stand212,
|
||||
FRAME_stand213,
|
||||
FRAME_stand214,
|
||||
FRAME_stand215,
|
||||
FRAME_stand216,
|
||||
FRAME_stand217,
|
||||
FRAME_stand218,
|
||||
FRAME_stand219,
|
||||
FRAME_stand220,
|
||||
FRAME_stand221,
|
||||
FRAME_stand222,
|
||||
FRAME_stand223,
|
||||
FRAME_stand224,
|
||||
FRAME_stand225,
|
||||
FRAME_stand226,
|
||||
FRAME_stand227,
|
||||
FRAME_stand228,
|
||||
FRAME_stand229,
|
||||
FRAME_stand230,
|
||||
FRAME_stand231,
|
||||
FRAME_stand232,
|
||||
FRAME_stand233,
|
||||
FRAME_stand234,
|
||||
FRAME_stand235,
|
||||
FRAME_stand236,
|
||||
FRAME_stand237,
|
||||
FRAME_stand238,
|
||||
FRAME_stand239,
|
||||
FRAME_stand240,
|
||||
FRAME_stand241,
|
||||
FRAME_stand242,
|
||||
FRAME_stand243,
|
||||
FRAME_stand244,
|
||||
FRAME_stand245,
|
||||
FRAME_stand246,
|
||||
FRAME_stand247,
|
||||
FRAME_stand248,
|
||||
FRAME_stand249,
|
||||
FRAME_stand250,
|
||||
FRAME_stand251,
|
||||
FRAME_stand252,
|
||||
FRAME_stand253,
|
||||
FRAME_stand254,
|
||||
FRAME_stand255,
|
||||
FRAME_stand256,
|
||||
FRAME_stand257,
|
||||
FRAME_stand258,
|
||||
FRAME_stand259,
|
||||
FRAME_stand260,
|
||||
FRAME_walk201,
|
||||
FRAME_walk202,
|
||||
FRAME_walk203,
|
||||
FRAME_walk204,
|
||||
FRAME_walk205,
|
||||
FRAME_walk206,
|
||||
FRAME_walk207,
|
||||
FRAME_walk208,
|
||||
FRAME_walk209,
|
||||
FRAME_walk210,
|
||||
FRAME_walk211,
|
||||
FRAME_walk212,
|
||||
FRAME_walk213,
|
||||
FRAME_walk214,
|
||||
FRAME_walk215,
|
||||
FRAME_walk216,
|
||||
FRAME_walk217
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
798
rerelease/m_brain.cpp
Normal file
798
rerelease/m_brain.cpp
Normal file
@@ -0,0 +1,798 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
brain
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_brain.h"
|
||||
|
||||
static int sound_chest_open;
|
||||
static int sound_tentacles_extend;
|
||||
static int sound_tentacles_retract;
|
||||
static int sound_death;
|
||||
static int sound_idle1;
|
||||
static int sound_idle2;
|
||||
static int sound_idle3;
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_sight;
|
||||
static int sound_search;
|
||||
static int sound_melee1;
|
||||
static int sound_melee2;
|
||||
static int sound_melee3;
|
||||
|
||||
MONSTERINFO_SIGHT(brain_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_SEARCH(brain_search) (edict_t *self) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void brain_run(edict_t *self);
|
||||
void brain_dead(edict_t *self);
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_BRAIN_NO_LASERS = 8_spawnflag;
|
||||
|
||||
//
|
||||
// STAND
|
||||
//
|
||||
|
||||
mframe_t brain_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(brain_move_stand) = { FRAME_stand01, FRAME_stand30, brain_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(brain_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &brain_move_stand);
|
||||
}
|
||||
|
||||
//
|
||||
// IDLE
|
||||
//
|
||||
|
||||
mframe_t brain_frames_idle[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(brain_move_idle) = { FRAME_stand31, FRAME_stand60, brain_frames_idle, brain_stand };
|
||||
|
||||
MONSTERINFO_IDLE(brain_idle) (edict_t *self) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_AUTO, sound_idle3, 1, ATTN_IDLE, 0);
|
||||
M_SetAnimation(self, &brain_move_idle);
|
||||
}
|
||||
|
||||
//
|
||||
// WALK
|
||||
//
|
||||
mframe_t brain_frames_walk1[] = {
|
||||
{ ai_walk, 7 },
|
||||
{ ai_walk, 2 },
|
||||
{ ai_walk, 3 },
|
||||
{ ai_walk, 3, monster_footstep },
|
||||
{ ai_walk, 1 },
|
||||
{ ai_walk },
|
||||
{ ai_walk },
|
||||
{ ai_walk, 9 },
|
||||
{ ai_walk, -4 },
|
||||
{ ai_walk, -1, monster_footstep },
|
||||
{ ai_walk, 2 }
|
||||
};
|
||||
MMOVE_T(brain_move_walk1) = { FRAME_walk101, FRAME_walk111, brain_frames_walk1, nullptr };
|
||||
|
||||
MONSTERINFO_WALK(brain_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &brain_move_walk1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
mframe_t brain_frames_defense[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(brain_move_defense) = { FRAME_defens01, FRAME_defens08, brain_frames_defense, nullptr };
|
||||
#endif
|
||||
|
||||
mframe_t brain_frames_pain3[] = {
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move },
|
||||
{ ai_move, -4 }
|
||||
};
|
||||
MMOVE_T(brain_move_pain3) = { FRAME_pain301, FRAME_pain306, brain_frames_pain3, brain_run };
|
||||
|
||||
mframe_t brain_frames_pain2[] = {
|
||||
{ ai_move, -2 },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, -2 }
|
||||
};
|
||||
MMOVE_T(brain_move_pain2) = { FRAME_pain201, FRAME_pain208, brain_frames_pain2, brain_run };
|
||||
|
||||
mframe_t brain_frames_pain1[] = {
|
||||
{ ai_move, -6 },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, -6, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 7 },
|
||||
{ ai_move },
|
||||
{ ai_move, 3, monster_footstep },
|
||||
{ ai_move, -1 }
|
||||
};
|
||||
MMOVE_T(brain_move_pain1) = { FRAME_pain101, FRAME_pain121, brain_frames_pain1, brain_run };
|
||||
|
||||
mframe_t brain_frames_duck[] = {
|
||||
{ ai_move },
|
||||
{ ai_move, -2, [](edict_t *self) { monster_duck_down(self); monster_footstep(self); } },
|
||||
{ ai_move, 17, monster_duck_hold },
|
||||
{ ai_move, -3 },
|
||||
{ ai_move, -1, monster_duck_up },
|
||||
{ ai_move, -5 },
|
||||
{ ai_move, -6 },
|
||||
{ ai_move, -6, monster_footstep }
|
||||
};
|
||||
MMOVE_T(brain_move_duck) = { FRAME_duck01, FRAME_duck08, brain_frames_duck, brain_run };
|
||||
|
||||
static void brain_shrink(edict_t *self)
|
||||
{
|
||||
self->maxs[2] = 0;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t brain_frames_death2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move, 0, brain_shrink },
|
||||
{ ai_move, 9 },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(brain_move_death2) = { FRAME_death201, FRAME_death205, brain_frames_death2, brain_dead };
|
||||
|
||||
mframe_t brain_frames_death1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, 9, [](edict_t *self) { brain_shrink(self); monster_footstep(self); } },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(brain_move_death1) = { FRAME_death101, FRAME_death118, brain_frames_death1, brain_dead };
|
||||
|
||||
//
|
||||
// MELEE
|
||||
//
|
||||
|
||||
void brain_swing_right(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_melee1, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void brain_hit_right(edict_t *self)
|
||||
{
|
||||
vec3_t aim = { MELEE_DISTANCE, self->maxs[0], 8 };
|
||||
if (fire_hit(self, aim, irandom(15, 20), 40))
|
||||
gi.sound(self, CHAN_WEAPON, sound_melee3, 1, ATTN_NORM, 0);
|
||||
else
|
||||
self->monsterinfo.melee_debounce_time = level.time + 3_sec;
|
||||
}
|
||||
|
||||
void brain_swing_left(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_melee2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void brain_hit_left(edict_t *self)
|
||||
{
|
||||
vec3_t aim = { MELEE_DISTANCE, self->mins[0], 8 };
|
||||
if (fire_hit(self, aim, irandom(15, 20), 40))
|
||||
gi.sound(self, CHAN_WEAPON, sound_melee3, 1, ATTN_NORM, 0);
|
||||
else
|
||||
self->monsterinfo.melee_debounce_time = level.time + 3_sec;
|
||||
}
|
||||
|
||||
mframe_t brain_frames_attack1[] = {
|
||||
{ ai_charge, 8 },
|
||||
{ ai_charge, 3 },
|
||||
{ ai_charge, 5 },
|
||||
{ ai_charge, 0, monster_footstep },
|
||||
{ ai_charge, -3, brain_swing_right },
|
||||
{ ai_charge },
|
||||
{ ai_charge, -5 },
|
||||
{ ai_charge, -7, brain_hit_right },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 6, brain_swing_left },
|
||||
{ ai_charge, 1 },
|
||||
{ ai_charge, 2, brain_hit_left },
|
||||
{ ai_charge, -3 },
|
||||
{ ai_charge, 6 },
|
||||
{ ai_charge, -1 },
|
||||
{ ai_charge, -3 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, -11, monster_footstep }
|
||||
};
|
||||
MMOVE_T(brain_move_attack1) = { FRAME_attak101, FRAME_attak118, brain_frames_attack1, brain_run };
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_BRAIN_TENTACLES_HIT = 65536_spawnflag;
|
||||
|
||||
void brain_chest_open(edict_t *self)
|
||||
{
|
||||
self->spawnflags &= ~SPAWNFLAG_BRAIN_TENTACLES_HIT;
|
||||
self->monsterinfo.power_armor_type = IT_NULL;
|
||||
gi.sound(self, CHAN_BODY, sound_chest_open, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void brain_tentacle_attack(edict_t *self)
|
||||
{
|
||||
vec3_t aim = { MELEE_DISTANCE, 0, 8 };
|
||||
if (fire_hit(self, aim, irandom(10, 15), -600))
|
||||
self->spawnflags |= SPAWNFLAG_BRAIN_TENTACLES_HIT;
|
||||
else
|
||||
self->monsterinfo.melee_debounce_time = level.time + 3_sec;
|
||||
gi.sound(self, CHAN_WEAPON, sound_tentacles_retract, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void brain_chest_closed(edict_t *self)
|
||||
{
|
||||
self->monsterinfo.power_armor_type = IT_ITEM_POWER_SCREEN;
|
||||
if (self->spawnflags.has(SPAWNFLAG_BRAIN_TENTACLES_HIT))
|
||||
{
|
||||
self->spawnflags &= ~SPAWNFLAG_BRAIN_TENTACLES_HIT;
|
||||
M_SetAnimation(self, &brain_move_attack1);
|
||||
}
|
||||
}
|
||||
|
||||
mframe_t brain_frames_attack2[] = {
|
||||
{ ai_charge, 5 },
|
||||
{ ai_charge, -4 },
|
||||
{ ai_charge, -4 },
|
||||
{ ai_charge, -3 },
|
||||
{ ai_charge, 0, brain_chest_open },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 13, brain_tentacle_attack },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge },
|
||||
{ ai_charge, -9, brain_chest_closed },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 4 },
|
||||
{ ai_charge, 3 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, -3 },
|
||||
{ ai_charge, -6 }
|
||||
};
|
||||
MMOVE_T(brain_move_attack2) = { FRAME_attak201, FRAME_attak217, brain_frames_attack2, brain_run };
|
||||
|
||||
MONSTERINFO_MELEE(brain_melee) (edict_t *self) -> void
|
||||
{
|
||||
if (frandom() <= 0.5f)
|
||||
M_SetAnimation(self, &brain_move_attack1);
|
||||
else
|
||||
M_SetAnimation(self, &brain_move_attack2);
|
||||
}
|
||||
|
||||
// RAFAEL
|
||||
static bool brain_tounge_attack_ok(const vec3_t &start, const vec3_t &end)
|
||||
{
|
||||
vec3_t dir, angles;
|
||||
|
||||
// check for max distance
|
||||
dir = start - end;
|
||||
if (dir.length() > 512)
|
||||
return false;
|
||||
|
||||
// check for min/max pitch
|
||||
angles = vectoangles(dir);
|
||||
if (angles[0] < -180)
|
||||
angles[0] += 360;
|
||||
if (fabsf(angles[0]) > 30)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void brain_tounge_attack(edict_t *self)
|
||||
{
|
||||
vec3_t offset, start, f, r, end, dir;
|
||||
trace_t tr;
|
||||
int damage;
|
||||
|
||||
AngleVectors(self->s.angles, f, r, nullptr);
|
||||
// offset = { 24, 0, 6 };
|
||||
offset = { 24, 0, 16 };
|
||||
start = M_ProjectFlashSource(self, offset, f, r);
|
||||
|
||||
end = self->enemy->s.origin;
|
||||
if (!brain_tounge_attack_ok(start, end))
|
||||
{
|
||||
end[2] = self->enemy->s.origin[2] + self->enemy->maxs[2] - 8;
|
||||
if (!brain_tounge_attack_ok(start, end))
|
||||
{
|
||||
end[2] = self->enemy->s.origin[2] + self->enemy->mins[2] + 8;
|
||||
if (!brain_tounge_attack_ok(start, end))
|
||||
return;
|
||||
}
|
||||
}
|
||||
end = self->enemy->s.origin;
|
||||
|
||||
tr = gi.traceline(start, end, self, MASK_PROJECTILE);
|
||||
if (tr.ent != self->enemy)
|
||||
return;
|
||||
|
||||
damage = 5;
|
||||
gi.sound(self, CHAN_WEAPON, sound_tentacles_retract, 1, ATTN_NORM, 0);
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_PARASITE_ATTACK);
|
||||
gi.WriteEntity(self);
|
||||
gi.WritePosition(start);
|
||||
gi.WritePosition(end);
|
||||
gi.multicast(self->s.origin, MULTICAST_PVS, false);
|
||||
|
||||
dir = start - end;
|
||||
T_Damage(self->enemy, self, self, dir, self->enemy->s.origin, vec3_origin, damage, 0, DAMAGE_NO_KNOCKBACK, MOD_BRAINTENTACLE);
|
||||
|
||||
// pull the enemy in
|
||||
vec3_t forward;
|
||||
self->s.origin[2] += 1;
|
||||
AngleVectors(self->s.angles, forward, nullptr, nullptr);
|
||||
self->enemy->velocity = forward * -1200;
|
||||
}
|
||||
|
||||
// Brian right eye center
|
||||
constexpr vec3_t brain_reye[] = {
|
||||
{ 0.746700f, 0.238370f, 34.167690f },
|
||||
{ -1.076390f, 0.238370f, 33.386372f },
|
||||
{ -1.335500f, 5.334300f, 32.177170f },
|
||||
{ -0.175360f, 8.846370f, 30.635479f },
|
||||
{ -2.757590f, 7.804610f, 30.150860f },
|
||||
{ -5.575090f, 5.152840f, 30.056160f },
|
||||
{ -7.017550f, 3.262470f, 30.552521f },
|
||||
{ -7.915740f, 0.638800f, 33.176189f },
|
||||
{ -3.915390f, 8.285730f, 33.976349f },
|
||||
{ -0.913540f, 10.933030f, 34.141811f },
|
||||
{ -0.369900f, 8.923900f, 34.189079f }
|
||||
};
|
||||
|
||||
// Brain left eye center
|
||||
constexpr vec3_t brain_leye[] = {
|
||||
{ -3.364710f, 0.327750f, 33.938381f },
|
||||
{ -5.140450f, 0.493480f, 32.659851f },
|
||||
{ -5.341980f, 5.646980f, 31.277901f },
|
||||
{ -4.134480f, 9.277440f, 29.925621f },
|
||||
{ -6.598340f, 6.815090f, 29.322620f },
|
||||
{ -8.610840f, 2.529650f, 29.251591f },
|
||||
{ -9.231360f, 0.093280f, 29.747959f },
|
||||
{ -11.004110f, 1.936930f, 32.395260f },
|
||||
{ -7.878310f, 7.648190f, 33.148151f },
|
||||
{ -4.947370f, 11.430050f, 33.313610f },
|
||||
{ -4.332820f, 9.444570f, 33.526340f }
|
||||
};
|
||||
|
||||
PRETHINK(brain_right_eye_laser_update) (edict_t *laser) -> void
|
||||
{
|
||||
edict_t *self = laser->owner;
|
||||
|
||||
vec3_t start, forward, right, up, dir;
|
||||
|
||||
// check for max distance
|
||||
AngleVectors(self->s.angles, forward, right, up);
|
||||
|
||||
// dis is my right eye
|
||||
start = self->s.origin + (right * brain_reye[self->s.frame - FRAME_walk101].x);
|
||||
start += forward * brain_reye[self->s.frame - FRAME_walk101].y;
|
||||
start += up * brain_reye[self->s.frame - FRAME_walk101].z;
|
||||
|
||||
PredictAim(self, self->enemy, start, 0, false, frandom(0.1f, 0.2f), &dir, nullptr);
|
||||
|
||||
laser->s.origin = start;
|
||||
laser->movedir = dir;
|
||||
gi.linkentity(laser);
|
||||
}
|
||||
|
||||
PRETHINK(brain_left_eye_laser_update) (edict_t *laser) -> void
|
||||
{
|
||||
edict_t *self = laser->owner;
|
||||
|
||||
vec3_t start, forward, right, up, dir;
|
||||
|
||||
// check for max distance
|
||||
AngleVectors(self->s.angles, forward, right, up);
|
||||
|
||||
// dis is my right eye
|
||||
start = self->s.origin + (right * brain_leye[self->s.frame - FRAME_walk101].x);
|
||||
start += forward * brain_leye[self->s.frame - FRAME_walk101].y;
|
||||
start += up * brain_leye[self->s.frame - FRAME_walk101].z;
|
||||
|
||||
PredictAim(self, self->enemy, start, 0, false, frandom(0.1f, 0.2f), &dir, nullptr);
|
||||
|
||||
laser->s.origin = start;
|
||||
laser->movedir = dir;
|
||||
gi.linkentity(laser);
|
||||
dabeam_update(laser, false);
|
||||
}
|
||||
|
||||
void brain_laserbeam(edict_t *self)
|
||||
{
|
||||
// dis is my right eye
|
||||
monster_fire_dabeam(self, 1, false, brain_right_eye_laser_update);
|
||||
|
||||
// dis is me left eye
|
||||
monster_fire_dabeam(self, 1, true, brain_left_eye_laser_update);
|
||||
}
|
||||
|
||||
void brain_laserbeam_reattack(edict_t *self)
|
||||
{
|
||||
if (frandom() < 0.5f)
|
||||
if (visible(self, self->enemy))
|
||||
if (self->enemy->health > 0)
|
||||
self->s.frame = FRAME_walk101;
|
||||
}
|
||||
|
||||
mframe_t brain_frames_attack3[] = {
|
||||
{ ai_charge, 5 },
|
||||
{ ai_charge, -4 },
|
||||
{ ai_charge, -4 },
|
||||
{ ai_charge, -3 },
|
||||
{ ai_charge, 0, brain_chest_open },
|
||||
{ ai_charge, 0, brain_tounge_attack },
|
||||
{ ai_charge, 13 },
|
||||
{ ai_charge, 0, brain_tentacle_attack },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, 0, brain_tounge_attack },
|
||||
{ ai_charge, -9, brain_chest_closed },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 4 },
|
||||
{ ai_charge, 3 },
|
||||
{ ai_charge, 2 },
|
||||
{ ai_charge, -3 },
|
||||
{ ai_charge, -6 }
|
||||
};
|
||||
MMOVE_T(brain_move_attack3) = { FRAME_attak201, FRAME_attak217, brain_frames_attack3, brain_run };
|
||||
|
||||
mframe_t brain_frames_attack4[] = {
|
||||
{ ai_charge, 9, brain_laserbeam },
|
||||
{ ai_charge, 2, brain_laserbeam },
|
||||
{ ai_charge, 3, brain_laserbeam },
|
||||
{ ai_charge, 3, brain_laserbeam },
|
||||
{ ai_charge, 1, brain_laserbeam },
|
||||
{ ai_charge, 0, brain_laserbeam },
|
||||
{ ai_charge, 0, brain_laserbeam },
|
||||
{ ai_charge, 10, brain_laserbeam },
|
||||
{ ai_charge, -4, brain_laserbeam },
|
||||
{ ai_charge, -1, brain_laserbeam },
|
||||
{ ai_charge, 2, brain_laserbeam_reattack }
|
||||
};
|
||||
MMOVE_T(brain_move_attack4) = { FRAME_walk101, FRAME_walk111, brain_frames_attack4, brain_run };
|
||||
|
||||
// RAFAEL
|
||||
MONSTERINFO_ATTACK(brain_attack) (edict_t *self) -> void
|
||||
{
|
||||
float r = range_to(self, self->enemy);
|
||||
if (r <= RANGE_NEAR)
|
||||
{
|
||||
if (frandom() < 0.5f)
|
||||
M_SetAnimation(self, &brain_move_attack3);
|
||||
else if (!self->spawnflags.has(SPAWNFLAG_BRAIN_NO_LASERS))
|
||||
M_SetAnimation(self, &brain_move_attack4);
|
||||
}
|
||||
else if (!self->spawnflags.has(SPAWNFLAG_BRAIN_NO_LASERS))
|
||||
M_SetAnimation(self, &brain_move_attack4);
|
||||
}
|
||||
// RAFAEL
|
||||
|
||||
//
|
||||
// RUN
|
||||
//
|
||||
|
||||
mframe_t brain_frames_run[] = {
|
||||
{ ai_run, 9 },
|
||||
{ ai_run, 2 },
|
||||
{ ai_run, 3 },
|
||||
{ ai_run, 3 },
|
||||
{ ai_run, 1 },
|
||||
{ ai_run },
|
||||
{ ai_run },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, -4 },
|
||||
{ ai_run, -1 },
|
||||
{ ai_run, 2 }
|
||||
};
|
||||
MMOVE_T(brain_move_run) = { FRAME_walk101, FRAME_walk111, brain_frames_run, nullptr };
|
||||
|
||||
MONSTERINFO_RUN(brain_run) (edict_t *self) -> void
|
||||
{
|
||||
self->monsterinfo.power_armor_type = IT_ITEM_POWER_SCREEN;
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &brain_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, &brain_move_run);
|
||||
}
|
||||
|
||||
PAIN(brain_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
float r;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
r = frandom();
|
||||
|
||||
if (r < 0.33f)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else if (r < 0.66f)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (r < 0.33f)
|
||||
M_SetAnimation(self, &brain_move_pain1);
|
||||
else if (r < 0.66f)
|
||||
M_SetAnimation(self, &brain_move_pain2);
|
||||
else
|
||||
M_SetAnimation(self, &brain_move_pain3);
|
||||
|
||||
// PMM - clear duck flag
|
||||
if (self->monsterinfo.aiflags & AI_DUCKED)
|
||||
monster_duck_up(self);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(brain_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
void brain_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, -8 };
|
||||
monster_dead(self);
|
||||
}
|
||||
|
||||
DIE(brain_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
self->s.effects = EF_NONE;
|
||||
self->monsterinfo.power_armor_type = IT_NULL;
|
||||
|
||||
// check for gib
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
if (self->beam)
|
||||
{
|
||||
G_FreeEdict(self->beam);
|
||||
self->beam = nullptr;
|
||||
}
|
||||
if (self->beam2)
|
||||
{
|
||||
G_FreeEdict(self->beam2);
|
||||
self->beam2 = nullptr;
|
||||
}
|
||||
|
||||
ThrowGibs(self, damage, {
|
||||
{ 1, "models/objects/gibs/bone/tris.md2" },
|
||||
{ 2, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ 2, "models/monsters/brain/gibs/arm.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/brain/gibs/boot.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/brain/gibs/pelvis.md2", GIB_SKINNED },
|
||||
{ "models/monsters/brain/gibs/chest.md2", GIB_SKINNED },
|
||||
{ 2, "models/monsters/brain/gibs/door.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/brain/gibs/head.md2", GIB_SKINNED | GIB_HEAD }
|
||||
});
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
if (frandom() <= 0.5f)
|
||||
M_SetAnimation(self, &brain_move_death1);
|
||||
else
|
||||
M_SetAnimation(self, &brain_move_death2);
|
||||
}
|
||||
|
||||
MONSTERINFO_DUCK(brain_duck) (edict_t *self, gtime_t eta) -> bool
|
||||
{
|
||||
M_SetAnimation(self, &brain_move_duck);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*QUAKED monster_brain (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_brain(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_chest_open = gi.soundindex("brain/brnatck1.wav");
|
||||
sound_tentacles_extend = gi.soundindex("brain/brnatck2.wav");
|
||||
sound_tentacles_retract = gi.soundindex("brain/brnatck3.wav");
|
||||
sound_death = gi.soundindex("brain/brndeth1.wav");
|
||||
sound_idle1 = gi.soundindex("brain/brnidle1.wav");
|
||||
sound_idle2 = gi.soundindex("brain/brnidle2.wav");
|
||||
sound_idle3 = gi.soundindex("brain/brnlens1.wav");
|
||||
sound_pain1 = gi.soundindex("brain/brnpain1.wav");
|
||||
sound_pain2 = gi.soundindex("brain/brnpain2.wav");
|
||||
sound_sight = gi.soundindex("brain/brnsght1.wav");
|
||||
sound_search = gi.soundindex("brain/brnsrch1.wav");
|
||||
sound_melee1 = gi.soundindex("brain/melee1.wav");
|
||||
sound_melee2 = gi.soundindex("brain/melee2.wav");
|
||||
sound_melee3 = gi.soundindex("brain/melee3.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/brain/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/brain/gibs/arm.md2");
|
||||
gi.modelindex("models/monsters/brain/gibs/boot.md2");
|
||||
gi.modelindex("models/monsters/brain/gibs/chest.md2");
|
||||
gi.modelindex("models/monsters/brain/gibs/door.md2");
|
||||
gi.modelindex("models/monsters/brain/gibs/head.md2");
|
||||
gi.modelindex("models/monsters/brain/gibs/pelvis.md2");
|
||||
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, 32 };
|
||||
|
||||
self->health = 300 * st.health_multiplier;
|
||||
self->gib_health = -150;
|
||||
self->mass = 400;
|
||||
|
||||
self->pain = brain_pain;
|
||||
self->die = brain_die;
|
||||
|
||||
self->monsterinfo.stand = brain_stand;
|
||||
self->monsterinfo.walk = brain_walk;
|
||||
self->monsterinfo.run = brain_run;
|
||||
// PMM
|
||||
self->monsterinfo.dodge = M_MonsterDodge;
|
||||
self->monsterinfo.duck = brain_duck;
|
||||
self->monsterinfo.unduck = monster_duck_up;
|
||||
// pmm
|
||||
// RAFAEL
|
||||
self->monsterinfo.attack = brain_attack;
|
||||
// RAFAEL
|
||||
self->monsterinfo.melee = brain_melee;
|
||||
self->monsterinfo.sight = brain_sight;
|
||||
self->monsterinfo.search = brain_search;
|
||||
self->monsterinfo.idle = brain_idle;
|
||||
self->monsterinfo.setskin = brain_setskin;
|
||||
|
||||
if (!st.was_key_specified("power_armor_type"))
|
||||
self->monsterinfo.power_armor_type = IT_ITEM_POWER_SCREEN;
|
||||
if (!st.was_key_specified("power_armor_power"))
|
||||
self->monsterinfo.power_armor_power = 100;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &brain_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
233
rerelease/m_brain.h
Normal file
233
rerelease/m_brain.h
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/brain
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_walk101,
|
||||
FRAME_walk102,
|
||||
FRAME_walk103,
|
||||
FRAME_walk104,
|
||||
FRAME_walk105,
|
||||
FRAME_walk106,
|
||||
FRAME_walk107,
|
||||
FRAME_walk108,
|
||||
FRAME_walk109,
|
||||
FRAME_walk110,
|
||||
FRAME_walk111,
|
||||
FRAME_walk112,
|
||||
FRAME_walk113,
|
||||
FRAME_walk201,
|
||||
FRAME_walk202,
|
||||
FRAME_walk203,
|
||||
FRAME_walk204,
|
||||
FRAME_walk205,
|
||||
FRAME_walk206,
|
||||
FRAME_walk207,
|
||||
FRAME_walk208,
|
||||
FRAME_walk209,
|
||||
FRAME_walk210,
|
||||
FRAME_walk211,
|
||||
FRAME_walk212,
|
||||
FRAME_walk213,
|
||||
FRAME_walk214,
|
||||
FRAME_walk215,
|
||||
FRAME_walk216,
|
||||
FRAME_walk217,
|
||||
FRAME_walk218,
|
||||
FRAME_walk219,
|
||||
FRAME_walk220,
|
||||
FRAME_walk221,
|
||||
FRAME_walk222,
|
||||
FRAME_walk223,
|
||||
FRAME_walk224,
|
||||
FRAME_walk225,
|
||||
FRAME_walk226,
|
||||
FRAME_walk227,
|
||||
FRAME_walk228,
|
||||
FRAME_walk229,
|
||||
FRAME_walk230,
|
||||
FRAME_walk231,
|
||||
FRAME_walk232,
|
||||
FRAME_walk233,
|
||||
FRAME_walk234,
|
||||
FRAME_walk235,
|
||||
FRAME_walk236,
|
||||
FRAME_walk237,
|
||||
FRAME_walk238,
|
||||
FRAME_walk239,
|
||||
FRAME_walk240,
|
||||
FRAME_attak101,
|
||||
FRAME_attak102,
|
||||
FRAME_attak103,
|
||||
FRAME_attak104,
|
||||
FRAME_attak105,
|
||||
FRAME_attak106,
|
||||
FRAME_attak107,
|
||||
FRAME_attak108,
|
||||
FRAME_attak109,
|
||||
FRAME_attak110,
|
||||
FRAME_attak111,
|
||||
FRAME_attak112,
|
||||
FRAME_attak113,
|
||||
FRAME_attak114,
|
||||
FRAME_attak115,
|
||||
FRAME_attak116,
|
||||
FRAME_attak117,
|
||||
FRAME_attak118,
|
||||
FRAME_attak201,
|
||||
FRAME_attak202,
|
||||
FRAME_attak203,
|
||||
FRAME_attak204,
|
||||
FRAME_attak205,
|
||||
FRAME_attak206,
|
||||
FRAME_attak207,
|
||||
FRAME_attak208,
|
||||
FRAME_attak209,
|
||||
FRAME_attak210,
|
||||
FRAME_attak211,
|
||||
FRAME_attak212,
|
||||
FRAME_attak213,
|
||||
FRAME_attak214,
|
||||
FRAME_attak215,
|
||||
FRAME_attak216,
|
||||
FRAME_attak217,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain104,
|
||||
FRAME_pain105,
|
||||
FRAME_pain106,
|
||||
FRAME_pain107,
|
||||
FRAME_pain108,
|
||||
FRAME_pain109,
|
||||
FRAME_pain110,
|
||||
FRAME_pain111,
|
||||
FRAME_pain112,
|
||||
FRAME_pain113,
|
||||
FRAME_pain114,
|
||||
FRAME_pain115,
|
||||
FRAME_pain116,
|
||||
FRAME_pain117,
|
||||
FRAME_pain118,
|
||||
FRAME_pain119,
|
||||
FRAME_pain120,
|
||||
FRAME_pain121,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain204,
|
||||
FRAME_pain205,
|
||||
FRAME_pain206,
|
||||
FRAME_pain207,
|
||||
FRAME_pain208,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_pain304,
|
||||
FRAME_pain305,
|
||||
FRAME_pain306,
|
||||
FRAME_death101,
|
||||
FRAME_death102,
|
||||
FRAME_death103,
|
||||
FRAME_death104,
|
||||
FRAME_death105,
|
||||
FRAME_death106,
|
||||
FRAME_death107,
|
||||
FRAME_death108,
|
||||
FRAME_death109,
|
||||
FRAME_death110,
|
||||
FRAME_death111,
|
||||
FRAME_death112,
|
||||
FRAME_death113,
|
||||
FRAME_death114,
|
||||
FRAME_death115,
|
||||
FRAME_death116,
|
||||
FRAME_death117,
|
||||
FRAME_death118,
|
||||
FRAME_death201,
|
||||
FRAME_death202,
|
||||
FRAME_death203,
|
||||
FRAME_death204,
|
||||
FRAME_death205,
|
||||
FRAME_duck01,
|
||||
FRAME_duck02,
|
||||
FRAME_duck03,
|
||||
FRAME_duck04,
|
||||
FRAME_duck05,
|
||||
FRAME_duck06,
|
||||
FRAME_duck07,
|
||||
FRAME_duck08,
|
||||
FRAME_defens01,
|
||||
FRAME_defens02,
|
||||
FRAME_defens03,
|
||||
FRAME_defens04,
|
||||
FRAME_defens05,
|
||||
FRAME_defens06,
|
||||
FRAME_defens07,
|
||||
FRAME_defens08,
|
||||
FRAME_stand01,
|
||||
FRAME_stand02,
|
||||
FRAME_stand03,
|
||||
FRAME_stand04,
|
||||
FRAME_stand05,
|
||||
FRAME_stand06,
|
||||
FRAME_stand07,
|
||||
FRAME_stand08,
|
||||
FRAME_stand09,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_stand18,
|
||||
FRAME_stand19,
|
||||
FRAME_stand20,
|
||||
FRAME_stand21,
|
||||
FRAME_stand22,
|
||||
FRAME_stand23,
|
||||
FRAME_stand24,
|
||||
FRAME_stand25,
|
||||
FRAME_stand26,
|
||||
FRAME_stand27,
|
||||
FRAME_stand28,
|
||||
FRAME_stand29,
|
||||
FRAME_stand30,
|
||||
FRAME_stand31,
|
||||
FRAME_stand32,
|
||||
FRAME_stand33,
|
||||
FRAME_stand34,
|
||||
FRAME_stand35,
|
||||
FRAME_stand36,
|
||||
FRAME_stand37,
|
||||
FRAME_stand38,
|
||||
FRAME_stand39,
|
||||
FRAME_stand40,
|
||||
FRAME_stand41,
|
||||
FRAME_stand42,
|
||||
FRAME_stand43,
|
||||
FRAME_stand44,
|
||||
FRAME_stand45,
|
||||
FRAME_stand46,
|
||||
FRAME_stand47,
|
||||
FRAME_stand48,
|
||||
FRAME_stand49,
|
||||
FRAME_stand50,
|
||||
FRAME_stand51,
|
||||
FRAME_stand52,
|
||||
FRAME_stand53,
|
||||
FRAME_stand54,
|
||||
FRAME_stand55,
|
||||
FRAME_stand56,
|
||||
FRAME_stand57,
|
||||
FRAME_stand58,
|
||||
FRAME_stand59,
|
||||
FRAME_stand60
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
869
rerelease/m_chick.cpp
Normal file
869
rerelease/m_chick.cpp
Normal file
@@ -0,0 +1,869 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
chick
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_chick.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
void chick_stand(edict_t *self);
|
||||
void chick_run(edict_t *self);
|
||||
void chick_reslash(edict_t *self);
|
||||
void chick_rerocket(edict_t *self);
|
||||
void chick_attack1(edict_t *self);
|
||||
|
||||
static int sound_missile_prelaunch;
|
||||
static int sound_missile_launch;
|
||||
static int sound_melee_swing;
|
||||
static int sound_melee_hit;
|
||||
static int sound_missile_reload;
|
||||
static int sound_death1;
|
||||
static int sound_death2;
|
||||
static int sound_fall_down;
|
||||
static int sound_idle1;
|
||||
static int sound_idle2;
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_pain3;
|
||||
static int sound_sight;
|
||||
static int sound_search;
|
||||
|
||||
void ChickMoan(edict_t *self)
|
||||
{
|
||||
if (frandom() < 0.5f)
|
||||
gi.sound(self, CHAN_VOICE, sound_idle1, 1, ATTN_IDLE, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_idle2, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
mframe_t chick_frames_fidget[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, ChickMoan },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(chick_move_fidget) = { FRAME_stand201, FRAME_stand230, chick_frames_fidget, chick_stand };
|
||||
|
||||
void chick_fidget(edict_t *self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
return;
|
||||
else if (self->enemy)
|
||||
return;
|
||||
if (frandom() <= 0.3f)
|
||||
M_SetAnimation(self, &chick_move_fidget);
|
||||
}
|
||||
|
||||
mframe_t chick_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, chick_fidget },
|
||||
};
|
||||
MMOVE_T(chick_move_stand) = { FRAME_stand101, FRAME_stand130, chick_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(chick_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_stand);
|
||||
}
|
||||
|
||||
mframe_t chick_frames_start_run[] = {
|
||||
{ ai_run, 1 },
|
||||
{ ai_run },
|
||||
{ ai_run, 0, monster_footstep },
|
||||
{ ai_run, -1 },
|
||||
{ ai_run, -1, monster_footstep },
|
||||
{ ai_run },
|
||||
{ ai_run, 1 },
|
||||
{ ai_run, 3 },
|
||||
{ ai_run, 6 },
|
||||
{ ai_run, 3 }
|
||||
};
|
||||
MMOVE_T(chick_move_start_run) = { FRAME_walk01, FRAME_walk10, chick_frames_start_run, chick_run };
|
||||
|
||||
mframe_t chick_frames_run[] = {
|
||||
{ ai_run, 6 },
|
||||
{ ai_run, 8, monster_footstep },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 5, monster_done_dodge }, // make sure to clear dodge bit
|
||||
{ ai_run, 7 },
|
||||
{ ai_run, 4 },
|
||||
{ ai_run, 11, monster_footstep },
|
||||
{ ai_run, 5 },
|
||||
{ ai_run, 9 },
|
||||
{ ai_run, 7 }
|
||||
};
|
||||
|
||||
MMOVE_T(chick_move_run) = { FRAME_walk11, FRAME_walk20, chick_frames_run, nullptr };
|
||||
|
||||
mframe_t chick_frames_walk[] = {
|
||||
{ ai_walk, 6 },
|
||||
{ ai_walk, 8, monster_footstep },
|
||||
{ ai_walk, 13 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 7 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 11, monster_footstep },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 9 },
|
||||
{ ai_walk, 7 }
|
||||
};
|
||||
|
||||
MMOVE_T(chick_move_walk) = { FRAME_walk11, FRAME_walk20, chick_frames_walk, nullptr };
|
||||
|
||||
MONSTERINFO_WALK(chick_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_walk);
|
||||
}
|
||||
|
||||
MONSTERINFO_RUN(chick_run) (edict_t *self) -> void
|
||||
{
|
||||
monster_done_dodge(self);
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_stand);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->monsterinfo.active_move == &chick_move_walk ||
|
||||
self->monsterinfo.active_move == &chick_move_start_run)
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_run);
|
||||
}
|
||||
else
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_start_run);
|
||||
}
|
||||
}
|
||||
|
||||
mframe_t chick_frames_pain1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(chick_move_pain1) = { FRAME_pain101, FRAME_pain105, chick_frames_pain1, chick_run };
|
||||
|
||||
mframe_t chick_frames_pain2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(chick_move_pain2) = { FRAME_pain201, FRAME_pain205, chick_frames_pain2, chick_run };
|
||||
|
||||
mframe_t chick_frames_pain3[] = {
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move, -6 },
|
||||
{ ai_move, 3, monster_footstep },
|
||||
{ ai_move, 11 },
|
||||
{ ai_move, 3, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 4 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move },
|
||||
{ ai_move, -3 },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, 5 },
|
||||
{ ai_move, 7 },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, -5 },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, -8 },
|
||||
{ ai_move, 2, monster_footstep }
|
||||
};
|
||||
MMOVE_T(chick_move_pain3) = { FRAME_pain301, FRAME_pain321, chick_frames_pain3, chick_run };
|
||||
|
||||
PAIN(chick_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
float r;
|
||||
|
||||
monster_done_dodge(self);
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
r = frandom();
|
||||
if (r < 0.33f)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else if (r < 0.66f)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain3, 1, ATTN_NORM, 0);
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
// PMM - clear this from blindfire
|
||||
self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
|
||||
|
||||
if (damage <= 10)
|
||||
M_SetAnimation(self, &chick_move_pain1);
|
||||
else if (damage <= 25)
|
||||
M_SetAnimation(self, &chick_move_pain2);
|
||||
else
|
||||
M_SetAnimation(self, &chick_move_pain3);
|
||||
|
||||
// PMM - clear duck flag
|
||||
if (self->monsterinfo.aiflags & AI_DUCKED)
|
||||
monster_duck_up(self);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(chick_setpain) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum |= 1;
|
||||
else
|
||||
self->s.skinnum &= ~1;
|
||||
}
|
||||
|
||||
void chick_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -16, -16, 0 };
|
||||
self->maxs = { 16, 16, 8 };
|
||||
monster_dead(self);
|
||||
}
|
||||
|
||||
static void chick_shrink(edict_t *self)
|
||||
{
|
||||
self->maxs[2] = 12;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t chick_frames_death2[] = {
|
||||
{ ai_move, -6 },
|
||||
{ ai_move },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, -5, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 10 },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, 3, monster_footstep },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 1, monster_footstep },
|
||||
{ ai_move, -3 },
|
||||
{ ai_move, -5 },
|
||||
{ ai_move, 4 },
|
||||
{ ai_move, 15, chick_shrink },
|
||||
{ ai_move, 14, monster_footstep },
|
||||
{ ai_move, 1 }
|
||||
};
|
||||
MMOVE_T(chick_move_death2) = { FRAME_death201, FRAME_death223, chick_frames_death2, chick_dead };
|
||||
|
||||
mframe_t chick_frames_death1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move, -7 },
|
||||
{ ai_move, 4, monster_footstep },
|
||||
{ ai_move, 11, chick_shrink },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(chick_move_death1) = { FRAME_death101, FRAME_death112, chick_frames_death1, chick_dead };
|
||||
|
||||
DIE(chick_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
int n;
|
||||
|
||||
// check for gib
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
ThrowGibs(self, damage, {
|
||||
{ 2, "models/objects/gibs/bone/tris.md2" },
|
||||
{ 3, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ "models/monsters/bitch/gibs/arm.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/bitch/gibs/foot.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/bitch/gibs/tube.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/bitch/gibs/chest.md2", GIB_SKINNED },
|
||||
{ "models/monsters/bitch/gibs/head.md2", GIB_HEAD | GIB_SKINNED }
|
||||
});
|
||||
self->deadflag = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
|
||||
n = brandom();
|
||||
|
||||
if (n == 0)
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_death1);
|
||||
gi.sound(self, CHAN_VOICE, sound_death1, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_death2);
|
||||
gi.sound(self, CHAN_VOICE, sound_death2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// PMM - changes to duck code for new dodge
|
||||
|
||||
mframe_t chick_frames_duck[] = {
|
||||
{ ai_move, 0, monster_duck_down },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 4, monster_duck_hold },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, -5, monster_duck_up },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 1 }
|
||||
};
|
||||
MMOVE_T(chick_move_duck) = { FRAME_duck01, FRAME_duck07, chick_frames_duck, chick_run };
|
||||
|
||||
void ChickSlash(edict_t *self)
|
||||
{
|
||||
vec3_t aim = { MELEE_DISTANCE, self->mins[0], 10 };
|
||||
gi.sound(self, CHAN_WEAPON, sound_melee_swing, 1, ATTN_NORM, 0);
|
||||
fire_hit(self, aim, irandom(10, 16), 100);
|
||||
}
|
||||
|
||||
void ChickRocket(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
trace_t trace; // PMM - check target
|
||||
int rocketSpeed;
|
||||
// pmm - blindfire
|
||||
vec3_t target;
|
||||
bool blindfire = false;
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
|
||||
blindfire = true;
|
||||
else
|
||||
blindfire = false;
|
||||
|
||||
if (!self->enemy || !self->enemy->inuse) // PGM
|
||||
return; // PGM
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_CHICK_ROCKET_1], forward, right);
|
||||
|
||||
// [Paril-KEX]
|
||||
if (self->s.skinnum > 1)
|
||||
rocketSpeed = 500;
|
||||
else
|
||||
rocketSpeed = 650;
|
||||
|
||||
// PMM
|
||||
if (blindfire)
|
||||
target = self->monsterinfo.blind_fire_target;
|
||||
else
|
||||
target = self->enemy->s.origin;
|
||||
// pmm
|
||||
// PGM
|
||||
// PMM - blindfire shooting
|
||||
if (blindfire)
|
||||
{
|
||||
vec = target;
|
||||
dir = vec - start;
|
||||
}
|
||||
// pmm
|
||||
// don't shoot at feet if they're above where i'm shooting from.
|
||||
else if (frandom() < 0.33f || (start[2] < self->enemy->absmin[2]))
|
||||
{
|
||||
vec = target;
|
||||
vec[2] += self->enemy->viewheight;
|
||||
dir = vec - start;
|
||||
}
|
||||
else
|
||||
{
|
||||
vec = target;
|
||||
vec[2] = self->enemy->absmin[2] + 1;
|
||||
dir = vec - start;
|
||||
}
|
||||
// PGM
|
||||
|
||||
//======
|
||||
// PMM - lead target (not when blindfiring)
|
||||
// 20, 35, 50, 65 chance of leading
|
||||
if ((!blindfire) && (frandom() < 0.35f))
|
||||
PredictAim(self, self->enemy, start, rocketSpeed, false, 0.f, &dir, &vec);
|
||||
// PMM - lead target
|
||||
//======
|
||||
|
||||
dir.normalize();
|
||||
|
||||
// pmm blindfire doesn't check target (done in checkattack)
|
||||
// paranoia, make sure we're not shooting a target right next to us
|
||||
trace = gi.traceline(start, vec, self, MASK_PROJECTILE);
|
||||
if (blindfire)
|
||||
{
|
||||
// blindfire has different fail criteria for the trace
|
||||
if (!(trace.startsolid || trace.allsolid || (trace.fraction < 0.5f)))
|
||||
{
|
||||
// RAFAEL
|
||||
if (self->s.skinnum > 1)
|
||||
monster_fire_heat(self, start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1, 0.075f);
|
||||
else
|
||||
// RAFAEL
|
||||
monster_fire_rocket(self, start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// geez, this is bad. she's avoiding about 80% of her blindfires due to hitting things.
|
||||
// hunt around for a good shot
|
||||
// try shifting the target to the left a little (to help counter her large offset)
|
||||
vec = target;
|
||||
vec += (right * -10);
|
||||
dir = vec - start;
|
||||
dir.normalize();
|
||||
trace = gi.traceline(start, vec, self, MASK_PROJECTILE);
|
||||
if (!(trace.startsolid || trace.allsolid || (trace.fraction < 0.5f)))
|
||||
{
|
||||
// RAFAEL
|
||||
if (self->s.skinnum > 1)
|
||||
monster_fire_heat(self, start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1, 0.075f);
|
||||
else
|
||||
// RAFAEL
|
||||
monster_fire_rocket(self, start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ok, that failed. try to the right
|
||||
vec = target;
|
||||
vec += (right * 10);
|
||||
dir = vec - start;
|
||||
dir.normalize();
|
||||
trace = gi.traceline(start, vec, self, MASK_PROJECTILE);
|
||||
if (!(trace.startsolid || trace.allsolid || (trace.fraction < 0.5f)))
|
||||
{
|
||||
// RAFAEL
|
||||
if (self->s.skinnum > 1)
|
||||
monster_fire_heat(self, start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1, 0.075f);
|
||||
else
|
||||
// RAFAEL
|
||||
monster_fire_rocket(self, start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (trace.fraction > 0.5f || trace.ent->solid != SOLID_BSP)
|
||||
{
|
||||
// RAFAEL
|
||||
if (self->s.skinnum > 1)
|
||||
monster_fire_heat(self, start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1, 0.15f);
|
||||
else
|
||||
// RAFAEL
|
||||
monster_fire_rocket(self, start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Chick_PreAttack1(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_missile_prelaunch, 1, ATTN_NORM, 0);
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
|
||||
{
|
||||
vec3_t aim = self->monsterinfo.blind_fire_target - self->s.origin;
|
||||
self->ideal_yaw = vectoyaw(aim);
|
||||
}
|
||||
}
|
||||
|
||||
void ChickReload(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_missile_reload, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t chick_frames_start_attack1[] = {
|
||||
{ ai_charge, 0, Chick_PreAttack1 },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 4 },
|
||||
{ ai_charge },
|
||||
{ ai_charge, -3 },
|
||||
{ ai_charge, 3 },
|
||||
{ ai_charge, 5 },
|
||||
{ ai_charge, 7, monster_footstep },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, chick_attack1 }
|
||||
};
|
||||
MMOVE_T(chick_move_start_attack1) = { FRAME_attak101, FRAME_attak113, chick_frames_start_attack1, nullptr };
|
||||
|
||||
mframe_t chick_frames_attack1[] = {
|
||||
{ ai_charge, 19, ChickRocket },
|
||||
{ ai_charge, -6, monster_footstep },
|
||||
{ ai_charge, -5 },
|
||||
{ ai_charge, -2 },
|
||||
{ ai_charge, -7, monster_footstep },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 1 },
|
||||
{ ai_charge, 10, ChickReload },
|
||||
{ ai_charge, 4 },
|
||||
{ ai_charge, 5, monster_footstep },
|
||||
{ ai_charge, 6 },
|
||||
{ ai_charge, 6 },
|
||||
{ ai_charge, 4 },
|
||||
{ ai_charge, 3, [](edict_t *self) { chick_rerocket(self); monster_footstep(self); } }
|
||||
};
|
||||
MMOVE_T(chick_move_attack1) = { FRAME_attak114, FRAME_attak127, chick_frames_attack1, nullptr };
|
||||
|
||||
mframe_t chick_frames_end_attack1[] = {
|
||||
{ ai_charge, -3 },
|
||||
{ ai_charge },
|
||||
{ ai_charge, -6 },
|
||||
{ ai_charge, -4 },
|
||||
{ ai_charge, -2, monster_footstep }
|
||||
};
|
||||
MMOVE_T(chick_move_end_attack1) = { FRAME_attak128, FRAME_attak132, chick_frames_end_attack1, chick_run };
|
||||
|
||||
void chick_rerocket(edict_t *self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
|
||||
{
|
||||
self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
|
||||
M_SetAnimation(self, &chick_move_end_attack1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!M_CheckClearShot(self, monster_flash_offset[MZ2_CHICK_ROCKET_1]))
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_end_attack1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->enemy->health > 0)
|
||||
{
|
||||
if (range_to(self, self->enemy) > RANGE_MELEE)
|
||||
if (visible(self, self->enemy))
|
||||
if (frandom() <= 0.7f)
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_attack1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
M_SetAnimation(self, &chick_move_end_attack1);
|
||||
}
|
||||
|
||||
void chick_attack1(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_attack1);
|
||||
}
|
||||
|
||||
mframe_t chick_frames_slash[] = {
|
||||
{ ai_charge, 1 },
|
||||
{ ai_charge, 7, ChickSlash },
|
||||
{ ai_charge, -7, monster_footstep },
|
||||
{ ai_charge, 1 },
|
||||
{ ai_charge, -1 },
|
||||
{ ai_charge, 1 },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 1 },
|
||||
{ ai_charge, -2, chick_reslash }
|
||||
};
|
||||
MMOVE_T(chick_move_slash) = { FRAME_attak204, FRAME_attak212, chick_frames_slash, nullptr };
|
||||
|
||||
mframe_t chick_frames_end_slash[] = {
|
||||
{ ai_charge, -6 },
|
||||
{ ai_charge, -1 },
|
||||
{ ai_charge, -6 },
|
||||
{ ai_charge, 0, monster_footstep }
|
||||
};
|
||||
MMOVE_T(chick_move_end_slash) = { FRAME_attak213, FRAME_attak216, chick_frames_end_slash, chick_run };
|
||||
|
||||
void chick_reslash(edict_t *self)
|
||||
{
|
||||
if (self->enemy->health > 0)
|
||||
{
|
||||
if (range_to(self, self->enemy) <= RANGE_MELEE)
|
||||
{
|
||||
if (frandom() <= 0.9f)
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_slash);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_end_slash);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
M_SetAnimation(self, &chick_move_end_slash);
|
||||
}
|
||||
|
||||
void chick_slash(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_slash);
|
||||
}
|
||||
|
||||
mframe_t chick_frames_start_slash[] = {
|
||||
{ ai_charge, 1 },
|
||||
{ ai_charge, 8 },
|
||||
{ ai_charge, 3 }
|
||||
};
|
||||
MMOVE_T(chick_move_start_slash) = { FRAME_attak201, FRAME_attak203, chick_frames_start_slash, chick_slash };
|
||||
|
||||
MONSTERINFO_MELEE(chick_melee) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &chick_move_start_slash);
|
||||
}
|
||||
|
||||
MONSTERINFO_ATTACK(chick_attack) (edict_t *self) -> void
|
||||
{
|
||||
if (!M_CheckClearShot(self, monster_flash_offset[MZ2_CHICK_ROCKET_1]))
|
||||
return;
|
||||
|
||||
float r, chance;
|
||||
|
||||
monster_done_dodge(self);
|
||||
|
||||
// PMM
|
||||
if (self->monsterinfo.attack_state == AS_BLIND)
|
||||
{
|
||||
// setup shot probabilities
|
||||
if (self->monsterinfo.blind_fire_delay < 1.0_sec)
|
||||
chance = 1.0;
|
||||
else if (self->monsterinfo.blind_fire_delay < 7.5_sec)
|
||||
chance = 0.4f;
|
||||
else
|
||||
chance = 0.1f;
|
||||
|
||||
r = frandom();
|
||||
|
||||
// minimum of 5.5 seconds, plus 0-1, after the shots are done
|
||||
self->monsterinfo.blind_fire_delay += random_time(5.5_sec, 6.5_sec);
|
||||
|
||||
// don't shoot at the origin
|
||||
if (!self->monsterinfo.blind_fire_target)
|
||||
return;
|
||||
|
||||
// don't shoot if the dice say not to
|
||||
if (r > chance)
|
||||
return;
|
||||
|
||||
// turn on manual steering to signal both manual steering and blindfire
|
||||
self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
|
||||
M_SetAnimation(self, &chick_move_start_attack1);
|
||||
self->monsterinfo.attack_finished = level.time + random_time(2_sec);
|
||||
return;
|
||||
}
|
||||
// pmm
|
||||
|
||||
M_SetAnimation(self, &chick_move_start_attack1);
|
||||
}
|
||||
|
||||
MONSTERINFO_SIGHT(chick_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
//===========
|
||||
// PGM
|
||||
MONSTERINFO_BLOCKED(chick_blocked) (edict_t *self, float dist) -> bool
|
||||
{
|
||||
if (blocked_checkplat(self, dist))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
// PGM
|
||||
//===========
|
||||
|
||||
MONSTERINFO_DUCK(chick_duck) (edict_t *self, gtime_t eta) -> bool
|
||||
{
|
||||
if ((self->monsterinfo.active_move == &chick_move_start_attack1) ||
|
||||
(self->monsterinfo.active_move == &chick_move_attack1))
|
||||
{
|
||||
// if we're shooting don't dodge
|
||||
self->monsterinfo.unduck(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
M_SetAnimation(self, &chick_move_duck);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MONSTERINFO_SIDESTEP(chick_sidestep) (edict_t *self) -> bool
|
||||
{
|
||||
if ((self->monsterinfo.active_move == &chick_move_start_attack1) ||
|
||||
(self->monsterinfo.active_move == &chick_move_attack1) ||
|
||||
(self->monsterinfo.active_move == &chick_move_pain3))
|
||||
{
|
||||
// if we're shooting, don't dodge
|
||||
return false;
|
||||
}
|
||||
|
||||
if (self->monsterinfo.active_move != &chick_move_run)
|
||||
M_SetAnimation(self, &chick_move_run);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*QUAKED monster_chick (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_chick(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_missile_prelaunch = gi.soundindex("chick/chkatck1.wav");
|
||||
sound_missile_launch = gi.soundindex("chick/chkatck2.wav");
|
||||
sound_melee_swing = gi.soundindex("chick/chkatck3.wav");
|
||||
sound_melee_hit = gi.soundindex("chick/chkatck4.wav");
|
||||
sound_missile_reload = gi.soundindex("chick/chkatck5.wav");
|
||||
sound_death1 = gi.soundindex("chick/chkdeth1.wav");
|
||||
sound_death2 = gi.soundindex("chick/chkdeth2.wav");
|
||||
sound_fall_down = gi.soundindex("chick/chkfall1.wav");
|
||||
sound_idle1 = gi.soundindex("chick/chkidle1.wav");
|
||||
sound_idle2 = gi.soundindex("chick/chkidle2.wav");
|
||||
sound_pain1 = gi.soundindex("chick/chkpain1.wav");
|
||||
sound_pain2 = gi.soundindex("chick/chkpain2.wav");
|
||||
sound_pain3 = gi.soundindex("chick/chkpain3.wav");
|
||||
sound_sight = gi.soundindex("chick/chksght1.wav");
|
||||
sound_search = gi.soundindex("chick/chksrch1.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/bitch/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/bitch/gibs/arm.md2");
|
||||
gi.modelindex("models/monsters/bitch/gibs/chest.md2");
|
||||
gi.modelindex("models/monsters/bitch/gibs/foot.md2");
|
||||
gi.modelindex("models/monsters/bitch/gibs/head.md2");
|
||||
gi.modelindex("models/monsters/bitch/gibs/tube.md2");
|
||||
|
||||
self->mins = { -16, -16, 0 };
|
||||
self->maxs = { 16, 16, 56 };
|
||||
|
||||
self->health = 175 * st.health_multiplier;
|
||||
self->gib_health = -70;
|
||||
self->mass = 200;
|
||||
|
||||
self->pain = chick_pain;
|
||||
self->die = chick_die;
|
||||
|
||||
self->monsterinfo.stand = chick_stand;
|
||||
self->monsterinfo.walk = chick_walk;
|
||||
self->monsterinfo.run = chick_run;
|
||||
// pmm
|
||||
self->monsterinfo.dodge = M_MonsterDodge;
|
||||
self->monsterinfo.duck = chick_duck;
|
||||
self->monsterinfo.unduck = monster_duck_up;
|
||||
self->monsterinfo.sidestep = chick_sidestep;
|
||||
self->monsterinfo.blocked = chick_blocked; // PGM
|
||||
// pmm
|
||||
self->monsterinfo.attack = chick_attack;
|
||||
self->monsterinfo.melee = chick_melee;
|
||||
self->monsterinfo.sight = chick_sight;
|
||||
self->monsterinfo.setskin = chick_setpain;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &chick_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
// PMM
|
||||
self->monsterinfo.blindfire = true;
|
||||
// pmm
|
||||
walkmonster_start(self);
|
||||
}
|
||||
|
||||
// RAFAEL
|
||||
/*QUAKED monster_chick_heat (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_chick_heat(edict_t *self)
|
||||
{
|
||||
SP_monster_chick(self);
|
||||
self->s.skinnum = 2;
|
||||
gi.soundindex("weapons/railgr1a.wav");
|
||||
}
|
||||
// RAFAEL
|
||||
299
rerelease/m_chick.h
Normal file
299
rerelease/m_chick.h
Normal file
@@ -0,0 +1,299 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/bitch
|
||||
|
||||
// This file generated by qdata - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_attak101,
|
||||
FRAME_attak102,
|
||||
FRAME_attak103,
|
||||
FRAME_attak104,
|
||||
FRAME_attak105,
|
||||
FRAME_attak106,
|
||||
FRAME_attak107,
|
||||
FRAME_attak108,
|
||||
FRAME_attak109,
|
||||
FRAME_attak110,
|
||||
FRAME_attak111,
|
||||
FRAME_attak112,
|
||||
FRAME_attak113,
|
||||
FRAME_attak114,
|
||||
FRAME_attak115,
|
||||
FRAME_attak116,
|
||||
FRAME_attak117,
|
||||
FRAME_attak118,
|
||||
FRAME_attak119,
|
||||
FRAME_attak120,
|
||||
FRAME_attak121,
|
||||
FRAME_attak122,
|
||||
FRAME_attak123,
|
||||
FRAME_attak124,
|
||||
FRAME_attak125,
|
||||
FRAME_attak126,
|
||||
FRAME_attak127,
|
||||
FRAME_attak128,
|
||||
FRAME_attak129,
|
||||
FRAME_attak130,
|
||||
FRAME_attak131,
|
||||
FRAME_attak132,
|
||||
FRAME_attak201,
|
||||
FRAME_attak202,
|
||||
FRAME_attak203,
|
||||
FRAME_attak204,
|
||||
FRAME_attak205,
|
||||
FRAME_attak206,
|
||||
FRAME_attak207,
|
||||
FRAME_attak208,
|
||||
FRAME_attak209,
|
||||
FRAME_attak210,
|
||||
FRAME_attak211,
|
||||
FRAME_attak212,
|
||||
FRAME_attak213,
|
||||
FRAME_attak214,
|
||||
FRAME_attak215,
|
||||
FRAME_attak216,
|
||||
FRAME_death101,
|
||||
FRAME_death102,
|
||||
FRAME_death103,
|
||||
FRAME_death104,
|
||||
FRAME_death105,
|
||||
FRAME_death106,
|
||||
FRAME_death107,
|
||||
FRAME_death108,
|
||||
FRAME_death109,
|
||||
FRAME_death110,
|
||||
FRAME_death111,
|
||||
FRAME_death112,
|
||||
FRAME_death201,
|
||||
FRAME_death202,
|
||||
FRAME_death203,
|
||||
FRAME_death204,
|
||||
FRAME_death205,
|
||||
FRAME_death206,
|
||||
FRAME_death207,
|
||||
FRAME_death208,
|
||||
FRAME_death209,
|
||||
FRAME_death210,
|
||||
FRAME_death211,
|
||||
FRAME_death212,
|
||||
FRAME_death213,
|
||||
FRAME_death214,
|
||||
FRAME_death215,
|
||||
FRAME_death216,
|
||||
FRAME_death217,
|
||||
FRAME_death218,
|
||||
FRAME_death219,
|
||||
FRAME_death220,
|
||||
FRAME_death221,
|
||||
FRAME_death222,
|
||||
FRAME_death223,
|
||||
FRAME_duck01,
|
||||
FRAME_duck02,
|
||||
FRAME_duck03,
|
||||
FRAME_duck04,
|
||||
FRAME_duck05,
|
||||
FRAME_duck06,
|
||||
FRAME_duck07,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain104,
|
||||
FRAME_pain105,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain204,
|
||||
FRAME_pain205,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_pain304,
|
||||
FRAME_pain305,
|
||||
FRAME_pain306,
|
||||
FRAME_pain307,
|
||||
FRAME_pain308,
|
||||
FRAME_pain309,
|
||||
FRAME_pain310,
|
||||
FRAME_pain311,
|
||||
FRAME_pain312,
|
||||
FRAME_pain313,
|
||||
FRAME_pain314,
|
||||
FRAME_pain315,
|
||||
FRAME_pain316,
|
||||
FRAME_pain317,
|
||||
FRAME_pain318,
|
||||
FRAME_pain319,
|
||||
FRAME_pain320,
|
||||
FRAME_pain321,
|
||||
FRAME_stand101,
|
||||
FRAME_stand102,
|
||||
FRAME_stand103,
|
||||
FRAME_stand104,
|
||||
FRAME_stand105,
|
||||
FRAME_stand106,
|
||||
FRAME_stand107,
|
||||
FRAME_stand108,
|
||||
FRAME_stand109,
|
||||
FRAME_stand110,
|
||||
FRAME_stand111,
|
||||
FRAME_stand112,
|
||||
FRAME_stand113,
|
||||
FRAME_stand114,
|
||||
FRAME_stand115,
|
||||
FRAME_stand116,
|
||||
FRAME_stand117,
|
||||
FRAME_stand118,
|
||||
FRAME_stand119,
|
||||
FRAME_stand120,
|
||||
FRAME_stand121,
|
||||
FRAME_stand122,
|
||||
FRAME_stand123,
|
||||
FRAME_stand124,
|
||||
FRAME_stand125,
|
||||
FRAME_stand126,
|
||||
FRAME_stand127,
|
||||
FRAME_stand128,
|
||||
FRAME_stand129,
|
||||
FRAME_stand130,
|
||||
FRAME_stand201,
|
||||
FRAME_stand202,
|
||||
FRAME_stand203,
|
||||
FRAME_stand204,
|
||||
FRAME_stand205,
|
||||
FRAME_stand206,
|
||||
FRAME_stand207,
|
||||
FRAME_stand208,
|
||||
FRAME_stand209,
|
||||
FRAME_stand210,
|
||||
FRAME_stand211,
|
||||
FRAME_stand212,
|
||||
FRAME_stand213,
|
||||
FRAME_stand214,
|
||||
FRAME_stand215,
|
||||
FRAME_stand216,
|
||||
FRAME_stand217,
|
||||
FRAME_stand218,
|
||||
FRAME_stand219,
|
||||
FRAME_stand220,
|
||||
FRAME_stand221,
|
||||
FRAME_stand222,
|
||||
FRAME_stand223,
|
||||
FRAME_stand224,
|
||||
FRAME_stand225,
|
||||
FRAME_stand226,
|
||||
FRAME_stand227,
|
||||
FRAME_stand228,
|
||||
FRAME_stand229,
|
||||
FRAME_stand230,
|
||||
FRAME_walk01,
|
||||
FRAME_walk02,
|
||||
FRAME_walk03,
|
||||
FRAME_walk04,
|
||||
FRAME_walk05,
|
||||
FRAME_walk06,
|
||||
FRAME_walk07,
|
||||
FRAME_walk08,
|
||||
FRAME_walk09,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_walk13,
|
||||
FRAME_walk14,
|
||||
FRAME_walk15,
|
||||
FRAME_walk16,
|
||||
FRAME_walk17,
|
||||
FRAME_walk18,
|
||||
FRAME_walk19,
|
||||
FRAME_walk20,
|
||||
FRAME_walk21,
|
||||
FRAME_walk22,
|
||||
FRAME_walk23,
|
||||
FRAME_walk24,
|
||||
FRAME_walk25,
|
||||
FRAME_walk26,
|
||||
FRAME_walk27,
|
||||
FRAME_recln201,
|
||||
FRAME_recln202,
|
||||
FRAME_recln203,
|
||||
FRAME_recln204,
|
||||
FRAME_recln205,
|
||||
FRAME_recln206,
|
||||
FRAME_recln207,
|
||||
FRAME_recln208,
|
||||
FRAME_recln209,
|
||||
FRAME_recln210,
|
||||
FRAME_recln211,
|
||||
FRAME_recln212,
|
||||
FRAME_recln213,
|
||||
FRAME_recln214,
|
||||
FRAME_recln215,
|
||||
FRAME_recln216,
|
||||
FRAME_recln217,
|
||||
FRAME_recln218,
|
||||
FRAME_recln219,
|
||||
FRAME_recln220,
|
||||
FRAME_recln221,
|
||||
FRAME_recln222,
|
||||
FRAME_recln223,
|
||||
FRAME_recln224,
|
||||
FRAME_recln225,
|
||||
FRAME_recln226,
|
||||
FRAME_recln227,
|
||||
FRAME_recln228,
|
||||
FRAME_recln229,
|
||||
FRAME_recln230,
|
||||
FRAME_recln231,
|
||||
FRAME_recln232,
|
||||
FRAME_recln233,
|
||||
FRAME_recln234,
|
||||
FRAME_recln235,
|
||||
FRAME_recln236,
|
||||
FRAME_recln237,
|
||||
FRAME_recln238,
|
||||
FRAME_recln239,
|
||||
FRAME_recln240,
|
||||
FRAME_recln101,
|
||||
FRAME_recln102,
|
||||
FRAME_recln103,
|
||||
FRAME_recln104,
|
||||
FRAME_recln105,
|
||||
FRAME_recln106,
|
||||
FRAME_recln107,
|
||||
FRAME_recln108,
|
||||
FRAME_recln109,
|
||||
FRAME_recln110,
|
||||
FRAME_recln111,
|
||||
FRAME_recln112,
|
||||
FRAME_recln113,
|
||||
FRAME_recln114,
|
||||
FRAME_recln115,
|
||||
FRAME_recln116,
|
||||
FRAME_recln117,
|
||||
FRAME_recln118,
|
||||
FRAME_recln119,
|
||||
FRAME_recln120,
|
||||
FRAME_recln121,
|
||||
FRAME_recln122,
|
||||
FRAME_recln123,
|
||||
FRAME_recln124,
|
||||
FRAME_recln125,
|
||||
FRAME_recln126,
|
||||
FRAME_recln127,
|
||||
FRAME_recln128,
|
||||
FRAME_recln129,
|
||||
FRAME_recln130,
|
||||
FRAME_recln131,
|
||||
FRAME_recln132,
|
||||
FRAME_recln133,
|
||||
FRAME_recln134,
|
||||
FRAME_recln135,
|
||||
FRAME_recln136,
|
||||
FRAME_recln137,
|
||||
FRAME_recln138,
|
||||
FRAME_recln139,
|
||||
FRAME_recln140
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
619
rerelease/m_flash.h
Normal file
619
rerelease/m_flash.h
Normal file
@@ -0,0 +1,619 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// m_flash.h
|
||||
|
||||
// this file is included in both the game dll and quake2,
|
||||
// the game needs it to source shot locations, the client
|
||||
// needs it to position muzzle flashes
|
||||
constexpr vec3_t monster_flash_offset[] = {
|
||||
// flash 0 is not used
|
||||
{ 0.0f, 0.0f, 0.0f },
|
||||
|
||||
// MZ2_TANK_BLASTER_1 1
|
||||
{ 28.7f, -18.5f, 28.7f },
|
||||
// MZ2_TANK_BLASTER_2 2
|
||||
{ 24.6f, -21.5f, 30.1f },
|
||||
// MZ2_TANK_BLASTER_3 3
|
||||
{ 19.8f, -23.9f, 32.1f },
|
||||
// MZ2_TANK_MACHINEGUN_1 4
|
||||
{ 22.9f, -0.7f, 25.3f },
|
||||
// MZ2_TANK_MACHINEGUN_2 5
|
||||
{ 22.2f, 6.2f, 22.3f },
|
||||
// MZ2_TANK_MACHINEGUN_3 6
|
||||
{ 19.4f, 13.1f, 18.6f },
|
||||
// MZ2_TANK_MACHINEGUN_4 7
|
||||
{ 19.4f, 18.8f, 18.6f },
|
||||
// MZ2_TANK_MACHINEGUN_5 8
|
||||
{ 17.9f, 25.0f, 18.6f },
|
||||
// MZ2_TANK_MACHINEGUN_6 9
|
||||
{ 14.1f, 30.5f, 20.6f },
|
||||
// MZ2_TANK_MACHINEGUN_7 10
|
||||
{ 9.3f, 35.3f, 22.1f },
|
||||
// MZ2_TANK_MACHINEGUN_8 11
|
||||
{ 4.7f, 38.4f, 22.1f },
|
||||
// MZ2_TANK_MACHINEGUN_9 12
|
||||
{ -1.1f, 40.4f, 24.1f },
|
||||
// MZ2_TANK_MACHINEGUN_10 13
|
||||
{ -6.5f, 41.2f, 24.1f },
|
||||
// MZ2_TANK_MACHINEGUN_11 14
|
||||
{ 3.2f, 40.1f, 24.7f },
|
||||
// MZ2_TANK_MACHINEGUN_12 15
|
||||
{ 11.7f, 36.7f, 26.0f },
|
||||
// MZ2_TANK_MACHINEGUN_13 16
|
||||
{ 18.9f, 31.3f, 26.0f },
|
||||
// MZ2_TANK_MACHINEGUN_14 17
|
||||
{ 24.4f, 24.4f, 26.4f },
|
||||
// MZ2_TANK_MACHINEGUN_15 18
|
||||
{ 27.1f, 17.1f, 27.2f },
|
||||
// MZ2_TANK_MACHINEGUN_16 19
|
||||
{ 28.5f, 9.1f, 28.0f },
|
||||
// MZ2_TANK_MACHINEGUN_17 20
|
||||
{ 27.1f, 2.2f, 28.0f },
|
||||
// MZ2_TANK_MACHINEGUN_18 21
|
||||
{ 24.9f, -2.8f, 28.0f },
|
||||
// MZ2_TANK_MACHINEGUN_19 22
|
||||
{ 21.6f, -7.0f, 26.4f },
|
||||
// MZ2_TANK_ROCKET_1 23
|
||||
{ 6.2f, 29.1f, 49.1f },
|
||||
// MZ2_TANK_ROCKET_2 24
|
||||
{ 6.9f, 23.8f, 49.1f },
|
||||
// MZ2_TANK_ROCKET_3 25
|
||||
{ 8.3f, 17.8f, 49.5f },
|
||||
|
||||
// MZ2_INFANTRY_MACHINEGUN_1 26
|
||||
{ 26.6f, 7.1f, 13.1f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_2 27
|
||||
{ 18.2f, 7.5f, 15.4f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_3 28
|
||||
{ 17.2f, 10.3f, 17.9f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_4 29
|
||||
{ 17.0f, 12.8f, 20.1f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_5 30
|
||||
{ 15.1f, 14.1f, 21.8f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_6 31
|
||||
{ 11.8f, 17.2f, 23.1f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_7 32
|
||||
{ 11.4f, 20.2f, 21.0f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_8 33
|
||||
{ 9.0f, 23.0f, 18.9f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_9 34
|
||||
{ 13.9f, 18.6f, 17.7f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_10 35
|
||||
{ 15.4f, 15.6f, 15.8f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_11 36
|
||||
{ 10.2f, 15.2f, 25.1f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_12 37
|
||||
{ -1.9f, 15.1f, 28.2f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_13 38
|
||||
{ -12.4f, 13.0f, 20.2f },
|
||||
|
||||
// MZ2_SOLDIER_BLASTER_1 39
|
||||
{ 10.6f * 1.2f, 7.7f * 1.2f, 7.8f * 1.2f },
|
||||
// MZ2_SOLDIER_BLASTER_2 40
|
||||
{ 25.1f * 1.2f, 3.6f * 1.2f, 19.0f * 1.2f },
|
||||
// MZ2_SOLDIER_SHOTGUN_1 41
|
||||
{ 10.6f * 1.2f, 7.7f * 1.2f, 7.8f * 1.2f },
|
||||
// MZ2_SOLDIER_SHOTGUN_2 42
|
||||
{ 25.1f * 1.2f, 3.6f * 1.2f, 19.0f * 1.2f },
|
||||
// MZ2_SOLDIER_MACHINEGUN_1 43
|
||||
{ 10.6f * 1.2f, 7.7f * 1.2f, 7.8f * 1.2f },
|
||||
// MZ2_SOLDIER_MACHINEGUN_2 44
|
||||
{ 25.1f * 1.2f, 3.6f * 1.2f, 19.0f * 1.2f },
|
||||
|
||||
// MZ2_GUNNER_MACHINEGUN_1 45
|
||||
{ 30.1f * 1.15f, 3.9f * 1.15f, 19.6f * 1.15f },
|
||||
// MZ2_GUNNER_MACHINEGUN_2 46
|
||||
{ 29.1f * 1.15f, 2.5f * 1.15f, 20.7f * 1.15f },
|
||||
// MZ2_GUNNER_MACHINEGUN_3 47
|
||||
{ 28.2f * 1.15f, 2.5f * 1.15f, 22.2f * 1.15f },
|
||||
// MZ2_GUNNER_MACHINEGUN_4 48
|
||||
{ 28.2f * 1.15f, 3.6f * 1.15f, 22.0f * 1.15f },
|
||||
// MZ2_GUNNER_MACHINEGUN_5 49
|
||||
{ 26.9f * 1.15f, 2.0f * 1.15f, 23.4f * 1.15f },
|
||||
// MZ2_GUNNER_MACHINEGUN_6 50
|
||||
{ 26.5f * 1.15f, 0.6f * 1.15f, 20.8f * 1.15f },
|
||||
// MZ2_GUNNER_MACHINEGUN_7 51
|
||||
{ 26.9f * 1.15f, 0.5f * 1.15f, 21.5f * 1.15f },
|
||||
// MZ2_GUNNER_MACHINEGUN_8 52
|
||||
{ 29.0f * 1.15f, 2.4f * 1.15f, 19.5f * 1.15f },
|
||||
// MZ2_GUNNER_GRENADE_1 53
|
||||
{ 4.6f * 1.15f, -16.8f * 1.15f, 7.3f * 1.15f },
|
||||
// MZ2_GUNNER_GRENADE_2 54
|
||||
{ 4.6f * 1.15f, -16.8f * 1.15f, 7.3f * 1.15f },
|
||||
// MZ2_GUNNER_GRENADE_3 55
|
||||
{ 4.6f * 1.15f, -16.8f * 1.15f, 7.3f * 1.15f },
|
||||
// MZ2_GUNNER_GRENADE_4 56
|
||||
{ 4.6f * 1.15f, -16.8f * 1.15f, 7.3f * 1.15f },
|
||||
|
||||
// MZ2_CHICK_ROCKET_1 57
|
||||
// -24.8f, -9.0f, 39.0f,
|
||||
{ 24.8f, -9.0f, 39.0f }, // PGM - this was incorrect in Q2
|
||||
|
||||
// MZ2_FLYER_BLASTER_1 58
|
||||
{ 14.1f, 13.4f, -7.0f },
|
||||
// MZ2_FLYER_BLASTER_2 59
|
||||
{ 14.1f, -13.4f, -7.0f },
|
||||
|
||||
// MZ2_MEDIC_BLASTER_1 60
|
||||
{ 44.0f, 3.0f, 14.4f },
|
||||
|
||||
// MZ2_GLADIATOR_RAILGUN_1 61
|
||||
{ 30.0f, 18.0f, 28.0f },
|
||||
|
||||
// MZ2_HOVER_BLASTER_1 62
|
||||
{ 1.7f, 7.0f, 11.3f },
|
||||
|
||||
// MZ2_ACTOR_MACHINEGUN_1 63
|
||||
{ 18.4f, 7.4f, 9.6f },
|
||||
|
||||
// MZ2_SUPERTANK_MACHINEGUN_1 64
|
||||
{ 30.0f, 39.0f, 85.5f },
|
||||
// MZ2_SUPERTANK_MACHINEGUN_2 65
|
||||
{ 30.0f, 39.0f, 85.5f },
|
||||
// MZ2_SUPERTANK_MACHINEGUN_3 66
|
||||
{ 30.0f, 39.0f, 85.5f },
|
||||
// MZ2_SUPERTANK_MACHINEGUN_4 67
|
||||
{ 30.0f, 39.0f, 85.5f },
|
||||
// MZ2_SUPERTANK_MACHINEGUN_5 68
|
||||
{ 30.0f, 39.0f, 85.5f },
|
||||
// MZ2_SUPERTANK_MACHINEGUN_6 69
|
||||
{ 30.0f, 39.0f, 85.5f },
|
||||
// MZ2_SUPERTANK_ROCKET_1 70
|
||||
{ 16.0f, -22.5f, 108.7f },
|
||||
// MZ2_SUPERTANK_ROCKET_2 71
|
||||
{ 16.0f, -33.4f, 106.7f },
|
||||
// MZ2_SUPERTANK_ROCKET_3 72
|
||||
{ 16.0f, -42.8f, 104.7f },
|
||||
|
||||
// --- Start Xian Stuff ---
|
||||
// MZ2_BOSS2_MACHINEGUN_L1 73
|
||||
{ 32.f, -40.f, 70.f },
|
||||
// MZ2_BOSS2_MACHINEGUN_L2 74
|
||||
{ 32.f, -40.f, 70.f },
|
||||
// MZ2_BOSS2_MACHINEGUN_L3 75
|
||||
{ 32.f, -40.f, 70.f },
|
||||
// MZ2_BOSS2_MACHINEGUN_L4 76
|
||||
{ 32.f, -40.f, 70.f },
|
||||
// MZ2_BOSS2_MACHINEGUN_L5 77
|
||||
{ 32.f, -40.f, 70.f },
|
||||
// --- End Xian Stuff
|
||||
|
||||
// MZ2_BOSS2_ROCKET_1 78
|
||||
{ 22.0f, 16.0f, 10.0f },
|
||||
// MZ2_BOSS2_ROCKET_2 79
|
||||
{ 22.0f, 8.0f, 10.0f },
|
||||
// MZ2_BOSS2_ROCKET_3 80
|
||||
{ 22.0f, -8.0f, 10.0f },
|
||||
// MZ2_BOSS2_ROCKET_4 81
|
||||
{ 22.0f, -16.0f, 10.0f },
|
||||
|
||||
// MZ2_FLOAT_BLASTER_1 82
|
||||
{ 32.5f, -0.8f, 10.f },
|
||||
|
||||
// MZ2_SOLDIER_BLASTER_3 83
|
||||
{ 20.8f * 1.2f, 10.1f * 1.2f, -2.7f * 1.2f },
|
||||
// MZ2_SOLDIER_SHOTGUN_3 84
|
||||
{ 20.8f * 1.2f, 10.1f * 1.2f, -2.7f * 1.2f },
|
||||
// MZ2_SOLDIER_MACHINEGUN_3 85
|
||||
{ 20.8f * 1.2f, 10.1f * 1.2f, -2.7f * 1.2f },
|
||||
// MZ2_SOLDIER_BLASTER_4 86
|
||||
{ 7.6f * 1.2f, 9.3f * 1.2f, 0.8f * 1.2f },
|
||||
// MZ2_SOLDIER_SHOTGUN_4 87
|
||||
{ 7.6f * 1.2f, 9.3f * 1.2f, 0.8f * 1.2f },
|
||||
// MZ2_SOLDIER_MACHINEGUN_4 88
|
||||
{ 7.6f * 1.2f, 9.3f * 1.2f, 0.8f * 1.2f },
|
||||
// MZ2_SOLDIER_BLASTER_5 89
|
||||
{ 30.5f * 1.2f, 9.9f * 1.2f, -18.7f * 1.2f },
|
||||
// MZ2_SOLDIER_SHOTGUN_5 90
|
||||
{ 30.5f * 1.2f, 9.9f * 1.2f, -18.7f * 1.2f },
|
||||
// MZ2_SOLDIER_MACHINEGUN_5 91
|
||||
{ 30.5f * 1.2f, 9.9f * 1.2f, -18.7f * 1.2f },
|
||||
// MZ2_SOLDIER_BLASTER_6 92
|
||||
{ 27.6f * 1.2f, 3.4f * 1.2f, -10.4f * 1.2f },
|
||||
// MZ2_SOLDIER_SHOTGUN_6 93
|
||||
{ 27.6f * 1.2f, 3.4f * 1.2f, -10.4f * 1.2f },
|
||||
// MZ2_SOLDIER_MACHINEGUN_6 94
|
||||
{ 27.6f * 1.2f, 3.4f * 1.2f, -10.4f * 1.2f },
|
||||
// MZ2_SOLDIER_BLASTER_7 95
|
||||
{ 28.9f * 1.2f, 4.6f * 1.2f, -8.1f * 1.2f },
|
||||
// MZ2_SOLDIER_SHOTGUN_7 96
|
||||
{ 28.9f * 1.2f, 4.6f * 1.2f, -8.1f * 1.2f },
|
||||
// MZ2_SOLDIER_MACHINEGUN_7 97
|
||||
{ 28.9f * 1.2f, 4.6f * 1.2f, -8.1f * 1.2f },
|
||||
// MZ2_SOLDIER_BLASTER_8 98
|
||||
{ 31.5f * 1.2f, 9.6f * 1.2f, 10.1f * 1.2f },
|
||||
// MZ2_SOLDIER_SHOTGUN_8 99
|
||||
{ 34.5f * 1.2f, 9.6f * 1.2f, 6.1f * 1.2f },
|
||||
// MZ2_SOLDIER_MACHINEGUN_8 100
|
||||
{ 34.5f * 1.2f, 9.6f * 1.2f, 6.1f * 1.2f },
|
||||
|
||||
// --- Xian shit below ---
|
||||
// MZ2_MAKRON_BFG 101
|
||||
{ 17.f, -19.5f, 62.9f },
|
||||
// MZ2_MAKRON_BLASTER_1 102
|
||||
{ -3.6f, -24.1f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_2 103
|
||||
{ -1.6f, -19.3f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_3 104
|
||||
{ -0.1f, -14.4f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_4 105
|
||||
{ 2.0f, -7.6f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_5 106
|
||||
{ 3.4f, 1.3f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_6 107
|
||||
{ 3.7f, 11.1f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_7 108
|
||||
{ -0.3f, 22.3f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_8 109
|
||||
{ -6.f, 33.f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_9 110
|
||||
{ -9.3f, 36.4f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_10 111
|
||||
{ -7.f, 35.f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_11 112
|
||||
{ -2.1f, 29.f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_12 113
|
||||
{ 3.9f, 17.3f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_13 114
|
||||
{ 6.1f, 5.8f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_14 115
|
||||
{ 5.9f, -4.4f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_15 116
|
||||
{ 4.2f, -14.1f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_16 117
|
||||
{ 2.4f, -18.8f, 59.5f },
|
||||
// MZ2_MAKRON_BLASTER_17 118
|
||||
{ -1.8f, -25.5f, 59.5f },
|
||||
// MZ2_MAKRON_RAILGUN_1 119
|
||||
{ 18.1f, 7.8f, 74.4f },
|
||||
|
||||
// MZ2_JORG_MACHINEGUN_L1 120
|
||||
{ 78.5f, -47.1f, 96.f },
|
||||
// MZ2_JORG_MACHINEGUN_L2 121
|
||||
{ 78.5f, -47.1f, 96.f },
|
||||
// MZ2_JORG_MACHINEGUN_L3 122
|
||||
{ 78.5f, -47.1f, 96.f },
|
||||
// MZ2_JORG_MACHINEGUN_L4 123
|
||||
{ 78.5f, -47.1f, 96.f },
|
||||
// MZ2_JORG_MACHINEGUN_L5 124
|
||||
{ 78.5f, -47.1f, 96.f },
|
||||
// MZ2_JORG_MACHINEGUN_L6 125
|
||||
{ 78.5f, -47.1f, 96.f },
|
||||
// MZ2_JORG_MACHINEGUN_R1 126
|
||||
{ 78.5f, 46.7f, 96.f },
|
||||
// MZ2_JORG_MACHINEGUN_R2 127
|
||||
{ 78.5f, 46.7f, 96.f },
|
||||
// MZ2_JORG_MACHINEGUN_R3 128
|
||||
{ 78.5f, 46.7f, 96.f },
|
||||
// MZ2_JORG_MACHINEGUN_R4 129
|
||||
{ 78.5f, 46.7f, 96.f },
|
||||
// MZ2_JORG_MACHINEGUN_R5 130
|
||||
{ 78.5f, 46.7f, 96.f },
|
||||
// MZ2_JORG_MACHINEGUN_R6 131
|
||||
{ 78.5f, 46.7f, 96.f },
|
||||
// MZ2_JORG_BFG_1 132
|
||||
{ 6.3f, -9.f, 111.2f },
|
||||
|
||||
// MZ2_BOSS2_MACHINEGUN_R1 73
|
||||
{ 32.f, 40.f, 70.f },
|
||||
// MZ2_BOSS2_MACHINEGUN_R2 74
|
||||
{ 32.f, 40.f, 70.f },
|
||||
// MZ2_BOSS2_MACHINEGUN_R3 75
|
||||
{ 32.f, 40.f, 70.f },
|
||||
// MZ2_BOSS2_MACHINEGUN_R4 76
|
||||
{ 32.f, 40.f, 70.f },
|
||||
// MZ2_BOSS2_MACHINEGUN_R5 77
|
||||
{ 32.f, 40.f, 70.f },
|
||||
|
||||
// --- End Xian Shit ---
|
||||
|
||||
// ROGUE
|
||||
// note that the above really ends at 137
|
||||
// carrier machineguns
|
||||
// MZ2_CARRIER_MACHINEGUN_L1
|
||||
{ 56.f, -32.f, 32.f },
|
||||
// MZ2_CARRIER_MACHINEGUN_R1
|
||||
{ 56.f, 32.f, 32.f },
|
||||
// MZ2_CARRIER_GRENADE
|
||||
{ 42.f, 24.f, 50.f },
|
||||
// MZ2_TURRET_MACHINEGUN 141
|
||||
{ 20.f, 0.f, 0.f },
|
||||
// MZ2_TURRET_ROCKET 142
|
||||
{ 20.f, 0.f, 0.f },
|
||||
// MZ2_TURRET_BLASTER 143
|
||||
{ 20.f, 0.f, 0.f },
|
||||
// MZ2_STALKER_BLASTER 144
|
||||
{ 24.f, 0.f, 6.f },
|
||||
// MZ2_DAEDALUS_BLASTER 145
|
||||
{ 1.7f, 7.0f, 11.3f },
|
||||
// MZ2_MEDIC_BLASTER_2 146
|
||||
{ 44.0f, 3.0f, 14.4f },
|
||||
// MZ2_CARRIER_RAILGUN 147
|
||||
{ 32.f, 0.f, 6.f },
|
||||
// MZ2_WIDOW_DISRUPTOR 148
|
||||
{ 64.72f, 14.50f, 88.81f },
|
||||
// MZ2_WIDOW_BLASTER 149
|
||||
{ 56.f, 32.f, 32.f },
|
||||
// MZ2_WIDOW_RAIL 150
|
||||
{ 62.f, -20.f, 84.f },
|
||||
// MZ2_WIDOW_PLASMABEAM 151 // PMM - not used!
|
||||
{ 32.f, 0.f, 6.f },
|
||||
// MZ2_CARRIER_MACHINEGUN_L2 152
|
||||
{ 61.f, -32.f, 12.f },
|
||||
// MZ2_CARRIER_MACHINEGUN_R2 153
|
||||
{ 61.f, 32.f, 12.f },
|
||||
// MZ2_WIDOW_RAIL_LEFT 154
|
||||
{ 17.f, -62.f, 91.f },
|
||||
// MZ2_WIDOW_RAIL_RIGHT 155
|
||||
{ 68.f, 12.f, 86.f },
|
||||
// MZ2_WIDOW_BLASTER_SWEEP1 156 pmm - the sweeps need to be in sequential order
|
||||
{ 47.5f, 56.f, 89.f },
|
||||
// MZ2_WIDOW_BLASTER_SWEEP2 157
|
||||
{ 54.f, 52.f, 91.f },
|
||||
// MZ2_WIDOW_BLASTER_SWEEP3 158
|
||||
{ 58.f, 40.f, 91.f },
|
||||
// MZ2_WIDOW_BLASTER_SWEEP4 159
|
||||
{ 68.f, 30.f, 88.f },
|
||||
// MZ2_WIDOW_BLASTER_SWEEP5 160
|
||||
{ 74.f, 20.f, 88.f },
|
||||
// MZ2_WIDOW_BLASTER_SWEEP6 161
|
||||
{ 73.f, 11.f, 87.f },
|
||||
// MZ2_WIDOW_BLASTER_SWEEP7 162
|
||||
{ 73.f, 3.f, 87.f },
|
||||
// MZ2_WIDOW_BLASTER_SWEEP8 163
|
||||
{ 70.f, -12.f, 87.f },
|
||||
// MZ2_WIDOW_BLASTER_SWEEP9 164
|
||||
{ 67.f, -20.f, 90.f },
|
||||
// MZ2_WIDOW_BLASTER_100 165
|
||||
{ -20.f, 76.f, 90.f },
|
||||
// MZ2_WIDOW_BLASTER_90 166
|
||||
{ -8.f, 74.f, 90.f },
|
||||
// MZ2_WIDOW_BLASTER_80 167
|
||||
{ 0.f, 72.f, 90.f },
|
||||
// MZ2_WIDOW_BLASTER_70 168 d06
|
||||
{ 10.f, 71.f, 89.f },
|
||||
// MZ2_WIDOW_BLASTER_60 169 d07
|
||||
{ 23.f, 70.f, 87.f },
|
||||
// MZ2_WIDOW_BLASTER_50 170 d08
|
||||
{ 32.f, 64.f, 85.f },
|
||||
// MZ2_WIDOW_BLASTER_40 171
|
||||
{ 40.f, 58.f, 84.f },
|
||||
// MZ2_WIDOW_BLASTER_30 172 d10
|
||||
{ 48.f, 50.f, 83.f },
|
||||
// MZ2_WIDOW_BLASTER_20 173
|
||||
{ 54.f, 42.f, 82.f },
|
||||
// MZ2_WIDOW_BLASTER_10 174 d12
|
||||
{ 56.f, 34.f, 82.f },
|
||||
// MZ2_WIDOW_BLASTER_0 175
|
||||
{ 58.f, 26.f, 82.f },
|
||||
// MZ2_WIDOW_BLASTER_10L 176 d14
|
||||
{ 60.f, 16.f, 82.f },
|
||||
// MZ2_WIDOW_BLASTER_20L 177
|
||||
{ 59.f, 6.f, 81.f },
|
||||
// MZ2_WIDOW_BLASTER_30L 178 d16
|
||||
{ 58.f, -2.f, 80.f },
|
||||
// MZ2_WIDOW_BLASTER_40L 179
|
||||
{ 57.f, -10.f, 79.f },
|
||||
// MZ2_WIDOW_BLASTER_50L 180 d18
|
||||
{ 54.f, -18.f, 78.f },
|
||||
// MZ2_WIDOW_BLASTER_60L 181
|
||||
{ 42.f, -32.f, 80.f },
|
||||
// MZ2_WIDOW_BLASTER_70L 182 d20
|
||||
{ 36.f, -40.f, 78.f },
|
||||
// MZ2_WIDOW_RUN_1 183
|
||||
{ 68.4f, 10.88f, 82.08f },
|
||||
// MZ2_WIDOW_RUN_2 184
|
||||
{ 68.51f, 8.64f, 85.14f },
|
||||
// MZ2_WIDOW_RUN_3 185
|
||||
{ 68.66f, 6.38f, 88.78f },
|
||||
// MZ2_WIDOW_RUN_4 186
|
||||
{ 68.73f, 5.1f, 84.47f },
|
||||
// MZ2_WIDOW_RUN_5 187
|
||||
{ 68.82f, 4.79f, 80.52f },
|
||||
// MZ2_WIDOW_RUN_6 188
|
||||
{ 68.77f, 6.11f, 85.37f },
|
||||
// MZ2_WIDOW_RUN_7 189
|
||||
{ 68.67f, 7.99f, 90.24f },
|
||||
// MZ2_WIDOW_RUN_8 190
|
||||
{ 68.55f, 9.54f, 87.36f },
|
||||
// MZ2_CARRIER_ROCKET_1 191
|
||||
{ 0.f, 0.f, -5.f },
|
||||
// MZ2_CARRIER_ROCKET_2 192
|
||||
{ 0.f, 0.f, -5.f },
|
||||
// MZ2_CARRIER_ROCKET_3 193
|
||||
{ 0.f, 0.f, -5.f },
|
||||
// MZ2_CARRIER_ROCKET_4 194
|
||||
{ 0.f, 0.f, -5.f },
|
||||
// MZ2_WIDOW2_BEAMER_1 195
|
||||
// 72.13f, -17.63f, 93.77f,
|
||||
{ 69.00f, -17.63f, 93.77f },
|
||||
// MZ2_WIDOW2_BEAMER_2 196
|
||||
// 71.46f, -17.08f, 89.82f,
|
||||
{ 69.00f, -17.08f, 89.82f },
|
||||
// MZ2_WIDOW2_BEAMER_3 197
|
||||
// 71.47f, -18.40f, 90.70f,
|
||||
{ 69.00f, -18.40f, 90.70f },
|
||||
// MZ2_WIDOW2_BEAMER_4 198
|
||||
// 71.96f, -18.34f, 94.32f,
|
||||
{ 69.00f, -18.34f, 94.32f },
|
||||
// MZ2_WIDOW2_BEAMER_5 199
|
||||
// 72.25f, -18.30f, 97.98f,
|
||||
{ 69.00f, -18.30f, 97.98f },
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_1 200
|
||||
{ 45.04f, -59.02f, 92.24f },
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_2 201
|
||||
{ 50.68f, -54.70f, 91.96f },
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_3 202
|
||||
{ 56.57f, -47.72f, 91.65f },
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_4 203
|
||||
{ 61.75f, -38.75f, 91.38f },
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_5 204
|
||||
{ 65.55f, -28.76f, 91.24f },
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_6 205
|
||||
{ 67.79f, -18.90f, 91.22f },
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_7 206
|
||||
{ 68.60f, -9.52f, 91.23f },
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_8 207
|
||||
{ 68.08f, 0.18f, 91.32f },
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_9 208
|
||||
{ 66.14f, 9.79f, 91.44f },
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_10 209
|
||||
{ 62.77f, 18.91f, 91.65f },
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_11 210
|
||||
{ 58.29f, 27.11f, 92.00f },
|
||||
|
||||
// MZ2_SOLDIER_RIPPER_1 211
|
||||
{ 10.6f * 1.2f, 7.7f * 1.2f, 7.8f * 1.2f },
|
||||
// MZ2_SOLDIER_RIPPER_2 212
|
||||
{ 25.1f * 1.2f, 3.6f * 1.2f, 19.0f * 1.2f },
|
||||
// MZ2_SOLDIER_RIPPER_3 213
|
||||
{ 20.8f * 1.2f, 10.1f * 1.2f, -2.7f * 1.2f },
|
||||
// MZ2_SOLDIER_RIPPER_4 214
|
||||
{ 7.6f * 1.2f, 9.3f * 1.2f, 0.8f * 1.2f },
|
||||
// MZ2_SOLDIER_RIPPER_5 215
|
||||
{ 30.5f * 1.2f, 9.9f * 1.2f, -18.7f * 1.2f },
|
||||
// MZ2_SOLDIER_RIPPER_6 216
|
||||
{ 27.6f * 1.2f, 3.4f * 1.2f, -10.4f * 1.2f },
|
||||
// MZ2_SOLDIER_RIPPER_7 217
|
||||
{ 28.9f * 1.2f, 4.6f * 1.2f, -8.1f * 1.2f },
|
||||
// MZ2_SOLDIER_RIPPER_8 218
|
||||
{ 31.5f * 1.2f, 9.6f * 1.2f, 10.1f * 1.2f },
|
||||
|
||||
// MZ2_SOLDIER_HYPERGUN_1 219
|
||||
{ 10.6f * 1.2f, 7.7f * 1.2f, 7.8f * 1.2f },
|
||||
// MZ2_SOLDIER_HYPERGUN_2 220
|
||||
{ 25.1f * 1.2f, 3.6f * 1.2f, 19.0f * 1.2f },
|
||||
// MZ2_SOLDIER_HYPERGUN_3 221
|
||||
{ 20.8f * 1.2f, 10.1f * 1.2f, -2.7f * 1.2f },
|
||||
// MZ2_SOLDIER_HYPERGUN_4 222
|
||||
{ 7.6f * 1.2f, 9.3f * 1.2f, 0.8f * 1.2f },
|
||||
// MZ2_SOLDIER_HYPERGUN_5 223
|
||||
{ 30.5f * 1.2f, 9.9f * 1.2f, -18.7f * 1.2f },
|
||||
// MZ2_SOLDIER_HYPERGUN_6 224
|
||||
{ 27.6f * 1.2f, 3.4f * 1.2f, -10.4f * 1.2f },
|
||||
// MZ2_SOLDIER_HYPERGUN_7 225
|
||||
{ 28.9f * 1.2f, 4.6f * 1.2f, -8.1f * 1.2f },
|
||||
// MZ2_SOLDIER_HYPERGUN_8 226
|
||||
{ 31.5f * 1.2f, 9.6f * 1.2f, 10.1f * 1.2f },
|
||||
|
||||
// MZ2_GUARDIAN_BLASTER 227
|
||||
{ 88.f, 50.f, 60.f },
|
||||
|
||||
// MZ2_ARACHNID_RAIL1 228
|
||||
{ 58.f, 20.f, 17.2f },
|
||||
// MZ2_ARACHNID_RAIL2 229
|
||||
{ 64.f, -22.f, 24.f },
|
||||
// MZ2_ARACHNID_RAIL_UP1 230
|
||||
{ 37.f, 13.f, 72.f },
|
||||
// MZ2_ARACHNID_RAIL_UP2 231
|
||||
{ 58.f, -25.f, 72.f },
|
||||
|
||||
// MZ2_INFANTRY_MACHINEGUN_14 232
|
||||
{ 34.f, 11.f, 13.f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_15 233
|
||||
{ 28.f, 13.f, 10.5f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_16 234
|
||||
{ 29.f, 13.f, 8.5f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_17 235
|
||||
{ 30.f, 12.5f, 12.f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_18 236
|
||||
{ 29.f, 12.5f, 14.7f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_19 237
|
||||
{ 30.f, 6.5f, 12.f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_20 238
|
||||
{ 29.f, 1.5f, 8.5f },
|
||||
// MZ2_INFANTRY_MACHINEGUN_21 239
|
||||
{ 29.f, 6.0f, 10.f },
|
||||
|
||||
// MZ2_GUNCMDR_CHAINGUN_1 240
|
||||
{ 25.0f, 11.f, 21.f },
|
||||
// MZ2_GUNCMDR_CHAINGUN_2 241
|
||||
{ 26.5f, 5.f, 21.f },
|
||||
|
||||
// MZ2_GUNCMDR_GRENADE_MORTAR_1 242
|
||||
{ 27.f, 6.5f, 4.0f },
|
||||
// MZ2_GUNCMDR_GRENADE_MORTAR_2 243
|
||||
{ 28.f, 4.f, 4.0f },
|
||||
// MZ2_GUNCMDR_GRENADE_MORTAR_3 244
|
||||
{ 27.f, 1.7f, 4.0f },
|
||||
|
||||
// MZ2_GUNCMDR_GRENADE_FRONT_1 245
|
||||
{ 21.7f, -1.5f, 22.5f },
|
||||
// MZ2_GUNCMDR_GRENADE_FRONT_2 246
|
||||
{ 22.f, 0.f, 20.5f },
|
||||
// MZ2_GUNCMDR_GRENADE_FRONT_3 247
|
||||
{ 22.5f, 3.7f, 20.5f },
|
||||
|
||||
// MZ2_GUNCMDR_GRENADE_CROUCH_1 248
|
||||
{ 8.0f, 40.0f, 18.0f },
|
||||
// MZ2_GUNCMDR_GRENADE_CROUCH_2 249
|
||||
{ 29.0f, 16.0f, 19.0f },
|
||||
// MZ2_GUNCMDR_GRENADE_CROUCH_3 250
|
||||
{ 4.7f, -30.0f, 20.0f },
|
||||
|
||||
// MZ2_SOLDIER_BLASTER_9 251
|
||||
{ 36.33f, 12.24f, -17.39f },
|
||||
// MZ2_SOLDIER_SHOTGUN_9 252
|
||||
{ 36.33f, 12.24f, -17.39f },
|
||||
// MZ2_SOLDIER_MACHINEGUN_9 253
|
||||
{ 36.33f, 12.24f, -17.39f },
|
||||
// MZ2_SOLDIER_RIPPER_9 254
|
||||
{ 36.33f, 12.24f, -17.39f },
|
||||
// MZ2_SOLDIER_HYPERGUN_9 255
|
||||
{ 36.33f, 12.24f, -17.39f },
|
||||
|
||||
// MZ2_GUNNER_GRENADE2_1
|
||||
{ 36.f, -6.2f, 19.59f },
|
||||
// MZ2_GUNNER_GRENADE2_2
|
||||
{ 36.f, -6.2f, 19.59f },
|
||||
// MZ2_GUNNER_GRENADE2_3
|
||||
{ 36.f, -6.2f, 19.59f },
|
||||
// MZ2_GUNNER_GRENADE2_4
|
||||
{ 36.f, -6.2f, 19.59f },
|
||||
|
||||
// MZ2_INFANTRY_MACHINEGUN_22
|
||||
{ 14.8f, 10.5f, 8.82f },
|
||||
|
||||
// MZ2_SUPERTANK_GRENADE_1
|
||||
{ 31.31f, -37.f, 54.32f },
|
||||
// MZ2_SUPERTANK_GRENADE_2
|
||||
{ 31.31f, 37.f, 54.32f },
|
||||
|
||||
// MZ2_HOVER_BLASTER_2
|
||||
{ 1.7f, -7.0f, 11.3f },
|
||||
// MZ2_DAEDALUS_BLASTER_2
|
||||
{ 1.7f, -7.0f, 11.3f },
|
||||
|
||||
// MZ2_MEDIC_HYPERBLASTER1_1-12
|
||||
{ 33.0f + 1.f, 12.5f, 15.0f },
|
||||
{ 32.4f + 1.f, 11.2f, 15.0f },
|
||||
{ 35.6f + 1.f, 7.4f, 15.0f },
|
||||
{ 34.0f + 1.f, 4.1f, 15.0f },
|
||||
{ 36.6f + 1.f, 1.0f, 15.0f },
|
||||
{ 34.7f + 1.f, -1.9f, 15.0f },
|
||||
{ 36.6f + 1.f, -0.5f, 15.0f },
|
||||
{ 34.2f + 1.f, 2.8f, 15.0f },
|
||||
{ 36.5f + 1.f, 3.8f, 15.0f },
|
||||
{ 33.5f + 1.f, 6.9f, 15.0f },
|
||||
{ 32.7f + 1.f, 9.9f, 15.0f },
|
||||
{ 34.5f + 1.f, 11.0f, 15.0f },
|
||||
|
||||
// MZ2_MEDIC_HYPERBLASTER2_1-12
|
||||
{ 33.0f + 1.f, 12.5f, 15.0f },
|
||||
{ 32.4f + 1.f, 11.2f, 15.0f },
|
||||
{ 35.6f + 1.f, 7.4f, 15.0f },
|
||||
{ 34.0f + 1.f, 4.1f, 15.0f },
|
||||
{ 36.6f + 1.f, 1.0f, 15.0f },
|
||||
{ 34.7f + 1.f, -1.9f, 15.0f },
|
||||
{ 36.6f + 1.f, -0.5f, 15.0f },
|
||||
{ 34.2f + 1.f, 2.8f, 15.0f },
|
||||
{ 36.5f + 1.f, 3.8f, 15.0f },
|
||||
{ 33.5f + 1.f, 6.9f, 15.0f },
|
||||
{ 32.7f + 1.f, 9.9f, 15.0f },
|
||||
{ 34.5f + 1.f, 11.0f, 15.0f },
|
||||
|
||||
// end of table
|
||||
{ 0.0f, 0.0f, 0.0f }
|
||||
};
|
||||
|
||||
static_assert(q_countof(monster_flash_offset) - 1 == MZ2_LAST);
|
||||
384
rerelease/m_flipper.cpp
Normal file
384
rerelease/m_flipper.cpp
Normal file
@@ -0,0 +1,384 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
FLIPPER
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_flipper.h"
|
||||
|
||||
static int sound_chomp;
|
||||
static int sound_attack;
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_death;
|
||||
static int sound_idle;
|
||||
static int sound_search;
|
||||
static int sound_sight;
|
||||
|
||||
mframe_t flipper_frames_stand[] = {
|
||||
{ ai_stand }
|
||||
};
|
||||
|
||||
MMOVE_T(flipper_move_stand) = { FRAME_flphor01, FRAME_flphor01, flipper_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(flipper_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &flipper_move_stand);
|
||||
}
|
||||
|
||||
constexpr float FLIPPER_RUN_SPEED = 24;
|
||||
|
||||
mframe_t flipper_frames_run[] = {
|
||||
{ ai_run, FLIPPER_RUN_SPEED }, // 6
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED }, // 10
|
||||
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED }, // 20
|
||||
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED },
|
||||
{ ai_run, FLIPPER_RUN_SPEED } // 29
|
||||
};
|
||||
MMOVE_T(flipper_move_run_loop) = { FRAME_flpver06, FRAME_flpver29, flipper_frames_run, nullptr };
|
||||
|
||||
void flipper_run_loop(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &flipper_move_run_loop);
|
||||
}
|
||||
|
||||
mframe_t flipper_frames_run_start[] = {
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 }
|
||||
};
|
||||
MMOVE_T(flipper_move_run_start) = { FRAME_flpver01, FRAME_flpver06, flipper_frames_run_start, flipper_run_loop };
|
||||
|
||||
void flipper_run(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &flipper_move_run_start);
|
||||
}
|
||||
|
||||
/* Standard Swimming */
|
||||
mframe_t flipper_frames_walk[] = {
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 }
|
||||
};
|
||||
MMOVE_T(flipper_move_walk) = { FRAME_flphor01, FRAME_flphor24, flipper_frames_walk, nullptr };
|
||||
|
||||
MONSTERINFO_WALK(flipper_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &flipper_move_walk);
|
||||
}
|
||||
|
||||
mframe_t flipper_frames_start_run[] = {
|
||||
{ ai_run },
|
||||
{ ai_run },
|
||||
{ ai_run },
|
||||
{ ai_run },
|
||||
{ ai_run, 8, flipper_run }
|
||||
};
|
||||
MMOVE_T(flipper_move_start_run) = { FRAME_flphor01, FRAME_flphor05, flipper_frames_start_run, nullptr };
|
||||
|
||||
MONSTERINFO_RUN(flipper_start_run) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &flipper_move_start_run);
|
||||
}
|
||||
|
||||
mframe_t flipper_frames_pain2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(flipper_move_pain2) = { FRAME_flppn101, FRAME_flppn105, flipper_frames_pain2, flipper_run };
|
||||
|
||||
mframe_t flipper_frames_pain1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(flipper_move_pain1) = { FRAME_flppn201, FRAME_flppn205, flipper_frames_pain1, flipper_run };
|
||||
|
||||
void flipper_bite(edict_t *self)
|
||||
{
|
||||
vec3_t aim = { MELEE_DISTANCE, 0, 0 };
|
||||
fire_hit(self, aim, 5, 0);
|
||||
}
|
||||
|
||||
void flipper_preattack(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_chomp, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t flipper_frames_attack[] = {
|
||||
{ ai_charge, 0, flipper_preattack },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, flipper_bite },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, flipper_bite },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(flipper_move_attack) = { FRAME_flpbit01, FRAME_flpbit20, flipper_frames_attack, flipper_run };
|
||||
|
||||
MONSTERINFO_MELEE(flipper_melee) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &flipper_move_attack);
|
||||
}
|
||||
|
||||
PAIN(flipper_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
int n;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
n = brandom();
|
||||
|
||||
if (n == 0)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (n == 0)
|
||||
M_SetAnimation(self, &flipper_move_pain1);
|
||||
else
|
||||
M_SetAnimation(self, &flipper_move_pain2);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(flipper_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
void flipper_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -16, -16, -8 };
|
||||
self->maxs = { 16, 16, 8 };
|
||||
monster_dead(self);
|
||||
}
|
||||
|
||||
mframe_t flipper_frames_death[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(flipper_move_death) = { FRAME_flpdth01, FRAME_flpdth56, flipper_frames_death, flipper_dead };
|
||||
|
||||
MONSTERINFO_SIGHT(flipper_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
DIE(flipper_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
// check for gib
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
ThrowGibs(self, damage, {
|
||||
{ 2, "models/objects/gibs/bone/tris.md2" },
|
||||
{ 2, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ "models/objects/gibs/head2/tris.md2", GIB_HEAD }
|
||||
});
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
M_SetAnimation(self, &flipper_move_death);
|
||||
}
|
||||
|
||||
static void flipper_set_fly_parameters(edict_t *self)
|
||||
{
|
||||
self->monsterinfo.fly_thrusters = false;
|
||||
self->monsterinfo.fly_acceleration = 30.f;
|
||||
self->monsterinfo.fly_speed = 110.f;
|
||||
// only melee, so get in close
|
||||
self->monsterinfo.fly_min_distance = 10.f;
|
||||
self->monsterinfo.fly_max_distance = 10.f;
|
||||
}
|
||||
|
||||
/*QUAKED monster_flipper (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_flipper(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_pain1 = gi.soundindex("flipper/flppain1.wav");
|
||||
sound_pain2 = gi.soundindex("flipper/flppain2.wav");
|
||||
sound_death = gi.soundindex("flipper/flpdeth1.wav");
|
||||
sound_chomp = gi.soundindex("flipper/flpatck1.wav");
|
||||
sound_attack = gi.soundindex("flipper/flpatck2.wav");
|
||||
sound_idle = gi.soundindex("flipper/flpidle1.wav");
|
||||
sound_search = gi.soundindex("flipper/flpsrch1.wav");
|
||||
sound_sight = gi.soundindex("flipper/flpsght1.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/flipper/tris.md2");
|
||||
self->mins = { -16, -16, -8 };
|
||||
self->maxs = { 16, 16, 20 };
|
||||
|
||||
self->health = 50 * st.health_multiplier;
|
||||
self->gib_health = -30;
|
||||
self->mass = 100;
|
||||
|
||||
self->pain = flipper_pain;
|
||||
self->die = flipper_die;
|
||||
|
||||
self->monsterinfo.stand = flipper_stand;
|
||||
self->monsterinfo.walk = flipper_walk;
|
||||
self->monsterinfo.run = flipper_start_run;
|
||||
self->monsterinfo.melee = flipper_melee;
|
||||
self->monsterinfo.sight = flipper_sight;
|
||||
self->monsterinfo.setskin = flipper_setskin;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &flipper_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
self->monsterinfo.aiflags |= AI_ALTERNATE_FLY;
|
||||
flipper_set_fly_parameters(self);
|
||||
|
||||
swimmonster_start(self);
|
||||
}
|
||||
171
rerelease/m_flipper.h
Normal file
171
rerelease/m_flipper.h
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/flipper
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_flpbit01,
|
||||
FRAME_flpbit02,
|
||||
FRAME_flpbit03,
|
||||
FRAME_flpbit04,
|
||||
FRAME_flpbit05,
|
||||
FRAME_flpbit06,
|
||||
FRAME_flpbit07,
|
||||
FRAME_flpbit08,
|
||||
FRAME_flpbit09,
|
||||
FRAME_flpbit10,
|
||||
FRAME_flpbit11,
|
||||
FRAME_flpbit12,
|
||||
FRAME_flpbit13,
|
||||
FRAME_flpbit14,
|
||||
FRAME_flpbit15,
|
||||
FRAME_flpbit16,
|
||||
FRAME_flpbit17,
|
||||
FRAME_flpbit18,
|
||||
FRAME_flpbit19,
|
||||
FRAME_flpbit20,
|
||||
FRAME_flptal01,
|
||||
FRAME_flptal02,
|
||||
FRAME_flptal03,
|
||||
FRAME_flptal04,
|
||||
FRAME_flptal05,
|
||||
FRAME_flptal06,
|
||||
FRAME_flptal07,
|
||||
FRAME_flptal08,
|
||||
FRAME_flptal09,
|
||||
FRAME_flptal10,
|
||||
FRAME_flptal11,
|
||||
FRAME_flptal12,
|
||||
FRAME_flptal13,
|
||||
FRAME_flptal14,
|
||||
FRAME_flptal15,
|
||||
FRAME_flptal16,
|
||||
FRAME_flptal17,
|
||||
FRAME_flptal18,
|
||||
FRAME_flptal19,
|
||||
FRAME_flptal20,
|
||||
FRAME_flptal21,
|
||||
FRAME_flphor01,
|
||||
FRAME_flphor02,
|
||||
FRAME_flphor03,
|
||||
FRAME_flphor04,
|
||||
FRAME_flphor05,
|
||||
FRAME_flphor06,
|
||||
FRAME_flphor07,
|
||||
FRAME_flphor08,
|
||||
FRAME_flphor09,
|
||||
FRAME_flphor10,
|
||||
FRAME_flphor11,
|
||||
FRAME_flphor12,
|
||||
FRAME_flphor13,
|
||||
FRAME_flphor14,
|
||||
FRAME_flphor15,
|
||||
FRAME_flphor16,
|
||||
FRAME_flphor17,
|
||||
FRAME_flphor18,
|
||||
FRAME_flphor19,
|
||||
FRAME_flphor20,
|
||||
FRAME_flphor21,
|
||||
FRAME_flphor22,
|
||||
FRAME_flphor23,
|
||||
FRAME_flphor24,
|
||||
FRAME_flpver01,
|
||||
FRAME_flpver02,
|
||||
FRAME_flpver03,
|
||||
FRAME_flpver04,
|
||||
FRAME_flpver05,
|
||||
FRAME_flpver06,
|
||||
FRAME_flpver07,
|
||||
FRAME_flpver08,
|
||||
FRAME_flpver09,
|
||||
FRAME_flpver10,
|
||||
FRAME_flpver11,
|
||||
FRAME_flpver12,
|
||||
FRAME_flpver13,
|
||||
FRAME_flpver14,
|
||||
FRAME_flpver15,
|
||||
FRAME_flpver16,
|
||||
FRAME_flpver17,
|
||||
FRAME_flpver18,
|
||||
FRAME_flpver19,
|
||||
FRAME_flpver20,
|
||||
FRAME_flpver21,
|
||||
FRAME_flpver22,
|
||||
FRAME_flpver23,
|
||||
FRAME_flpver24,
|
||||
FRAME_flpver25,
|
||||
FRAME_flpver26,
|
||||
FRAME_flpver27,
|
||||
FRAME_flpver28,
|
||||
FRAME_flpver29,
|
||||
FRAME_flppn101,
|
||||
FRAME_flppn102,
|
||||
FRAME_flppn103,
|
||||
FRAME_flppn104,
|
||||
FRAME_flppn105,
|
||||
FRAME_flppn201,
|
||||
FRAME_flppn202,
|
||||
FRAME_flppn203,
|
||||
FRAME_flppn204,
|
||||
FRAME_flppn205,
|
||||
FRAME_flpdth01,
|
||||
FRAME_flpdth02,
|
||||
FRAME_flpdth03,
|
||||
FRAME_flpdth04,
|
||||
FRAME_flpdth05,
|
||||
FRAME_flpdth06,
|
||||
FRAME_flpdth07,
|
||||
FRAME_flpdth08,
|
||||
FRAME_flpdth09,
|
||||
FRAME_flpdth10,
|
||||
FRAME_flpdth11,
|
||||
FRAME_flpdth12,
|
||||
FRAME_flpdth13,
|
||||
FRAME_flpdth14,
|
||||
FRAME_flpdth15,
|
||||
FRAME_flpdth16,
|
||||
FRAME_flpdth17,
|
||||
FRAME_flpdth18,
|
||||
FRAME_flpdth19,
|
||||
FRAME_flpdth20,
|
||||
FRAME_flpdth21,
|
||||
FRAME_flpdth22,
|
||||
FRAME_flpdth23,
|
||||
FRAME_flpdth24,
|
||||
FRAME_flpdth25,
|
||||
FRAME_flpdth26,
|
||||
FRAME_flpdth27,
|
||||
FRAME_flpdth28,
|
||||
FRAME_flpdth29,
|
||||
FRAME_flpdth30,
|
||||
FRAME_flpdth31,
|
||||
FRAME_flpdth32,
|
||||
FRAME_flpdth33,
|
||||
FRAME_flpdth34,
|
||||
FRAME_flpdth35,
|
||||
FRAME_flpdth36,
|
||||
FRAME_flpdth37,
|
||||
FRAME_flpdth38,
|
||||
FRAME_flpdth39,
|
||||
FRAME_flpdth40,
|
||||
FRAME_flpdth41,
|
||||
FRAME_flpdth42,
|
||||
FRAME_flpdth43,
|
||||
FRAME_flpdth44,
|
||||
FRAME_flpdth45,
|
||||
FRAME_flpdth46,
|
||||
FRAME_flpdth47,
|
||||
FRAME_flpdth48,
|
||||
FRAME_flpdth49,
|
||||
FRAME_flpdth50,
|
||||
FRAME_flpdth51,
|
||||
FRAME_flpdth52,
|
||||
FRAME_flpdth53,
|
||||
FRAME_flpdth54,
|
||||
FRAME_flpdth55,
|
||||
FRAME_flpdth56
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
716
rerelease/m_float.cpp
Normal file
716
rerelease/m_float.cpp
Normal file
@@ -0,0 +1,716 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
floater
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_float.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
static int sound_attack2;
|
||||
static int sound_attack3;
|
||||
static int sound_death1;
|
||||
static int sound_idle;
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_sight;
|
||||
|
||||
MONSTERINFO_SIGHT(floater_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_IDLE(floater_idle) (edict_t *self) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
void floater_dead(edict_t *self);
|
||||
void floater_run(edict_t *self);
|
||||
void floater_wham(edict_t *self);
|
||||
void floater_zap(edict_t *self);
|
||||
|
||||
void floater_fire_blaster(edict_t *self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
vec3_t end;
|
||||
vec3_t dir;
|
||||
|
||||
if (!self->enemy || !self->enemy->inuse) // PGM
|
||||
return; // PGM
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_FLOAT_BLASTER_1], forward, right);
|
||||
|
||||
end = self->enemy->s.origin;
|
||||
end[2] += self->enemy->viewheight;
|
||||
dir = end - start;
|
||||
dir.normalize();
|
||||
|
||||
monster_fire_blaster(self, start, dir, 1, 1000, MZ2_FLOAT_BLASTER_1, (self->s.frame % 4) ? EF_NONE : EF_HYPERBLASTER);
|
||||
}
|
||||
|
||||
mframe_t floater_frames_stand1[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(floater_move_stand1) = { FRAME_stand101, FRAME_stand152, floater_frames_stand1, nullptr };
|
||||
|
||||
mframe_t floater_frames_stand2[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(floater_move_stand2) = { FRAME_stand201, FRAME_stand252, floater_frames_stand2, nullptr };
|
||||
|
||||
mframe_t floater_frames_pop[] = {
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
};
|
||||
MMOVE_T(floater_move_pop) = { FRAME_actvat05, FRAME_actvat31, floater_frames_pop, floater_run };
|
||||
|
||||
mframe_t floater_frames_disguise[] = {
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(floater_move_disguise) = { FRAME_actvat01, FRAME_actvat01, floater_frames_disguise, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(floater_stand) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.active_move == &floater_move_disguise)
|
||||
M_SetAnimation(self, &floater_move_disguise);
|
||||
else if (frandom() <= 0.5f)
|
||||
M_SetAnimation(self, &floater_move_stand1);
|
||||
else
|
||||
M_SetAnimation(self, &floater_move_stand2);
|
||||
}
|
||||
|
||||
mframe_t floater_frames_attack1[] = {
|
||||
{ ai_charge }, // Blaster attack
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, floater_fire_blaster }, // BOOM (0, -25.8, 32.5) -- LOOP Starts
|
||||
{ ai_charge, 0, floater_fire_blaster },
|
||||
{ ai_charge, 0, floater_fire_blaster },
|
||||
{ ai_charge, 0, floater_fire_blaster },
|
||||
{ ai_charge, 0, floater_fire_blaster },
|
||||
{ ai_charge, 0, floater_fire_blaster },
|
||||
{ ai_charge, 0, floater_fire_blaster },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge } // -- LOOP Ends
|
||||
};
|
||||
MMOVE_T(floater_move_attack1) = { FRAME_attak101, FRAME_attak114, floater_frames_attack1, floater_run };
|
||||
|
||||
// PMM - circle strafe frames
|
||||
mframe_t floater_frames_attack1a[] = {
|
||||
{ ai_charge, 10 }, // Blaster attack
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10, floater_fire_blaster }, // BOOM (0, -25.8, 32.5) -- LOOP Starts
|
||||
{ ai_charge, 10, floater_fire_blaster },
|
||||
{ ai_charge, 10, floater_fire_blaster },
|
||||
{ ai_charge, 10, floater_fire_blaster },
|
||||
{ ai_charge, 10, floater_fire_blaster },
|
||||
{ ai_charge, 10, floater_fire_blaster },
|
||||
{ ai_charge, 10, floater_fire_blaster },
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10 } // -- LOOP Ends
|
||||
};
|
||||
MMOVE_T(floater_move_attack1a) = { FRAME_attak101, FRAME_attak114, floater_frames_attack1a, floater_run };
|
||||
// pmm
|
||||
|
||||
mframe_t floater_frames_attack2[] = {
|
||||
{ ai_charge }, // Claws
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, floater_wham }, // WHAM (0, -45, 29.6) -- LOOP Starts
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }, // -- LOOP Ends
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(floater_move_attack2) = { FRAME_attak201, FRAME_attak225, floater_frames_attack2, floater_run };
|
||||
|
||||
mframe_t floater_frames_attack3[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, floater_zap }, // -- LOOP Starts
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }, // -- LOOP Ends
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(floater_move_attack3) = { FRAME_attak301, FRAME_attak334, floater_frames_attack3, floater_run };
|
||||
|
||||
#if 0
|
||||
mframe_t floater_frames_death[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(floater_move_death) = { FRAME_death01, FRAME_death13, floater_frames_death, floater_dead };
|
||||
#endif
|
||||
|
||||
mframe_t floater_frames_pain1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(floater_move_pain1) = { FRAME_pain101, FRAME_pain107, floater_frames_pain1, floater_run };
|
||||
|
||||
mframe_t floater_frames_pain2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(floater_move_pain2) = { FRAME_pain201, FRAME_pain208, floater_frames_pain2, floater_run };
|
||||
|
||||
#if 0
|
||||
mframe_t floater_frames_pain3[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(floater_move_pain3) = { FRAME_pain301, FRAME_pain312, floater_frames_pain3, floater_run };
|
||||
#endif
|
||||
|
||||
mframe_t floater_frames_walk[] = {
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 }
|
||||
};
|
||||
MMOVE_T(floater_move_walk) = { FRAME_stand101, FRAME_stand152, floater_frames_walk, nullptr };
|
||||
|
||||
mframe_t floater_frames_run[] = {
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 13 }
|
||||
};
|
||||
MMOVE_T(floater_move_run) = { FRAME_stand101, FRAME_stand152, floater_frames_run, nullptr };
|
||||
|
||||
MONSTERINFO_RUN(floater_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.active_move == &floater_move_disguise)
|
||||
M_SetAnimation(self, &floater_move_pop);
|
||||
else if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &floater_move_stand1);
|
||||
else
|
||||
M_SetAnimation(self, &floater_move_run);
|
||||
}
|
||||
|
||||
MONSTERINFO_WALK(floater_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &floater_move_walk);
|
||||
}
|
||||
|
||||
void floater_wham(edict_t *self)
|
||||
{
|
||||
constexpr vec3_t aim = { MELEE_DISTANCE, 0, 0 };
|
||||
gi.sound(self, CHAN_WEAPON, sound_attack3, 1, ATTN_NORM, 0);
|
||||
|
||||
if (!fire_hit(self, aim, irandom(5, 11), -50))
|
||||
self->monsterinfo.melee_debounce_time = level.time + 3_sec;
|
||||
}
|
||||
|
||||
void floater_zap(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t origin;
|
||||
vec3_t dir;
|
||||
vec3_t offset;
|
||||
|
||||
dir = self->enemy->s.origin - self->s.origin;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
// FIXME use a flash and replace these two lines with the commented one
|
||||
offset = { 18.5f, -0.9f, 10 };
|
||||
origin = M_ProjectFlashSource(self, offset, forward, right);
|
||||
|
||||
gi.sound(self, CHAN_WEAPON, sound_attack2, 1, ATTN_NORM, 0);
|
||||
|
||||
// FIXME use the flash, Luke
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_SPLASH);
|
||||
gi.WriteByte(32);
|
||||
gi.WritePosition(origin);
|
||||
gi.WriteDir(dir);
|
||||
gi.WriteByte(SPLASH_SPARKS);
|
||||
gi.multicast(origin, MULTICAST_PVS, false);
|
||||
|
||||
T_Damage(self->enemy, self, self, dir, self->enemy->s.origin, vec3_origin, irandom(5, 11), -10, DAMAGE_ENERGY, MOD_UNKNOWN);
|
||||
}
|
||||
|
||||
MONSTERINFO_ATTACK(floater_attack) (edict_t *self) -> void
|
||||
{
|
||||
float chance = 0.5f;
|
||||
|
||||
if (frandom() > chance)
|
||||
{
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
M_SetAnimation(self, &floater_move_attack1);
|
||||
}
|
||||
else // circle strafe
|
||||
{
|
||||
if (frandom() <= 0.5f) // switch directions
|
||||
self->monsterinfo.lefty = !self->monsterinfo.lefty;
|
||||
self->monsterinfo.attack_state = AS_SLIDING;
|
||||
M_SetAnimation(self, &floater_move_attack1a);
|
||||
}
|
||||
}
|
||||
|
||||
MONSTERINFO_MELEE(floater_melee) (edict_t *self) -> void
|
||||
{
|
||||
if (frandom() < 0.5f)
|
||||
M_SetAnimation(self, &floater_move_attack3);
|
||||
else
|
||||
M_SetAnimation(self, &floater_move_attack2);
|
||||
}
|
||||
|
||||
PAIN(floater_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
int n;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
// no pain anims if poppin'
|
||||
if (self->monsterinfo.active_move == &floater_move_disguise ||
|
||||
self->monsterinfo.active_move == &floater_move_pop)
|
||||
return;
|
||||
|
||||
n = irandom(3);
|
||||
if (n == 0)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (n == 0)
|
||||
M_SetAnimation(self, &floater_move_pain1);
|
||||
else
|
||||
M_SetAnimation(self, &floater_move_pain2);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(floater_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
void floater_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, -8 };
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0_ms;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
DIE(floater_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_death1, 1, ATTN_NORM, 0);
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.multicast(self->s.origin, MULTICAST_PHS, false);
|
||||
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
ThrowGibs(self, 55, {
|
||||
{ 2, "models/objects/gibs/sm_metal/tris.md2" },
|
||||
{ 3, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ "models/monsters/float/gibs/piece.md2", GIB_SKINNED },
|
||||
{ "models/monsters/float/gibs/gun.md2", GIB_SKINNED },
|
||||
{ "models/monsters/float/gibs/base.md2", GIB_SKINNED },
|
||||
{ "models/monsters/float/gibs/jar.md2", GIB_SKINNED | GIB_HEAD }
|
||||
});
|
||||
}
|
||||
|
||||
static void float_set_fly_parameters(edict_t *self)
|
||||
{
|
||||
self->monsterinfo.fly_thrusters = false;
|
||||
self->monsterinfo.fly_acceleration = 10.f;
|
||||
self->monsterinfo.fly_speed = 100.f;
|
||||
// Technician gets in closer because he has two melee attacks
|
||||
self->monsterinfo.fly_min_distance = 20.f;
|
||||
self->monsterinfo.fly_max_distance = 200.f;
|
||||
}
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_FLOATER_DISGUISE = 8_spawnflag;
|
||||
|
||||
/*QUAKED monster_floater (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight Disguise
|
||||
*/
|
||||
void SP_monster_floater(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_attack2 = gi.soundindex("floater/fltatck2.wav");
|
||||
sound_attack3 = gi.soundindex("floater/fltatck3.wav");
|
||||
sound_death1 = gi.soundindex("floater/fltdeth1.wav");
|
||||
sound_idle = gi.soundindex("floater/fltidle1.wav");
|
||||
sound_pain1 = gi.soundindex("floater/fltpain1.wav");
|
||||
sound_pain2 = gi.soundindex("floater/fltpain2.wav");
|
||||
sound_sight = gi.soundindex("floater/fltsght1.wav");
|
||||
|
||||
gi.soundindex("floater/fltatck1.wav");
|
||||
|
||||
self->monsterinfo.engine_sound = gi.soundindex("floater/fltsrch1.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/float/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/float/gibs/base.md2");
|
||||
gi.modelindex("models/monsters/float/gibs/gun.md2");
|
||||
gi.modelindex("models/monsters/float/gibs/jar.md2");
|
||||
gi.modelindex("models/monsters/float/gibs/piece.md2");
|
||||
|
||||
self->mins = { -24, -24, -24 };
|
||||
self->maxs = { 24, 24, 48 };
|
||||
|
||||
self->health = 200 * st.health_multiplier;
|
||||
self->gib_health = -80;
|
||||
self->mass = 300;
|
||||
|
||||
self->pain = floater_pain;
|
||||
self->die = floater_die;
|
||||
|
||||
self->monsterinfo.stand = floater_stand;
|
||||
self->monsterinfo.walk = floater_walk;
|
||||
self->monsterinfo.run = floater_run;
|
||||
self->monsterinfo.attack = floater_attack;
|
||||
self->monsterinfo.melee = floater_melee;
|
||||
self->monsterinfo.sight = floater_sight;
|
||||
self->monsterinfo.idle = floater_idle;
|
||||
self->monsterinfo.setskin = floater_setskin;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
if (self->spawnflags.has(SPAWNFLAG_FLOATER_DISGUISE))
|
||||
M_SetAnimation(self, &floater_move_disguise);
|
||||
else if (frandom() <= 0.5f)
|
||||
M_SetAnimation(self, &floater_move_stand1);
|
||||
else
|
||||
M_SetAnimation(self, &floater_move_stand2);
|
||||
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
self->monsterinfo.aiflags |= AI_ALTERNATE_FLY;
|
||||
float_set_fly_parameters(self);
|
||||
|
||||
flymonster_start(self);
|
||||
}
|
||||
259
rerelease/m_float.h
Normal file
259
rerelease/m_float.h
Normal file
@@ -0,0 +1,259 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/float
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_actvat01,
|
||||
FRAME_actvat02,
|
||||
FRAME_actvat03,
|
||||
FRAME_actvat04,
|
||||
FRAME_actvat05,
|
||||
FRAME_actvat06,
|
||||
FRAME_actvat07,
|
||||
FRAME_actvat08,
|
||||
FRAME_actvat09,
|
||||
FRAME_actvat10,
|
||||
FRAME_actvat11,
|
||||
FRAME_actvat12,
|
||||
FRAME_actvat13,
|
||||
FRAME_actvat14,
|
||||
FRAME_actvat15,
|
||||
FRAME_actvat16,
|
||||
FRAME_actvat17,
|
||||
FRAME_actvat18,
|
||||
FRAME_actvat19,
|
||||
FRAME_actvat20,
|
||||
FRAME_actvat21,
|
||||
FRAME_actvat22,
|
||||
FRAME_actvat23,
|
||||
FRAME_actvat24,
|
||||
FRAME_actvat25,
|
||||
FRAME_actvat26,
|
||||
FRAME_actvat27,
|
||||
FRAME_actvat28,
|
||||
FRAME_actvat29,
|
||||
FRAME_actvat30,
|
||||
FRAME_actvat31,
|
||||
FRAME_attak101,
|
||||
FRAME_attak102,
|
||||
FRAME_attak103,
|
||||
FRAME_attak104,
|
||||
FRAME_attak105,
|
||||
FRAME_attak106,
|
||||
FRAME_attak107,
|
||||
FRAME_attak108,
|
||||
FRAME_attak109,
|
||||
FRAME_attak110,
|
||||
FRAME_attak111,
|
||||
FRAME_attak112,
|
||||
FRAME_attak113,
|
||||
FRAME_attak114,
|
||||
FRAME_attak201,
|
||||
FRAME_attak202,
|
||||
FRAME_attak203,
|
||||
FRAME_attak204,
|
||||
FRAME_attak205,
|
||||
FRAME_attak206,
|
||||
FRAME_attak207,
|
||||
FRAME_attak208,
|
||||
FRAME_attak209,
|
||||
FRAME_attak210,
|
||||
FRAME_attak211,
|
||||
FRAME_attak212,
|
||||
FRAME_attak213,
|
||||
FRAME_attak214,
|
||||
FRAME_attak215,
|
||||
FRAME_attak216,
|
||||
FRAME_attak217,
|
||||
FRAME_attak218,
|
||||
FRAME_attak219,
|
||||
FRAME_attak220,
|
||||
FRAME_attak221,
|
||||
FRAME_attak222,
|
||||
FRAME_attak223,
|
||||
FRAME_attak224,
|
||||
FRAME_attak225,
|
||||
FRAME_attak301,
|
||||
FRAME_attak302,
|
||||
FRAME_attak303,
|
||||
FRAME_attak304,
|
||||
FRAME_attak305,
|
||||
FRAME_attak306,
|
||||
FRAME_attak307,
|
||||
FRAME_attak308,
|
||||
FRAME_attak309,
|
||||
FRAME_attak310,
|
||||
FRAME_attak311,
|
||||
FRAME_attak312,
|
||||
FRAME_attak313,
|
||||
FRAME_attak314,
|
||||
FRAME_attak315,
|
||||
FRAME_attak316,
|
||||
FRAME_attak317,
|
||||
FRAME_attak318,
|
||||
FRAME_attak319,
|
||||
FRAME_attak320,
|
||||
FRAME_attak321,
|
||||
FRAME_attak322,
|
||||
FRAME_attak323,
|
||||
FRAME_attak324,
|
||||
FRAME_attak325,
|
||||
FRAME_attak326,
|
||||
FRAME_attak327,
|
||||
FRAME_attak328,
|
||||
FRAME_attak329,
|
||||
FRAME_attak330,
|
||||
FRAME_attak331,
|
||||
FRAME_attak332,
|
||||
FRAME_attak333,
|
||||
FRAME_attak334,
|
||||
FRAME_death01,
|
||||
FRAME_death02,
|
||||
FRAME_death03,
|
||||
FRAME_death04,
|
||||
FRAME_death05,
|
||||
FRAME_death06,
|
||||
FRAME_death07,
|
||||
FRAME_death08,
|
||||
FRAME_death09,
|
||||
FRAME_death10,
|
||||
FRAME_death11,
|
||||
FRAME_death12,
|
||||
FRAME_death13,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain104,
|
||||
FRAME_pain105,
|
||||
FRAME_pain106,
|
||||
FRAME_pain107,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain204,
|
||||
FRAME_pain205,
|
||||
FRAME_pain206,
|
||||
FRAME_pain207,
|
||||
FRAME_pain208,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_pain304,
|
||||
FRAME_pain305,
|
||||
FRAME_pain306,
|
||||
FRAME_pain307,
|
||||
FRAME_pain308,
|
||||
FRAME_pain309,
|
||||
FRAME_pain310,
|
||||
FRAME_pain311,
|
||||
FRAME_pain312,
|
||||
FRAME_stand101,
|
||||
FRAME_stand102,
|
||||
FRAME_stand103,
|
||||
FRAME_stand104,
|
||||
FRAME_stand105,
|
||||
FRAME_stand106,
|
||||
FRAME_stand107,
|
||||
FRAME_stand108,
|
||||
FRAME_stand109,
|
||||
FRAME_stand110,
|
||||
FRAME_stand111,
|
||||
FRAME_stand112,
|
||||
FRAME_stand113,
|
||||
FRAME_stand114,
|
||||
FRAME_stand115,
|
||||
FRAME_stand116,
|
||||
FRAME_stand117,
|
||||
FRAME_stand118,
|
||||
FRAME_stand119,
|
||||
FRAME_stand120,
|
||||
FRAME_stand121,
|
||||
FRAME_stand122,
|
||||
FRAME_stand123,
|
||||
FRAME_stand124,
|
||||
FRAME_stand125,
|
||||
FRAME_stand126,
|
||||
FRAME_stand127,
|
||||
FRAME_stand128,
|
||||
FRAME_stand129,
|
||||
FRAME_stand130,
|
||||
FRAME_stand131,
|
||||
FRAME_stand132,
|
||||
FRAME_stand133,
|
||||
FRAME_stand134,
|
||||
FRAME_stand135,
|
||||
FRAME_stand136,
|
||||
FRAME_stand137,
|
||||
FRAME_stand138,
|
||||
FRAME_stand139,
|
||||
FRAME_stand140,
|
||||
FRAME_stand141,
|
||||
FRAME_stand142,
|
||||
FRAME_stand143,
|
||||
FRAME_stand144,
|
||||
FRAME_stand145,
|
||||
FRAME_stand146,
|
||||
FRAME_stand147,
|
||||
FRAME_stand148,
|
||||
FRAME_stand149,
|
||||
FRAME_stand150,
|
||||
FRAME_stand151,
|
||||
FRAME_stand152,
|
||||
FRAME_stand201,
|
||||
FRAME_stand202,
|
||||
FRAME_stand203,
|
||||
FRAME_stand204,
|
||||
FRAME_stand205,
|
||||
FRAME_stand206,
|
||||
FRAME_stand207,
|
||||
FRAME_stand208,
|
||||
FRAME_stand209,
|
||||
FRAME_stand210,
|
||||
FRAME_stand211,
|
||||
FRAME_stand212,
|
||||
FRAME_stand213,
|
||||
FRAME_stand214,
|
||||
FRAME_stand215,
|
||||
FRAME_stand216,
|
||||
FRAME_stand217,
|
||||
FRAME_stand218,
|
||||
FRAME_stand219,
|
||||
FRAME_stand220,
|
||||
FRAME_stand221,
|
||||
FRAME_stand222,
|
||||
FRAME_stand223,
|
||||
FRAME_stand224,
|
||||
FRAME_stand225,
|
||||
FRAME_stand226,
|
||||
FRAME_stand227,
|
||||
FRAME_stand228,
|
||||
FRAME_stand229,
|
||||
FRAME_stand230,
|
||||
FRAME_stand231,
|
||||
FRAME_stand232,
|
||||
FRAME_stand233,
|
||||
FRAME_stand234,
|
||||
FRAME_stand235,
|
||||
FRAME_stand236,
|
||||
FRAME_stand237,
|
||||
FRAME_stand238,
|
||||
FRAME_stand239,
|
||||
FRAME_stand240,
|
||||
FRAME_stand241,
|
||||
FRAME_stand242,
|
||||
FRAME_stand243,
|
||||
FRAME_stand244,
|
||||
FRAME_stand245,
|
||||
FRAME_stand246,
|
||||
FRAME_stand247,
|
||||
FRAME_stand248,
|
||||
FRAME_stand249,
|
||||
FRAME_stand250,
|
||||
FRAME_stand251,
|
||||
FRAME_stand252
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
784
rerelease/m_flyer.cpp
Normal file
784
rerelease/m_flyer.cpp
Normal file
@@ -0,0 +1,784 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
flyer
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_flyer.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
static int sound_sight;
|
||||
static int sound_idle;
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_slash;
|
||||
static int sound_sproing;
|
||||
static int sound_die;
|
||||
|
||||
void flyer_check_melee(edict_t *self);
|
||||
void flyer_loop_melee(edict_t *self);
|
||||
void flyer_setstart(edict_t *self);
|
||||
|
||||
// ROGUE - kamikaze stuff
|
||||
void flyer_kamikaze(edict_t *self);
|
||||
void flyer_kamikaze_check(edict_t *self);
|
||||
void flyer_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod);
|
||||
|
||||
MONSTERINFO_SIGHT(flyer_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_IDLE(flyer_idle) (edict_t *self) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
void flyer_pop_blades(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sproing, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t flyer_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(flyer_move_stand) = { FRAME_stand01, FRAME_stand45, flyer_frames_stand, nullptr };
|
||||
|
||||
mframe_t flyer_frames_walk[] = {
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 }
|
||||
};
|
||||
MMOVE_T(flyer_move_walk) = { FRAME_stand01, FRAME_stand45, flyer_frames_walk, nullptr };
|
||||
|
||||
mframe_t flyer_frames_run[] = {
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 }
|
||||
};
|
||||
MMOVE_T(flyer_move_run) = { FRAME_stand01, FRAME_stand45, flyer_frames_run, nullptr };
|
||||
|
||||
mframe_t flyer_frames_kamizake[] = {
|
||||
{ ai_charge, 40, flyer_kamikaze_check },
|
||||
{ ai_charge, 40, flyer_kamikaze_check },
|
||||
{ ai_charge, 40, flyer_kamikaze_check },
|
||||
{ ai_charge, 40, flyer_kamikaze_check },
|
||||
{ ai_charge, 40, flyer_kamikaze_check }
|
||||
};
|
||||
MMOVE_T(flyer_move_kamikaze) = { FRAME_rollr02, FRAME_rollr06, flyer_frames_kamizake, flyer_kamikaze };
|
||||
|
||||
MONSTERINFO_RUN(flyer_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->mass > 50)
|
||||
M_SetAnimation(self, &flyer_move_kamikaze);
|
||||
else if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &flyer_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, &flyer_move_run);
|
||||
}
|
||||
|
||||
MONSTERINFO_WALK(flyer_walk) (edict_t *self) -> void
|
||||
{
|
||||
if (self->mass > 50)
|
||||
flyer_run(self);
|
||||
else
|
||||
M_SetAnimation(self, &flyer_move_walk);
|
||||
}
|
||||
|
||||
MONSTERINFO_STAND(flyer_stand) (edict_t *self) -> void
|
||||
{
|
||||
if (self->mass > 50)
|
||||
flyer_run(self);
|
||||
else
|
||||
M_SetAnimation(self, &flyer_move_stand);
|
||||
}
|
||||
|
||||
// ROGUE - kamikaze stuff
|
||||
|
||||
void flyer_kamikaze_explode(edict_t *self)
|
||||
{
|
||||
vec3_t dir;
|
||||
|
||||
if (self->monsterinfo.commander && self->monsterinfo.commander->inuse &&
|
||||
!strcmp(self->monsterinfo.commander->classname, "monster_carrier"))
|
||||
self->monsterinfo.commander->monsterinfo.monster_slots++;
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
dir = self->enemy->s.origin - self->s.origin;
|
||||
T_Damage(self->enemy, self, self, dir, self->s.origin, vec3_origin, (int) 50, (int) 50, DAMAGE_RADIUS, MOD_UNKNOWN);
|
||||
}
|
||||
|
||||
flyer_die(self, nullptr, nullptr, 0, dir, MOD_EXPLOSIVE);
|
||||
}
|
||||
|
||||
void flyer_kamikaze(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &flyer_move_kamikaze);
|
||||
}
|
||||
|
||||
void flyer_kamikaze_check(edict_t *self)
|
||||
{
|
||||
float dist;
|
||||
|
||||
// PMM - this needed because we could have gone away before we get here (blocked code)
|
||||
if (!self->inuse)
|
||||
return;
|
||||
|
||||
if ((!self->enemy) || (!self->enemy->inuse))
|
||||
{
|
||||
flyer_kamikaze_explode(self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->s.angles[0] = vectoangles(self->enemy->s.origin - self->s.origin).x;
|
||||
|
||||
self->goalentity = self->enemy;
|
||||
|
||||
dist = realrange(self, self->enemy);
|
||||
|
||||
if (dist < 90)
|
||||
flyer_kamikaze_explode(self);
|
||||
}
|
||||
|
||||
#if 0
|
||||
mframe_t flyer_frames_rollright[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(flyer_move_rollright) = { FRAME_rollr01, FRAME_rollr09, flyer_frames_rollright, nullptr };
|
||||
|
||||
mframe_t flyer_frames_rollleft[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(flyer_move_rollleft) = { FRAME_rollf01, FRAME_rollf09, flyer_frames_rollleft, nullptr };
|
||||
#endif
|
||||
|
||||
mframe_t flyer_frames_pain3[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(flyer_move_pain3) = { FRAME_pain301, FRAME_pain304, flyer_frames_pain3, flyer_run };
|
||||
|
||||
mframe_t flyer_frames_pain2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(flyer_move_pain2) = { FRAME_pain201, FRAME_pain204, flyer_frames_pain2, flyer_run };
|
||||
|
||||
mframe_t flyer_frames_pain1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(flyer_move_pain1) = { FRAME_pain101, FRAME_pain109, flyer_frames_pain1, flyer_run };
|
||||
|
||||
#if 0
|
||||
mframe_t flyer_frames_defense[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // Hold this frame
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(flyer_move_defense) = { FRAME_defens01, FRAME_defens06, flyer_frames_defense, nullptr };
|
||||
|
||||
mframe_t flyer_frames_bankright[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(flyer_move_bankright) = { FRAME_bankr01, FRAME_bankr07, flyer_frames_bankright, nullptr };
|
||||
|
||||
mframe_t flyer_frames_bankleft[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(flyer_move_bankleft) = { FRAME_bankl01, FRAME_bankl07, flyer_frames_bankleft, nullptr };
|
||||
#endif
|
||||
|
||||
void flyer_fire(edict_t *self, monster_muzzleflash_id_t flash_number)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
vec3_t end;
|
||||
vec3_t dir;
|
||||
|
||||
if (!self->enemy || !self->enemy->inuse) // PGM
|
||||
return; // PGM
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[flash_number], forward, right);
|
||||
|
||||
end = self->enemy->s.origin;
|
||||
end[2] += self->enemy->viewheight;
|
||||
dir = end - start;
|
||||
dir.normalize();
|
||||
|
||||
monster_fire_blaster(self, start, dir, 1, 1000, flash_number, (self->s.frame % 4) ? EF_NONE : EF_HYPERBLASTER);
|
||||
}
|
||||
|
||||
void flyer_fireleft(edict_t *self)
|
||||
{
|
||||
flyer_fire(self, MZ2_FLYER_BLASTER_1);
|
||||
}
|
||||
|
||||
void flyer_fireright(edict_t *self)
|
||||
{
|
||||
flyer_fire(self, MZ2_FLYER_BLASTER_2);
|
||||
}
|
||||
|
||||
mframe_t flyer_frames_attack2[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, -10, flyer_fireleft }, // left gun
|
||||
{ ai_charge, -10, flyer_fireright }, // right gun
|
||||
{ ai_charge, -10, flyer_fireleft }, // left gun
|
||||
{ ai_charge, -10, flyer_fireright }, // right gun
|
||||
{ ai_charge, -10, flyer_fireleft }, // left gun
|
||||
{ ai_charge, -10, flyer_fireright }, // right gun
|
||||
{ ai_charge, -10, flyer_fireleft }, // left gun
|
||||
{ ai_charge, -10, flyer_fireright }, // right gun
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(flyer_move_attack2) = { FRAME_attak201, FRAME_attak217, flyer_frames_attack2, flyer_run };
|
||||
|
||||
// PMM
|
||||
// circle strafe frames
|
||||
|
||||
mframe_t flyer_frames_attack3[] = {
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10, flyer_fireleft }, // left gun
|
||||
{ ai_charge, 10, flyer_fireright }, // right gun
|
||||
{ ai_charge, 10, flyer_fireleft }, // left gun
|
||||
{ ai_charge, 10, flyer_fireright }, // right gun
|
||||
{ ai_charge, 10, flyer_fireleft }, // left gun
|
||||
{ ai_charge, 10, flyer_fireright }, // right gun
|
||||
{ ai_charge, 10, flyer_fireleft }, // left gun
|
||||
{ ai_charge, 10, flyer_fireright }, // right gun
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10 },
|
||||
{ ai_charge, 10 }
|
||||
};
|
||||
MMOVE_T(flyer_move_attack3) = { FRAME_attak201, FRAME_attak217, flyer_frames_attack3, flyer_run };
|
||||
// pmm
|
||||
|
||||
void flyer_slash_left(edict_t *self)
|
||||
{
|
||||
vec3_t aim = { MELEE_DISTANCE, self->mins[0], 0 };
|
||||
if (!fire_hit(self, aim, 5, 0))
|
||||
self->monsterinfo.melee_debounce_time = level.time + 1.5_sec;
|
||||
gi.sound(self, CHAN_WEAPON, sound_slash, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void flyer_slash_right(edict_t *self)
|
||||
{
|
||||
vec3_t aim = { MELEE_DISTANCE, self->maxs[0], 0 };
|
||||
if (!fire_hit(self, aim, 5, 0))
|
||||
self->monsterinfo.melee_debounce_time = level.time + 1.5_sec;
|
||||
gi.sound(self, CHAN_WEAPON, sound_slash, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t flyer_frames_start_melee[] = {
|
||||
{ ai_charge, 0, flyer_pop_blades },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(flyer_move_start_melee) = { FRAME_attak101, FRAME_attak106, flyer_frames_start_melee, flyer_loop_melee };
|
||||
|
||||
mframe_t flyer_frames_end_melee[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(flyer_move_end_melee) = { FRAME_attak119, FRAME_attak121, flyer_frames_end_melee, flyer_run };
|
||||
|
||||
mframe_t flyer_frames_loop_melee[] = {
|
||||
{ ai_charge }, // Loop Start
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, flyer_slash_left }, // Left Wing Strike
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, flyer_slash_right }, // Right Wing Strike
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge } // Loop Ends
|
||||
|
||||
};
|
||||
MMOVE_T(flyer_move_loop_melee) = { FRAME_attak107, FRAME_attak118, flyer_frames_loop_melee, flyer_check_melee };
|
||||
|
||||
void flyer_loop_melee(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &flyer_move_loop_melee);
|
||||
}
|
||||
|
||||
static void flyer_set_fly_parameters(edict_t *self, bool melee)
|
||||
{
|
||||
if (melee)
|
||||
{
|
||||
// engage thrusters for a slice
|
||||
self->monsterinfo.fly_pinned = false;
|
||||
self->monsterinfo.fly_thrusters = true;
|
||||
self->monsterinfo.fly_position_time = 0_sec;
|
||||
self->monsterinfo.fly_acceleration = 20.f;
|
||||
self->monsterinfo.fly_speed = 210.f;
|
||||
self->monsterinfo.fly_min_distance = 0.f;
|
||||
self->monsterinfo.fly_max_distance = 10.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.fly_thrusters = false;
|
||||
self->monsterinfo.fly_acceleration = 15.f;
|
||||
self->monsterinfo.fly_speed = 165.f;
|
||||
self->monsterinfo.fly_min_distance = 45.f;
|
||||
self->monsterinfo.fly_max_distance = 200.f;
|
||||
}
|
||||
}
|
||||
|
||||
MONSTERINFO_ATTACK(flyer_attack) (edict_t *self) -> void
|
||||
{
|
||||
if (self->mass > 50)
|
||||
{
|
||||
flyer_run(self);
|
||||
return;
|
||||
}
|
||||
|
||||
float range = range_to(self, self->enemy);
|
||||
|
||||
if (self->enemy && visible(self, self->enemy) && range <= 225.f && frandom() > (range / 225.f) * 0.35f)
|
||||
{
|
||||
// fly-by slicing!
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
M_SetAnimation(self, &flyer_move_start_melee);
|
||||
flyer_set_fly_parameters(self, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
M_SetAnimation(self, &flyer_move_attack2);
|
||||
}
|
||||
|
||||
// [Paril-KEX] for alternate fly mode, sometimes we'll pin us
|
||||
// down, kind of like a pseudo-stand ground
|
||||
if (!self->monsterinfo.fly_pinned && brandom() && self->enemy && visible(self, self->enemy))
|
||||
{
|
||||
self->monsterinfo.fly_pinned = true;
|
||||
self->monsterinfo.fly_position_time = max(self->monsterinfo.fly_position_time, self->monsterinfo.fly_position_time + 1.7_sec); // make sure there's enough time for attack2/3
|
||||
|
||||
if (brandom())
|
||||
self->monsterinfo.fly_ideal_position = self->s.origin + (self->velocity * frandom()); // pin to our current position
|
||||
else
|
||||
self->monsterinfo.fly_ideal_position += self->enemy->s.origin; // make un-relative
|
||||
}
|
||||
|
||||
// if we're currently pinned, fly_position_time will unpin us eventually
|
||||
}
|
||||
|
||||
MONSTERINFO_MELEE(flyer_melee) (edict_t *self) -> void
|
||||
{
|
||||
if (self->mass > 50)
|
||||
flyer_run(self);
|
||||
else
|
||||
{
|
||||
M_SetAnimation(self, &flyer_move_start_melee);
|
||||
flyer_set_fly_parameters(self, true);
|
||||
}
|
||||
}
|
||||
|
||||
void flyer_check_melee(edict_t *self)
|
||||
{
|
||||
if (range_to(self, self->enemy) <= RANGE_MELEE)
|
||||
{
|
||||
if (self->monsterinfo.melee_debounce_time <= level.time)
|
||||
{
|
||||
M_SetAnimation(self, &flyer_move_loop_melee);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
M_SetAnimation(self, &flyer_move_end_melee);
|
||||
flyer_set_fly_parameters(self, false);
|
||||
}
|
||||
|
||||
PAIN(flyer_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
int n;
|
||||
|
||||
// pmm - kamikaze's don't feel pain
|
||||
if (self->mass != 50)
|
||||
return;
|
||||
// pmm
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
n = irandom(3);
|
||||
if (n == 0)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else if (n == 1)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
flyer_set_fly_parameters(self, false);
|
||||
|
||||
if (n == 0)
|
||||
M_SetAnimation(self, &flyer_move_pain1);
|
||||
else if (n == 1)
|
||||
M_SetAnimation(self, &flyer_move_pain2);
|
||||
else
|
||||
M_SetAnimation(self, &flyer_move_pain3);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(flyer_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
DIE(flyer_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.multicast(self->s.origin, MULTICAST_PHS, false);
|
||||
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
ThrowGibs(self, 55, {
|
||||
{ 2, "models/objects/gibs/sm_metal/tris.md2" },
|
||||
{ 2, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ "models/monsters/flyer/gibs/base.md2", GIB_SKINNED },
|
||||
{ 2, "models/monsters/flyer/gibs/gun.md2", GIB_SKINNED },
|
||||
{ 2, "models/monsters/flyer/gibs/wing.md2", GIB_SKINNED },
|
||||
{ "models/monsters/flyer/gibs/head.md2", GIB_SKINNED | GIB_HEAD }
|
||||
});
|
||||
|
||||
self->touch = nullptr;
|
||||
}
|
||||
|
||||
// PMM - kamikaze code .. blow up if blocked
|
||||
MONSTERINFO_BLOCKED(flyer_blocked) (edict_t *self, float dist) -> bool
|
||||
{
|
||||
// kamikaze = 100, normal = 50
|
||||
if (self->mass == 100)
|
||||
{
|
||||
flyer_kamikaze_check(self);
|
||||
|
||||
// if the above didn't blow us up (i.e. I got blocked by the player)
|
||||
if (self->inuse)
|
||||
T_Damage(self, self, self, vec3_origin, self->s.origin, vec3_origin, 9999, 100, DAMAGE_NONE, MOD_UNKNOWN);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TOUCH(kamikaze_touch) (edict_t *ent, edict_t *other, const trace_t &tr, bool other_touching_self) -> void
|
||||
{
|
||||
T_Damage(ent, ent, ent, ent->velocity.normalized(), ent->s.origin, ent->velocity.normalized(), 9999, 100, DAMAGE_NONE, MOD_UNKNOWN);
|
||||
}
|
||||
|
||||
TOUCH(flyer_touch) (edict_t *ent, edict_t *other, const trace_t &tr, bool other_touching_self) -> void
|
||||
{
|
||||
if ((other->monsterinfo.aiflags & AI_ALTERNATE_FLY) && (other->flags & FL_FLY) &&
|
||||
(ent->monsterinfo.duck_wait_time < level.time))
|
||||
{
|
||||
ent->monsterinfo.duck_wait_time = level.time + 1_sec;
|
||||
ent->monsterinfo.fly_thrusters = false;
|
||||
|
||||
vec3_t dir = (ent->s.origin - other->s.origin).normalized();
|
||||
ent->velocity = dir * 500.f;
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_SPLASH);
|
||||
gi.WriteByte(32);
|
||||
gi.WritePosition(tr.endpos);
|
||||
gi.WriteDir(dir);
|
||||
gi.WriteByte(SPLASH_SPARKS);
|
||||
gi.multicast(tr.endpos, MULTICAST_PVS, false);
|
||||
}
|
||||
}
|
||||
|
||||
/*QUAKED monster_flyer (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_flyer(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_sight = gi.soundindex("flyer/flysght1.wav");
|
||||
sound_idle = gi.soundindex("flyer/flysrch1.wav");
|
||||
sound_pain1 = gi.soundindex("flyer/flypain1.wav");
|
||||
sound_pain2 = gi.soundindex("flyer/flypain2.wav");
|
||||
sound_slash = gi.soundindex("flyer/flyatck2.wav");
|
||||
sound_sproing = gi.soundindex("flyer/flyatck1.wav");
|
||||
sound_die = gi.soundindex("flyer/flydeth1.wav");
|
||||
|
||||
gi.soundindex("flyer/flyatck3.wav");
|
||||
|
||||
self->s.modelindex = gi.modelindex("models/monsters/flyer/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/flyer/gibs/base.md2");
|
||||
gi.modelindex("models/monsters/flyer/gibs/wing.md2");
|
||||
gi.modelindex("models/monsters/flyer/gibs/gun.md2");
|
||||
gi.modelindex("models/monsters/flyer/gibs/head.md2");
|
||||
|
||||
self->mins = { -16, -16, -24 };
|
||||
// PMM - shortened to 16 from 32
|
||||
self->maxs = { 16, 16, 16 };
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
|
||||
self->viewheight = 12;
|
||||
|
||||
self->monsterinfo.engine_sound = gi.soundindex("flyer/flyidle1.wav");
|
||||
|
||||
self->health = 50 * st.health_multiplier;
|
||||
self->mass = 50;
|
||||
|
||||
self->pain = flyer_pain;
|
||||
self->die = flyer_die;
|
||||
|
||||
self->monsterinfo.stand = flyer_stand;
|
||||
self->monsterinfo.walk = flyer_walk;
|
||||
self->monsterinfo.run = flyer_run;
|
||||
self->monsterinfo.attack = flyer_attack;
|
||||
self->monsterinfo.melee = flyer_melee;
|
||||
self->monsterinfo.sight = flyer_sight;
|
||||
self->monsterinfo.idle = flyer_idle;
|
||||
self->monsterinfo.blocked = flyer_blocked;
|
||||
self->monsterinfo.setskin = flyer_setskin;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &flyer_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
if (self->s.effects & EF_ROCKET)
|
||||
{
|
||||
// PMM - normal flyer has mass of 50
|
||||
self->mass = 100;
|
||||
self->yaw_speed = 5;
|
||||
self->touch = kamikaze_touch;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.aiflags |= AI_ALTERNATE_FLY;
|
||||
self->monsterinfo.fly_buzzard = true;
|
||||
flyer_set_fly_parameters(self, false);
|
||||
self->touch = flyer_touch;
|
||||
}
|
||||
|
||||
flymonster_start(self);
|
||||
}
|
||||
|
||||
// PMM - suicide fliers
|
||||
void SP_monster_kamikaze(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
self->s.effects |= EF_ROCKET;
|
||||
|
||||
SP_monster_flyer(self);
|
||||
}
|
||||
161
rerelease/m_flyer.h
Normal file
161
rerelease/m_flyer.h
Normal file
@@ -0,0 +1,161 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/flyer
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
enum
|
||||
{
|
||||
FRAME_start01,
|
||||
FRAME_start02,
|
||||
FRAME_start03,
|
||||
FRAME_start04,
|
||||
FRAME_start05,
|
||||
FRAME_start06,
|
||||
FRAME_stop01,
|
||||
FRAME_stop02,
|
||||
FRAME_stop03,
|
||||
FRAME_stop04,
|
||||
FRAME_stop05,
|
||||
FRAME_stop06,
|
||||
FRAME_stop07,
|
||||
FRAME_stand01,
|
||||
FRAME_stand02,
|
||||
FRAME_stand03,
|
||||
FRAME_stand04,
|
||||
FRAME_stand05,
|
||||
FRAME_stand06,
|
||||
FRAME_stand07,
|
||||
FRAME_stand08,
|
||||
FRAME_stand09,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_stand18,
|
||||
FRAME_stand19,
|
||||
FRAME_stand20,
|
||||
FRAME_stand21,
|
||||
FRAME_stand22,
|
||||
FRAME_stand23,
|
||||
FRAME_stand24,
|
||||
FRAME_stand25,
|
||||
FRAME_stand26,
|
||||
FRAME_stand27,
|
||||
FRAME_stand28,
|
||||
FRAME_stand29,
|
||||
FRAME_stand30,
|
||||
FRAME_stand31,
|
||||
FRAME_stand32,
|
||||
FRAME_stand33,
|
||||
FRAME_stand34,
|
||||
FRAME_stand35,
|
||||
FRAME_stand36,
|
||||
FRAME_stand37,
|
||||
FRAME_stand38,
|
||||
FRAME_stand39,
|
||||
FRAME_stand40,
|
||||
FRAME_stand41,
|
||||
FRAME_stand42,
|
||||
FRAME_stand43,
|
||||
FRAME_stand44,
|
||||
FRAME_stand45,
|
||||
FRAME_attak101,
|
||||
FRAME_attak102,
|
||||
FRAME_attak103,
|
||||
FRAME_attak104,
|
||||
FRAME_attak105,
|
||||
FRAME_attak106,
|
||||
FRAME_attak107,
|
||||
FRAME_attak108,
|
||||
FRAME_attak109,
|
||||
FRAME_attak110,
|
||||
FRAME_attak111,
|
||||
FRAME_attak112,
|
||||
FRAME_attak113,
|
||||
FRAME_attak114,
|
||||
FRAME_attak115,
|
||||
FRAME_attak116,
|
||||
FRAME_attak117,
|
||||
FRAME_attak118,
|
||||
FRAME_attak119,
|
||||
FRAME_attak120,
|
||||
FRAME_attak121,
|
||||
FRAME_attak201,
|
||||
FRAME_attak202,
|
||||
FRAME_attak203,
|
||||
FRAME_attak204,
|
||||
FRAME_attak205,
|
||||
FRAME_attak206,
|
||||
FRAME_attak207,
|
||||
FRAME_attak208,
|
||||
FRAME_attak209,
|
||||
FRAME_attak210,
|
||||
FRAME_attak211,
|
||||
FRAME_attak212,
|
||||
FRAME_attak213,
|
||||
FRAME_attak214,
|
||||
FRAME_attak215,
|
||||
FRAME_attak216,
|
||||
FRAME_attak217,
|
||||
FRAME_bankl01,
|
||||
FRAME_bankl02,
|
||||
FRAME_bankl03,
|
||||
FRAME_bankl04,
|
||||
FRAME_bankl05,
|
||||
FRAME_bankl06,
|
||||
FRAME_bankl07,
|
||||
FRAME_bankr01,
|
||||
FRAME_bankr02,
|
||||
FRAME_bankr03,
|
||||
FRAME_bankr04,
|
||||
FRAME_bankr05,
|
||||
FRAME_bankr06,
|
||||
FRAME_bankr07,
|
||||
FRAME_rollf01,
|
||||
FRAME_rollf02,
|
||||
FRAME_rollf03,
|
||||
FRAME_rollf04,
|
||||
FRAME_rollf05,
|
||||
FRAME_rollf06,
|
||||
FRAME_rollf07,
|
||||
FRAME_rollf08,
|
||||
FRAME_rollf09,
|
||||
FRAME_rollr01,
|
||||
FRAME_rollr02,
|
||||
FRAME_rollr03,
|
||||
FRAME_rollr04,
|
||||
FRAME_rollr05,
|
||||
FRAME_rollr06,
|
||||
FRAME_rollr07,
|
||||
FRAME_rollr08,
|
||||
FRAME_rollr09,
|
||||
FRAME_defens01,
|
||||
FRAME_defens02,
|
||||
FRAME_defens03,
|
||||
FRAME_defens04,
|
||||
FRAME_defens05,
|
||||
FRAME_defens06,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain104,
|
||||
FRAME_pain105,
|
||||
FRAME_pain106,
|
||||
FRAME_pain107,
|
||||
FRAME_pain108,
|
||||
FRAME_pain109,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain204,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_pain304
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
489
rerelease/m_gladiator.cpp
Normal file
489
rerelease/m_gladiator.cpp
Normal file
@@ -0,0 +1,489 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
GLADIATOR
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_gladiator.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_die;
|
||||
static int sound_die2;
|
||||
static int sound_gun;
|
||||
static int sound_gunb;
|
||||
static int sound_cleaver_swing;
|
||||
static int sound_cleaver_hit;
|
||||
static int sound_cleaver_miss;
|
||||
static int sound_idle;
|
||||
static int sound_search;
|
||||
static int sound_sight;
|
||||
|
||||
MONSTERINFO_IDLE(gladiator_idle) (edict_t *self) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_SIGHT(gladiator_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_SEARCH(gladiator_search) (edict_t *self) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void gladiator_cleaver_swing(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_cleaver_swing, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(gladiator_move_stand) = { FRAME_stand1, FRAME_stand7, gladiator_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(gladiator_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &gladiator_move_stand);
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_walk[] = {
|
||||
{ ai_walk, 15 },
|
||||
{ ai_walk, 7 },
|
||||
{ ai_walk, 6 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 2, monster_footstep },
|
||||
{ ai_walk },
|
||||
{ ai_walk, 2 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 12 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 2, monster_footstep },
|
||||
{ ai_walk, 2 },
|
||||
{ ai_walk, 1 },
|
||||
{ ai_walk, 8 }
|
||||
};
|
||||
MMOVE_T(gladiator_move_walk) = { FRAME_walk1, FRAME_walk16, gladiator_frames_walk, nullptr };
|
||||
|
||||
MONSTERINFO_WALK(gladiator_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &gladiator_move_walk);
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_run[] = {
|
||||
{ ai_run, 23 },
|
||||
{ ai_run, 14 },
|
||||
{ ai_run, 14, monster_footstep },
|
||||
{ ai_run, 21 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 13, monster_footstep }
|
||||
};
|
||||
MMOVE_T(gladiator_move_run) = { FRAME_run1, FRAME_run6, gladiator_frames_run, nullptr };
|
||||
|
||||
MONSTERINFO_RUN(gladiator_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &gladiator_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, &gladiator_move_run);
|
||||
}
|
||||
|
||||
void GladiatorMelee(edict_t *self)
|
||||
{
|
||||
vec3_t aim = { MELEE_DISTANCE, self->mins[0], -4 };
|
||||
if (fire_hit(self, aim, irandom(20, 25), 300))
|
||||
gi.sound(self, CHAN_AUTO, sound_cleaver_hit, 1, ATTN_NORM, 0);
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_AUTO, sound_cleaver_miss, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.melee_debounce_time = level.time + 1.5_sec;
|
||||
}
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_attack_melee[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, gladiator_cleaver_swing },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, GladiatorMelee },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, gladiator_cleaver_swing },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, GladiatorMelee },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(gladiator_move_attack_melee) = { FRAME_melee3, FRAME_melee16, gladiator_frames_attack_melee, gladiator_run };
|
||||
|
||||
MONSTERINFO_MELEE(gladiator_melee) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &gladiator_move_attack_melee);
|
||||
}
|
||||
|
||||
void GladiatorGun(edict_t *self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t forward, right;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_GLADIATOR_RAILGUN_1], forward, right);
|
||||
|
||||
// calc direction to where we targted
|
||||
dir = self->pos1 - start;
|
||||
dir.normalize();
|
||||
|
||||
monster_fire_railgun(self, start, dir, 50, 100, MZ2_GLADIATOR_RAILGUN_1);
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_attack_gun[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, GladiatorGun },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, monster_footstep },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(gladiator_move_attack_gun) = { FRAME_attack1, FRAME_attack9, gladiator_frames_attack_gun, gladiator_run };
|
||||
|
||||
// RAFAEL
|
||||
void gladbGun(edict_t *self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t forward, right;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_GLADIATOR_RAILGUN_1], forward, right);
|
||||
|
||||
// calc direction to where we targeted
|
||||
dir = self->pos1 - start;
|
||||
dir.normalize();
|
||||
|
||||
int damage = 35;
|
||||
int radius_damage = 45;
|
||||
|
||||
if (self->s.frame > FRAME_attack3)
|
||||
{
|
||||
damage /= 2;
|
||||
radius_damage /= 2;
|
||||
}
|
||||
|
||||
fire_plasma(self, start, dir, damage, 725, radius_damage, radius_damage);
|
||||
|
||||
// save for aiming the shot
|
||||
self->pos1 = self->enemy->s.origin;
|
||||
self->pos1[2] += self->enemy->viewheight;
|
||||
}
|
||||
|
||||
void gladbGun_check(edict_t *self)
|
||||
{
|
||||
if (skill->integer == 3)
|
||||
gladbGun(self);
|
||||
}
|
||||
|
||||
mframe_t gladb_frames_attack_gun[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, gladbGun },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, gladbGun },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, gladbGun_check }
|
||||
};
|
||||
MMOVE_T(gladb_move_attack_gun) = { FRAME_attack1, FRAME_attack9, gladb_frames_attack_gun, gladiator_run };
|
||||
// RAFAEL
|
||||
|
||||
MONSTERINFO_ATTACK(gladiator_attack) (edict_t *self) -> void
|
||||
{
|
||||
float range;
|
||||
vec3_t v;
|
||||
|
||||
// a small safe zone
|
||||
v = self->s.origin - self->enemy->s.origin;
|
||||
range = v.length();
|
||||
if (range <= (MELEE_DISTANCE + 32) && self->monsterinfo.melee_debounce_time <= level.time)
|
||||
return;
|
||||
else if (!M_CheckClearShot(self, monster_flash_offset[MZ2_GLADIATOR_RAILGUN_1]))
|
||||
return;
|
||||
|
||||
// charge up the railgun
|
||||
self->pos1 = self->enemy->s.origin; // save for aiming the shot
|
||||
self->pos1[2] += self->enemy->viewheight;
|
||||
// RAFAEL
|
||||
if (self->style == 1)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_gunb, 1, ATTN_NORM, 0);
|
||||
M_SetAnimation(self, &gladb_move_attack_gun);
|
||||
}
|
||||
else
|
||||
{
|
||||
// RAFAEL
|
||||
gi.sound(self, CHAN_WEAPON, sound_gun, 1, ATTN_NORM, 0);
|
||||
M_SetAnimation(self, &gladiator_move_attack_gun);
|
||||
// RAFAEL
|
||||
}
|
||||
// RAFAEL
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_pain[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(gladiator_move_pain) = { FRAME_pain2, FRAME_pain5, gladiator_frames_pain, gladiator_run };
|
||||
|
||||
mframe_t gladiator_frames_pain_air[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(gladiator_move_pain_air) = { FRAME_painup2, FRAME_painup6, gladiator_frames_pain_air, gladiator_run };
|
||||
|
||||
PAIN(gladiator_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
if (level.time < self->pain_debounce_time)
|
||||
{
|
||||
if ((self->velocity[2] > 100) && (self->monsterinfo.active_move == &gladiator_move_pain))
|
||||
M_SetAnimation(self, &gladiator_move_pain_air);
|
||||
return;
|
||||
}
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
if (frandom() < 0.5f)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (self->velocity[2] > 100)
|
||||
M_SetAnimation(self, &gladiator_move_pain_air);
|
||||
else
|
||||
M_SetAnimation(self, &gladiator_move_pain);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(gladiator_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum |= 1;
|
||||
else
|
||||
self->s.skinnum &= ~1;
|
||||
}
|
||||
|
||||
void gladiator_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, -8 };
|
||||
monster_dead(self);
|
||||
}
|
||||
|
||||
static void gladiator_shrink(edict_t *self)
|
||||
{
|
||||
self->maxs[2] = 0;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_death[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, gladiator_shrink },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(gladiator_move_death) = { FRAME_death2, FRAME_death22, gladiator_frames_death, gladiator_dead };
|
||||
|
||||
DIE(gladiator_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
// check for gib
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
ThrowGibs(self, damage, {
|
||||
{ 2, "models/objects/gibs/bone/tris.md2" },
|
||||
{ 2, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ 2, "models/monsters/gladiatr/gibs/thigh.md2", GIB_SKINNED },
|
||||
{ "models/monsters/gladiatr/gibs/larm.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/gladiatr/gibs/rarm.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/gladiatr/gibs/chest.md2", GIB_SKINNED },
|
||||
{ "models/monsters/gladiatr/gibs/head.md2", GIB_SKINNED | GIB_HEAD }
|
||||
});
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_BODY, sound_die, 1, ATTN_NORM, 0);
|
||||
|
||||
if (brandom())
|
||||
gi.sound(self, CHAN_VOICE, sound_die2, 1, ATTN_NORM, 0);
|
||||
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
|
||||
M_SetAnimation(self, &gladiator_move_death);
|
||||
}
|
||||
|
||||
//===========
|
||||
// PGM
|
||||
MONSTERINFO_BLOCKED(gladiator_blocked) (edict_t *self, float dist) -> bool
|
||||
{
|
||||
if (blocked_checkplat(self, dist))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
// PGM
|
||||
//===========
|
||||
|
||||
/*QUAKED monster_gladiator (1 .5 0) (-32 -32 -24) (32 32 64) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_gladiator(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_pain1 = gi.soundindex("gladiator/pain.wav");
|
||||
sound_pain2 = gi.soundindex("gladiator/gldpain2.wav");
|
||||
sound_die = gi.soundindex("gladiator/glddeth2.wav");
|
||||
sound_die2 = gi.soundindex("gladiator/death.wav");
|
||||
sound_cleaver_swing = gi.soundindex("gladiator/melee1.wav");
|
||||
sound_cleaver_hit = gi.soundindex("gladiator/melee2.wav");
|
||||
sound_cleaver_miss = gi.soundindex("gladiator/melee3.wav");
|
||||
sound_idle = gi.soundindex("gladiator/gldidle1.wav");
|
||||
sound_search = gi.soundindex("gladiator/gldsrch1.wav");
|
||||
sound_sight = gi.soundindex("gladiator/sight.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/gladiatr/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/gladiatr/gibs/chest.md2");
|
||||
gi.modelindex("models/monsters/gladiatr/gibs/head.md2");
|
||||
gi.modelindex("models/monsters/gladiatr/gibs/larm.md2");
|
||||
gi.modelindex("models/monsters/gladiatr/gibs/rarm.md2");
|
||||
gi.modelindex("models/monsters/gladiatr/gibs/thigh.md2");
|
||||
|
||||
// RAFAEL
|
||||
if (strcmp(self->classname, "monster_gladb") == 0)
|
||||
{
|
||||
sound_gunb = gi.soundindex("weapons/plasshot.wav");
|
||||
|
||||
self->health = 250 * st.health_multiplier;
|
||||
self->mass = 350;
|
||||
|
||||
if (!st.was_key_specified("power_armor_type"))
|
||||
self->monsterinfo.power_armor_type = IT_ITEM_POWER_SHIELD;
|
||||
if (!st.was_key_specified("power_armor_power"))
|
||||
self->monsterinfo.power_armor_power = 250;
|
||||
|
||||
self->s.skinnum = 2;
|
||||
|
||||
self->style = 1;
|
||||
|
||||
self->monsterinfo.weapon_sound = gi.soundindex("weapons/phaloop.wav");
|
||||
}
|
||||
else
|
||||
{
|
||||
// RAFAEL
|
||||
sound_gun = gi.soundindex("gladiator/railgun.wav");
|
||||
|
||||
self->health = 400 * st.health_multiplier;
|
||||
self->mass = 400;
|
||||
// RAFAEL
|
||||
|
||||
self->monsterinfo.weapon_sound = gi.soundindex("weapons/rg_hum.wav");
|
||||
}
|
||||
// RAFAEL
|
||||
|
||||
self->gib_health = -175;
|
||||
|
||||
self->mins = { -32, -32, -24 };
|
||||
self->maxs = { 32, 32, 42 };
|
||||
|
||||
self->pain = gladiator_pain;
|
||||
self->die = gladiator_die;
|
||||
|
||||
self->monsterinfo.stand = gladiator_stand;
|
||||
self->monsterinfo.walk = gladiator_walk;
|
||||
self->monsterinfo.run = gladiator_run;
|
||||
self->monsterinfo.dodge = nullptr;
|
||||
self->monsterinfo.attack = gladiator_attack;
|
||||
self->monsterinfo.melee = gladiator_melee;
|
||||
self->monsterinfo.sight = gladiator_sight;
|
||||
self->monsterinfo.idle = gladiator_idle;
|
||||
self->monsterinfo.search = gladiator_search;
|
||||
self->monsterinfo.blocked = gladiator_blocked; // PGM
|
||||
self->monsterinfo.setskin = gladiator_setskin;
|
||||
|
||||
gi.linkentity(self);
|
||||
M_SetAnimation(self, &gladiator_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
|
||||
//
|
||||
// monster_gladb
|
||||
// RAFAEL
|
||||
//
|
||||
/*QUAKED monster_gladb (1 .5 0) (-32 -32 -24) (32 32 64) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_gladb(edict_t *self)
|
||||
{
|
||||
SP_monster_gladiator(self);
|
||||
}
|
||||
101
rerelease/m_gladiator.h
Normal file
101
rerelease/m_gladiator.h
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/gladiatr
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_stand1,
|
||||
FRAME_stand2,
|
||||
FRAME_stand3,
|
||||
FRAME_stand4,
|
||||
FRAME_stand5,
|
||||
FRAME_stand6,
|
||||
FRAME_stand7,
|
||||
FRAME_walk1,
|
||||
FRAME_walk2,
|
||||
FRAME_walk3,
|
||||
FRAME_walk4,
|
||||
FRAME_walk5,
|
||||
FRAME_walk6,
|
||||
FRAME_walk7,
|
||||
FRAME_walk8,
|
||||
FRAME_walk9,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_walk13,
|
||||
FRAME_walk14,
|
||||
FRAME_walk15,
|
||||
FRAME_walk16,
|
||||
FRAME_run1,
|
||||
FRAME_run2,
|
||||
FRAME_run3,
|
||||
FRAME_run4,
|
||||
FRAME_run5,
|
||||
FRAME_run6,
|
||||
FRAME_melee1,
|
||||
FRAME_melee2,
|
||||
FRAME_melee3,
|
||||
FRAME_melee4,
|
||||
FRAME_melee5,
|
||||
FRAME_melee6,
|
||||
FRAME_melee7,
|
||||
FRAME_melee8,
|
||||
FRAME_melee9,
|
||||
FRAME_melee10,
|
||||
FRAME_melee11,
|
||||
FRAME_melee12,
|
||||
FRAME_melee13,
|
||||
FRAME_melee14,
|
||||
FRAME_melee15,
|
||||
FRAME_melee16,
|
||||
FRAME_melee17,
|
||||
FRAME_attack1,
|
||||
FRAME_attack2,
|
||||
FRAME_attack3,
|
||||
FRAME_attack4,
|
||||
FRAME_attack5,
|
||||
FRAME_attack6,
|
||||
FRAME_attack7,
|
||||
FRAME_attack8,
|
||||
FRAME_attack9,
|
||||
FRAME_pain1,
|
||||
FRAME_pain2,
|
||||
FRAME_pain3,
|
||||
FRAME_pain4,
|
||||
FRAME_pain5,
|
||||
FRAME_pain6,
|
||||
FRAME_death1,
|
||||
FRAME_death2,
|
||||
FRAME_death3,
|
||||
FRAME_death4,
|
||||
FRAME_death5,
|
||||
FRAME_death6,
|
||||
FRAME_death7,
|
||||
FRAME_death8,
|
||||
FRAME_death9,
|
||||
FRAME_death10,
|
||||
FRAME_death11,
|
||||
FRAME_death12,
|
||||
FRAME_death13,
|
||||
FRAME_death14,
|
||||
FRAME_death15,
|
||||
FRAME_death16,
|
||||
FRAME_death17,
|
||||
FRAME_death18,
|
||||
FRAME_death19,
|
||||
FRAME_death20,
|
||||
FRAME_death21,
|
||||
FRAME_death22,
|
||||
FRAME_painup1,
|
||||
FRAME_painup2,
|
||||
FRAME_painup3,
|
||||
FRAME_painup4,
|
||||
FRAME_painup5,
|
||||
FRAME_painup6,
|
||||
FRAME_painup7
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
523
rerelease/m_guardian.cpp
Normal file
523
rerelease/m_guardian.cpp
Normal file
@@ -0,0 +1,523 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
GUARDIAN
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_guardian.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
//
|
||||
// stand
|
||||
//
|
||||
|
||||
mframe_t guardian_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(guardian_move_stand) = { FRAME_idle1, FRAME_idle52, guardian_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(guardian_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &guardian_move_stand);
|
||||
}
|
||||
|
||||
//
|
||||
// walk
|
||||
//
|
||||
|
||||
static int sound_step;
|
||||
|
||||
void guardian_footstep(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_step, 1.f, ATTN_NORM, 0.0f);
|
||||
}
|
||||
|
||||
mframe_t guardian_frames_walk[] = {
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8, guardian_footstep },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 8, guardian_footstep },
|
||||
{ ai_walk, 8 }
|
||||
};
|
||||
MMOVE_T(guardian_move_walk) = { FRAME_walk1, FRAME_walk19, guardian_frames_walk, nullptr };
|
||||
|
||||
MONSTERINFO_WALK(guardian_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &guardian_move_walk);
|
||||
}
|
||||
|
||||
//
|
||||
// run
|
||||
//
|
||||
|
||||
mframe_t guardian_frames_run[] = {
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8, guardian_footstep },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 8, guardian_footstep },
|
||||
{ ai_run, 8 }
|
||||
};
|
||||
MMOVE_T(guardian_move_run) = { FRAME_walk1, FRAME_walk19, guardian_frames_run, nullptr };
|
||||
|
||||
MONSTERINFO_RUN(guardian_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
M_SetAnimation(self, &guardian_move_stand);
|
||||
return;
|
||||
}
|
||||
|
||||
M_SetAnimation(self, &guardian_move_run);
|
||||
}
|
||||
|
||||
//
|
||||
// pain
|
||||
//
|
||||
|
||||
mframe_t guardian_frames_pain1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(guardian_move_pain1) = { FRAME_pain1_1, FRAME_pain1_8, guardian_frames_pain1, guardian_run };
|
||||
|
||||
PAIN(guardian_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
if (mod.id != MOD_CHAINFIST && damage <= 10)
|
||||
return;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
if (mod.id != MOD_CHAINFIST && damage <= 75)
|
||||
if (frandom() > 0.2f)
|
||||
return;
|
||||
|
||||
// don't go into pain while attacking
|
||||
if ((self->s.frame >= FRAME_atk1_spin1) && (self->s.frame <= FRAME_atk1_spin15))
|
||||
return;
|
||||
if ((self->s.frame >= FRAME_atk2_fire1) && (self->s.frame <= FRAME_atk2_fire4))
|
||||
return;
|
||||
if ((self->s.frame >= FRAME_kick_in1) && (self->s.frame <= FRAME_kick_in13))
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
//gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
M_SetAnimation(self, &guardian_move_pain1);
|
||||
self->monsterinfo.weapon_sound = 0;
|
||||
}
|
||||
|
||||
mframe_t guardian_frames_atk1_out[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(guardian_atk1_out) = { FRAME_atk1_out1, FRAME_atk1_out3, guardian_frames_atk1_out, guardian_run };
|
||||
|
||||
void guardian_atk1_finish(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &guardian_atk1_out);
|
||||
self->monsterinfo.weapon_sound = 0;
|
||||
}
|
||||
|
||||
static int sound_charge;
|
||||
static int sound_spin_loop;
|
||||
|
||||
void guardian_atk1_charge(edict_t *self)
|
||||
{
|
||||
self->monsterinfo.weapon_sound = sound_spin_loop;
|
||||
gi.sound(self, CHAN_WEAPON, sound_charge, 1.f, ATTN_NORM, 0.f);
|
||||
}
|
||||
|
||||
void guardian_fire_blaster(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right, target;
|
||||
vec3_t start;
|
||||
monster_muzzleflash_id_t id = MZ2_GUARDIAN_BLASTER;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[id], forward, right);
|
||||
target = self->enemy->s.origin;
|
||||
target[2] += self->enemy->viewheight;
|
||||
for (int i = 0; i < 3; i++)
|
||||
target[i] += crandom_open() * 5.f;
|
||||
forward = target - start;
|
||||
forward.normalize();
|
||||
|
||||
monster_fire_blaster(self, start, forward, 2, 1000, id, (self->s.frame % 4) ? EF_NONE : EF_HYPERBLASTER);
|
||||
|
||||
if (self->enemy && self->enemy->health > 0 &&
|
||||
self->s.frame == FRAME_atk1_spin12 && self->timestamp > level.time && visible(self, self->enemy))
|
||||
self->monsterinfo.nextframe = FRAME_atk1_spin5;
|
||||
}
|
||||
|
||||
mframe_t guardian_frames_atk1_spin[] = {
|
||||
{ ai_charge, 0, guardian_atk1_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, guardian_fire_blaster },
|
||||
{ ai_charge, 0, guardian_fire_blaster },
|
||||
{ ai_charge, 0, guardian_fire_blaster },
|
||||
{ ai_charge, 0, guardian_fire_blaster },
|
||||
{ ai_charge, 0, guardian_fire_blaster },
|
||||
{ ai_charge, 0, guardian_fire_blaster },
|
||||
{ ai_charge, 0, guardian_fire_blaster },
|
||||
{ ai_charge, 0, guardian_fire_blaster },
|
||||
{ ai_charge, 0 },
|
||||
{ ai_charge, 0 },
|
||||
{ ai_charge, 0 }
|
||||
};
|
||||
MMOVE_T(guardian_move_atk1_spin) = { FRAME_atk1_spin1, FRAME_atk1_spin15, guardian_frames_atk1_spin, guardian_atk1_finish };
|
||||
|
||||
void guardian_atk1(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &guardian_move_atk1_spin);
|
||||
self->timestamp = level.time + 650_ms + random_time(1.5_sec);
|
||||
}
|
||||
|
||||
mframe_t guardian_frames_atk1_in[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(guardian_move_atk1_in) = { FRAME_atk1_in1, FRAME_atk1_in3, guardian_frames_atk1_in, guardian_atk1 };
|
||||
|
||||
mframe_t guardian_frames_atk2_out[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, guardian_footstep },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(guardian_move_atk2_out) = { FRAME_atk2_out1, FRAME_atk2_out7, guardian_frames_atk2_out, guardian_run };
|
||||
|
||||
void guardian_atk2_out(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &guardian_move_atk2_out);
|
||||
}
|
||||
|
||||
static int sound_laser;
|
||||
|
||||
constexpr vec3_t laser_positions[] = {
|
||||
{ 125.0f, -70.f, 60.f },
|
||||
{ 112.0f, -62.f, 60.f }
|
||||
};
|
||||
|
||||
PRETHINK(guardian_fire_update) (edict_t *laser) -> void
|
||||
{
|
||||
edict_t *self = laser->owner;
|
||||
|
||||
vec3_t forward, right, target;
|
||||
vec3_t start;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, laser_positions[1 - (self->s.frame & 1)], forward, right);
|
||||
target = self->enemy->s.origin + self->enemy->mins;
|
||||
for (int i = 0; i < 3; i++)
|
||||
target[i] += frandom() * self->enemy->size[i];
|
||||
forward = target - start;
|
||||
forward.normalize();
|
||||
|
||||
laser->s.origin = start;
|
||||
laser->movedir = forward;
|
||||
gi.linkentity(laser);
|
||||
dabeam_update(laser, false);
|
||||
}
|
||||
|
||||
void guardian_laser_fire(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_laser, 1.f, ATTN_NORM, 0.f);
|
||||
monster_fire_dabeam(self, 25, self->s.frame & 1, guardian_fire_update);
|
||||
}
|
||||
|
||||
mframe_t guardian_frames_atk2_fire[] = {
|
||||
{ ai_charge, 0, guardian_laser_fire },
|
||||
{ ai_charge, 0, guardian_laser_fire },
|
||||
{ ai_charge, 0, guardian_laser_fire },
|
||||
{ ai_charge, 0, guardian_laser_fire }
|
||||
};
|
||||
MMOVE_T(guardian_move_atk2_fire) = { FRAME_atk2_fire1, FRAME_atk2_fire4, guardian_frames_atk2_fire, guardian_atk2_out };
|
||||
|
||||
void guardian_atk2(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &guardian_move_atk2_fire);
|
||||
}
|
||||
|
||||
mframe_t guardian_frames_atk2_in[] = {
|
||||
{ ai_charge, 0, guardian_footstep },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, guardian_footstep },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, guardian_footstep },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(guardian_move_atk2_in) = { FRAME_atk2_in1, FRAME_atk2_in12, guardian_frames_atk2_in, guardian_atk2 };
|
||||
|
||||
void guardian_kick(edict_t *self)
|
||||
{
|
||||
if (!fire_hit(self, { MELEE_DISTANCE, 0, -80 }, 85, 700))
|
||||
self->monsterinfo.melee_debounce_time = level.time + 1000_ms;
|
||||
}
|
||||
|
||||
mframe_t guardian_frames_kick[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, guardian_footstep },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, guardian_kick },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, guardian_footstep },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(guardian_move_kick) = { FRAME_kick_in1, FRAME_kick_in13, guardian_frames_kick, guardian_run };
|
||||
|
||||
MONSTERINFO_ATTACK(guardian_attack) (edict_t *self) -> void
|
||||
{
|
||||
if (!self->enemy || !self->enemy->inuse)
|
||||
return;
|
||||
|
||||
float r = range_to(self, self->enemy);
|
||||
|
||||
if (r > RANGE_NEAR)
|
||||
M_SetAnimation(self, &guardian_move_atk2_in);
|
||||
else if (self->monsterinfo.melee_debounce_time < level.time && r < 120.f)
|
||||
M_SetAnimation(self, &guardian_move_kick);
|
||||
else
|
||||
M_SetAnimation(self, &guardian_move_atk1_in);
|
||||
}
|
||||
|
||||
//
|
||||
// death
|
||||
//
|
||||
|
||||
void guardian_explode(edict_t *self)
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1_BIG);
|
||||
gi.WritePosition((self->s.origin + self->mins) + vec3_t { frandom() * self->size[0], frandom() * self->size[1], frandom() * self->size[2] });
|
||||
gi.multicast(self->s.origin, MULTICAST_ALL, false);
|
||||
}
|
||||
|
||||
constexpr const char *gibs[] = {
|
||||
"models/monsters/guardian/gib1.md2",
|
||||
"models/monsters/guardian/gib2.md2",
|
||||
"models/monsters/guardian/gib3.md2",
|
||||
"models/monsters/guardian/gib4.md2",
|
||||
"models/monsters/guardian/gib5.md2",
|
||||
"models/monsters/guardian/gib6.md2",
|
||||
"models/monsters/guardian/gib7.md2"
|
||||
};
|
||||
|
||||
void guardian_dead(edict_t *self)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
guardian_explode(self);
|
||||
|
||||
ThrowGibs(self, 125, {
|
||||
{ 2, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ 4, "models/objects/gibs/sm_metal/tris.md2", GIB_METALLIC },
|
||||
{ 2, gibs[0], GIB_METALLIC },
|
||||
{ 2, gibs[1], GIB_METALLIC },
|
||||
{ 2, gibs[2], GIB_METALLIC },
|
||||
{ 2, gibs[3], GIB_METALLIC },
|
||||
{ 2, gibs[4], GIB_METALLIC },
|
||||
{ 2, gibs[5], GIB_METALLIC },
|
||||
{ gibs[6], GIB_METALLIC | GIB_HEAD }
|
||||
});
|
||||
}
|
||||
|
||||
mframe_t guardian_frames_death1[FRAME_death26 - FRAME_death1 + 1] = {
|
||||
{ ai_move, 0, BossExplode },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(guardian_move_death) = { FRAME_death1, FRAME_death26, guardian_frames_death1, guardian_dead };
|
||||
|
||||
DIE(guardian_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
// regular death
|
||||
//gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.weapon_sound = 0;
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
|
||||
M_SetAnimation(self, &guardian_move_death);
|
||||
}
|
||||
|
||||
//
|
||||
// monster_tank
|
||||
//
|
||||
|
||||
/*QUAKED monster_guardian (1 .5 0) (-96 -96 -66) (96 96 62) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_guardian(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_step = gi.soundindex("zortemp/step.wav");
|
||||
sound_charge = gi.soundindex("weapons/hyprbu1a.wav");
|
||||
sound_spin_loop = gi.soundindex("weapons/hyprbl1a.wav");
|
||||
sound_laser = gi.soundindex("weapons/laser2.wav");
|
||||
|
||||
for (auto &gib : gibs)
|
||||
gi.modelindex(gib);
|
||||
|
||||
self->s.modelindex = gi.modelindex("models/monsters/guardian/tris.md2");
|
||||
self->mins = { -96, -96, -66 };
|
||||
self->maxs = { 96, 96, 62 };
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
|
||||
self->health = 2500 * st.health_multiplier;
|
||||
self->gib_health = -200;
|
||||
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
self->mass = 850;
|
||||
|
||||
self->pain = guardian_pain;
|
||||
self->die = guardian_die;
|
||||
self->monsterinfo.stand = guardian_stand;
|
||||
self->monsterinfo.walk = guardian_walk;
|
||||
self->monsterinfo.run = guardian_run;
|
||||
self->monsterinfo.attack = guardian_attack;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &guardian_move_stand);
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
225
rerelease/m_guardian.h
Normal file
225
rerelease/m_guardian.h
Normal file
@@ -0,0 +1,225 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/guardian
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum {
|
||||
FRAME_sleep1,
|
||||
FRAME_sleep2,
|
||||
FRAME_sleep3,
|
||||
FRAME_sleep4,
|
||||
FRAME_sleep5,
|
||||
FRAME_sleep6,
|
||||
FRAME_sleep7,
|
||||
FRAME_sleep8,
|
||||
FRAME_sleep9,
|
||||
FRAME_sleep10,
|
||||
FRAME_sleep11,
|
||||
FRAME_sleep12,
|
||||
FRAME_sleep13,
|
||||
FRAME_sleep14,
|
||||
FRAME_death1,
|
||||
FRAME_death2,
|
||||
FRAME_death3,
|
||||
FRAME_death4,
|
||||
FRAME_death5,
|
||||
FRAME_death6,
|
||||
FRAME_death7,
|
||||
FRAME_death8,
|
||||
FRAME_death9,
|
||||
FRAME_death10,
|
||||
FRAME_death11,
|
||||
FRAME_death12,
|
||||
FRAME_death13,
|
||||
FRAME_death14,
|
||||
FRAME_death15,
|
||||
FRAME_death16,
|
||||
FRAME_death17,
|
||||
FRAME_death18,
|
||||
FRAME_death19,
|
||||
FRAME_death20,
|
||||
FRAME_death21,
|
||||
FRAME_death22,
|
||||
FRAME_death23,
|
||||
FRAME_death24,
|
||||
FRAME_death25,
|
||||
FRAME_death26,
|
||||
FRAME_atk1_out1,
|
||||
FRAME_atk1_out2,
|
||||
FRAME_atk1_out3,
|
||||
FRAME_atk2_out1,
|
||||
FRAME_atk2_out2,
|
||||
FRAME_atk2_out3,
|
||||
FRAME_atk2_out4,
|
||||
FRAME_atk2_out5,
|
||||
FRAME_atk2_out6,
|
||||
FRAME_atk2_out7,
|
||||
FRAME_kick_out1,
|
||||
FRAME_kick_out2,
|
||||
FRAME_kick_out3,
|
||||
FRAME_kick_out4,
|
||||
FRAME_kick_out5,
|
||||
FRAME_kick_out6,
|
||||
FRAME_kick_out7,
|
||||
FRAME_kick_out8,
|
||||
FRAME_kick_out9,
|
||||
FRAME_kick_out10,
|
||||
FRAME_kick_out11,
|
||||
FRAME_kick_out12,
|
||||
FRAME_pain1_1,
|
||||
FRAME_pain1_2,
|
||||
FRAME_pain1_3,
|
||||
FRAME_pain1_4,
|
||||
FRAME_pain1_5,
|
||||
FRAME_pain1_6,
|
||||
FRAME_pain1_7,
|
||||
FRAME_pain1_8,
|
||||
FRAME_idle1,
|
||||
FRAME_idle2,
|
||||
FRAME_idle3,
|
||||
FRAME_idle4,
|
||||
FRAME_idle5,
|
||||
FRAME_idle6,
|
||||
FRAME_idle7,
|
||||
FRAME_idle8,
|
||||
FRAME_idle9,
|
||||
FRAME_idle10,
|
||||
FRAME_idle11,
|
||||
FRAME_idle12,
|
||||
FRAME_idle13,
|
||||
FRAME_idle14,
|
||||
FRAME_idle15,
|
||||
FRAME_idle16,
|
||||
FRAME_idle17,
|
||||
FRAME_idle18,
|
||||
FRAME_idle19,
|
||||
FRAME_idle20,
|
||||
FRAME_idle21,
|
||||
FRAME_idle22,
|
||||
FRAME_idle23,
|
||||
FRAME_idle24,
|
||||
FRAME_idle25,
|
||||
FRAME_idle26,
|
||||
FRAME_idle27,
|
||||
FRAME_idle28,
|
||||
FRAME_idle29,
|
||||
FRAME_idle30,
|
||||
FRAME_idle31,
|
||||
FRAME_idle32,
|
||||
FRAME_idle33,
|
||||
FRAME_idle34,
|
||||
FRAME_idle35,
|
||||
FRAME_idle36,
|
||||
FRAME_idle37,
|
||||
FRAME_idle38,
|
||||
FRAME_idle39,
|
||||
FRAME_idle40,
|
||||
FRAME_idle41,
|
||||
FRAME_idle42,
|
||||
FRAME_idle43,
|
||||
FRAME_idle44,
|
||||
FRAME_idle45,
|
||||
FRAME_idle46,
|
||||
FRAME_idle47,
|
||||
FRAME_idle48,
|
||||
FRAME_idle49,
|
||||
FRAME_idle50,
|
||||
FRAME_idle51,
|
||||
FRAME_idle52,
|
||||
FRAME_atk1_in1,
|
||||
FRAME_atk1_in2,
|
||||
FRAME_atk1_in3,
|
||||
FRAME_kick_in1,
|
||||
FRAME_kick_in2,
|
||||
FRAME_kick_in3,
|
||||
FRAME_kick_in4,
|
||||
FRAME_kick_in5,
|
||||
FRAME_kick_in6,
|
||||
FRAME_kick_in7,
|
||||
FRAME_kick_in8,
|
||||
FRAME_kick_in9,
|
||||
FRAME_kick_in10,
|
||||
FRAME_kick_in11,
|
||||
FRAME_kick_in12,
|
||||
FRAME_kick_in13,
|
||||
FRAME_walk1,
|
||||
FRAME_walk2,
|
||||
FRAME_walk3,
|
||||
FRAME_walk4,
|
||||
FRAME_walk5,
|
||||
FRAME_walk6,
|
||||
FRAME_walk7,
|
||||
FRAME_walk8,
|
||||
FRAME_walk9,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_walk13,
|
||||
FRAME_walk14,
|
||||
FRAME_walk15,
|
||||
FRAME_walk16,
|
||||
FRAME_walk17,
|
||||
FRAME_walk18,
|
||||
FRAME_walk19,
|
||||
FRAME_wake1,
|
||||
FRAME_wake2,
|
||||
FRAME_wake3,
|
||||
FRAME_wake4,
|
||||
FRAME_wake5,
|
||||
FRAME_atk1_spin1,
|
||||
FRAME_atk1_spin2,
|
||||
FRAME_atk1_spin3,
|
||||
FRAME_atk1_spin4,
|
||||
FRAME_atk1_spin5,
|
||||
FRAME_atk1_spin6,
|
||||
FRAME_atk1_spin7,
|
||||
FRAME_atk1_spin8,
|
||||
FRAME_atk1_spin9,
|
||||
FRAME_atk1_spin10,
|
||||
FRAME_atk1_spin11,
|
||||
FRAME_atk1_spin12,
|
||||
FRAME_atk1_spin13,
|
||||
FRAME_atk1_spin14,
|
||||
FRAME_atk1_spin15,
|
||||
FRAME_atk2_fire1,
|
||||
FRAME_atk2_fire2,
|
||||
FRAME_atk2_fire3,
|
||||
FRAME_atk2_fire4,
|
||||
FRAME_turnl_1,
|
||||
FRAME_turnl_2,
|
||||
FRAME_turnl_3,
|
||||
FRAME_turnl_4,
|
||||
FRAME_turnl_5,
|
||||
FRAME_turnl_6,
|
||||
FRAME_turnl_7,
|
||||
FRAME_turnl_8,
|
||||
FRAME_turnl_9,
|
||||
FRAME_turnl_10,
|
||||
FRAME_turnl_11,
|
||||
FRAME_turnr_1,
|
||||
FRAME_turnr_2,
|
||||
FRAME_turnr_3,
|
||||
FRAME_turnr_4,
|
||||
FRAME_turnr_5,
|
||||
FRAME_turnr_6,
|
||||
FRAME_turnr_7,
|
||||
FRAME_turnr_8,
|
||||
FRAME_turnr_9,
|
||||
FRAME_turnr_10,
|
||||
FRAME_turnr_11,
|
||||
FRAME_atk2_in1,
|
||||
FRAME_atk2_in2,
|
||||
FRAME_atk2_in3,
|
||||
FRAME_atk2_in4,
|
||||
FRAME_atk2_in5,
|
||||
FRAME_atk2_in6,
|
||||
FRAME_atk2_in7,
|
||||
FRAME_atk2_in8,
|
||||
FRAME_atk2_in9,
|
||||
FRAME_atk2_in10,
|
||||
FRAME_atk2_in11,
|
||||
FRAME_atk2_in12
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000;
|
||||
1473
rerelease/m_guncmdr.cpp
Normal file
1473
rerelease/m_guncmdr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
920
rerelease/m_gunner.cpp
Normal file
920
rerelease/m_gunner.cpp
Normal file
@@ -0,0 +1,920 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
GUNNER
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_gunner.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
static int sound_pain;
|
||||
static int sound_pain2;
|
||||
static int sound_death;
|
||||
static int sound_idle;
|
||||
static int sound_open;
|
||||
static int sound_search;
|
||||
static int sound_sight;
|
||||
|
||||
void gunner_idlesound(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_SIGHT(gunner_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_SEARCH(gunner_search) (edict_t *self) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void GunnerGrenade(edict_t *self);
|
||||
void GunnerFire(edict_t *self);
|
||||
void gunner_fire_chain(edict_t *self);
|
||||
void gunner_refire_chain(edict_t *self);
|
||||
|
||||
void gunner_stand(edict_t *self);
|
||||
|
||||
mframe_t gunner_frames_fidget[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, gunner_idlesound },
|
||||
{ ai_stand },
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(gunner_move_fidget) = { FRAME_stand31, FRAME_stand70, gunner_frames_fidget, gunner_stand };
|
||||
|
||||
void gunner_fidget(edict_t *self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
return;
|
||||
else if (self->enemy)
|
||||
return;
|
||||
if (frandom() <= 0.05f)
|
||||
M_SetAnimation(self, &gunner_move_fidget);
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, gunner_fidget },
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, gunner_fidget },
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, gunner_fidget }
|
||||
};
|
||||
MMOVE_T(gunner_move_stand) = { FRAME_stand01, FRAME_stand30, gunner_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(gunner_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &gunner_move_stand);
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_walk[] = {
|
||||
{ ai_walk },
|
||||
{ ai_walk, 3 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 7 },
|
||||
{ ai_walk, 2, monster_footstep },
|
||||
{ ai_walk, 6 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 2 },
|
||||
{ ai_walk, 7 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 7 },
|
||||
{ ai_walk, 4, monster_footstep }
|
||||
};
|
||||
MMOVE_T(gunner_move_walk) = { FRAME_walk07, FRAME_walk19, gunner_frames_walk, nullptr };
|
||||
|
||||
MONSTERINFO_WALK(gunner_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &gunner_move_walk);
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_run[] = {
|
||||
{ ai_run, 26 },
|
||||
{ ai_run, 9, monster_footstep },
|
||||
{ ai_run, 9 },
|
||||
{ ai_run, 9, monster_done_dodge },
|
||||
{ ai_run, 15 },
|
||||
{ ai_run, 10, monster_footstep },
|
||||
{ ai_run, 13 },
|
||||
{ ai_run, 6 }
|
||||
};
|
||||
|
||||
MMOVE_T(gunner_move_run) = { FRAME_run01, FRAME_run08, gunner_frames_run, nullptr };
|
||||
|
||||
MONSTERINFO_RUN(gunner_run) (edict_t *self) -> void
|
||||
{
|
||||
monster_done_dodge(self);
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &gunner_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, &gunner_move_run);
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_runandshoot[] = {
|
||||
{ ai_run, 32 },
|
||||
{ ai_run, 15 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 18 },
|
||||
{ ai_run, 8 },
|
||||
{ ai_run, 20 }
|
||||
};
|
||||
|
||||
MMOVE_T(gunner_move_runandshoot) = { FRAME_runs01, FRAME_runs06, gunner_frames_runandshoot, nullptr };
|
||||
|
||||
void gunner_runandshoot(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &gunner_move_runandshoot);
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_pain3[] = {
|
||||
{ ai_move, -3 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move },
|
||||
{ ai_move, 1 }
|
||||
};
|
||||
MMOVE_T(gunner_move_pain3) = { FRAME_pain301, FRAME_pain305, gunner_frames_pain3, gunner_run };
|
||||
|
||||
mframe_t gunner_frames_pain2[] = {
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, 11 },
|
||||
{ ai_move, 6, monster_footstep },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, -7 },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, -7, monster_footstep }
|
||||
};
|
||||
MMOVE_T(gunner_move_pain2) = { FRAME_pain201, FRAME_pain208, gunner_frames_pain2, gunner_run };
|
||||
|
||||
mframe_t gunner_frames_pain1[] = {
|
||||
{ ai_move, 2 },
|
||||
{ ai_move },
|
||||
{ ai_move, -5 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, -1, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, 1, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep }
|
||||
};
|
||||
MMOVE_T(gunner_move_pain1) = { FRAME_pain101, FRAME_pain118, gunner_frames_pain1, gunner_run };
|
||||
|
||||
extern const mmove_t gunner_move_jump;
|
||||
extern const mmove_t gunner_move_jump2;
|
||||
|
||||
PAIN(gunner_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
monster_done_dodge(self);
|
||||
|
||||
if (self->monsterinfo.active_move == &gunner_move_jump ||
|
||||
self->monsterinfo.active_move == &gunner_move_jump2)
|
||||
return;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
if (brandom())
|
||||
gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (damage <= 10)
|
||||
M_SetAnimation(self, &gunner_move_pain3);
|
||||
else if (damage <= 25)
|
||||
M_SetAnimation(self, &gunner_move_pain2);
|
||||
else
|
||||
M_SetAnimation(self, &gunner_move_pain1);
|
||||
|
||||
self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
|
||||
|
||||
// PMM - clear duck flag
|
||||
if (self->monsterinfo.aiflags & AI_DUCKED)
|
||||
monster_duck_up(self);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(gunner_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
void gunner_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, -8 };
|
||||
monster_dead(self);
|
||||
}
|
||||
|
||||
static void gunner_shrink(edict_t *self)
|
||||
{
|
||||
self->maxs[2] = -4;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_death[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move, -7, gunner_shrink },
|
||||
{ ai_move, -3 },
|
||||
{ ai_move, -5 },
|
||||
{ ai_move, 8 },
|
||||
{ ai_move, 6 },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(gunner_move_death) = { FRAME_death01, FRAME_death11, gunner_frames_death, gunner_dead };
|
||||
|
||||
DIE(gunner_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
// check for gib
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
ThrowGibs(self, damage, {
|
||||
{ 2, "models/objects/gibs/bone/tris.md2" },
|
||||
{ 2, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ "models/monsters/gunner/gibs/chest.md2", GIB_SKINNED },
|
||||
{ "models/monsters/gunner/gibs/garm.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/gunner/gibs/gun.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/gunner/gibs/foot.md2", GIB_SKINNED },
|
||||
{ "models/monsters/gunner/gibs/head.md2", GIB_SKINNED | GIB_HEAD }
|
||||
});
|
||||
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
M_SetAnimation(self, &gunner_move_death);
|
||||
}
|
||||
|
||||
// PMM - changed to duck code for new dodge
|
||||
|
||||
mframe_t gunner_frames_duck[] = {
|
||||
{ ai_move, 1, monster_duck_down },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 1, monster_duck_hold },
|
||||
{ ai_move },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, 0, monster_duck_up },
|
||||
{ ai_move, -1 }
|
||||
};
|
||||
MMOVE_T(gunner_move_duck) = { FRAME_duck01, FRAME_duck08, gunner_frames_duck, gunner_run };
|
||||
|
||||
// PMM - gunner dodge moved below so I know about attack sequences
|
||||
|
||||
void gunner_opengun(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_open, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
void GunnerFire(edict_t *self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
vec3_t aim;
|
||||
monster_muzzleflash_id_t flash_number;
|
||||
|
||||
if (!self->enemy || !self->enemy->inuse) // PGM
|
||||
return; // PGM
|
||||
|
||||
flash_number = static_cast<monster_muzzleflash_id_t>(MZ2_GUNNER_MACHINEGUN_1 + (self->s.frame - FRAME_attak216));
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[flash_number], forward, right);
|
||||
PredictAim(self, self->enemy, start, 0, true, -0.2f, &aim, nullptr);
|
||||
monster_fire_bullet(self, start, aim, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
|
||||
}
|
||||
|
||||
bool gunner_grenade_check(edict_t *self)
|
||||
{
|
||||
vec3_t dir;
|
||||
|
||||
if (!self->enemy)
|
||||
return false;
|
||||
|
||||
vec3_t start;
|
||||
|
||||
if (!M_CheckClearShot(self, monster_flash_offset[MZ2_GUNNER_GRENADE_1], start))
|
||||
return false;
|
||||
|
||||
vec3_t target;
|
||||
|
||||
// check for flag telling us that we're blindfiring
|
||||
if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
|
||||
target = self->monsterinfo.blind_fire_target;
|
||||
else
|
||||
target = self->enemy->s.origin;
|
||||
|
||||
// see if we're too close
|
||||
dir = target - start;
|
||||
|
||||
if (dir.length() < 100)
|
||||
return false;
|
||||
|
||||
// check to see that we can trace to the player before we start
|
||||
// tossing grenades around.
|
||||
vec3_t aim = dir.normalized();
|
||||
return M_CalculatePitchToFire(self, target, start, aim, 600, 2.5f, false);
|
||||
}
|
||||
|
||||
void GunnerGrenade(edict_t *self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t forward, right, up;
|
||||
vec3_t aim;
|
||||
monster_muzzleflash_id_t flash_number;
|
||||
float spread;
|
||||
float pitch = 0;
|
||||
// PMM
|
||||
vec3_t target;
|
||||
bool blindfire = false;
|
||||
|
||||
if (!self->enemy || !self->enemy->inuse) // PGM
|
||||
return; // PGM
|
||||
|
||||
// pmm
|
||||
if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
|
||||
blindfire = true;
|
||||
|
||||
if (self->s.frame == FRAME_attak105 || self->s.frame == FRAME_attak309)
|
||||
{
|
||||
spread = -0.10f;
|
||||
flash_number = MZ2_GUNNER_GRENADE_1;
|
||||
}
|
||||
else if (self->s.frame == FRAME_attak108 || self->s.frame == FRAME_attak312)
|
||||
{
|
||||
spread = -0.05f;
|
||||
flash_number = MZ2_GUNNER_GRENADE_2;
|
||||
}
|
||||
else if (self->s.frame == FRAME_attak111 || self->s.frame == FRAME_attak315)
|
||||
{
|
||||
spread = 0.05f;
|
||||
flash_number = MZ2_GUNNER_GRENADE_3;
|
||||
}
|
||||
else // (self->s.frame == FRAME_attak114)
|
||||
{
|
||||
self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
|
||||
spread = 0.10f;
|
||||
flash_number = MZ2_GUNNER_GRENADE_4;
|
||||
}
|
||||
|
||||
if (self->s.frame >= FRAME_attak301 && self->s.frame <= FRAME_attak324)
|
||||
flash_number = static_cast<monster_muzzleflash_id_t>(MZ2_GUNNER_GRENADE2_1 + (MZ2_GUNNER_GRENADE_4 - flash_number));
|
||||
|
||||
// pmm
|
||||
// if we're shooting blind and we still can't see our enemy
|
||||
if ((blindfire) && (!visible(self, self->enemy)))
|
||||
{
|
||||
// and we have a valid blind_fire_target
|
||||
if (!self->monsterinfo.blind_fire_target)
|
||||
return;
|
||||
|
||||
target = self->monsterinfo.blind_fire_target;
|
||||
}
|
||||
else
|
||||
target = self->enemy->s.origin;
|
||||
// pmm
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, up); // PGM
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[flash_number], forward, right);
|
||||
|
||||
// PGM
|
||||
if (self->enemy)
|
||||
{
|
||||
float dist;
|
||||
|
||||
aim = target - self->s.origin;
|
||||
dist = aim.length();
|
||||
|
||||
// aim up if they're on the same level as me and far away.
|
||||
if ((dist > 512) && (aim[2] < 64) && (aim[2] > -64))
|
||||
{
|
||||
aim[2] += (dist - 512);
|
||||
}
|
||||
|
||||
aim.normalize();
|
||||
pitch = aim[2];
|
||||
if (pitch > 0.4f)
|
||||
pitch = 0.4f;
|
||||
else if (pitch < -0.5f)
|
||||
pitch = -0.5f;
|
||||
}
|
||||
// PGM
|
||||
|
||||
aim = forward + (right * spread);
|
||||
aim += (up * pitch);
|
||||
|
||||
// try search for best pitch
|
||||
if (M_CalculatePitchToFire(self, target, start, aim, 600, 2.5f, false))
|
||||
monster_fire_grenade(self, start, aim, 50, 600, flash_number, (crandom_open() * 10.0f), frandom() * 10.f);
|
||||
else
|
||||
// normal shot
|
||||
monster_fire_grenade(self, start, aim, 50, 600, flash_number, (crandom_open() * 10.0f), 200.f + (crandom_open() * 10.0f));
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_attack_chain[] = {
|
||||
{ ai_charge, 0, gunner_opengun },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(gunner_move_attack_chain) = { FRAME_attak209, FRAME_attak215, gunner_frames_attack_chain, gunner_fire_chain };
|
||||
|
||||
mframe_t gunner_frames_fire_chain[] = {
|
||||
{ ai_charge, 0, GunnerFire },
|
||||
{ ai_charge, 0, GunnerFire },
|
||||
{ ai_charge, 0, GunnerFire },
|
||||
{ ai_charge, 0, GunnerFire },
|
||||
{ ai_charge, 0, GunnerFire },
|
||||
{ ai_charge, 0, GunnerFire },
|
||||
{ ai_charge, 0, GunnerFire },
|
||||
{ ai_charge, 0, GunnerFire }
|
||||
};
|
||||
MMOVE_T(gunner_move_fire_chain) = { FRAME_attak216, FRAME_attak223, gunner_frames_fire_chain, gunner_refire_chain };
|
||||
|
||||
mframe_t gunner_frames_endfire_chain[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, monster_footstep }
|
||||
};
|
||||
MMOVE_T(gunner_move_endfire_chain) = { FRAME_attak224, FRAME_attak230, gunner_frames_endfire_chain, gunner_run };
|
||||
|
||||
void gunner_blind_check(edict_t *self)
|
||||
{
|
||||
vec3_t aim;
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
|
||||
{
|
||||
aim = self->monsterinfo.blind_fire_target - self->s.origin;
|
||||
self->ideal_yaw = vectoyaw(aim);
|
||||
}
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_attack_grenade[] = {
|
||||
{ ai_charge, 0, gunner_blind_check },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, GunnerGrenade },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, GunnerGrenade },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, GunnerGrenade },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, GunnerGrenade },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(gunner_move_attack_grenade) = { FRAME_attak101, FRAME_attak121, gunner_frames_attack_grenade, gunner_run };
|
||||
|
||||
mframe_t gunner_frames_attack_grenade2[] = {
|
||||
//{ ai_charge },
|
||||
//{ ai_charge },
|
||||
//{ ai_charge },
|
||||
//{ ai_charge },
|
||||
|
||||
{ ai_charge, 0, gunner_blind_check },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, GunnerGrenade },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, GunnerGrenade },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, GunnerGrenade },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, GunnerGrenade },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(gunner_move_attack_grenade2) = { FRAME_attak305, FRAME_attak324, gunner_frames_attack_grenade2, gunner_run };
|
||||
|
||||
MONSTERINFO_ATTACK(gunner_attack) (edict_t *self) -> void
|
||||
{
|
||||
float chance, r;
|
||||
|
||||
monster_done_dodge(self);
|
||||
|
||||
// PMM
|
||||
if (self->monsterinfo.attack_state == AS_BLIND)
|
||||
{
|
||||
if (self->timestamp > level.time)
|
||||
return;
|
||||
|
||||
// setup shot probabilities
|
||||
if (self->monsterinfo.blind_fire_delay < 1_sec)
|
||||
chance = 1.0f;
|
||||
else if (self->monsterinfo.blind_fire_delay < 7.5_sec)
|
||||
chance = 0.4f;
|
||||
else
|
||||
chance = 0.1f;
|
||||
|
||||
r = frandom();
|
||||
|
||||
// minimum of 4.1 seconds, plus 0-3, after the shots are done
|
||||
self->monsterinfo.blind_fire_delay += 4.1_sec + random_time(3_sec);
|
||||
|
||||
// don't shoot at the origin
|
||||
if (!self->monsterinfo.blind_fire_target)
|
||||
return;
|
||||
|
||||
// don't shoot if the dice say not to
|
||||
if (r > chance)
|
||||
return;
|
||||
|
||||
// turn on manual steering to signal both manual steering and blindfire
|
||||
self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
|
||||
|
||||
if (gunner_grenade_check(self))
|
||||
{
|
||||
// if the check passes, go for the attack
|
||||
M_SetAnimation(self, brandom() ? &gunner_move_attack_grenade2 : &gunner_move_attack_grenade);
|
||||
self->monsterinfo.attack_finished = level.time + random_time(2_sec);
|
||||
}
|
||||
else
|
||||
// turn off blindfire flag
|
||||
self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
|
||||
|
||||
self->timestamp = level.time + random_time(2_sec, 3_sec);
|
||||
|
||||
return;
|
||||
}
|
||||
// pmm
|
||||
|
||||
// PGM - gunner needs to use his chaingun if he's being attacked by a tesla.
|
||||
if (self->bad_area || self->timestamp > level.time ||
|
||||
(range_to(self, self->enemy) <= RANGE_NEAR * 0.35f && M_CheckClearShot(self, monster_flash_offset[MZ2_GUNNER_MACHINEGUN_1])))
|
||||
{
|
||||
M_SetAnimation(self, &gunner_move_attack_chain);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self->timestamp <= level.time && frandom() <= 0.5f && gunner_grenade_check(self))
|
||||
{
|
||||
M_SetAnimation(self, brandom() ? &gunner_move_attack_grenade2 : &gunner_move_attack_grenade);
|
||||
self->timestamp = level.time + random_time(2_sec, 3_sec);
|
||||
}
|
||||
else if (M_CheckClearShot(self, monster_flash_offset[MZ2_GUNNER_MACHINEGUN_1]))
|
||||
M_SetAnimation(self, &gunner_move_attack_chain);
|
||||
}
|
||||
}
|
||||
|
||||
void gunner_fire_chain(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &gunner_move_fire_chain);
|
||||
}
|
||||
|
||||
void gunner_refire_chain(edict_t *self)
|
||||
{
|
||||
if (self->enemy->health > 0)
|
||||
if (visible(self, self->enemy))
|
||||
if (frandom() <= 0.5f)
|
||||
{
|
||||
M_SetAnimation(self, &gunner_move_fire_chain, false);
|
||||
return;
|
||||
}
|
||||
M_SetAnimation(self, &gunner_move_endfire_chain, false);
|
||||
}
|
||||
|
||||
//===========
|
||||
// PGM
|
||||
void gunner_jump_now(edict_t *self)
|
||||
{
|
||||
vec3_t forward, up;
|
||||
|
||||
AngleVectors(self->s.angles, forward, nullptr, up);
|
||||
self->velocity += (forward * 100);
|
||||
self->velocity += (up * 300);
|
||||
}
|
||||
|
||||
void gunner_jump2_now(edict_t *self)
|
||||
{
|
||||
vec3_t forward, up;
|
||||
|
||||
AngleVectors(self->s.angles, forward, nullptr, up);
|
||||
self->velocity += (forward * 150);
|
||||
self->velocity += (up * 400);
|
||||
}
|
||||
|
||||
void gunner_jump_wait_land(edict_t *self)
|
||||
{
|
||||
if (self->groundentity == nullptr)
|
||||
{
|
||||
self->monsterinfo.nextframe = self->s.frame;
|
||||
|
||||
if (monster_jump_finished(self))
|
||||
self->monsterinfo.nextframe = self->s.frame + 1;
|
||||
}
|
||||
else
|
||||
self->monsterinfo.nextframe = self->s.frame + 1;
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_jump[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, gunner_jump_now },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, gunner_jump_wait_land },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(gunner_move_jump) = { FRAME_jump01, FRAME_jump10, gunner_frames_jump, gunner_run };
|
||||
|
||||
mframe_t gunner_frames_jump2[] = {
|
||||
{ ai_move, -8 },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, 0, gunner_jump2_now },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, gunner_jump_wait_land },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(gunner_move_jump2) = { FRAME_jump01, FRAME_jump10, gunner_frames_jump2, gunner_run };
|
||||
|
||||
void gunner_jump(edict_t *self, blocked_jump_result_t result)
|
||||
{
|
||||
if (!self->enemy)
|
||||
return;
|
||||
|
||||
monster_done_dodge(self);
|
||||
|
||||
if (result == blocked_jump_result_t::JUMP_JUMP_UP)
|
||||
M_SetAnimation(self, &gunner_move_jump2);
|
||||
else
|
||||
M_SetAnimation(self, &gunner_move_jump);
|
||||
}
|
||||
|
||||
//===========
|
||||
// PGM
|
||||
MONSTERINFO_BLOCKED(gunner_blocked) (edict_t *self, float dist) -> bool
|
||||
{
|
||||
if (blocked_checkplat(self, dist))
|
||||
return true;
|
||||
|
||||
if (auto result = blocked_checkjump(self, dist); result != blocked_jump_result_t::NO_JUMP)
|
||||
{
|
||||
if (result != blocked_jump_result_t::JUMP_TURN)
|
||||
gunner_jump(self, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// PGM
|
||||
//===========
|
||||
|
||||
// PMM - new duck code
|
||||
MONSTERINFO_DUCK(gunner_duck) (edict_t *self, gtime_t eta) -> bool
|
||||
{
|
||||
if ((self->monsterinfo.active_move == &gunner_move_jump2) ||
|
||||
(self->monsterinfo.active_move == &gunner_move_jump))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((self->monsterinfo.active_move == &gunner_move_attack_chain) ||
|
||||
(self->monsterinfo.active_move == &gunner_move_fire_chain) ||
|
||||
(self->monsterinfo.active_move == &gunner_move_attack_grenade) ||
|
||||
(self->monsterinfo.active_move == &gunner_move_attack_grenade2))
|
||||
{
|
||||
// if we're shooting don't dodge
|
||||
self->monsterinfo.unduck(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frandom() > 0.5f)
|
||||
GunnerGrenade(self);
|
||||
|
||||
M_SetAnimation(self, &gunner_move_duck);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MONSTERINFO_SIDESTEP(gunner_sidestep) (edict_t *self) -> bool
|
||||
{
|
||||
if ((self->monsterinfo.active_move == &gunner_move_jump2) ||
|
||||
(self->monsterinfo.active_move == &gunner_move_jump) ||
|
||||
(self->monsterinfo.active_move == &gunner_move_pain1))
|
||||
return false;
|
||||
|
||||
if ((self->monsterinfo.active_move == &gunner_move_attack_chain) ||
|
||||
(self->monsterinfo.active_move == &gunner_move_fire_chain) ||
|
||||
(self->monsterinfo.active_move == &gunner_move_attack_grenade) ||
|
||||
(self->monsterinfo.active_move == &gunner_move_attack_grenade2))
|
||||
{
|
||||
// if we're shooting, don't dodge
|
||||
return false;
|
||||
}
|
||||
|
||||
if (self->monsterinfo.active_move != &gunner_move_run)
|
||||
M_SetAnimation(self, &gunner_move_run);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_GUNNER_NOJUMPING = 8_spawnflag;
|
||||
|
||||
/*QUAKED monster_gunner (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight NoJumping
|
||||
model="models/monsters/gunner/tris.md2"
|
||||
*/
|
||||
void SP_monster_gunner(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_death = gi.soundindex("gunner/death1.wav");
|
||||
sound_pain = gi.soundindex("gunner/gunpain2.wav");
|
||||
sound_pain2 = gi.soundindex("gunner/gunpain1.wav");
|
||||
sound_idle = gi.soundindex("gunner/gunidle1.wav");
|
||||
sound_open = gi.soundindex("gunner/gunatck1.wav");
|
||||
sound_search = gi.soundindex("gunner/gunsrch1.wav");
|
||||
sound_sight = gi.soundindex("gunner/sight1.wav");
|
||||
|
||||
gi.soundindex("gunner/gunatck2.wav");
|
||||
gi.soundindex("gunner/gunatck3.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/gunner/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/gunner/gibs/chest.md2");
|
||||
gi.modelindex("models/monsters/gunner/gibs/foot.md2");
|
||||
gi.modelindex("models/monsters/gunner/gibs/garm.md2");
|
||||
gi.modelindex("models/monsters/gunner/gibs/gun.md2");
|
||||
gi.modelindex("models/monsters/gunner/gibs/head.md2");
|
||||
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, 36 };
|
||||
|
||||
self->health = 175 * st.health_multiplier;
|
||||
self->gib_health = -70;
|
||||
self->mass = 200;
|
||||
|
||||
self->pain = gunner_pain;
|
||||
self->die = gunner_die;
|
||||
|
||||
self->monsterinfo.stand = gunner_stand;
|
||||
self->monsterinfo.walk = gunner_walk;
|
||||
self->monsterinfo.run = gunner_run;
|
||||
// pmm
|
||||
self->monsterinfo.dodge = M_MonsterDodge;
|
||||
self->monsterinfo.duck = gunner_duck;
|
||||
self->monsterinfo.unduck = monster_duck_up;
|
||||
self->monsterinfo.sidestep = gunner_sidestep;
|
||||
self->monsterinfo.blocked = gunner_blocked; // PGM
|
||||
// pmm
|
||||
self->monsterinfo.attack = gunner_attack;
|
||||
self->monsterinfo.melee = nullptr;
|
||||
self->monsterinfo.sight = gunner_sight;
|
||||
self->monsterinfo.search = gunner_search;
|
||||
self->monsterinfo.setskin = gunner_setskin;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &gunner_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
// PMM
|
||||
self->monsterinfo.blindfire = true;
|
||||
self->monsterinfo.can_jump = !self->spawnflags.has(SPAWNFLAG_GUNNER_NOJUMPING);
|
||||
self->monsterinfo.drop_height = 192;
|
||||
self->monsterinfo.jump_height = 40;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
809
rerelease/m_gunner.h
Normal file
809
rerelease/m_gunner.h
Normal file
@@ -0,0 +1,809 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// E:\G Drive\md2f\quake2\baseq2\models/monsters/gunner
|
||||
|
||||
// This file generated by qdata - Do NOT Modify
|
||||
|
||||
enum {
|
||||
FRAME_stand01,
|
||||
FRAME_stand02,
|
||||
FRAME_stand03,
|
||||
FRAME_stand04,
|
||||
FRAME_stand05,
|
||||
FRAME_stand06,
|
||||
FRAME_stand07,
|
||||
FRAME_stand08,
|
||||
FRAME_stand09,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_stand18,
|
||||
FRAME_stand19,
|
||||
FRAME_stand20,
|
||||
FRAME_stand21,
|
||||
FRAME_stand22,
|
||||
FRAME_stand23,
|
||||
FRAME_stand24,
|
||||
FRAME_stand25,
|
||||
FRAME_stand26,
|
||||
FRAME_stand27,
|
||||
FRAME_stand28,
|
||||
FRAME_stand29,
|
||||
FRAME_stand30,
|
||||
FRAME_stand31,
|
||||
FRAME_stand32,
|
||||
FRAME_stand33,
|
||||
FRAME_stand34,
|
||||
FRAME_stand35,
|
||||
FRAME_stand36,
|
||||
FRAME_stand37,
|
||||
FRAME_stand38,
|
||||
FRAME_stand39,
|
||||
FRAME_stand40,
|
||||
FRAME_stand41,
|
||||
FRAME_stand42,
|
||||
FRAME_stand43,
|
||||
FRAME_stand44,
|
||||
FRAME_stand45,
|
||||
FRAME_stand46,
|
||||
FRAME_stand47,
|
||||
FRAME_stand48,
|
||||
FRAME_stand49,
|
||||
FRAME_stand50,
|
||||
FRAME_stand51,
|
||||
FRAME_stand52,
|
||||
FRAME_stand53,
|
||||
FRAME_stand54,
|
||||
FRAME_stand55,
|
||||
FRAME_stand56,
|
||||
FRAME_stand57,
|
||||
FRAME_stand58,
|
||||
FRAME_stand59,
|
||||
FRAME_stand60,
|
||||
FRAME_stand61,
|
||||
FRAME_stand62,
|
||||
FRAME_stand63,
|
||||
FRAME_stand64,
|
||||
FRAME_stand65,
|
||||
FRAME_stand66,
|
||||
FRAME_stand67,
|
||||
FRAME_stand68,
|
||||
FRAME_stand69,
|
||||
FRAME_stand70,
|
||||
FRAME_walk01,
|
||||
FRAME_walk02,
|
||||
FRAME_walk03,
|
||||
FRAME_walk04,
|
||||
FRAME_walk05,
|
||||
FRAME_walk06,
|
||||
FRAME_walk07,
|
||||
FRAME_walk08,
|
||||
FRAME_walk09,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_walk13,
|
||||
FRAME_walk14,
|
||||
FRAME_walk15,
|
||||
FRAME_walk16,
|
||||
FRAME_walk17,
|
||||
FRAME_walk18,
|
||||
FRAME_walk19,
|
||||
FRAME_walk20,
|
||||
FRAME_walk21,
|
||||
FRAME_walk22,
|
||||
FRAME_walk23,
|
||||
FRAME_walk24,
|
||||
FRAME_run01,
|
||||
FRAME_run02,
|
||||
FRAME_run03,
|
||||
FRAME_run04,
|
||||
FRAME_run05,
|
||||
FRAME_run06,
|
||||
FRAME_run07,
|
||||
FRAME_run08,
|
||||
FRAME_runs01,
|
||||
FRAME_runs02,
|
||||
FRAME_runs03,
|
||||
FRAME_runs04,
|
||||
FRAME_runs05,
|
||||
FRAME_runs06,
|
||||
FRAME_attak101,
|
||||
FRAME_attak102,
|
||||
FRAME_attak103,
|
||||
FRAME_attak104,
|
||||
FRAME_attak105,
|
||||
FRAME_attak106,
|
||||
FRAME_attak107,
|
||||
FRAME_attak108,
|
||||
FRAME_attak109,
|
||||
FRAME_attak110,
|
||||
FRAME_attak111,
|
||||
FRAME_attak112,
|
||||
FRAME_attak113,
|
||||
FRAME_attak114,
|
||||
FRAME_attak115,
|
||||
FRAME_attak116,
|
||||
FRAME_attak117,
|
||||
FRAME_attak118,
|
||||
FRAME_attak119,
|
||||
FRAME_attak120,
|
||||
FRAME_attak121,
|
||||
FRAME_attak201,
|
||||
FRAME_attak202,
|
||||
FRAME_attak203,
|
||||
FRAME_attak204,
|
||||
FRAME_attak205,
|
||||
FRAME_attak206,
|
||||
FRAME_attak207,
|
||||
FRAME_attak208,
|
||||
FRAME_attak209,
|
||||
FRAME_attak210,
|
||||
FRAME_attak211,
|
||||
FRAME_attak212,
|
||||
FRAME_attak213,
|
||||
FRAME_attak214,
|
||||
FRAME_attak215,
|
||||
FRAME_attak216,
|
||||
FRAME_attak217,
|
||||
FRAME_attak218,
|
||||
FRAME_attak219,
|
||||
FRAME_attak220,
|
||||
FRAME_attak221,
|
||||
FRAME_attak222,
|
||||
FRAME_attak223,
|
||||
FRAME_attak224,
|
||||
FRAME_attak225,
|
||||
FRAME_attak226,
|
||||
FRAME_attak227,
|
||||
FRAME_attak228,
|
||||
FRAME_attak229,
|
||||
FRAME_attak230,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain104,
|
||||
FRAME_pain105,
|
||||
FRAME_pain106,
|
||||
FRAME_pain107,
|
||||
FRAME_pain108,
|
||||
FRAME_pain109,
|
||||
FRAME_pain110,
|
||||
FRAME_pain111,
|
||||
FRAME_pain112,
|
||||
FRAME_pain113,
|
||||
FRAME_pain114,
|
||||
FRAME_pain115,
|
||||
FRAME_pain116,
|
||||
FRAME_pain117,
|
||||
FRAME_pain118,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain204,
|
||||
FRAME_pain205,
|
||||
FRAME_pain206,
|
||||
FRAME_pain207,
|
||||
FRAME_pain208,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_pain304,
|
||||
FRAME_pain305,
|
||||
FRAME_death01,
|
||||
FRAME_death02,
|
||||
FRAME_death03,
|
||||
FRAME_death04,
|
||||
FRAME_death05,
|
||||
FRAME_death06,
|
||||
FRAME_death07,
|
||||
FRAME_death08,
|
||||
FRAME_death09,
|
||||
FRAME_death10,
|
||||
FRAME_death11,
|
||||
FRAME_duck01,
|
||||
FRAME_duck02,
|
||||
FRAME_duck03,
|
||||
FRAME_duck04,
|
||||
FRAME_duck05,
|
||||
FRAME_duck06,
|
||||
FRAME_duck07,
|
||||
FRAME_duck08,
|
||||
FRAME_jump01,
|
||||
FRAME_jump02,
|
||||
FRAME_jump03,
|
||||
FRAME_jump04,
|
||||
FRAME_jump05,
|
||||
FRAME_jump06,
|
||||
FRAME_jump07,
|
||||
FRAME_jump08,
|
||||
FRAME_jump09,
|
||||
FRAME_jump10,
|
||||
FRAME_shield01,
|
||||
FRAME_shield02,
|
||||
FRAME_shield03,
|
||||
FRAME_shield04,
|
||||
FRAME_shield05,
|
||||
FRAME_shield06,
|
||||
FRAME_attak301,
|
||||
FRAME_attak302,
|
||||
FRAME_attak303,
|
||||
FRAME_attak304,
|
||||
FRAME_attak305,
|
||||
FRAME_attak306,
|
||||
FRAME_attak307,
|
||||
FRAME_attak308,
|
||||
FRAME_attak309,
|
||||
FRAME_attak310,
|
||||
FRAME_attak311,
|
||||
FRAME_attak312,
|
||||
FRAME_attak313,
|
||||
FRAME_attak314,
|
||||
FRAME_attak315,
|
||||
FRAME_attak316,
|
||||
FRAME_attak317,
|
||||
FRAME_attak318,
|
||||
FRAME_attak319,
|
||||
FRAME_attak320,
|
||||
FRAME_attak321,
|
||||
FRAME_attak322,
|
||||
FRAME_attak323,
|
||||
FRAME_attak324,
|
||||
FRAME_c_stand101,
|
||||
FRAME_c_stand102,
|
||||
FRAME_c_stand103,
|
||||
FRAME_c_stand104,
|
||||
FRAME_c_stand105,
|
||||
FRAME_c_stand106,
|
||||
FRAME_c_stand107,
|
||||
FRAME_c_stand108,
|
||||
FRAME_c_stand109,
|
||||
FRAME_c_stand110,
|
||||
FRAME_c_stand111,
|
||||
FRAME_c_stand112,
|
||||
FRAME_c_stand113,
|
||||
FRAME_c_stand114,
|
||||
FRAME_c_stand115,
|
||||
FRAME_c_stand116,
|
||||
FRAME_c_stand117,
|
||||
FRAME_c_stand118,
|
||||
FRAME_c_stand119,
|
||||
FRAME_c_stand120,
|
||||
FRAME_c_stand121,
|
||||
FRAME_c_stand122,
|
||||
FRAME_c_stand123,
|
||||
FRAME_c_stand124,
|
||||
FRAME_c_stand125,
|
||||
FRAME_c_stand126,
|
||||
FRAME_c_stand127,
|
||||
FRAME_c_stand128,
|
||||
FRAME_c_stand129,
|
||||
FRAME_c_stand130,
|
||||
FRAME_c_stand131,
|
||||
FRAME_c_stand132,
|
||||
FRAME_c_stand133,
|
||||
FRAME_c_stand134,
|
||||
FRAME_c_stand135,
|
||||
FRAME_c_stand136,
|
||||
FRAME_c_stand137,
|
||||
FRAME_c_stand138,
|
||||
FRAME_c_stand139,
|
||||
FRAME_c_stand140,
|
||||
FRAME_c_stand201,
|
||||
FRAME_c_stand202,
|
||||
FRAME_c_stand203,
|
||||
FRAME_c_stand204,
|
||||
FRAME_c_stand205,
|
||||
FRAME_c_stand206,
|
||||
FRAME_c_stand207,
|
||||
FRAME_c_stand208,
|
||||
FRAME_c_stand209,
|
||||
FRAME_c_stand210,
|
||||
FRAME_c_stand211,
|
||||
FRAME_c_stand212,
|
||||
FRAME_c_stand213,
|
||||
FRAME_c_stand214,
|
||||
FRAME_c_stand215,
|
||||
FRAME_c_stand216,
|
||||
FRAME_c_stand217,
|
||||
FRAME_c_stand218,
|
||||
FRAME_c_stand219,
|
||||
FRAME_c_stand220,
|
||||
FRAME_c_stand221,
|
||||
FRAME_c_stand222,
|
||||
FRAME_c_stand223,
|
||||
FRAME_c_stand224,
|
||||
FRAME_c_stand225,
|
||||
FRAME_c_stand226,
|
||||
FRAME_c_stand227,
|
||||
FRAME_c_stand228,
|
||||
FRAME_c_stand229,
|
||||
FRAME_c_stand230,
|
||||
FRAME_c_stand231,
|
||||
FRAME_c_stand232,
|
||||
FRAME_c_stand233,
|
||||
FRAME_c_stand234,
|
||||
FRAME_c_stand235,
|
||||
FRAME_c_stand236,
|
||||
FRAME_c_stand237,
|
||||
FRAME_c_stand238,
|
||||
FRAME_c_stand239,
|
||||
FRAME_c_stand240,
|
||||
FRAME_c_stand241,
|
||||
FRAME_c_stand242,
|
||||
FRAME_c_stand243,
|
||||
FRAME_c_stand244,
|
||||
FRAME_c_stand245,
|
||||
FRAME_c_stand246,
|
||||
FRAME_c_stand247,
|
||||
FRAME_c_stand248,
|
||||
FRAME_c_stand249,
|
||||
FRAME_c_stand250,
|
||||
FRAME_c_stand251,
|
||||
FRAME_c_stand252,
|
||||
FRAME_c_stand253,
|
||||
FRAME_c_stand254,
|
||||
FRAME_c_attack101,
|
||||
FRAME_c_attack102,
|
||||
FRAME_c_attack103,
|
||||
FRAME_c_attack104,
|
||||
FRAME_c_attack105,
|
||||
FRAME_c_attack106,
|
||||
FRAME_c_attack107,
|
||||
FRAME_c_attack108,
|
||||
FRAME_c_attack109,
|
||||
FRAME_c_attack110,
|
||||
FRAME_c_attack111,
|
||||
FRAME_c_attack112,
|
||||
FRAME_c_attack113,
|
||||
FRAME_c_attack114,
|
||||
FRAME_c_attack115,
|
||||
FRAME_c_attack116,
|
||||
FRAME_c_attack117,
|
||||
FRAME_c_attack118,
|
||||
FRAME_c_attack119,
|
||||
FRAME_c_attack120,
|
||||
FRAME_c_attack121,
|
||||
FRAME_c_attack122,
|
||||
FRAME_c_attack123,
|
||||
FRAME_c_attack124,
|
||||
FRAME_c_jump01,
|
||||
FRAME_c_jump02,
|
||||
FRAME_c_jump03,
|
||||
FRAME_c_jump04,
|
||||
FRAME_c_jump05,
|
||||
FRAME_c_jump06,
|
||||
FRAME_c_jump07,
|
||||
FRAME_c_jump08,
|
||||
FRAME_c_jump09,
|
||||
FRAME_c_jump10,
|
||||
FRAME_c_attack201,
|
||||
FRAME_c_attack202,
|
||||
FRAME_c_attack203,
|
||||
FRAME_c_attack204,
|
||||
FRAME_c_attack205,
|
||||
FRAME_c_attack206,
|
||||
FRAME_c_attack207,
|
||||
FRAME_c_attack208,
|
||||
FRAME_c_attack209,
|
||||
FRAME_c_attack210,
|
||||
FRAME_c_attack211,
|
||||
FRAME_c_attack212,
|
||||
FRAME_c_attack213,
|
||||
FRAME_c_attack214,
|
||||
FRAME_c_attack215,
|
||||
FRAME_c_attack216,
|
||||
FRAME_c_attack217,
|
||||
FRAME_c_attack218,
|
||||
FRAME_c_attack219,
|
||||
FRAME_c_attack220,
|
||||
FRAME_c_attack221,
|
||||
FRAME_c_attack301,
|
||||
FRAME_c_attack302,
|
||||
FRAME_c_attack303,
|
||||
FRAME_c_attack304,
|
||||
FRAME_c_attack305,
|
||||
FRAME_c_attack306,
|
||||
FRAME_c_attack307,
|
||||
FRAME_c_attack308,
|
||||
FRAME_c_attack309,
|
||||
FRAME_c_attack310,
|
||||
FRAME_c_attack311,
|
||||
FRAME_c_attack312,
|
||||
FRAME_c_attack313,
|
||||
FRAME_c_attack314,
|
||||
FRAME_c_attack315,
|
||||
FRAME_c_attack316,
|
||||
FRAME_c_attack317,
|
||||
FRAME_c_attack318,
|
||||
FRAME_c_attack319,
|
||||
FRAME_c_attack320,
|
||||
FRAME_c_attack321,
|
||||
FRAME_c_attack401,
|
||||
FRAME_c_attack402,
|
||||
FRAME_c_attack403,
|
||||
FRAME_c_attack404,
|
||||
FRAME_c_attack405,
|
||||
FRAME_c_attack501,
|
||||
FRAME_c_attack502,
|
||||
FRAME_c_attack503,
|
||||
FRAME_c_attack504,
|
||||
FRAME_c_attack505,
|
||||
FRAME_c_attack601,
|
||||
FRAME_c_attack602,
|
||||
FRAME_c_attack603,
|
||||
FRAME_c_attack604,
|
||||
FRAME_c_attack605,
|
||||
FRAME_c_attack701,
|
||||
FRAME_c_attack702,
|
||||
FRAME_c_attack703,
|
||||
FRAME_c_attack704,
|
||||
FRAME_c_attack705,
|
||||
FRAME_c_pain101,
|
||||
FRAME_c_pain102,
|
||||
FRAME_c_pain103,
|
||||
FRAME_c_pain104,
|
||||
FRAME_c_pain201,
|
||||
FRAME_c_pain202,
|
||||
FRAME_c_pain203,
|
||||
FRAME_c_pain204,
|
||||
FRAME_c_pain301,
|
||||
FRAME_c_pain302,
|
||||
FRAME_c_pain303,
|
||||
FRAME_c_pain304,
|
||||
FRAME_c_pain401,
|
||||
FRAME_c_pain402,
|
||||
FRAME_c_pain403,
|
||||
FRAME_c_pain404,
|
||||
FRAME_c_pain405,
|
||||
FRAME_c_pain406,
|
||||
FRAME_c_pain407,
|
||||
FRAME_c_pain408,
|
||||
FRAME_c_pain409,
|
||||
FRAME_c_pain410,
|
||||
FRAME_c_pain411,
|
||||
FRAME_c_pain412,
|
||||
FRAME_c_pain413,
|
||||
FRAME_c_pain414,
|
||||
FRAME_c_pain415,
|
||||
FRAME_c_pain501,
|
||||
FRAME_c_pain502,
|
||||
FRAME_c_pain503,
|
||||
FRAME_c_pain504,
|
||||
FRAME_c_pain505,
|
||||
FRAME_c_pain506,
|
||||
FRAME_c_pain507,
|
||||
FRAME_c_pain508,
|
||||
FRAME_c_pain509,
|
||||
FRAME_c_pain510,
|
||||
FRAME_c_pain511,
|
||||
FRAME_c_pain512,
|
||||
FRAME_c_pain513,
|
||||
FRAME_c_pain514,
|
||||
FRAME_c_pain515,
|
||||
FRAME_c_pain516,
|
||||
FRAME_c_pain517,
|
||||
FRAME_c_pain518,
|
||||
FRAME_c_pain519,
|
||||
FRAME_c_pain520,
|
||||
FRAME_c_pain521,
|
||||
FRAME_c_pain522,
|
||||
FRAME_c_pain523,
|
||||
FRAME_c_pain524,
|
||||
FRAME_c_death101,
|
||||
FRAME_c_death102,
|
||||
FRAME_c_death103,
|
||||
FRAME_c_death104,
|
||||
FRAME_c_death105,
|
||||
FRAME_c_death106,
|
||||
FRAME_c_death107,
|
||||
FRAME_c_death108,
|
||||
FRAME_c_death109,
|
||||
FRAME_c_death110,
|
||||
FRAME_c_death111,
|
||||
FRAME_c_death112,
|
||||
FRAME_c_death113,
|
||||
FRAME_c_death114,
|
||||
FRAME_c_death115,
|
||||
FRAME_c_death116,
|
||||
FRAME_c_death117,
|
||||
FRAME_c_death118,
|
||||
FRAME_c_death201,
|
||||
FRAME_c_death202,
|
||||
FRAME_c_death203,
|
||||
FRAME_c_death204,
|
||||
FRAME_c_death301,
|
||||
FRAME_c_death302,
|
||||
FRAME_c_death303,
|
||||
FRAME_c_death304,
|
||||
FRAME_c_death305,
|
||||
FRAME_c_death306,
|
||||
FRAME_c_death307,
|
||||
FRAME_c_death308,
|
||||
FRAME_c_death309,
|
||||
FRAME_c_death310,
|
||||
FRAME_c_death311,
|
||||
FRAME_c_death312,
|
||||
FRAME_c_death313,
|
||||
FRAME_c_death314,
|
||||
FRAME_c_death315,
|
||||
FRAME_c_death316,
|
||||
FRAME_c_death317,
|
||||
FRAME_c_death318,
|
||||
FRAME_c_death319,
|
||||
FRAME_c_death320,
|
||||
FRAME_c_death321,
|
||||
FRAME_c_death401,
|
||||
FRAME_c_death402,
|
||||
FRAME_c_death403,
|
||||
FRAME_c_death404,
|
||||
FRAME_c_death405,
|
||||
FRAME_c_death406,
|
||||
FRAME_c_death407,
|
||||
FRAME_c_death408,
|
||||
FRAME_c_death409,
|
||||
FRAME_c_death410,
|
||||
FRAME_c_death411,
|
||||
FRAME_c_death412,
|
||||
FRAME_c_death413,
|
||||
FRAME_c_death414,
|
||||
FRAME_c_death415,
|
||||
FRAME_c_death416,
|
||||
FRAME_c_death417,
|
||||
FRAME_c_death418,
|
||||
FRAME_c_death419,
|
||||
FRAME_c_death420,
|
||||
FRAME_c_death421,
|
||||
FRAME_c_death422,
|
||||
FRAME_c_death423,
|
||||
FRAME_c_death424,
|
||||
FRAME_c_death425,
|
||||
FRAME_c_death426,
|
||||
FRAME_c_death427,
|
||||
FRAME_c_death428,
|
||||
FRAME_c_death429,
|
||||
FRAME_c_death430,
|
||||
FRAME_c_death431,
|
||||
FRAME_c_death432,
|
||||
FRAME_c_death433,
|
||||
FRAME_c_death434,
|
||||
FRAME_c_death435,
|
||||
FRAME_c_death436,
|
||||
FRAME_c_death501,
|
||||
FRAME_c_death502,
|
||||
FRAME_c_death503,
|
||||
FRAME_c_death504,
|
||||
FRAME_c_death505,
|
||||
FRAME_c_death506,
|
||||
FRAME_c_death507,
|
||||
FRAME_c_death508,
|
||||
FRAME_c_death509,
|
||||
FRAME_c_death510,
|
||||
FRAME_c_death511,
|
||||
FRAME_c_death512,
|
||||
FRAME_c_death513,
|
||||
FRAME_c_death514,
|
||||
FRAME_c_death515,
|
||||
FRAME_c_death516,
|
||||
FRAME_c_death517,
|
||||
FRAME_c_death518,
|
||||
FRAME_c_death519,
|
||||
FRAME_c_death520,
|
||||
FRAME_c_death521,
|
||||
FRAME_c_death522,
|
||||
FRAME_c_death523,
|
||||
FRAME_c_death524,
|
||||
FRAME_c_death525,
|
||||
FRAME_c_death526,
|
||||
FRAME_c_death527,
|
||||
FRAME_c_death528,
|
||||
FRAME_c_run101,
|
||||
FRAME_c_run102,
|
||||
FRAME_c_run103,
|
||||
FRAME_c_run104,
|
||||
FRAME_c_run105,
|
||||
FRAME_c_run106,
|
||||
FRAME_c_run201,
|
||||
FRAME_c_run202,
|
||||
FRAME_c_run203,
|
||||
FRAME_c_run204,
|
||||
FRAME_c_run205,
|
||||
FRAME_c_run206,
|
||||
FRAME_c_run301,
|
||||
FRAME_c_run302,
|
||||
FRAME_c_run303,
|
||||
FRAME_c_run304,
|
||||
FRAME_c_run305,
|
||||
FRAME_c_run306,
|
||||
FRAME_c_walk101,
|
||||
FRAME_c_walk102,
|
||||
FRAME_c_walk103,
|
||||
FRAME_c_walk104,
|
||||
FRAME_c_walk105,
|
||||
FRAME_c_walk106,
|
||||
FRAME_c_walk107,
|
||||
FRAME_c_walk108,
|
||||
FRAME_c_walk109,
|
||||
FRAME_c_walk110,
|
||||
FRAME_c_walk111,
|
||||
FRAME_c_walk112,
|
||||
FRAME_c_walk113,
|
||||
FRAME_c_walk114,
|
||||
FRAME_c_walk115,
|
||||
FRAME_c_walk116,
|
||||
FRAME_c_walk117,
|
||||
FRAME_c_walk118,
|
||||
FRAME_c_walk119,
|
||||
FRAME_c_walk120,
|
||||
FRAME_c_walk121,
|
||||
FRAME_c_walk122,
|
||||
FRAME_c_walk123,
|
||||
FRAME_c_walk124,
|
||||
FRAME_c_pain601,
|
||||
FRAME_c_pain602,
|
||||
FRAME_c_pain603,
|
||||
FRAME_c_pain604,
|
||||
FRAME_c_pain605,
|
||||
FRAME_c_pain606,
|
||||
FRAME_c_pain607,
|
||||
FRAME_c_pain608,
|
||||
FRAME_c_pain609,
|
||||
FRAME_c_pain610,
|
||||
FRAME_c_pain611,
|
||||
FRAME_c_pain612,
|
||||
FRAME_c_pain613,
|
||||
FRAME_c_pain614,
|
||||
FRAME_c_pain615,
|
||||
FRAME_c_pain616,
|
||||
FRAME_c_pain617,
|
||||
FRAME_c_pain618,
|
||||
FRAME_c_pain619,
|
||||
FRAME_c_pain620,
|
||||
FRAME_c_pain621,
|
||||
FRAME_c_pain622,
|
||||
FRAME_c_pain623,
|
||||
FRAME_c_pain624,
|
||||
FRAME_c_pain625,
|
||||
FRAME_c_pain626,
|
||||
FRAME_c_pain627,
|
||||
FRAME_c_pain628,
|
||||
FRAME_c_pain629,
|
||||
FRAME_c_pain630,
|
||||
FRAME_c_pain631,
|
||||
FRAME_c_pain632,
|
||||
FRAME_c_death601,
|
||||
FRAME_c_death602,
|
||||
FRAME_c_death603,
|
||||
FRAME_c_death604,
|
||||
FRAME_c_death605,
|
||||
FRAME_c_death606,
|
||||
FRAME_c_death607,
|
||||
FRAME_c_death608,
|
||||
FRAME_c_death609,
|
||||
FRAME_c_death610,
|
||||
FRAME_c_death611,
|
||||
FRAME_c_death612,
|
||||
FRAME_c_death613,
|
||||
FRAME_c_death614,
|
||||
FRAME_c_death701,
|
||||
FRAME_c_death702,
|
||||
FRAME_c_death703,
|
||||
FRAME_c_death704,
|
||||
FRAME_c_death705,
|
||||
FRAME_c_death706,
|
||||
FRAME_c_death707,
|
||||
FRAME_c_death708,
|
||||
FRAME_c_death709,
|
||||
FRAME_c_death710,
|
||||
FRAME_c_death711,
|
||||
FRAME_c_death712,
|
||||
FRAME_c_death713,
|
||||
FRAME_c_death714,
|
||||
FRAME_c_death715,
|
||||
FRAME_c_death716,
|
||||
FRAME_c_death717,
|
||||
FRAME_c_death718,
|
||||
FRAME_c_death719,
|
||||
FRAME_c_death720,
|
||||
FRAME_c_death721,
|
||||
FRAME_c_death722,
|
||||
FRAME_c_death723,
|
||||
FRAME_c_death724,
|
||||
FRAME_c_death725,
|
||||
FRAME_c_death726,
|
||||
FRAME_c_death727,
|
||||
FRAME_c_death728,
|
||||
FRAME_c_death729,
|
||||
FRAME_c_death730,
|
||||
FRAME_c_pain701,
|
||||
FRAME_c_pain702,
|
||||
FRAME_c_pain703,
|
||||
FRAME_c_pain704,
|
||||
FRAME_c_pain705,
|
||||
FRAME_c_pain706,
|
||||
FRAME_c_pain707,
|
||||
FRAME_c_pain708,
|
||||
FRAME_c_pain709,
|
||||
FRAME_c_pain710,
|
||||
FRAME_c_pain711,
|
||||
FRAME_c_pain712,
|
||||
FRAME_c_pain713,
|
||||
FRAME_c_pain714,
|
||||
FRAME_c_attack801,
|
||||
FRAME_c_attack802,
|
||||
FRAME_c_attack803,
|
||||
FRAME_c_attack804,
|
||||
FRAME_c_attack805,
|
||||
FRAME_c_attack806,
|
||||
FRAME_c_attack807,
|
||||
FRAME_c_attack808,
|
||||
FRAME_c_attack809,
|
||||
FRAME_c_attack901,
|
||||
FRAME_c_attack902,
|
||||
FRAME_c_attack903,
|
||||
FRAME_c_attack904,
|
||||
FRAME_c_attack905,
|
||||
FRAME_c_attack906,
|
||||
FRAME_c_attack907,
|
||||
FRAME_c_attack908,
|
||||
FRAME_c_attack909,
|
||||
FRAME_c_attack910,
|
||||
FRAME_c_attack911,
|
||||
FRAME_c_attack912,
|
||||
FRAME_c_attack913,
|
||||
FRAME_c_attack914,
|
||||
FRAME_c_attack915,
|
||||
FRAME_c_attack916,
|
||||
FRAME_c_attack917,
|
||||
FRAME_c_attack918,
|
||||
FRAME_c_attack919,
|
||||
FRAME_c_duck01,
|
||||
FRAME_c_duck02,
|
||||
FRAME_c_duckstep01,
|
||||
FRAME_c_duckstep02,
|
||||
FRAME_c_duckstep03,
|
||||
FRAME_c_duckstep04,
|
||||
FRAME_c_duckstep05,
|
||||
FRAME_c_duckstep06,
|
||||
FRAME_c_duckpain01,
|
||||
FRAME_c_duckpain02,
|
||||
FRAME_c_duckpain03,
|
||||
FRAME_c_duckpain04,
|
||||
FRAME_c_duckpain05,
|
||||
FRAME_c_duckdeath01,
|
||||
FRAME_c_duckdeath02,
|
||||
FRAME_c_duckdeath03,
|
||||
FRAME_c_duckdeath04,
|
||||
FRAME_c_duckdeath05,
|
||||
FRAME_c_duckdeath06,
|
||||
FRAME_c_duckdeath07,
|
||||
FRAME_c_duckdeath08,
|
||||
FRAME_c_duckdeath09,
|
||||
FRAME_c_duckdeath10,
|
||||
FRAME_c_duckdeath11,
|
||||
FRAME_c_duckdeath12,
|
||||
FRAME_c_duckdeath13,
|
||||
FRAME_c_duckdeath14,
|
||||
FRAME_c_duckdeath15,
|
||||
FRAME_c_duckdeath16,
|
||||
FRAME_c_duckdeath17,
|
||||
FRAME_c_duckdeath18,
|
||||
FRAME_c_duckdeath19,
|
||||
FRAME_c_duckdeath20,
|
||||
FRAME_c_duckdeath21,
|
||||
FRAME_c_duckdeath22,
|
||||
FRAME_c_duckdeath23,
|
||||
FRAME_c_duckdeath24,
|
||||
FRAME_c_duckdeath25,
|
||||
FRAME_c_duckdeath26,
|
||||
FRAME_c_duckdeath27,
|
||||
FRAME_c_duckdeath28,
|
||||
FRAME_c_duckdeath29
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.150000f;
|
||||
662
rerelease/m_hover.cpp
Normal file
662
rerelease/m_hover.cpp
Normal file
@@ -0,0 +1,662 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
hover
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_hover.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_death1;
|
||||
static int sound_death2;
|
||||
static int sound_sight;
|
||||
static int sound_search1;
|
||||
static int sound_search2;
|
||||
|
||||
// ROGUE
|
||||
// daedalus sounds
|
||||
static int daed_sound_pain1;
|
||||
static int daed_sound_pain2;
|
||||
static int daed_sound_death1;
|
||||
static int daed_sound_death2;
|
||||
static int daed_sound_sight;
|
||||
static int daed_sound_search1;
|
||||
static int daed_sound_search2;
|
||||
// ROGUE
|
||||
|
||||
MONSTERINFO_SIGHT(hover_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
// PMM - daedalus sounds
|
||||
if (self->mass < 225)
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, daed_sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_SEARCH(hover_search) (edict_t *self) -> void
|
||||
{
|
||||
// PMM - daedalus sounds
|
||||
if (self->mass < 225)
|
||||
{
|
||||
if (frandom() < 0.5f)
|
||||
gi.sound(self, CHAN_VOICE, sound_search1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_search2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (frandom() < 0.5f)
|
||||
gi.sound(self, CHAN_VOICE, daed_sound_search1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, daed_sound_search2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void hover_run(edict_t *self);
|
||||
void hover_dead(edict_t *self);
|
||||
void hover_attack(edict_t *self);
|
||||
void hover_reattack(edict_t *self);
|
||||
void hover_fire_blaster(edict_t *self);
|
||||
|
||||
mframe_t hover_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(hover_move_stand) = { FRAME_stand01, FRAME_stand30, hover_frames_stand, nullptr };
|
||||
|
||||
mframe_t hover_frames_pain3[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(hover_move_pain3) = { FRAME_pain301, FRAME_pain309, hover_frames_pain3, hover_run };
|
||||
|
||||
mframe_t hover_frames_pain2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(hover_move_pain2) = { FRAME_pain201, FRAME_pain212, hover_frames_pain2, hover_run };
|
||||
|
||||
mframe_t hover_frames_pain1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, -8 },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, -6 },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, -3 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, 7 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 5 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 4 }
|
||||
};
|
||||
MMOVE_T(hover_move_pain1) = { FRAME_pain101, FRAME_pain128, hover_frames_pain1, hover_run };
|
||||
|
||||
mframe_t hover_frames_walk[] = {
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 }
|
||||
};
|
||||
MMOVE_T(hover_move_walk) = { FRAME_forwrd01, FRAME_forwrd35, hover_frames_walk, nullptr };
|
||||
|
||||
mframe_t hover_frames_run[] = {
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 10 }
|
||||
};
|
||||
MMOVE_T(hover_move_run) = { FRAME_forwrd01, FRAME_forwrd35, hover_frames_run, nullptr };
|
||||
|
||||
static void hover_gib(edict_t *self)
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.multicast(self->s.origin, MULTICAST_PHS, false);
|
||||
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
ThrowGibs(self, 150, {
|
||||
{ 2, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ 2, "models/objects/gibs/sm_metal/tris.md2", GIB_METALLIC },
|
||||
{ "models/monsters/hover/gibs/chest.md2", GIB_SKINNED },
|
||||
{ 2, "models/monsters/hover/gibs/ring.md2", GIB_SKINNED | GIB_METALLIC },
|
||||
{ 2, "models/monsters/hover/gibs/foot.md2", GIB_SKINNED },
|
||||
{ "models/monsters/hover/gibs/head.md2", GIB_SKINNED | GIB_HEAD },
|
||||
});
|
||||
}
|
||||
|
||||
THINK(hover_deadthink) (edict_t *self) -> void
|
||||
{
|
||||
if (!self->groundentity && level.time < self->timestamp)
|
||||
{
|
||||
self->nextthink = level.time + FRAME_TIME_S;
|
||||
return;
|
||||
}
|
||||
|
||||
hover_gib(self);
|
||||
}
|
||||
|
||||
void hover_dying(edict_t *self)
|
||||
{
|
||||
if (self->groundentity)
|
||||
{
|
||||
hover_deadthink(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (brandom())
|
||||
return;
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_PLAIN_EXPLOSION);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.multicast(self->s.origin, MULTICAST_PHS, false);
|
||||
|
||||
if (brandom())
|
||||
ThrowGibs(self, 120, {
|
||||
{ "models/objects/gibs/sm_meat/tris.md2" }
|
||||
});
|
||||
else
|
||||
ThrowGibs(self, 120, {
|
||||
{ "models/objects/gibs/sm_metal/tris.md2", GIB_METALLIC }
|
||||
});
|
||||
}
|
||||
|
||||
mframe_t hover_frames_death1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move, 0.f, hover_dying },
|
||||
{ ai_move },
|
||||
{ ai_move, 0.f, hover_dying },
|
||||
{ ai_move },
|
||||
{ ai_move, 0.f, hover_dying },
|
||||
{ ai_move, -10, hover_dying },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 5, hover_dying },
|
||||
{ ai_move, 4, hover_dying },
|
||||
{ ai_move, 7 }
|
||||
};
|
||||
MMOVE_T(hover_move_death1) = { FRAME_death101, FRAME_death111, hover_frames_death1, hover_dead };
|
||||
|
||||
mframe_t hover_frames_start_attack[] = {
|
||||
{ ai_charge, 1 },
|
||||
{ ai_charge, 1 },
|
||||
{ ai_charge, 1 }
|
||||
};
|
||||
MMOVE_T(hover_move_start_attack) = { FRAME_attak101, FRAME_attak103, hover_frames_start_attack, hover_attack };
|
||||
|
||||
mframe_t hover_frames_attack1[] = {
|
||||
{ ai_charge, -10, hover_fire_blaster },
|
||||
{ ai_charge, -10, hover_fire_blaster },
|
||||
{ ai_charge, 0, hover_reattack },
|
||||
};
|
||||
MMOVE_T(hover_move_attack1) = { FRAME_attak104, FRAME_attak106, hover_frames_attack1, nullptr };
|
||||
|
||||
mframe_t hover_frames_end_attack[] = {
|
||||
{ ai_charge, 1 },
|
||||
{ ai_charge, 1 }
|
||||
};
|
||||
MMOVE_T(hover_move_end_attack) = { FRAME_attak107, FRAME_attak108, hover_frames_end_attack, hover_run };
|
||||
|
||||
/* PMM - circle strafing code */
|
||||
#if 0
|
||||
mframe_t hover_frames_start_attack2[] = {
|
||||
{ ai_charge, 15 },
|
||||
{ ai_charge, 15 },
|
||||
{ ai_charge, 15 }
|
||||
};
|
||||
MMOVE_T(hover_move_start_attack2) = { FRAME_attak101, FRAME_attak103, hover_frames_start_attack2, hover_attack };
|
||||
#endif
|
||||
|
||||
mframe_t hover_frames_attack2[] = {
|
||||
{ ai_charge, 10, hover_fire_blaster },
|
||||
{ ai_charge, 10, hover_fire_blaster },
|
||||
{ ai_charge, 10, hover_reattack },
|
||||
};
|
||||
MMOVE_T(hover_move_attack2) = { FRAME_attak104, FRAME_attak106, hover_frames_attack2, nullptr };
|
||||
|
||||
#if 0
|
||||
mframe_t hover_frames_end_attack2[] = {
|
||||
{ ai_charge, 15 },
|
||||
{ ai_charge, 15 }
|
||||
};
|
||||
MMOVE_T(hover_move_end_attack2) = { FRAME_attak107, FRAME_attak108, hover_frames_end_attack2, hover_run };
|
||||
#endif
|
||||
|
||||
// end of circle strafe
|
||||
|
||||
void hover_reattack(edict_t *self)
|
||||
{
|
||||
if (self->enemy->health > 0)
|
||||
if (visible(self, self->enemy))
|
||||
if (frandom() <= 0.6f)
|
||||
{
|
||||
if (self->monsterinfo.attack_state == AS_STRAIGHT)
|
||||
{
|
||||
M_SetAnimation(self, &hover_move_attack1);
|
||||
return;
|
||||
}
|
||||
else if (self->monsterinfo.attack_state == AS_SLIDING)
|
||||
{
|
||||
M_SetAnimation(self, &hover_move_attack2);
|
||||
return;
|
||||
}
|
||||
else
|
||||
gi.Com_PrintFmt("hover_reattack: unexpected state {}\n", (int32_t) self->monsterinfo.attack_state);
|
||||
}
|
||||
M_SetAnimation(self, &hover_move_end_attack);
|
||||
}
|
||||
|
||||
void hover_fire_blaster(edict_t *self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
vec3_t end;
|
||||
vec3_t dir;
|
||||
|
||||
if (!self->enemy || !self->enemy->inuse) // PGM
|
||||
return; // PGM
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
vec3_t o = monster_flash_offset[(self->s.frame & 1) ? MZ2_HOVER_BLASTER_2 : MZ2_HOVER_BLASTER_1];
|
||||
start = M_ProjectFlashSource(self, o, forward, right);
|
||||
|
||||
end = self->enemy->s.origin;
|
||||
end[2] += self->enemy->viewheight;
|
||||
dir = end - start;
|
||||
dir.normalize();
|
||||
|
||||
// PGM - daedalus fires blaster2
|
||||
if (self->mass < 200)
|
||||
monster_fire_blaster(self, start, dir, 1, 1000, (self->s.frame & 1) ? MZ2_HOVER_BLASTER_2 : MZ2_HOVER_BLASTER_1, (self->s.frame % 4) ? EF_NONE : EF_HYPERBLASTER);
|
||||
else
|
||||
monster_fire_blaster2(self, start, dir, 1, 1000, (self->s.frame & 1) ? MZ2_DAEDALUS_BLASTER_2 : MZ2_DAEDALUS_BLASTER, (self->s.frame % 4) ? EF_NONE : EF_BLASTER);
|
||||
// PGM
|
||||
}
|
||||
|
||||
MONSTERINFO_STAND(hover_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &hover_move_stand);
|
||||
}
|
||||
|
||||
MONSTERINFO_RUN(hover_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &hover_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, &hover_move_run);
|
||||
}
|
||||
|
||||
MONSTERINFO_WALK(hover_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &hover_move_walk);
|
||||
}
|
||||
|
||||
MONSTERINFO_ATTACK(hover_start_attack) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &hover_move_start_attack);
|
||||
}
|
||||
|
||||
void hover_attack(edict_t *self)
|
||||
{
|
||||
float chance = 0.5f;
|
||||
|
||||
if (self->mass > 150) // the daedalus strafes more
|
||||
chance += 0.1f;
|
||||
|
||||
if (frandom() > chance)
|
||||
{
|
||||
M_SetAnimation(self, &hover_move_attack1);
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
}
|
||||
else // circle strafe
|
||||
{
|
||||
if (frandom() <= 0.5f) // switch directions
|
||||
self->monsterinfo.lefty = !self->monsterinfo.lefty;
|
||||
M_SetAnimation(self, &hover_move_attack2);
|
||||
self->monsterinfo.attack_state = AS_SLIDING;
|
||||
}
|
||||
}
|
||||
|
||||
PAIN(hover_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
float r = frandom();
|
||||
|
||||
//====
|
||||
if (r < 0.5f)
|
||||
{
|
||||
// PMM - daedalus sounds
|
||||
if (self->mass < 225)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, daed_sound_pain1, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PMM - daedalus sounds
|
||||
if (self->mass < 225)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, daed_sound_pain2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
// PGM
|
||||
//====
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
r = frandom();
|
||||
|
||||
if (damage <= 25)
|
||||
{
|
||||
if (r < 0.5f)
|
||||
M_SetAnimation(self, &hover_move_pain3);
|
||||
else
|
||||
M_SetAnimation(self, &hover_move_pain2);
|
||||
}
|
||||
else
|
||||
{
|
||||
//====
|
||||
// PGM pain sequence is WAY too long
|
||||
if (r < 0.3f)
|
||||
M_SetAnimation(self, &hover_move_pain1);
|
||||
else
|
||||
M_SetAnimation(self, &hover_move_pain2);
|
||||
// PGM
|
||||
//====
|
||||
}
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(hover_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum |= 1; // PGM support for skins 2 & 3.
|
||||
else
|
||||
self->s.skinnum &= ~1; // PGM support for skins 2 & 3.
|
||||
}
|
||||
|
||||
void hover_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, -8 };
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->think = hover_deadthink;
|
||||
self->nextthink = level.time + FRAME_TIME_S;
|
||||
self->timestamp = level.time + 15_sec;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
DIE(hover_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
self->s.effects = EF_NONE;
|
||||
self->monsterinfo.power_armor_type = IT_NULL;
|
||||
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
hover_gib(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
// PMM - daedalus sounds
|
||||
if (self->mass < 225)
|
||||
{
|
||||
if (frandom() < 0.5f)
|
||||
gi.sound(self, CHAN_VOICE, sound_death1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_death2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (frandom() < 0.5f)
|
||||
gi.sound(self, CHAN_VOICE, daed_sound_death1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, daed_sound_death2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
M_SetAnimation(self, &hover_move_death1);
|
||||
}
|
||||
|
||||
static void hover_set_fly_parameters(edict_t *self)
|
||||
{
|
||||
self->monsterinfo.fly_thrusters = false;
|
||||
self->monsterinfo.fly_acceleration = 20.f;
|
||||
self->monsterinfo.fly_speed = 120.f;
|
||||
// Icarus prefers to keep its distance, but flies slower than the flyer.
|
||||
// he never pins because of this.
|
||||
self->monsterinfo.fly_min_distance = 150.f;
|
||||
self->monsterinfo.fly_max_distance = 350.f;
|
||||
}
|
||||
|
||||
/*QUAKED monster_hover (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
/*QUAKED monster_daedalus (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
This is the improved icarus monster.
|
||||
*/
|
||||
void SP_monster_hover(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/hover/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/hover/gibs/chest.md2");
|
||||
gi.modelindex("models/monsters/hover/gibs/foot.md2");
|
||||
gi.modelindex("models/monsters/hover/gibs/head.md2");
|
||||
gi.modelindex("models/monsters/hover/gibs/ring.md2");
|
||||
|
||||
self->mins = { -24, -24, -24 };
|
||||
self->maxs = { 24, 24, 32 };
|
||||
|
||||
self->health = 240 * st.health_multiplier;
|
||||
self->gib_health = -100;
|
||||
self->mass = 150;
|
||||
|
||||
self->pain = hover_pain;
|
||||
self->die = hover_die;
|
||||
|
||||
self->monsterinfo.stand = hover_stand;
|
||||
self->monsterinfo.walk = hover_walk;
|
||||
self->monsterinfo.run = hover_run;
|
||||
self->monsterinfo.attack = hover_start_attack;
|
||||
self->monsterinfo.sight = hover_sight;
|
||||
self->monsterinfo.search = hover_search;
|
||||
self->monsterinfo.setskin = hover_setskin;
|
||||
|
||||
// PGM
|
||||
if (strcmp(self->classname, "monster_daedalus") == 0)
|
||||
{
|
||||
self->health = 450 * st.health_multiplier;
|
||||
self->mass = 225;
|
||||
self->yaw_speed = 23;
|
||||
if (!st.was_key_specified("power_armor_type"))
|
||||
self->monsterinfo.power_armor_type = IT_ITEM_POWER_SCREEN;
|
||||
if (!st.was_key_specified("power_armor_power"))
|
||||
self->monsterinfo.power_armor_power = 100;
|
||||
// PMM - daedalus sounds
|
||||
self->monsterinfo.engine_sound = gi.soundindex("daedalus/daedidle1.wav");
|
||||
daed_sound_pain1 = gi.soundindex("daedalus/daedpain1.wav");
|
||||
daed_sound_pain2 = gi.soundindex("daedalus/daedpain2.wav");
|
||||
daed_sound_death1 = gi.soundindex("daedalus/daeddeth1.wav");
|
||||
daed_sound_death2 = gi.soundindex("daedalus/daeddeth2.wav");
|
||||
daed_sound_sight = gi.soundindex("daedalus/daedsght1.wav");
|
||||
daed_sound_search1 = gi.soundindex("daedalus/daedsrch1.wav");
|
||||
daed_sound_search2 = gi.soundindex("daedalus/daedsrch2.wav");
|
||||
gi.soundindex("tank/tnkatck3.wav");
|
||||
// pmm
|
||||
}
|
||||
else
|
||||
{
|
||||
self->yaw_speed = 18;
|
||||
sound_pain1 = gi.soundindex("hover/hovpain1.wav");
|
||||
sound_pain2 = gi.soundindex("hover/hovpain2.wav");
|
||||
sound_death1 = gi.soundindex("hover/hovdeth1.wav");
|
||||
sound_death2 = gi.soundindex("hover/hovdeth2.wav");
|
||||
sound_sight = gi.soundindex("hover/hovsght1.wav");
|
||||
sound_search1 = gi.soundindex("hover/hovsrch1.wav");
|
||||
sound_search2 = gi.soundindex("hover/hovsrch2.wav");
|
||||
gi.soundindex("hover/hovatck1.wav");
|
||||
|
||||
self->monsterinfo.engine_sound = gi.soundindex("hover/hovidle1.wav");
|
||||
}
|
||||
// PGM
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &hover_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
flymonster_start(self);
|
||||
|
||||
// PGM
|
||||
if (strcmp(self->classname, "monster_daedalus") == 0)
|
||||
self->s.skinnum = 2;
|
||||
// PGM
|
||||
|
||||
self->monsterinfo.aiflags |= AI_ALTERNATE_FLY;
|
||||
hover_set_fly_parameters(self);
|
||||
}
|
||||
216
rerelease/m_hover.h
Normal file
216
rerelease/m_hover.h
Normal file
@@ -0,0 +1,216 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/hover
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_stand01,
|
||||
FRAME_stand02,
|
||||
FRAME_stand03,
|
||||
FRAME_stand04,
|
||||
FRAME_stand05,
|
||||
FRAME_stand06,
|
||||
FRAME_stand07,
|
||||
FRAME_stand08,
|
||||
FRAME_stand09,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_stand18,
|
||||
FRAME_stand19,
|
||||
FRAME_stand20,
|
||||
FRAME_stand21,
|
||||
FRAME_stand22,
|
||||
FRAME_stand23,
|
||||
FRAME_stand24,
|
||||
FRAME_stand25,
|
||||
FRAME_stand26,
|
||||
FRAME_stand27,
|
||||
FRAME_stand28,
|
||||
FRAME_stand29,
|
||||
FRAME_stand30,
|
||||
FRAME_forwrd01,
|
||||
FRAME_forwrd02,
|
||||
FRAME_forwrd03,
|
||||
FRAME_forwrd04,
|
||||
FRAME_forwrd05,
|
||||
FRAME_forwrd06,
|
||||
FRAME_forwrd07,
|
||||
FRAME_forwrd08,
|
||||
FRAME_forwrd09,
|
||||
FRAME_forwrd10,
|
||||
FRAME_forwrd11,
|
||||
FRAME_forwrd12,
|
||||
FRAME_forwrd13,
|
||||
FRAME_forwrd14,
|
||||
FRAME_forwrd15,
|
||||
FRAME_forwrd16,
|
||||
FRAME_forwrd17,
|
||||
FRAME_forwrd18,
|
||||
FRAME_forwrd19,
|
||||
FRAME_forwrd20,
|
||||
FRAME_forwrd21,
|
||||
FRAME_forwrd22,
|
||||
FRAME_forwrd23,
|
||||
FRAME_forwrd24,
|
||||
FRAME_forwrd25,
|
||||
FRAME_forwrd26,
|
||||
FRAME_forwrd27,
|
||||
FRAME_forwrd28,
|
||||
FRAME_forwrd29,
|
||||
FRAME_forwrd30,
|
||||
FRAME_forwrd31,
|
||||
FRAME_forwrd32,
|
||||
FRAME_forwrd33,
|
||||
FRAME_forwrd34,
|
||||
FRAME_forwrd35,
|
||||
FRAME_stop101,
|
||||
FRAME_stop102,
|
||||
FRAME_stop103,
|
||||
FRAME_stop104,
|
||||
FRAME_stop105,
|
||||
FRAME_stop106,
|
||||
FRAME_stop107,
|
||||
FRAME_stop108,
|
||||
FRAME_stop109,
|
||||
FRAME_stop201,
|
||||
FRAME_stop202,
|
||||
FRAME_stop203,
|
||||
FRAME_stop204,
|
||||
FRAME_stop205,
|
||||
FRAME_stop206,
|
||||
FRAME_stop207,
|
||||
FRAME_stop208,
|
||||
FRAME_takeof01,
|
||||
FRAME_takeof02,
|
||||
FRAME_takeof03,
|
||||
FRAME_takeof04,
|
||||
FRAME_takeof05,
|
||||
FRAME_takeof06,
|
||||
FRAME_takeof07,
|
||||
FRAME_takeof08,
|
||||
FRAME_takeof09,
|
||||
FRAME_takeof10,
|
||||
FRAME_takeof11,
|
||||
FRAME_takeof12,
|
||||
FRAME_takeof13,
|
||||
FRAME_takeof14,
|
||||
FRAME_takeof15,
|
||||
FRAME_takeof16,
|
||||
FRAME_takeof17,
|
||||
FRAME_takeof18,
|
||||
FRAME_takeof19,
|
||||
FRAME_takeof20,
|
||||
FRAME_takeof21,
|
||||
FRAME_takeof22,
|
||||
FRAME_takeof23,
|
||||
FRAME_takeof24,
|
||||
FRAME_takeof25,
|
||||
FRAME_takeof26,
|
||||
FRAME_takeof27,
|
||||
FRAME_takeof28,
|
||||
FRAME_takeof29,
|
||||
FRAME_takeof30,
|
||||
FRAME_land01,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain104,
|
||||
FRAME_pain105,
|
||||
FRAME_pain106,
|
||||
FRAME_pain107,
|
||||
FRAME_pain108,
|
||||
FRAME_pain109,
|
||||
FRAME_pain110,
|
||||
FRAME_pain111,
|
||||
FRAME_pain112,
|
||||
FRAME_pain113,
|
||||
FRAME_pain114,
|
||||
FRAME_pain115,
|
||||
FRAME_pain116,
|
||||
FRAME_pain117,
|
||||
FRAME_pain118,
|
||||
FRAME_pain119,
|
||||
FRAME_pain120,
|
||||
FRAME_pain121,
|
||||
FRAME_pain122,
|
||||
FRAME_pain123,
|
||||
FRAME_pain124,
|
||||
FRAME_pain125,
|
||||
FRAME_pain126,
|
||||
FRAME_pain127,
|
||||
FRAME_pain128,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain204,
|
||||
FRAME_pain205,
|
||||
FRAME_pain206,
|
||||
FRAME_pain207,
|
||||
FRAME_pain208,
|
||||
FRAME_pain209,
|
||||
FRAME_pain210,
|
||||
FRAME_pain211,
|
||||
FRAME_pain212,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_pain304,
|
||||
FRAME_pain305,
|
||||
FRAME_pain306,
|
||||
FRAME_pain307,
|
||||
FRAME_pain308,
|
||||
FRAME_pain309,
|
||||
FRAME_death101,
|
||||
FRAME_death102,
|
||||
FRAME_death103,
|
||||
FRAME_death104,
|
||||
FRAME_death105,
|
||||
FRAME_death106,
|
||||
FRAME_death107,
|
||||
FRAME_death108,
|
||||
FRAME_death109,
|
||||
FRAME_death110,
|
||||
FRAME_death111,
|
||||
FRAME_backwd01,
|
||||
FRAME_backwd02,
|
||||
FRAME_backwd03,
|
||||
FRAME_backwd04,
|
||||
FRAME_backwd05,
|
||||
FRAME_backwd06,
|
||||
FRAME_backwd07,
|
||||
FRAME_backwd08,
|
||||
FRAME_backwd09,
|
||||
FRAME_backwd10,
|
||||
FRAME_backwd11,
|
||||
FRAME_backwd12,
|
||||
FRAME_backwd13,
|
||||
FRAME_backwd14,
|
||||
FRAME_backwd15,
|
||||
FRAME_backwd16,
|
||||
FRAME_backwd17,
|
||||
FRAME_backwd18,
|
||||
FRAME_backwd19,
|
||||
FRAME_backwd20,
|
||||
FRAME_backwd21,
|
||||
FRAME_backwd22,
|
||||
FRAME_backwd23,
|
||||
FRAME_backwd24,
|
||||
FRAME_attak101,
|
||||
FRAME_attak102,
|
||||
FRAME_attak103,
|
||||
FRAME_attak104,
|
||||
FRAME_attak105,
|
||||
FRAME_attak106,
|
||||
FRAME_attak107,
|
||||
FRAME_attak108
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
928
rerelease/m_infantry.cpp
Normal file
928
rerelease/m_infantry.cpp
Normal file
@@ -0,0 +1,928 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
INFANTRY
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_infantry.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
void InfantryMachineGun(edict_t *self);
|
||||
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_die1;
|
||||
static int sound_die2;
|
||||
|
||||
static int sound_gunshot;
|
||||
static int sound_weapon_cock;
|
||||
static int sound_punch_swing;
|
||||
static int sound_punch_hit;
|
||||
static int sound_sight;
|
||||
static int sound_search;
|
||||
static int sound_idle;
|
||||
|
||||
// range at which we'll try to initiate a run-attack to close distance
|
||||
constexpr float RANGE_RUN_ATTACK = RANGE_NEAR * 0.75f;
|
||||
|
||||
mframe_t infantry_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(infantry_move_stand) = { FRAME_stand50, FRAME_stand71, infantry_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(infantry_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &infantry_move_stand);
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_fidget[] = {
|
||||
{ ai_stand, 1 },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 1 },
|
||||
{ ai_stand, 3 },
|
||||
{ ai_stand, 6 },
|
||||
{ ai_stand, 3, monster_footstep },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 1 },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 1 },
|
||||
{ ai_stand },
|
||||
{ ai_stand, -1 },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 1 },
|
||||
{ ai_stand },
|
||||
{ ai_stand, -2 },
|
||||
{ ai_stand, 1 },
|
||||
{ ai_stand, 1 },
|
||||
{ ai_stand, 1 },
|
||||
{ ai_stand, -1 },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, -1 },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, -1 },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 1 },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, -1 },
|
||||
{ ai_stand, -1 },
|
||||
{ ai_stand },
|
||||
{ ai_stand, -3 },
|
||||
{ ai_stand, -2 },
|
||||
{ ai_stand, -3 },
|
||||
{ ai_stand, -3, monster_footstep },
|
||||
{ ai_stand, -2 }
|
||||
};
|
||||
MMOVE_T(infantry_move_fidget) = { FRAME_stand01, FRAME_stand49, infantry_frames_fidget, infantry_stand };
|
||||
|
||||
MONSTERINFO_IDLE(infantry_fidget) (edict_t *self) -> void
|
||||
{
|
||||
if (self->enemy)
|
||||
return;
|
||||
|
||||
M_SetAnimation(self, &infantry_move_fidget);
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_walk[] = {
|
||||
{ ai_walk, 5, monster_footstep },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 6, monster_footstep },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 5 }
|
||||
};
|
||||
MMOVE_T(infantry_move_walk) = { FRAME_walk03, FRAME_walk14, infantry_frames_walk, nullptr };
|
||||
|
||||
MONSTERINFO_WALK(infantry_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &infantry_move_walk);
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_run[] = {
|
||||
{ ai_run, 10 },
|
||||
{ ai_run, 15, monster_footstep },
|
||||
{ ai_run, 5 },
|
||||
{ ai_run, 7, monster_done_dodge },
|
||||
{ ai_run, 18 },
|
||||
{ ai_run, 20, monster_footstep },
|
||||
{ ai_run, 2 },
|
||||
{ ai_run, 6 }
|
||||
};
|
||||
MMOVE_T(infantry_move_run) = { FRAME_run01, FRAME_run08, infantry_frames_run, nullptr };
|
||||
|
||||
MONSTERINFO_RUN(infantry_run) (edict_t *self) -> void
|
||||
{
|
||||
monster_done_dodge(self);
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &infantry_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, &infantry_move_run);
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_pain1[] = {
|
||||
{ ai_move, -3 },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, -1, monster_footstep },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 6 },
|
||||
{ ai_move, 2, monster_footstep }
|
||||
};
|
||||
MMOVE_T(infantry_move_pain1) = { FRAME_pain101, FRAME_pain110, infantry_frames_pain1, infantry_run };
|
||||
|
||||
mframe_t infantry_frames_pain2[] = {
|
||||
{ ai_move, -3 },
|
||||
{ ai_move, -3 },
|
||||
{ ai_move },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, -2, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, 5 },
|
||||
{ ai_move, 2, monster_footstep }
|
||||
};
|
||||
MMOVE_T(infantry_move_pain2) = { FRAME_pain201, FRAME_pain210, infantry_frames_pain2, infantry_run };
|
||||
|
||||
extern const mmove_t infantry_move_jump;
|
||||
extern const mmove_t infantry_move_jump2;
|
||||
|
||||
PAIN(infantry_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
int n;
|
||||
|
||||
// allow turret to pain
|
||||
if ((self->monsterinfo.active_move == &infantry_move_jump ||
|
||||
self->monsterinfo.active_move == &infantry_move_jump2) && self->think == monster_think)
|
||||
return;
|
||||
|
||||
monster_done_dodge(self);
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
{
|
||||
if (self->think == monster_think && frandom() < 0.33f)
|
||||
self->monsterinfo.dodge(self, other, FRAME_TIME_S, nullptr, false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
n = brandom();
|
||||
|
||||
if (n == 0)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
|
||||
if (self->think != monster_think)
|
||||
return;
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
{
|
||||
if (self->think == monster_think && frandom() < 0.33f)
|
||||
self->monsterinfo.dodge(self, other, FRAME_TIME_S, nullptr, false);
|
||||
|
||||
return; // no pain anims in nightmare
|
||||
}
|
||||
|
||||
if (n == 0)
|
||||
M_SetAnimation(self, &infantry_move_pain1);
|
||||
else
|
||||
M_SetAnimation(self, &infantry_move_pain2);
|
||||
|
||||
// PMM - clear duck flag
|
||||
if (self->monsterinfo.aiflags & AI_DUCKED)
|
||||
monster_duck_up(self);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(infantry_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
constexpr vec3_t aimangles[] = {
|
||||
{ 0.0f, 5.0f, 0.0f },
|
||||
{ 10.0f, 15.0f, 0.0f },
|
||||
{ 20.0f, 25.0f, 0.0f },
|
||||
{ 25.0f, 35.0f, 0.0f },
|
||||
{ 30.0f, 40.0f, 0.0f },
|
||||
{ 30.0f, 45.0f, 0.0f },
|
||||
{ 25.0f, 50.0f, 0.0f },
|
||||
{ 20.0f, 40.0f, 0.0f },
|
||||
{ 15.0f, 35.0f, 0.0f },
|
||||
{ 40.0f, 35.0f, 0.0f },
|
||||
{ 70.0f, 35.0f, 0.0f },
|
||||
{ 90.0f, 35.0f, 0.0f }
|
||||
};
|
||||
|
||||
void InfantryMachineGun(edict_t *self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
vec3_t vec;
|
||||
monster_muzzleflash_id_t flash_number;
|
||||
|
||||
if (!self->enemy || !self->enemy->inuse) // PGM
|
||||
return; // PGM
|
||||
|
||||
bool is_run_attack = (self->s.frame >= FRAME_run201 && self->s.frame <= FRAME_run208);
|
||||
|
||||
if (self->s.frame == FRAME_attak103 || self->s.frame == FRAME_attak311 || is_run_attack || self->s.frame == FRAME_attak416)
|
||||
{
|
||||
if (is_run_attack)
|
||||
flash_number = static_cast<monster_muzzleflash_id_t>(MZ2_INFANTRY_MACHINEGUN_14 + (self->s.frame - MZ2_INFANTRY_MACHINEGUN_14));
|
||||
else if (self->s.frame == FRAME_attak416)
|
||||
flash_number = MZ2_INFANTRY_MACHINEGUN_22;
|
||||
else
|
||||
flash_number = MZ2_INFANTRY_MACHINEGUN_1;
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[flash_number], forward, right);
|
||||
|
||||
if (self->enemy)
|
||||
PredictAim(self, self->enemy, start, 0, true, -0.2f, &forward, nullptr);
|
||||
else
|
||||
{
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
flash_number = static_cast<monster_muzzleflash_id_t>(MZ2_INFANTRY_MACHINEGUN_2 + (self->s.frame - FRAME_death211));
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[flash_number], forward, right);
|
||||
|
||||
vec = self->s.angles - aimangles[flash_number - MZ2_INFANTRY_MACHINEGUN_2];
|
||||
AngleVectors(vec, forward, nullptr, nullptr);
|
||||
}
|
||||
|
||||
monster_fire_bullet(self, start, forward, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
|
||||
}
|
||||
|
||||
MONSTERINFO_SIGHT(infantry_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
if (brandom())
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void infantry_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, -8 };
|
||||
monster_dead(self);
|
||||
}
|
||||
|
||||
static void infantry_shrink(edict_t *self)
|
||||
{
|
||||
self->maxs[2] = 0;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_death1[] = {
|
||||
{ ai_move, -4, nullptr, FRAME_death102 },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move, -4, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, -1, monster_footstep },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, 9, [](edict_t *self) { infantry_shrink(self); monster_footstep(self); } },
|
||||
{ ai_move, 9 },
|
||||
{ ai_move, 5, monster_footstep },
|
||||
{ ai_move, -3 },
|
||||
{ ai_move, -3 }
|
||||
};
|
||||
MMOVE_T(infantry_move_death1) = { FRAME_death101, FRAME_death120, infantry_frames_death1, infantry_dead };
|
||||
|
||||
// Off with his head
|
||||
mframe_t infantry_frames_death2[] = {
|
||||
{ ai_move, 0, nullptr, FRAME_death202 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 5 },
|
||||
{ ai_move, -1 },
|
||||
{ ai_move },
|
||||
{ ai_move, 1, monster_footstep },
|
||||
{ ai_move, 1, monster_footstep },
|
||||
{ ai_move, 4 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move },
|
||||
{ ai_move, -2, InfantryMachineGun },
|
||||
{ ai_move, -2, InfantryMachineGun },
|
||||
{ ai_move, -3, InfantryMachineGun },
|
||||
{ ai_move, -1, InfantryMachineGun },
|
||||
{ ai_move, -2, InfantryMachineGun },
|
||||
{ ai_move, 0, InfantryMachineGun },
|
||||
{ ai_move, 2, InfantryMachineGun },
|
||||
{ ai_move, 2, InfantryMachineGun },
|
||||
{ ai_move, 3, InfantryMachineGun },
|
||||
{ ai_move, -10, InfantryMachineGun },
|
||||
{ ai_move, -7, InfantryMachineGun },
|
||||
{ ai_move, -8, InfantryMachineGun },
|
||||
{ ai_move, -6, [](edict_t *self) { infantry_shrink(self); monster_footstep(self); } },
|
||||
{ ai_move, 4 },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(infantry_move_death2) = { FRAME_death201, FRAME_death225, infantry_frames_death2, infantry_dead };
|
||||
|
||||
mframe_t infantry_frames_death3[] = {
|
||||
{ ai_move, 0 },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, [](edict_t *self) { infantry_shrink(self); monster_footstep(self); } },
|
||||
{ ai_move, -6 },
|
||||
{ ai_move, -11, [](edict_t *self) { self->monsterinfo.nextframe = FRAME_death307; } },
|
||||
{ ai_move, -3 },
|
||||
{ ai_move, -11 },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(infantry_move_death3) = { FRAME_death301, FRAME_death309, infantry_frames_death3, infantry_dead };
|
||||
|
||||
DIE(infantry_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
int n;
|
||||
|
||||
// check for gib
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
|
||||
const char *head_gib = (self->monsterinfo.active_move != &infantry_move_death3) ? "models/objects/gibs/sm_meat/tris.md2" : "models/monsters/infantry/gibs/head.md2";
|
||||
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
ThrowGibs(self, damage, {
|
||||
{ "models/objects/gibs/bone/tris.md2" },
|
||||
{ 3, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ "models/monsters/infantry/gibs/chest.md2", GIB_SKINNED },
|
||||
{ "models/monsters/infantry/gibs/gun.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ 2, "models/monsters/infantry/gibs/foot.md2", GIB_SKINNED },
|
||||
{ 2, "models/monsters/infantry/gibs/arm.md2", GIB_SKINNED },
|
||||
{ head_gib, GIB_HEAD | GIB_SKINNED }
|
||||
});
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
|
||||
n = irandom(3);
|
||||
|
||||
if (n == 0)
|
||||
{
|
||||
M_SetAnimation(self, &infantry_move_death1);
|
||||
gi.sound(self, CHAN_VOICE, sound_die2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else if (n == 1)
|
||||
{
|
||||
M_SetAnimation(self, &infantry_move_death2);
|
||||
gi.sound(self, CHAN_VOICE, sound_die1, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
M_SetAnimation(self, &infantry_move_death3);
|
||||
gi.sound(self, CHAN_VOICE, sound_die2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
// don't always pop a head gib, it gets old
|
||||
if (n != 2 && frandom() <= 0.25f)
|
||||
{
|
||||
edict_t *head = ThrowGib(self, "models/monsters/infantry/gibs/head.md2", damage, GIB_NONE, self->s.scale);
|
||||
|
||||
if (head)
|
||||
{
|
||||
head->s.angles = self->s.angles;
|
||||
head->s.origin = self->s.origin + vec3_t{0, 0, 32.f};
|
||||
vec3_t headDir = (self->s.origin - inflictor->s.origin);
|
||||
head->velocity = headDir / headDir.length() * 100.0f;
|
||||
head->velocity[2] = 200.0f;
|
||||
head->avelocity *= 0.15f;
|
||||
head->s.skinnum = 0;
|
||||
gi.linkentity(head);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_duck[] = {
|
||||
{ ai_move, -2, monster_duck_down },
|
||||
{ ai_move, -5, monster_duck_hold },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 4, monster_duck_up },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(infantry_move_duck) = { FRAME_duck01, FRAME_duck05, infantry_frames_duck, infantry_run };
|
||||
|
||||
// PMM - dodge code moved below so I can see the attack frames
|
||||
|
||||
extern const mmove_t infantry_move_attack4;
|
||||
|
||||
void infantry_set_firetime(edict_t *self)
|
||||
{
|
||||
self->monsterinfo.fire_wait = level.time + random_time(0.7_sec, 2_sec);
|
||||
|
||||
if (!(self->monsterinfo.aiflags & AI_STAND_GROUND) && self->enemy && range_to(self, self->enemy) >= RANGE_RUN_ATTACK && ai_check_move(self, 8.0f))
|
||||
M_SetAnimation(self, &infantry_move_attack4, false);
|
||||
}
|
||||
|
||||
void infantry_cock_gun(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_weapon_cock, 1, ATTN_NORM, 0);
|
||||
|
||||
// gun cocked
|
||||
self->count = 1;
|
||||
}
|
||||
|
||||
void infantry_fire(edict_t *self);
|
||||
|
||||
// cock-less attack, used if he has already cocked his gun
|
||||
mframe_t infantry_frames_attack1[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge, 6, [](edict_t *self) { infantry_set_firetime(self); monster_footstep(self); } },
|
||||
{ ai_charge, 0, infantry_fire },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 1 },
|
||||
{ ai_charge, -7 },
|
||||
{ ai_charge, -6, [](edict_t *self) { self->monsterinfo.nextframe = FRAME_attak114; monster_footstep(self); } },
|
||||
// dead frames start
|
||||
{ ai_charge, -1 },
|
||||
{ ai_charge, 0, infantry_cock_gun },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
// dead frames end
|
||||
{ ai_charge, -1 },
|
||||
{ ai_charge, -1 }
|
||||
};
|
||||
MMOVE_T(infantry_move_attack1) = { FRAME_attak101, FRAME_attak115, infantry_frames_attack1, infantry_run };
|
||||
|
||||
// old animation, full cock + shoot
|
||||
mframe_t infantry_frames_attack3[] = {
|
||||
{ ai_charge, 4, NULL },
|
||||
{ ai_charge, -1, NULL },
|
||||
{ ai_charge, -1, NULL },
|
||||
{ ai_charge, 0, infantry_cock_gun },
|
||||
{ ai_charge, -1, NULL },
|
||||
{ ai_charge, 1, NULL },
|
||||
{ ai_charge, 1, NULL },
|
||||
{ ai_charge, 2, NULL },
|
||||
{ ai_charge, -2, NULL },
|
||||
{ ai_charge, -3, [](edict_t *self) { infantry_set_firetime(self); monster_footstep(self); } },
|
||||
{ ai_charge, 1, infantry_fire },
|
||||
{ ai_charge, 5, NULL },
|
||||
{ ai_charge, -1, NULL },
|
||||
{ ai_charge, -2, NULL },
|
||||
{ ai_charge, -3, NULL },
|
||||
};
|
||||
MMOVE_T(infantry_move_attack3) = { FRAME_attak301, FRAME_attak315, infantry_frames_attack3, infantry_run };
|
||||
|
||||
// even older animation, full cock + shoot
|
||||
mframe_t infantry_frames_attack5[] = {
|
||||
// skipped frames
|
||||
{ ai_charge, 0, NULL },
|
||||
{ ai_charge, 0, NULL },
|
||||
{ ai_charge, 0, NULL },
|
||||
{ ai_charge, 0, NULL },
|
||||
|
||||
{ ai_charge, 0, NULL },
|
||||
{ ai_charge, 0, nullptr },
|
||||
{ ai_charge, 0, monster_footstep },
|
||||
{ ai_charge, 0, infantry_cock_gun },
|
||||
{ ai_charge, 0, NULL },
|
||||
{ ai_charge, 0, NULL },
|
||||
{ ai_charge, 0, [](edict_t *self) { self->monsterinfo.nextframe = self->s.frame + 1; } },
|
||||
{ ai_charge, 0, NULL }, // skipped frame
|
||||
{ ai_charge, 0, NULL },
|
||||
{ ai_charge, 0, nullptr },
|
||||
{ ai_charge, 0, infantry_set_firetime },
|
||||
{ ai_charge, 0, infantry_fire },
|
||||
|
||||
// skipped frames
|
||||
{ ai_charge, 0, NULL },
|
||||
{ ai_charge, 0, NULL },
|
||||
{ ai_charge, 0, NULL },
|
||||
|
||||
{ ai_charge, 0, NULL },
|
||||
{ ai_charge, 0, NULL },
|
||||
{ ai_charge, 0, NULL },
|
||||
{ ai_charge, 0, monster_footstep }
|
||||
};
|
||||
MMOVE_T(infantry_move_attack5) = { FRAME_attak401, FRAME_attak423, infantry_frames_attack5, infantry_run };
|
||||
|
||||
extern const mmove_t infantry_move_attack4;
|
||||
|
||||
void infantry_fire(edict_t *self)
|
||||
{
|
||||
InfantryMachineGun(self);
|
||||
|
||||
// we fired, so we must cock again before firing
|
||||
self->count = 0;
|
||||
|
||||
// check if we ran out of firing time
|
||||
if (self->monsterinfo.active_move == &infantry_move_attack4)
|
||||
{
|
||||
if (level.time >= self->monsterinfo.fire_wait)
|
||||
{
|
||||
monster_done_dodge(self);
|
||||
M_SetAnimation(self, &infantry_move_attack1, false);
|
||||
self->monsterinfo.nextframe = FRAME_attak114;
|
||||
}
|
||||
// got close to an edge
|
||||
else if (!ai_check_move(self, 8.0f))
|
||||
{
|
||||
M_SetAnimation(self, &infantry_move_attack1, false);
|
||||
self->monsterinfo.nextframe = FRAME_attak103;
|
||||
monster_done_dodge(self);
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
}
|
||||
}
|
||||
else if ((self->s.frame >= FRAME_attak101 && self->s.frame <= FRAME_attak115) ||
|
||||
(self->s.frame >= FRAME_attak301 && self->s.frame <= FRAME_attak315) ||
|
||||
(self->s.frame >= FRAME_attak401 && self->s.frame <= FRAME_attak424))
|
||||
{
|
||||
if (level.time >= self->monsterinfo.fire_wait)
|
||||
{
|
||||
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
|
||||
|
||||
if (self->s.frame == FRAME_attak416)
|
||||
self->monsterinfo.nextframe = FRAME_attak420;
|
||||
}
|
||||
else
|
||||
self->monsterinfo.aiflags |= AI_HOLD_FRAME;
|
||||
}
|
||||
}
|
||||
|
||||
void infantry_swing(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_punch_swing, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void infantry_smack(edict_t *self)
|
||||
{
|
||||
vec3_t aim = { MELEE_DISTANCE, 0, 0 };
|
||||
|
||||
if (fire_hit(self, aim, irandom(5, 10), 50))
|
||||
gi.sound(self, CHAN_WEAPON, sound_punch_hit, 1, ATTN_NORM, 0);
|
||||
else
|
||||
self->monsterinfo.melee_debounce_time = level.time + 1.5_sec;
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_attack2[] = {
|
||||
{ ai_charge, 3 },
|
||||
{ ai_charge, 6 },
|
||||
{ ai_charge, 0, infantry_swing },
|
||||
{ ai_charge, 8, monster_footstep },
|
||||
{ ai_charge, 5 },
|
||||
{ ai_charge, 8, infantry_smack },
|
||||
{ ai_charge, 6 },
|
||||
{ ai_charge, 3 }
|
||||
};
|
||||
MMOVE_T(infantry_move_attack2) = { FRAME_attak201, FRAME_attak208, infantry_frames_attack2, infantry_run };
|
||||
|
||||
// [Paril-KEX] run-attack, inspired by q2test
|
||||
void infantry_attack4_refire(edict_t *self)
|
||||
{
|
||||
// ran out of firing time
|
||||
if (level.time >= self->monsterinfo.fire_wait)
|
||||
{
|
||||
monster_done_dodge(self);
|
||||
M_SetAnimation(self, &infantry_move_attack1, false);
|
||||
self->monsterinfo.nextframe = FRAME_attak114;
|
||||
}
|
||||
// we got too close, or we can't move forward, switch us back to regular attack
|
||||
else if ((self->monsterinfo.aiflags & AI_STAND_GROUND) || (self->enemy && (range_to(self, self->enemy) < RANGE_RUN_ATTACK || !ai_check_move(self, 8.0f))))
|
||||
{
|
||||
M_SetAnimation(self, &infantry_move_attack1, false);
|
||||
self->monsterinfo.nextframe = FRAME_attak103;
|
||||
monster_done_dodge(self);
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
}
|
||||
else
|
||||
self->monsterinfo.nextframe = FRAME_run201;
|
||||
|
||||
infantry_fire(self);
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_attack4[] = {
|
||||
{ ai_charge, 16, infantry_fire },
|
||||
{ ai_charge, 16, [](edict_t *self) { monster_footstep(self); infantry_fire(self); } },
|
||||
{ ai_charge, 13, infantry_fire },
|
||||
{ ai_charge, 10, infantry_fire },
|
||||
{ ai_charge, 16, infantry_fire },
|
||||
{ ai_charge, 16, [](edict_t *self) { monster_footstep(self); infantry_fire(self); } },
|
||||
{ ai_charge, 16, infantry_fire },
|
||||
{ ai_charge, 16, infantry_attack4_refire }
|
||||
};
|
||||
MMOVE_T(infantry_move_attack4) = { FRAME_run201, FRAME_run208, infantry_frames_attack4, infantry_run, 0.5f };
|
||||
|
||||
MONSTERINFO_ATTACK(infantry_attack) (edict_t *self) -> void
|
||||
{
|
||||
monster_done_dodge(self);
|
||||
|
||||
float r = range_to(self, self->enemy);
|
||||
|
||||
if (r <= RANGE_MELEE && self->monsterinfo.melee_debounce_time <= level.time)
|
||||
M_SetAnimation(self, &infantry_move_attack2);
|
||||
else if (M_CheckClearShot(self, monster_flash_offset[MZ2_INFANTRY_MACHINEGUN_1]))
|
||||
{
|
||||
if (self->count)
|
||||
M_SetAnimation(self, &infantry_move_attack1);
|
||||
else
|
||||
{
|
||||
M_SetAnimation(self, frandom() <= 0.1f ? &infantry_move_attack5 : &infantry_move_attack3);
|
||||
self->monsterinfo.nextframe = FRAME_attak405;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========
|
||||
// PGM
|
||||
void infantry_jump_now(edict_t *self)
|
||||
{
|
||||
vec3_t forward, up;
|
||||
|
||||
AngleVectors(self->s.angles, forward, nullptr, up);
|
||||
self->velocity += (forward * 100);
|
||||
self->velocity += (up * 300);
|
||||
}
|
||||
|
||||
void infantry_jump2_now(edict_t *self)
|
||||
{
|
||||
vec3_t forward, up;
|
||||
|
||||
AngleVectors(self->s.angles, forward, nullptr, up);
|
||||
self->velocity += (forward * 150);
|
||||
self->velocity += (up * 400);
|
||||
}
|
||||
|
||||
void infantry_jump_wait_land(edict_t *self)
|
||||
{
|
||||
if (self->groundentity == nullptr)
|
||||
{
|
||||
self->monsterinfo.nextframe = self->s.frame;
|
||||
|
||||
if (monster_jump_finished(self))
|
||||
self->monsterinfo.nextframe = self->s.frame + 1;
|
||||
}
|
||||
else
|
||||
self->monsterinfo.nextframe = self->s.frame + 1;
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_jump[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, infantry_jump_now },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, infantry_jump_wait_land },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(infantry_move_jump) = { FRAME_jump01, FRAME_jump10, infantry_frames_jump, infantry_run };
|
||||
|
||||
mframe_t infantry_frames_jump2[] = {
|
||||
{ ai_move, -8 },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, -4 },
|
||||
{ ai_move, 0, infantry_jump2_now },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, infantry_jump_wait_land },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(infantry_move_jump2) = { FRAME_jump01, FRAME_jump10, infantry_frames_jump2, infantry_run };
|
||||
|
||||
void infantry_jump(edict_t *self, blocked_jump_result_t result)
|
||||
{
|
||||
if (!self->enemy)
|
||||
return;
|
||||
|
||||
monster_done_dodge(self);
|
||||
|
||||
if (result == blocked_jump_result_t::JUMP_JUMP_UP)
|
||||
M_SetAnimation(self, &infantry_move_jump2);
|
||||
else
|
||||
M_SetAnimation(self, &infantry_move_jump);
|
||||
}
|
||||
|
||||
MONSTERINFO_BLOCKED(infantry_blocked) (edict_t *self, float dist) -> bool
|
||||
{
|
||||
if (auto result = blocked_checkjump(self, dist); result != blocked_jump_result_t::NO_JUMP)
|
||||
{
|
||||
if (result != blocked_jump_result_t::JUMP_TURN)
|
||||
infantry_jump(self, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (blocked_checkplat(self, dist))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MONSTERINFO_DUCK(infantry_duck) (edict_t *self, gtime_t eta) -> bool
|
||||
{
|
||||
// if we're jumping, don't dodge
|
||||
if ((self->monsterinfo.active_move == &infantry_move_jump) ||
|
||||
(self->monsterinfo.active_move == &infantry_move_jump2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// don't duck during our firing or melee frames
|
||||
if (self->s.frame == FRAME_attak103 ||
|
||||
self->s.frame == FRAME_attak315 ||
|
||||
(self->monsterinfo.active_move == &infantry_move_attack2))
|
||||
{
|
||||
self->monsterinfo.unduck(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
M_SetAnimation(self, &infantry_move_duck);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MONSTERINFO_SIDESTEP(infantry_sidestep) (edict_t *self) -> bool
|
||||
{
|
||||
// if we're jumping, don't dodge
|
||||
if ((self->monsterinfo.active_move == &infantry_move_jump) ||
|
||||
(self->monsterinfo.active_move == &infantry_move_jump2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (self->monsterinfo.active_move == &infantry_move_run)
|
||||
return true;
|
||||
|
||||
// Don't sidestep if we're already sidestepping, and def not unless we're actually shooting
|
||||
// or if we already cocked
|
||||
if (self->monsterinfo.active_move != &infantry_move_attack4 &&
|
||||
self->monsterinfo.next_move != &infantry_move_attack4 &&
|
||||
((self->s.frame == FRAME_attak103 ||
|
||||
self->s.frame == FRAME_attak311 ||
|
||||
self->s.frame == FRAME_attak416) &&
|
||||
!self->count))
|
||||
{
|
||||
// give us a fire time boost so we don't end up firing for 1 frame
|
||||
self->monsterinfo.fire_wait += random_time(300_ms, 600_ms);
|
||||
|
||||
M_SetAnimation(self, &infantry_move_attack4, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InfantryPrecache()
|
||||
{
|
||||
sound_pain1 = gi.soundindex("infantry/infpain1.wav");
|
||||
sound_pain2 = gi.soundindex("infantry/infpain2.wav");
|
||||
sound_die1 = gi.soundindex("infantry/infdeth1.wav");
|
||||
sound_die2 = gi.soundindex("infantry/infdeth2.wav");
|
||||
|
||||
sound_gunshot = gi.soundindex("infantry/infatck1.wav");
|
||||
sound_weapon_cock = gi.soundindex("infantry/infatck3.wav");
|
||||
sound_punch_swing = gi.soundindex("infantry/infatck2.wav");
|
||||
sound_punch_hit = gi.soundindex("infantry/melee2.wav");
|
||||
|
||||
sound_sight = gi.soundindex("infantry/infsght1.wav");
|
||||
sound_search = gi.soundindex("infantry/infsrch1.wav");
|
||||
sound_idle = gi.soundindex("infantry/infidle1.wav");
|
||||
}
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_INFANTRY_NOJUMPING = 8_spawnflag;
|
||||
|
||||
/*QUAKED monster_infantry (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight NoJumping
|
||||
*/
|
||||
void SP_monster_infantry(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
InfantryPrecache();
|
||||
|
||||
self->monsterinfo.aiflags |= AI_STINKY;
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/infantry/gibs/head.md2");
|
||||
gi.modelindex("models/monsters/infantry/gibs/chest.md2");
|
||||
gi.modelindex("models/monsters/infantry/gibs/gun.md2");
|
||||
gi.modelindex("models/monsters/infantry/gibs/arm.md2");
|
||||
gi.modelindex("models/monsters/infantry/gibs/foot.md2");
|
||||
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, 32 };
|
||||
|
||||
self->health = 100 * st.health_multiplier;
|
||||
self->gib_health = -65;
|
||||
self->mass = 200;
|
||||
|
||||
self->pain = infantry_pain;
|
||||
self->die = infantry_die;
|
||||
|
||||
self->monsterinfo.combat_style = COMBAT_MIXED;
|
||||
|
||||
self->monsterinfo.stand = infantry_stand;
|
||||
self->monsterinfo.walk = infantry_walk;
|
||||
self->monsterinfo.run = infantry_run;
|
||||
// pmm
|
||||
self->monsterinfo.dodge = M_MonsterDodge;
|
||||
self->monsterinfo.duck = infantry_duck;
|
||||
self->monsterinfo.unduck = monster_duck_up;
|
||||
self->monsterinfo.sidestep = infantry_sidestep;
|
||||
self->monsterinfo.blocked = infantry_blocked;
|
||||
// pmm
|
||||
self->monsterinfo.attack = infantry_attack;
|
||||
self->monsterinfo.melee = nullptr;
|
||||
self->monsterinfo.sight = infantry_sight;
|
||||
self->monsterinfo.idle = infantry_fidget;
|
||||
self->monsterinfo.setskin = infantry_setskin;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &infantry_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
self->monsterinfo.can_jump = !self->spawnflags.has(SPAWNFLAG_INFANTRY_NOJUMPING);
|
||||
self->monsterinfo.drop_height = 192;
|
||||
self->monsterinfo.jump_height = 40;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
280
rerelease/m_infantry.h
Normal file
280
rerelease/m_infantry.h
Normal file
@@ -0,0 +1,280 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/infantry
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_gun02,
|
||||
FRAME_stand01,
|
||||
FRAME_stand02,
|
||||
FRAME_stand03,
|
||||
FRAME_stand04,
|
||||
FRAME_stand05,
|
||||
FRAME_stand06,
|
||||
FRAME_stand07,
|
||||
FRAME_stand08,
|
||||
FRAME_stand09,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_stand18,
|
||||
FRAME_stand19,
|
||||
FRAME_stand20,
|
||||
FRAME_stand21,
|
||||
FRAME_stand22,
|
||||
FRAME_stand23,
|
||||
FRAME_stand24,
|
||||
FRAME_stand25,
|
||||
FRAME_stand26,
|
||||
FRAME_stand27,
|
||||
FRAME_stand28,
|
||||
FRAME_stand29,
|
||||
FRAME_stand30,
|
||||
FRAME_stand31,
|
||||
FRAME_stand32,
|
||||
FRAME_stand33,
|
||||
FRAME_stand34,
|
||||
FRAME_stand35,
|
||||
FRAME_stand36,
|
||||
FRAME_stand37,
|
||||
FRAME_stand38,
|
||||
FRAME_stand39,
|
||||
FRAME_stand40,
|
||||
FRAME_stand41,
|
||||
FRAME_stand42,
|
||||
FRAME_stand43,
|
||||
FRAME_stand44,
|
||||
FRAME_stand45,
|
||||
FRAME_stand46,
|
||||
FRAME_stand47,
|
||||
FRAME_stand48,
|
||||
FRAME_stand49,
|
||||
FRAME_stand50,
|
||||
FRAME_stand51,
|
||||
FRAME_stand52,
|
||||
FRAME_stand53,
|
||||
FRAME_stand54,
|
||||
FRAME_stand55,
|
||||
FRAME_stand56,
|
||||
FRAME_stand57,
|
||||
FRAME_stand58,
|
||||
FRAME_stand59,
|
||||
FRAME_stand60,
|
||||
FRAME_stand61,
|
||||
FRAME_stand62,
|
||||
FRAME_stand63,
|
||||
FRAME_stand64,
|
||||
FRAME_stand65,
|
||||
FRAME_stand66,
|
||||
FRAME_stand67,
|
||||
FRAME_stand68,
|
||||
FRAME_stand69,
|
||||
FRAME_stand70,
|
||||
FRAME_stand71,
|
||||
FRAME_walk01,
|
||||
FRAME_walk02,
|
||||
FRAME_walk03,
|
||||
FRAME_walk04,
|
||||
FRAME_walk05,
|
||||
FRAME_walk06,
|
||||
FRAME_walk07,
|
||||
FRAME_walk08,
|
||||
FRAME_walk09,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_walk13,
|
||||
FRAME_walk14,
|
||||
FRAME_walk15,
|
||||
FRAME_walk16,
|
||||
FRAME_walk17,
|
||||
FRAME_walk18,
|
||||
FRAME_walk19,
|
||||
FRAME_walk20,
|
||||
FRAME_run01,
|
||||
FRAME_run02,
|
||||
FRAME_run03,
|
||||
FRAME_run04,
|
||||
FRAME_run05,
|
||||
FRAME_run06,
|
||||
FRAME_run07,
|
||||
FRAME_run08,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain104,
|
||||
FRAME_pain105,
|
||||
FRAME_pain106,
|
||||
FRAME_pain107,
|
||||
FRAME_pain108,
|
||||
FRAME_pain109,
|
||||
FRAME_pain110,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain204,
|
||||
FRAME_pain205,
|
||||
FRAME_pain206,
|
||||
FRAME_pain207,
|
||||
FRAME_pain208,
|
||||
FRAME_pain209,
|
||||
FRAME_pain210,
|
||||
FRAME_duck01,
|
||||
FRAME_duck02,
|
||||
FRAME_duck03,
|
||||
FRAME_duck04,
|
||||
FRAME_duck05,
|
||||
FRAME_death101,
|
||||
FRAME_death102,
|
||||
FRAME_death103,
|
||||
FRAME_death104,
|
||||
FRAME_death105,
|
||||
FRAME_death106,
|
||||
FRAME_death107,
|
||||
FRAME_death108,
|
||||
FRAME_death109,
|
||||
FRAME_death110,
|
||||
FRAME_death111,
|
||||
FRAME_death112,
|
||||
FRAME_death113,
|
||||
FRAME_death114,
|
||||
FRAME_death115,
|
||||
FRAME_death116,
|
||||
FRAME_death117,
|
||||
FRAME_death118,
|
||||
FRAME_death119,
|
||||
FRAME_death120,
|
||||
FRAME_death201,
|
||||
FRAME_death202,
|
||||
FRAME_death203,
|
||||
FRAME_death204,
|
||||
FRAME_death205,
|
||||
FRAME_death206,
|
||||
FRAME_death207,
|
||||
FRAME_death208,
|
||||
FRAME_death209,
|
||||
FRAME_death210,
|
||||
FRAME_death211,
|
||||
FRAME_death212,
|
||||
FRAME_death213,
|
||||
FRAME_death214,
|
||||
FRAME_death215,
|
||||
FRAME_death216,
|
||||
FRAME_death217,
|
||||
FRAME_death218,
|
||||
FRAME_death219,
|
||||
FRAME_death220,
|
||||
FRAME_death221,
|
||||
FRAME_death222,
|
||||
FRAME_death223,
|
||||
FRAME_death224,
|
||||
FRAME_death225,
|
||||
FRAME_death301,
|
||||
FRAME_death302,
|
||||
FRAME_death303,
|
||||
FRAME_death304,
|
||||
FRAME_death305,
|
||||
FRAME_death306,
|
||||
FRAME_death307,
|
||||
FRAME_death308,
|
||||
FRAME_death309,
|
||||
FRAME_block01,
|
||||
FRAME_block02,
|
||||
FRAME_block03,
|
||||
FRAME_block04,
|
||||
FRAME_block05,
|
||||
FRAME_attak101,
|
||||
FRAME_attak102,
|
||||
FRAME_attak103,
|
||||
FRAME_attak104,
|
||||
FRAME_attak105,
|
||||
FRAME_attak106,
|
||||
FRAME_attak107,
|
||||
FRAME_attak108,
|
||||
FRAME_attak109,
|
||||
FRAME_attak110,
|
||||
FRAME_attak111,
|
||||
FRAME_attak112,
|
||||
FRAME_attak113,
|
||||
FRAME_attak114,
|
||||
FRAME_attak115,
|
||||
FRAME_attak201,
|
||||
FRAME_attak202,
|
||||
FRAME_attak203,
|
||||
FRAME_attak204,
|
||||
FRAME_attak205,
|
||||
FRAME_attak206,
|
||||
FRAME_attak207,
|
||||
FRAME_attak208,
|
||||
// ROGUE
|
||||
FRAME_jump01,
|
||||
FRAME_jump02,
|
||||
FRAME_jump03,
|
||||
FRAME_jump04,
|
||||
FRAME_jump05,
|
||||
FRAME_jump06,
|
||||
FRAME_jump07,
|
||||
FRAME_jump08,
|
||||
FRAME_jump09,
|
||||
FRAME_jump10,
|
||||
// ROGUE
|
||||
// [Paril-KEX] old attack, for demos
|
||||
FRAME_attak301,
|
||||
FRAME_attak302,
|
||||
FRAME_attak303,
|
||||
FRAME_attak304,
|
||||
FRAME_attak305,
|
||||
FRAME_attak306,
|
||||
FRAME_attak307,
|
||||
FRAME_attak308,
|
||||
FRAME_attak309,
|
||||
FRAME_attak310,
|
||||
FRAME_attak311,
|
||||
FRAME_attak312,
|
||||
FRAME_attak313,
|
||||
FRAME_attak314,
|
||||
FRAME_attak315,
|
||||
// [Paril-KEX] run attack
|
||||
FRAME_run201,
|
||||
FRAME_run202,
|
||||
FRAME_run203,
|
||||
FRAME_run204,
|
||||
FRAME_run205,
|
||||
FRAME_run206,
|
||||
FRAME_run207,
|
||||
FRAME_run208,
|
||||
// [Paril-KEX]
|
||||
FRAME_attak401,
|
||||
FRAME_attak402,
|
||||
FRAME_attak403,
|
||||
FRAME_attak404,
|
||||
FRAME_attak405,
|
||||
FRAME_attak406,
|
||||
FRAME_attak407,
|
||||
FRAME_attak408,
|
||||
FRAME_attak409,
|
||||
FRAME_attak410,
|
||||
FRAME_attak411,
|
||||
FRAME_attak412,
|
||||
FRAME_attak413,
|
||||
FRAME_attak414,
|
||||
FRAME_attak415,
|
||||
FRAME_attak416,
|
||||
FRAME_attak417,
|
||||
FRAME_attak418,
|
||||
FRAME_attak419,
|
||||
FRAME_attak420,
|
||||
FRAME_attak421,
|
||||
FRAME_attak422,
|
||||
FRAME_attak423,
|
||||
FRAME_attak424
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
692
rerelease/m_insane.cpp
Normal file
692
rerelease/m_insane.cpp
Normal file
@@ -0,0 +1,692 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
insane
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_insane.h"
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_INSANE_CRAWL = 4_spawnflag;
|
||||
constexpr spawnflags_t SPAWNFLAG_INSANE_CRUCIFIED = 8_spawnflag;
|
||||
constexpr spawnflags_t SPAWNFLAG_INSANE_STAND_GROUND = 16_spawnflag;
|
||||
constexpr spawnflags_t SPAWNFLAG_INSANE_ALWAYS_STAND = 32_spawnflag;
|
||||
constexpr spawnflags_t SPAWNFLAG_INSANE_QUIET = 64_spawnflag;
|
||||
|
||||
static int sound_fist;
|
||||
static int sound_shake;
|
||||
static int sound_moan;
|
||||
static int sound_scream[8];
|
||||
|
||||
void insane_fist(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_fist, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
void insane_shake(edict_t *self)
|
||||
{
|
||||
if (!self->spawnflags.has(SPAWNFLAG_INSANE_QUIET))
|
||||
gi.sound(self, CHAN_VOICE, sound_shake, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
extern const mmove_t insane_move_cross, insane_move_struggle_cross;
|
||||
|
||||
void insane_moan(edict_t *self)
|
||||
{
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_QUIET))
|
||||
return;
|
||||
|
||||
// Paril: don't moan every second
|
||||
if (self->monsterinfo.attack_finished < level.time)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_moan, 1, ATTN_IDLE, 0);
|
||||
self->monsterinfo.attack_finished = level.time + random_time(1_sec, 3_sec);
|
||||
}
|
||||
}
|
||||
|
||||
void insane_scream(edict_t *self)
|
||||
{
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_QUIET))
|
||||
return;
|
||||
|
||||
// Paril: don't moan every second
|
||||
if (self->monsterinfo.attack_finished < level.time)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, random_element(sound_scream), 1, ATTN_IDLE, 0);
|
||||
self->monsterinfo.attack_finished = level.time + random_time(1_sec, 3_sec);
|
||||
}
|
||||
}
|
||||
|
||||
void insane_stand(edict_t *self);
|
||||
void insane_dead(edict_t *self);
|
||||
void insane_cross(edict_t *self);
|
||||
void insane_walk(edict_t *self);
|
||||
void insane_run(edict_t *self);
|
||||
void insane_checkdown(edict_t *self);
|
||||
void insane_checkup(edict_t *self);
|
||||
void insane_onground(edict_t *self);
|
||||
|
||||
// Paril: unused atm because it breaks N64.
|
||||
// may fix later
|
||||
void insane_shrink(edict_t *self)
|
||||
{
|
||||
self->maxs[2] = 0;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t insane_frames_stand_normal[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, insane_checkdown }
|
||||
};
|
||||
MMOVE_T(insane_move_stand_normal) = { FRAME_stand60, FRAME_stand65, insane_frames_stand_normal, insane_stand };
|
||||
|
||||
mframe_t insane_frames_stand_insane[] = {
|
||||
{ ai_stand, 0, insane_shake },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, insane_checkdown }
|
||||
};
|
||||
MMOVE_T(insane_move_stand_insane) = { FRAME_stand65, FRAME_stand94, insane_frames_stand_insane, insane_stand };
|
||||
|
||||
mframe_t insane_frames_uptodown[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, insane_moan },
|
||||
{ ai_move },//, 0, monster_duck_down },
|
||||
{ ai_move },
|
||||
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
|
||||
{ ai_move, 2.7f },
|
||||
{ ai_move, 4.1f },
|
||||
{ ai_move, 6 },
|
||||
{ ai_move, 7.6f },
|
||||
{ ai_move, 3.6f },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, insane_fist },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, insane_fist },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(insane_move_uptodown) = { FRAME_stand1, FRAME_stand40, insane_frames_uptodown, insane_onground };
|
||||
|
||||
mframe_t insane_frames_downtoup[] = {
|
||||
{ ai_move, -0.7f }, // 41
|
||||
{ ai_move, -1.2f }, // 42
|
||||
{ ai_move, -1.5f }, // 43
|
||||
{ ai_move, -4.5f }, // 44
|
||||
{ ai_move, -3.5f }, // 45
|
||||
{ ai_move, -0.2f }, // 46
|
||||
{ ai_move }, // 47
|
||||
{ ai_move, -1.3f }, // 48
|
||||
{ ai_move, -3 }, // 49
|
||||
{ ai_move, -2 }, // 50
|
||||
{ ai_move },//, 0, monster_duck_up }, // 51
|
||||
{ ai_move }, // 52
|
||||
{ ai_move }, // 53
|
||||
{ ai_move, -3.3f }, // 54
|
||||
{ ai_move, -1.6f }, // 55
|
||||
{ ai_move, -0.3f }, // 56
|
||||
{ ai_move }, // 57
|
||||
{ ai_move }, // 58
|
||||
{ ai_move } // 59
|
||||
};
|
||||
MMOVE_T(insane_move_downtoup) = { FRAME_stand41, FRAME_stand59, insane_frames_downtoup, insane_stand };
|
||||
|
||||
mframe_t insane_frames_jumpdown[] = {
|
||||
{ ai_move, 0.2f },
|
||||
{ ai_move, 11.5f },
|
||||
{ ai_move, 5.1f },
|
||||
{ ai_move, 7.1f },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(insane_move_jumpdown) = { FRAME_stand96, FRAME_stand100, insane_frames_jumpdown, insane_onground };
|
||||
|
||||
mframe_t insane_frames_down[] = {
|
||||
{ ai_move }, // 100
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 110
|
||||
{ ai_move, -1.7f },
|
||||
{ ai_move, -1.6f },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, insane_fist },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 120
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 130
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, insane_moan },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 140
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }, // 150
|
||||
{ ai_move, 0.5f },
|
||||
{ ai_move },
|
||||
{ ai_move, -0.2f, insane_scream },
|
||||
{ ai_move },
|
||||
{ ai_move, 0.2f },
|
||||
{ ai_move, 0.4f },
|
||||
{ ai_move, 0.6f },
|
||||
{ ai_move, 0.8f },
|
||||
{ ai_move, 0.7f },
|
||||
{ ai_move, 0, insane_checkup } // 160
|
||||
};
|
||||
MMOVE_T(insane_move_down) = { FRAME_stand100, FRAME_stand160, insane_frames_down, insane_onground };
|
||||
|
||||
mframe_t insane_frames_walk_normal[] = {
|
||||
{ ai_walk, 0, insane_scream },
|
||||
{ ai_walk, 2.5f },
|
||||
{ ai_walk, 3.5f },
|
||||
{ ai_walk, 1.7f },
|
||||
{ ai_walk, 2.3f },
|
||||
{ ai_walk, 2.4f },
|
||||
{ ai_walk, 2.2f, monster_footstep },
|
||||
{ ai_walk, 4.2f },
|
||||
{ ai_walk, 5.6f },
|
||||
{ ai_walk, 3.3f },
|
||||
{ ai_walk, 2.4f },
|
||||
{ ai_walk, 0.9f },
|
||||
{ ai_walk, 0, monster_footstep }
|
||||
};
|
||||
MMOVE_T(insane_move_walk_normal) = { FRAME_walk27, FRAME_walk39, insane_frames_walk_normal, insane_walk };
|
||||
MMOVE_T(insane_move_run_normal) = { FRAME_walk27, FRAME_walk39, insane_frames_walk_normal, insane_run };
|
||||
|
||||
mframe_t insane_frames_walk_insane[] = {
|
||||
{ ai_walk, 0, insane_scream }, // walk 1
|
||||
{ ai_walk, 3.4f }, // walk 2
|
||||
{ ai_walk, 3.6f }, // 3
|
||||
{ ai_walk, 2.9f }, // 4
|
||||
{ ai_walk, 2.2f }, // 5
|
||||
{ ai_walk, 2.6f, monster_footstep }, // 6
|
||||
{ ai_walk }, // 7
|
||||
{ ai_walk, 0.7f }, // 8
|
||||
{ ai_walk, 4.8f }, // 9
|
||||
{ ai_walk, 5.3f }, // 10
|
||||
{ ai_walk, 1.1f }, // 11
|
||||
{ ai_walk, 2, monster_footstep }, // 12
|
||||
{ ai_walk, 0.5f }, // 13
|
||||
{ ai_walk }, // 14
|
||||
{ ai_walk }, // 15
|
||||
{ ai_walk, 4.9f }, // 16
|
||||
{ ai_walk, 6.7f }, // 17
|
||||
{ ai_walk, 3.8f }, // 18
|
||||
{ ai_walk, 2, monster_footstep }, // 19
|
||||
{ ai_walk, 0.2f }, // 20
|
||||
{ ai_walk }, // 21
|
||||
{ ai_walk, 3.4f }, // 22
|
||||
{ ai_walk, 6.4f }, // 23
|
||||
{ ai_walk, 5 }, // 24
|
||||
{ ai_walk, 1.8f, monster_footstep }, // 25
|
||||
{ ai_walk } // 26
|
||||
};
|
||||
MMOVE_T(insane_move_walk_insane) = { FRAME_walk1, FRAME_walk26, insane_frames_walk_insane, insane_walk };
|
||||
MMOVE_T(insane_move_run_insane) = { FRAME_walk1, FRAME_walk26, insane_frames_walk_insane, insane_run };
|
||||
|
||||
mframe_t insane_frames_stand_pain[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep }
|
||||
};
|
||||
MMOVE_T(insane_move_stand_pain) = { FRAME_st_pain2, FRAME_st_pain12, insane_frames_stand_pain, insane_run };
|
||||
|
||||
mframe_t insane_frames_stand_death[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(insane_move_stand_death) = { FRAME_st_death2, FRAME_st_death18, insane_frames_stand_death, insane_dead };
|
||||
|
||||
mframe_t insane_frames_crawl[] = {
|
||||
{ ai_walk, 0, insane_scream },
|
||||
{ ai_walk, 1.5f },
|
||||
{ ai_walk, 2.1f },
|
||||
{ ai_walk, 3.6f },
|
||||
{ ai_walk, 2, monster_footstep },
|
||||
{ ai_walk, 0.9f },
|
||||
{ ai_walk, 3 },
|
||||
{ ai_walk, 3.4f },
|
||||
{ ai_walk, 2.4f, monster_footstep }
|
||||
};
|
||||
MMOVE_T(insane_move_crawl) = { FRAME_crawl1, FRAME_crawl9, insane_frames_crawl, nullptr };
|
||||
MMOVE_T(insane_move_runcrawl) = { FRAME_crawl1, FRAME_crawl9, insane_frames_crawl, nullptr };
|
||||
|
||||
mframe_t insane_frames_crawl_pain[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(insane_move_crawl_pain) = { FRAME_cr_pain2, FRAME_cr_pain10, insane_frames_crawl_pain, insane_run };
|
||||
|
||||
mframe_t insane_frames_crawl_death[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(insane_move_crawl_death) = { FRAME_cr_death10, FRAME_cr_death16, insane_frames_crawl_death, insane_dead };
|
||||
|
||||
mframe_t insane_frames_cross[] = {
|
||||
{ ai_move, 0, insane_moan },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(insane_move_cross) = { FRAME_cross1, FRAME_cross15, insane_frames_cross, insane_cross };
|
||||
|
||||
mframe_t insane_frames_struggle_cross[] = {
|
||||
{ ai_move, 0, insane_scream },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(insane_move_struggle_cross) = { FRAME_cross16, FRAME_cross30, insane_frames_struggle_cross, insane_cross };
|
||||
|
||||
void insane_cross(edict_t *self)
|
||||
{
|
||||
if (frandom() < 0.8f)
|
||||
M_SetAnimation(self, &insane_move_cross);
|
||||
else
|
||||
M_SetAnimation(self, &insane_move_struggle_cross);
|
||||
}
|
||||
|
||||
MONSTERINFO_WALK(insane_walk) (edict_t *self) -> void
|
||||
{
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_STAND_GROUND)) // Hold Ground?
|
||||
if (self->s.frame == FRAME_cr_pain10)
|
||||
{
|
||||
M_SetAnimation(self, &insane_move_down);
|
||||
//monster_duck_down(self);
|
||||
return;
|
||||
}
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_CRAWL))
|
||||
M_SetAnimation(self, &insane_move_crawl);
|
||||
else if (frandom() <= 0.5f)
|
||||
M_SetAnimation(self, &insane_move_walk_normal);
|
||||
else
|
||||
M_SetAnimation(self, &insane_move_walk_insane);
|
||||
}
|
||||
|
||||
MONSTERINFO_RUN(insane_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_STAND_GROUND)) // Hold Ground?
|
||||
if (self->s.frame == FRAME_cr_pain10)
|
||||
{
|
||||
M_SetAnimation(self, &insane_move_down);
|
||||
//monster_duck_down(self);
|
||||
return;
|
||||
}
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_CRAWL) || (self->s.frame >= FRAME_cr_pain2 && self->s.frame <= FRAME_cr_pain10) || (self->s.frame >= FRAME_crawl1 && self->s.frame <= FRAME_crawl9) ||
|
||||
(self->s.frame >= FRAME_stand99 && self->s.frame <= FRAME_stand160)) // Crawling?
|
||||
M_SetAnimation(self, &insane_move_runcrawl);
|
||||
else if (frandom() <= 0.5f) // Else, mix it up
|
||||
{
|
||||
M_SetAnimation(self, &insane_move_run_normal);
|
||||
//monster_duck_up(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
M_SetAnimation(self, &insane_move_run_insane);
|
||||
//monster_duck_up(self);
|
||||
}
|
||||
}
|
||||
|
||||
PAIN(insane_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
int l, r;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
r = 1 + brandom();
|
||||
if (self->health < 25)
|
||||
l = 25;
|
||||
else if (self->health < 50)
|
||||
l = 50;
|
||||
else if (self->health < 75)
|
||||
l = 75;
|
||||
else
|
||||
l = 100;
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex(G_Fmt("player/male/pain{}_{}.wav", l, r).data()), 1, ATTN_IDLE, 0);
|
||||
|
||||
// Don't go into pain frames if crucified.
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_CRUCIFIED))
|
||||
{
|
||||
M_SetAnimation(self, &insane_move_struggle_cross);
|
||||
return;
|
||||
}
|
||||
|
||||
if (((self->s.frame >= FRAME_crawl1) && (self->s.frame <= FRAME_crawl9)) || ((self->s.frame >= FRAME_stand99) && (self->s.frame <= FRAME_stand160)) || ((self->s.frame >= FRAME_stand1 && self->s.frame <= FRAME_stand40)))
|
||||
{
|
||||
M_SetAnimation(self, &insane_move_crawl_pain);
|
||||
}
|
||||
else
|
||||
{
|
||||
M_SetAnimation(self, &insane_move_stand_pain);
|
||||
//monster_duck_up(self);
|
||||
}
|
||||
}
|
||||
|
||||
void insane_onground(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &insane_move_down);
|
||||
//monster_duck_down(self);
|
||||
}
|
||||
|
||||
void insane_checkdown(edict_t *self)
|
||||
{
|
||||
// if ( (self->s.frame == FRAME_stand94) || (self->s.frame == FRAME_stand65) )
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_ALWAYS_STAND)) // Always stand
|
||||
return;
|
||||
if (frandom() < 0.3f)
|
||||
{
|
||||
if (frandom() < 0.5f)
|
||||
M_SetAnimation(self, &insane_move_uptodown);
|
||||
else
|
||||
M_SetAnimation(self, &insane_move_jumpdown);
|
||||
}
|
||||
}
|
||||
|
||||
void insane_checkup(edict_t *self)
|
||||
{
|
||||
// If Hold_Ground and Crawl are set
|
||||
if (self->spawnflags.has_all(SPAWNFLAG_INSANE_CRAWL | SPAWNFLAG_INSANE_STAND_GROUND))
|
||||
return;
|
||||
if (frandom() < 0.5f)
|
||||
{
|
||||
M_SetAnimation(self, &insane_move_downtoup);
|
||||
//monster_duck_up(self);
|
||||
}
|
||||
}
|
||||
|
||||
MONSTERINFO_STAND(insane_stand) (edict_t *self) -> void
|
||||
{
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_CRUCIFIED)) // If crucified
|
||||
{
|
||||
M_SetAnimation(self, &insane_move_cross);
|
||||
self->monsterinfo.aiflags |= AI_STAND_GROUND;
|
||||
}
|
||||
// If Hold_Ground and Crawl are set
|
||||
else if (self->spawnflags.has_all(SPAWNFLAG_INSANE_CRAWL | SPAWNFLAG_INSANE_STAND_GROUND))
|
||||
{
|
||||
M_SetAnimation(self, &insane_move_down);
|
||||
//monster_duck_down(self);
|
||||
}
|
||||
else if (frandom() < 0.5f)
|
||||
M_SetAnimation(self, &insane_move_stand_normal);
|
||||
else
|
||||
M_SetAnimation(self, &insane_move_stand_insane);
|
||||
}
|
||||
|
||||
void insane_dead(edict_t *self)
|
||||
{
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_CRUCIFIED))
|
||||
{
|
||||
self->flags |= FL_FLY;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, -8 };
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
}
|
||||
monster_dead(self);
|
||||
}
|
||||
|
||||
DIE(insane_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_IDLE, 0);
|
||||
ThrowGibs(self, damage, {
|
||||
{ 2, "models/objects/gibs/bone/tris.md2" },
|
||||
{ 4, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ "models/objects/gibs/head2/tris.md2", GIB_HEAD }
|
||||
});
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex(G_Fmt("player/male/death{}.wav", irandom(1, 5)).data()), 1, ATTN_IDLE, 0);
|
||||
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_CRUCIFIED))
|
||||
{
|
||||
insane_dead(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (((self->s.frame >= FRAME_crawl1) && (self->s.frame <= FRAME_crawl9)) || ((self->s.frame >= FRAME_stand99) && (self->s.frame <= FRAME_stand160)))
|
||||
M_SetAnimation(self, &insane_move_crawl_death);
|
||||
else
|
||||
M_SetAnimation(self, &insane_move_stand_death);
|
||||
}
|
||||
}
|
||||
|
||||
/*QUAKED misc_insane (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn CRAWL CRUCIFIED STAND_GROUND ALWAYS_STAND QUIET
|
||||
*/
|
||||
void SP_misc_insane(edict_t *self)
|
||||
{
|
||||
// static int skin = 0; //@@
|
||||
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_fist = gi.soundindex("insane/insane11.wav");
|
||||
if (!self->spawnflags.has(SPAWNFLAG_INSANE_QUIET))
|
||||
{
|
||||
sound_shake = gi.soundindex("insane/insane5.wav");
|
||||
sound_moan = gi.soundindex("insane/insane7.wav");
|
||||
sound_scream[0] = gi.soundindex("insane/insane1.wav");
|
||||
sound_scream[1] = gi.soundindex("insane/insane2.wav");
|
||||
sound_scream[2] = gi.soundindex("insane/insane3.wav");
|
||||
sound_scream[3] = gi.soundindex("insane/insane4.wav");
|
||||
sound_scream[4] = gi.soundindex("insane/insane6.wav");
|
||||
sound_scream[5] = gi.soundindex("insane/insane8.wav");
|
||||
sound_scream[6] = gi.soundindex("insane/insane9.wav");
|
||||
sound_scream[7] = gi.soundindex("insane/insane10.wav");
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/insane/tris.md2");
|
||||
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, 32 };
|
||||
|
||||
self->health = 100 * st.health_multiplier;
|
||||
self->gib_health = -50;
|
||||
self->mass = 300;
|
||||
|
||||
self->pain = insane_pain;
|
||||
self->die = insane_die;
|
||||
|
||||
self->monsterinfo.stand = insane_stand;
|
||||
self->monsterinfo.walk = insane_walk;
|
||||
self->monsterinfo.run = insane_run;
|
||||
self->monsterinfo.dodge = nullptr;
|
||||
self->monsterinfo.attack = nullptr;
|
||||
self->monsterinfo.melee = nullptr;
|
||||
self->monsterinfo.sight = nullptr;
|
||||
self->monsterinfo.aiflags |= AI_GOOD_GUY;
|
||||
|
||||
//@@
|
||||
// self->s.skinnum = skin;
|
||||
// skin++;
|
||||
// if (skin > 12)
|
||||
// skin = 0;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_STAND_GROUND)) // Stand Ground
|
||||
self->monsterinfo.aiflags |= AI_STAND_GROUND;
|
||||
|
||||
M_SetAnimation(self, &insane_move_stand_normal);
|
||||
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
if (self->spawnflags.has(SPAWNFLAG_INSANE_CRUCIFIED)) // Crucified ?
|
||||
{
|
||||
self->flags |= FL_NO_KNOCKBACK | FL_STATIONARY;
|
||||
stationarymonster_start(self);
|
||||
}
|
||||
else
|
||||
walkmonster_start(self);
|
||||
|
||||
self->s.skinnum = irandom(3);
|
||||
}
|
||||
293
rerelease/m_insane.h
Normal file
293
rerelease/m_insane.h
Normal file
@@ -0,0 +1,293 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/insane
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_stand1,
|
||||
FRAME_stand2,
|
||||
FRAME_stand3,
|
||||
FRAME_stand4,
|
||||
FRAME_stand5,
|
||||
FRAME_stand6,
|
||||
FRAME_stand7,
|
||||
FRAME_stand8,
|
||||
FRAME_stand9,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_stand18,
|
||||
FRAME_stand19,
|
||||
FRAME_stand20,
|
||||
FRAME_stand21,
|
||||
FRAME_stand22,
|
||||
FRAME_stand23,
|
||||
FRAME_stand24,
|
||||
FRAME_stand25,
|
||||
FRAME_stand26,
|
||||
FRAME_stand27,
|
||||
FRAME_stand28,
|
||||
FRAME_stand29,
|
||||
FRAME_stand30,
|
||||
FRAME_stand31,
|
||||
FRAME_stand32,
|
||||
FRAME_stand33,
|
||||
FRAME_stand34,
|
||||
FRAME_stand35,
|
||||
FRAME_stand36,
|
||||
FRAME_stand37,
|
||||
FRAME_stand38,
|
||||
FRAME_stand39,
|
||||
FRAME_stand40,
|
||||
FRAME_stand41,
|
||||
FRAME_stand42,
|
||||
FRAME_stand43,
|
||||
FRAME_stand44,
|
||||
FRAME_stand45,
|
||||
FRAME_stand46,
|
||||
FRAME_stand47,
|
||||
FRAME_stand48,
|
||||
FRAME_stand49,
|
||||
FRAME_stand50,
|
||||
FRAME_stand51,
|
||||
FRAME_stand52,
|
||||
FRAME_stand53,
|
||||
FRAME_stand54,
|
||||
FRAME_stand55,
|
||||
FRAME_stand56,
|
||||
FRAME_stand57,
|
||||
FRAME_stand58,
|
||||
FRAME_stand59,
|
||||
FRAME_stand60,
|
||||
FRAME_stand61,
|
||||
FRAME_stand62,
|
||||
FRAME_stand63,
|
||||
FRAME_stand64,
|
||||
FRAME_stand65,
|
||||
FRAME_stand66,
|
||||
FRAME_stand67,
|
||||
FRAME_stand68,
|
||||
FRAME_stand69,
|
||||
FRAME_stand70,
|
||||
FRAME_stand71,
|
||||
FRAME_stand72,
|
||||
FRAME_stand73,
|
||||
FRAME_stand74,
|
||||
FRAME_stand75,
|
||||
FRAME_stand76,
|
||||
FRAME_stand77,
|
||||
FRAME_stand78,
|
||||
FRAME_stand79,
|
||||
FRAME_stand80,
|
||||
FRAME_stand81,
|
||||
FRAME_stand82,
|
||||
FRAME_stand83,
|
||||
FRAME_stand84,
|
||||
FRAME_stand85,
|
||||
FRAME_stand86,
|
||||
FRAME_stand87,
|
||||
FRAME_stand88,
|
||||
FRAME_stand89,
|
||||
FRAME_stand90,
|
||||
FRAME_stand91,
|
||||
FRAME_stand92,
|
||||
FRAME_stand93,
|
||||
FRAME_stand94,
|
||||
FRAME_stand95,
|
||||
FRAME_stand96,
|
||||
FRAME_stand97,
|
||||
FRAME_stand98,
|
||||
FRAME_stand99,
|
||||
FRAME_stand100,
|
||||
FRAME_stand101,
|
||||
FRAME_stand102,
|
||||
FRAME_stand103,
|
||||
FRAME_stand104,
|
||||
FRAME_stand105,
|
||||
FRAME_stand106,
|
||||
FRAME_stand107,
|
||||
FRAME_stand108,
|
||||
FRAME_stand109,
|
||||
FRAME_stand110,
|
||||
FRAME_stand111,
|
||||
FRAME_stand112,
|
||||
FRAME_stand113,
|
||||
FRAME_stand114,
|
||||
FRAME_stand115,
|
||||
FRAME_stand116,
|
||||
FRAME_stand117,
|
||||
FRAME_stand118,
|
||||
FRAME_stand119,
|
||||
FRAME_stand120,
|
||||
FRAME_stand121,
|
||||
FRAME_stand122,
|
||||
FRAME_stand123,
|
||||
FRAME_stand124,
|
||||
FRAME_stand125,
|
||||
FRAME_stand126,
|
||||
FRAME_stand127,
|
||||
FRAME_stand128,
|
||||
FRAME_stand129,
|
||||
FRAME_stand130,
|
||||
FRAME_stand131,
|
||||
FRAME_stand132,
|
||||
FRAME_stand133,
|
||||
FRAME_stand134,
|
||||
FRAME_stand135,
|
||||
FRAME_stand136,
|
||||
FRAME_stand137,
|
||||
FRAME_stand138,
|
||||
FRAME_stand139,
|
||||
FRAME_stand140,
|
||||
FRAME_stand141,
|
||||
FRAME_stand142,
|
||||
FRAME_stand143,
|
||||
FRAME_stand144,
|
||||
FRAME_stand145,
|
||||
FRAME_stand146,
|
||||
FRAME_stand147,
|
||||
FRAME_stand148,
|
||||
FRAME_stand149,
|
||||
FRAME_stand150,
|
||||
FRAME_stand151,
|
||||
FRAME_stand152,
|
||||
FRAME_stand153,
|
||||
FRAME_stand154,
|
||||
FRAME_stand155,
|
||||
FRAME_stand156,
|
||||
FRAME_stand157,
|
||||
FRAME_stand158,
|
||||
FRAME_stand159,
|
||||
FRAME_stand160,
|
||||
FRAME_walk27,
|
||||
FRAME_walk28,
|
||||
FRAME_walk29,
|
||||
FRAME_walk30,
|
||||
FRAME_walk31,
|
||||
FRAME_walk32,
|
||||
FRAME_walk33,
|
||||
FRAME_walk34,
|
||||
FRAME_walk35,
|
||||
FRAME_walk36,
|
||||
FRAME_walk37,
|
||||
FRAME_walk38,
|
||||
FRAME_walk39,
|
||||
FRAME_walk1,
|
||||
FRAME_walk2,
|
||||
FRAME_walk3,
|
||||
FRAME_walk4,
|
||||
FRAME_walk5,
|
||||
FRAME_walk6,
|
||||
FRAME_walk7,
|
||||
FRAME_walk8,
|
||||
FRAME_walk9,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_walk13,
|
||||
FRAME_walk14,
|
||||
FRAME_walk15,
|
||||
FRAME_walk16,
|
||||
FRAME_walk17,
|
||||
FRAME_walk18,
|
||||
FRAME_walk19,
|
||||
FRAME_walk20,
|
||||
FRAME_walk21,
|
||||
FRAME_walk22,
|
||||
FRAME_walk23,
|
||||
FRAME_walk24,
|
||||
FRAME_walk25,
|
||||
FRAME_walk26,
|
||||
FRAME_st_pain2,
|
||||
FRAME_st_pain3,
|
||||
FRAME_st_pain4,
|
||||
FRAME_st_pain5,
|
||||
FRAME_st_pain6,
|
||||
FRAME_st_pain7,
|
||||
FRAME_st_pain8,
|
||||
FRAME_st_pain9,
|
||||
FRAME_st_pain10,
|
||||
FRAME_st_pain11,
|
||||
FRAME_st_pain12,
|
||||
FRAME_st_death2,
|
||||
FRAME_st_death3,
|
||||
FRAME_st_death4,
|
||||
FRAME_st_death5,
|
||||
FRAME_st_death6,
|
||||
FRAME_st_death7,
|
||||
FRAME_st_death8,
|
||||
FRAME_st_death9,
|
||||
FRAME_st_death10,
|
||||
FRAME_st_death11,
|
||||
FRAME_st_death12,
|
||||
FRAME_st_death13,
|
||||
FRAME_st_death14,
|
||||
FRAME_st_death15,
|
||||
FRAME_st_death16,
|
||||
FRAME_st_death17,
|
||||
FRAME_st_death18,
|
||||
FRAME_crawl1,
|
||||
FRAME_crawl2,
|
||||
FRAME_crawl3,
|
||||
FRAME_crawl4,
|
||||
FRAME_crawl5,
|
||||
FRAME_crawl6,
|
||||
FRAME_crawl7,
|
||||
FRAME_crawl8,
|
||||
FRAME_crawl9,
|
||||
FRAME_cr_pain2,
|
||||
FRAME_cr_pain3,
|
||||
FRAME_cr_pain4,
|
||||
FRAME_cr_pain5,
|
||||
FRAME_cr_pain6,
|
||||
FRAME_cr_pain7,
|
||||
FRAME_cr_pain8,
|
||||
FRAME_cr_pain9,
|
||||
FRAME_cr_pain10,
|
||||
FRAME_cr_death10,
|
||||
FRAME_cr_death11,
|
||||
FRAME_cr_death12,
|
||||
FRAME_cr_death13,
|
||||
FRAME_cr_death14,
|
||||
FRAME_cr_death15,
|
||||
FRAME_cr_death16,
|
||||
FRAME_cross1,
|
||||
FRAME_cross2,
|
||||
FRAME_cross3,
|
||||
FRAME_cross4,
|
||||
FRAME_cross5,
|
||||
FRAME_cross6,
|
||||
FRAME_cross7,
|
||||
FRAME_cross8,
|
||||
FRAME_cross9,
|
||||
FRAME_cross10,
|
||||
FRAME_cross11,
|
||||
FRAME_cross12,
|
||||
FRAME_cross13,
|
||||
FRAME_cross14,
|
||||
FRAME_cross15,
|
||||
FRAME_cross16,
|
||||
FRAME_cross17,
|
||||
FRAME_cross18,
|
||||
FRAME_cross19,
|
||||
FRAME_cross20,
|
||||
FRAME_cross21,
|
||||
FRAME_cross22,
|
||||
FRAME_cross23,
|
||||
FRAME_cross24,
|
||||
FRAME_cross25,
|
||||
FRAME_cross26,
|
||||
FRAME_cross27,
|
||||
FRAME_cross28,
|
||||
FRAME_cross29,
|
||||
FRAME_cross30
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
1643
rerelease/m_medic.cpp
Normal file
1643
rerelease/m_medic.cpp
Normal file
File diff suppressed because it is too large
Load Diff
248
rerelease/m_medic.h
Normal file
248
rerelease/m_medic.h
Normal file
@@ -0,0 +1,248 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/medic
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_walk1,
|
||||
FRAME_walk2,
|
||||
FRAME_walk3,
|
||||
FRAME_walk4,
|
||||
FRAME_walk5,
|
||||
FRAME_walk6,
|
||||
FRAME_walk7,
|
||||
FRAME_walk8,
|
||||
FRAME_walk9,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_wait1,
|
||||
FRAME_wait2,
|
||||
FRAME_wait3,
|
||||
FRAME_wait4,
|
||||
FRAME_wait5,
|
||||
FRAME_wait6,
|
||||
FRAME_wait7,
|
||||
FRAME_wait8,
|
||||
FRAME_wait9,
|
||||
FRAME_wait10,
|
||||
FRAME_wait11,
|
||||
FRAME_wait12,
|
||||
FRAME_wait13,
|
||||
FRAME_wait14,
|
||||
FRAME_wait15,
|
||||
FRAME_wait16,
|
||||
FRAME_wait17,
|
||||
FRAME_wait18,
|
||||
FRAME_wait19,
|
||||
FRAME_wait20,
|
||||
FRAME_wait21,
|
||||
FRAME_wait22,
|
||||
FRAME_wait23,
|
||||
FRAME_wait24,
|
||||
FRAME_wait25,
|
||||
FRAME_wait26,
|
||||
FRAME_wait27,
|
||||
FRAME_wait28,
|
||||
FRAME_wait29,
|
||||
FRAME_wait30,
|
||||
FRAME_wait31,
|
||||
FRAME_wait32,
|
||||
FRAME_wait33,
|
||||
FRAME_wait34,
|
||||
FRAME_wait35,
|
||||
FRAME_wait36,
|
||||
FRAME_wait37,
|
||||
FRAME_wait38,
|
||||
FRAME_wait39,
|
||||
FRAME_wait40,
|
||||
FRAME_wait41,
|
||||
FRAME_wait42,
|
||||
FRAME_wait43,
|
||||
FRAME_wait44,
|
||||
FRAME_wait45,
|
||||
FRAME_wait46,
|
||||
FRAME_wait47,
|
||||
FRAME_wait48,
|
||||
FRAME_wait49,
|
||||
FRAME_wait50,
|
||||
FRAME_wait51,
|
||||
FRAME_wait52,
|
||||
FRAME_wait53,
|
||||
FRAME_wait54,
|
||||
FRAME_wait55,
|
||||
FRAME_wait56,
|
||||
FRAME_wait57,
|
||||
FRAME_wait58,
|
||||
FRAME_wait59,
|
||||
FRAME_wait60,
|
||||
FRAME_wait61,
|
||||
FRAME_wait62,
|
||||
FRAME_wait63,
|
||||
FRAME_wait64,
|
||||
FRAME_wait65,
|
||||
FRAME_wait66,
|
||||
FRAME_wait67,
|
||||
FRAME_wait68,
|
||||
FRAME_wait69,
|
||||
FRAME_wait70,
|
||||
FRAME_wait71,
|
||||
FRAME_wait72,
|
||||
FRAME_wait73,
|
||||
FRAME_wait74,
|
||||
FRAME_wait75,
|
||||
FRAME_wait76,
|
||||
FRAME_wait77,
|
||||
FRAME_wait78,
|
||||
FRAME_wait79,
|
||||
FRAME_wait80,
|
||||
FRAME_wait81,
|
||||
FRAME_wait82,
|
||||
FRAME_wait83,
|
||||
FRAME_wait84,
|
||||
FRAME_wait85,
|
||||
FRAME_wait86,
|
||||
FRAME_wait87,
|
||||
FRAME_wait88,
|
||||
FRAME_wait89,
|
||||
FRAME_wait90,
|
||||
FRAME_run1,
|
||||
FRAME_run2,
|
||||
FRAME_run3,
|
||||
FRAME_run4,
|
||||
FRAME_run5,
|
||||
FRAME_run6,
|
||||
FRAME_paina1,
|
||||
FRAME_paina2,
|
||||
FRAME_paina3,
|
||||
FRAME_paina4,
|
||||
FRAME_paina5,
|
||||
FRAME_paina6,
|
||||
FRAME_paina7,
|
||||
FRAME_paina8,
|
||||
FRAME_painb1,
|
||||
FRAME_painb2,
|
||||
FRAME_painb3,
|
||||
FRAME_painb4,
|
||||
FRAME_painb5,
|
||||
FRAME_painb6,
|
||||
FRAME_painb7,
|
||||
FRAME_painb8,
|
||||
FRAME_painb9,
|
||||
FRAME_painb10,
|
||||
FRAME_painb11,
|
||||
FRAME_painb12,
|
||||
FRAME_painb13,
|
||||
FRAME_painb14,
|
||||
FRAME_painb15,
|
||||
FRAME_duck1,
|
||||
FRAME_duck2,
|
||||
FRAME_duck3,
|
||||
FRAME_duck4,
|
||||
FRAME_duck5,
|
||||
FRAME_duck6,
|
||||
FRAME_duck7,
|
||||
FRAME_duck8,
|
||||
FRAME_duck9,
|
||||
FRAME_duck10,
|
||||
FRAME_duck11,
|
||||
FRAME_duck12,
|
||||
FRAME_duck13,
|
||||
FRAME_duck14,
|
||||
FRAME_duck15,
|
||||
FRAME_duck16,
|
||||
FRAME_death1,
|
||||
FRAME_death2,
|
||||
FRAME_death3,
|
||||
FRAME_death4,
|
||||
FRAME_death5,
|
||||
FRAME_death6,
|
||||
FRAME_death7,
|
||||
FRAME_death8,
|
||||
FRAME_death9,
|
||||
FRAME_death10,
|
||||
FRAME_death11,
|
||||
FRAME_death12,
|
||||
FRAME_death13,
|
||||
FRAME_death14,
|
||||
FRAME_death15,
|
||||
FRAME_death16,
|
||||
FRAME_death17,
|
||||
FRAME_death18,
|
||||
FRAME_death19,
|
||||
FRAME_death20,
|
||||
FRAME_death21,
|
||||
FRAME_death22,
|
||||
FRAME_death23,
|
||||
FRAME_death24,
|
||||
FRAME_death25,
|
||||
FRAME_death26,
|
||||
FRAME_death27,
|
||||
FRAME_death28,
|
||||
FRAME_death29,
|
||||
FRAME_death30,
|
||||
FRAME_attack1,
|
||||
FRAME_attack2,
|
||||
FRAME_attack3,
|
||||
FRAME_attack4,
|
||||
FRAME_attack5,
|
||||
FRAME_attack6,
|
||||
FRAME_attack7,
|
||||
FRAME_attack8,
|
||||
FRAME_attack9,
|
||||
FRAME_attack10,
|
||||
FRAME_attack11,
|
||||
FRAME_attack12,
|
||||
FRAME_attack13,
|
||||
FRAME_attack14,
|
||||
FRAME_attack15,
|
||||
FRAME_attack16,
|
||||
FRAME_attack17,
|
||||
FRAME_attack18,
|
||||
FRAME_attack19,
|
||||
FRAME_attack20,
|
||||
FRAME_attack21,
|
||||
FRAME_attack22,
|
||||
FRAME_attack23,
|
||||
FRAME_attack24,
|
||||
FRAME_attack25,
|
||||
FRAME_attack26,
|
||||
FRAME_attack27,
|
||||
FRAME_attack28,
|
||||
FRAME_attack29,
|
||||
FRAME_attack30,
|
||||
FRAME_attack31,
|
||||
FRAME_attack32,
|
||||
FRAME_attack33,
|
||||
FRAME_attack34,
|
||||
FRAME_attack35,
|
||||
FRAME_attack36,
|
||||
FRAME_attack37,
|
||||
FRAME_attack38,
|
||||
FRAME_attack39,
|
||||
FRAME_attack40,
|
||||
FRAME_attack41,
|
||||
FRAME_attack42,
|
||||
FRAME_attack43,
|
||||
FRAME_attack44,
|
||||
FRAME_attack45,
|
||||
FRAME_attack46,
|
||||
FRAME_attack47,
|
||||
FRAME_attack48,
|
||||
FRAME_attack49,
|
||||
FRAME_attack50,
|
||||
FRAME_attack51,
|
||||
FRAME_attack52,
|
||||
FRAME_attack53,
|
||||
FRAME_attack54,
|
||||
FRAME_attack55,
|
||||
FRAME_attack56,
|
||||
FRAME_attack57,
|
||||
FRAME_attack58,
|
||||
FRAME_attack59,
|
||||
FRAME_attack60
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
1497
rerelease/m_move.cpp
Normal file
1497
rerelease/m_move.cpp
Normal file
File diff suppressed because it is too large
Load Diff
739
rerelease/m_mutant.cpp
Normal file
739
rerelease/m_mutant.cpp
Normal file
@@ -0,0 +1,739 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
mutant
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_mutant.h"
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_MUTANT_NOJUMPING = 8_spawnflag;
|
||||
|
||||
static int sound_swing;
|
||||
static int sound_hit;
|
||||
static int sound_hit2;
|
||||
static int sound_death;
|
||||
static int sound_idle;
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_sight;
|
||||
static int sound_search;
|
||||
static int sound_step1;
|
||||
static int sound_step2;
|
||||
static int sound_step3;
|
||||
static int sound_thud;
|
||||
|
||||
//
|
||||
// SOUNDS
|
||||
//
|
||||
|
||||
void mutant_step(edict_t *self)
|
||||
{
|
||||
int n = irandom(3);
|
||||
if (n == 0)
|
||||
gi.sound(self, CHAN_BODY, sound_step1, 1, ATTN_NORM, 0);
|
||||
else if (n == 1)
|
||||
gi.sound(self, CHAN_BODY, sound_step2, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_BODY, sound_step3, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_SIGHT(mutant_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_SEARCH(mutant_search) (edict_t *self) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void mutant_swing(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_swing, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
//
|
||||
// STAND
|
||||
//
|
||||
|
||||
mframe_t mutant_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 10
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 20
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 30
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 40
|
||||
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // 50
|
||||
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(mutant_move_stand) = { FRAME_stand101, FRAME_stand151, mutant_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(mutant_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &mutant_move_stand);
|
||||
}
|
||||
|
||||
//
|
||||
// IDLE
|
||||
//
|
||||
|
||||
void mutant_idle_loop(edict_t *self)
|
||||
{
|
||||
if (frandom() < 0.75f)
|
||||
self->monsterinfo.nextframe = FRAME_stand155;
|
||||
}
|
||||
|
||||
mframe_t mutant_frames_idle[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }, // scratch loop start
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, mutant_idle_loop }, // scratch loop end
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(mutant_move_idle) = { FRAME_stand152, FRAME_stand164, mutant_frames_idle, mutant_stand };
|
||||
|
||||
MONSTERINFO_IDLE(mutant_idle) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &mutant_move_idle);
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
//
|
||||
// WALK
|
||||
//
|
||||
|
||||
mframe_t mutant_frames_walk[] = {
|
||||
{ ai_walk, 3 },
|
||||
{ ai_walk, 1 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk, 13 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 6 },
|
||||
{ ai_walk, 16 },
|
||||
{ ai_walk, 15 },
|
||||
{ ai_walk, 6 }
|
||||
};
|
||||
MMOVE_T(mutant_move_walk) = { FRAME_walk05, FRAME_walk16, mutant_frames_walk, nullptr };
|
||||
|
||||
void mutant_walk_loop(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &mutant_move_walk);
|
||||
}
|
||||
|
||||
mframe_t mutant_frames_start_walk[] = {
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, -2 },
|
||||
{ ai_walk, 1 }
|
||||
};
|
||||
MMOVE_T(mutant_move_start_walk) = { FRAME_walk01, FRAME_walk04, mutant_frames_start_walk, mutant_walk_loop };
|
||||
|
||||
MONSTERINFO_WALK(mutant_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &mutant_move_start_walk);
|
||||
}
|
||||
|
||||
//
|
||||
// RUN
|
||||
//
|
||||
|
||||
mframe_t mutant_frames_run[] = {
|
||||
{ ai_run, 40 },
|
||||
{ ai_run, 40, mutant_step },
|
||||
{ ai_run, 24 },
|
||||
{ ai_run, 5, mutant_step },
|
||||
{ ai_run, 17 },
|
||||
{ ai_run, 10 }
|
||||
};
|
||||
MMOVE_T(mutant_move_run) = { FRAME_run03, FRAME_run08, mutant_frames_run, nullptr };
|
||||
|
||||
MONSTERINFO_RUN(mutant_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &mutant_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, &mutant_move_run);
|
||||
}
|
||||
|
||||
//
|
||||
// MELEE
|
||||
//
|
||||
|
||||
void mutant_hit_left(edict_t *self)
|
||||
{
|
||||
vec3_t aim = { MELEE_DISTANCE, self->mins[0], 8 };
|
||||
if (fire_hit(self, aim, irandom(5, 15), 100))
|
||||
gi.sound(self, CHAN_WEAPON, sound_hit, 1, ATTN_NORM, 0);
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_swing, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.melee_debounce_time = level.time + 1.5_sec;
|
||||
}
|
||||
}
|
||||
|
||||
void mutant_hit_right(edict_t *self)
|
||||
{
|
||||
vec3_t aim = { MELEE_DISTANCE, self->maxs[0], 8 };
|
||||
if (fire_hit(self, aim, irandom(5, 15), 100))
|
||||
gi.sound(self, CHAN_WEAPON, sound_hit2, 1, ATTN_NORM, 0);
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_swing, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.melee_debounce_time = level.time + 1.5_sec;
|
||||
}
|
||||
}
|
||||
|
||||
void mutant_check_refire(edict_t *self)
|
||||
{
|
||||
if (!self->enemy || !self->enemy->inuse || self->enemy->health <= 0)
|
||||
return;
|
||||
|
||||
if ((self->monsterinfo.melee_debounce_time <= level.time) && ((frandom() < 0.5f) || (range_to(self, self->enemy) <= RANGE_MELEE)))
|
||||
self->monsterinfo.nextframe = FRAME_attack09;
|
||||
}
|
||||
|
||||
mframe_t mutant_frames_attack[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, mutant_hit_left },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, mutant_hit_right },
|
||||
{ ai_charge, 0, mutant_check_refire }
|
||||
};
|
||||
MMOVE_T(mutant_move_attack) = { FRAME_attack09, FRAME_attack15, mutant_frames_attack, mutant_run };
|
||||
|
||||
MONSTERINFO_MELEE(mutant_melee) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &mutant_move_attack);
|
||||
}
|
||||
|
||||
//
|
||||
// ATTACK
|
||||
//
|
||||
|
||||
TOUCH(mutant_jump_touch) (edict_t *self, edict_t *other, const trace_t &tr, bool other_touching_self) -> void
|
||||
{
|
||||
if (self->health <= 0)
|
||||
{
|
||||
self->touch = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->style == 1 && other->takedamage)
|
||||
{
|
||||
if (self->velocity.length() > 400)
|
||||
{
|
||||
vec3_t point;
|
||||
vec3_t normal;
|
||||
int damage;
|
||||
|
||||
normal = self->velocity;
|
||||
normal.normalize();
|
||||
point = self->s.origin + (normal * self->maxs[0]);
|
||||
damage = (int) frandom(40, 50);
|
||||
T_Damage(other, self, self, self->velocity, point, normal, damage, damage, DAMAGE_NONE, MOD_UNKNOWN);
|
||||
self->style = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!M_CheckBottom(self))
|
||||
{
|
||||
if (self->groundentity)
|
||||
{
|
||||
self->monsterinfo.nextframe = FRAME_attack02;
|
||||
self->touch = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self->touch = nullptr;
|
||||
}
|
||||
|
||||
void mutant_jump_takeoff(edict_t *self)
|
||||
{
|
||||
vec3_t forward;
|
||||
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
AngleVectors(self->s.angles, forward, nullptr, nullptr);
|
||||
self->s.origin[2] += 1;
|
||||
self->velocity = forward * 400;
|
||||
self->velocity[2] = 150;
|
||||
self->groundentity = nullptr;
|
||||
self->monsterinfo.aiflags |= AI_DUCKED;
|
||||
self->monsterinfo.attack_finished = level.time + 3_sec;
|
||||
self->style = 1;
|
||||
self->touch = mutant_jump_touch;
|
||||
}
|
||||
|
||||
void mutant_check_landing(edict_t *self)
|
||||
{
|
||||
monster_jump_finished(self);
|
||||
|
||||
if (self->groundentity)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_thud, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.attack_finished = level.time + random_time(500_ms, 1.5_sec);
|
||||
|
||||
if (self->monsterinfo.unduck)
|
||||
self->monsterinfo.unduck(self);
|
||||
|
||||
if (range_to(self, self->enemy) <= RANGE_MELEE * 2.f)
|
||||
self->monsterinfo.melee(self);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (level.time > self->monsterinfo.attack_finished)
|
||||
self->monsterinfo.nextframe = FRAME_attack02;
|
||||
else
|
||||
self->monsterinfo.nextframe = FRAME_attack05;
|
||||
}
|
||||
|
||||
mframe_t mutant_frames_jump[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge, 17 },
|
||||
{ ai_charge, 15, mutant_jump_takeoff },
|
||||
{ ai_charge, 15 },
|
||||
{ ai_charge, 15, mutant_check_landing },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 3 },
|
||||
{ ai_charge }
|
||||
};
|
||||
MMOVE_T(mutant_move_jump) = { FRAME_attack01, FRAME_attack08, mutant_frames_jump, mutant_run };
|
||||
|
||||
MONSTERINFO_ATTACK(mutant_jump) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &mutant_move_jump);
|
||||
}
|
||||
|
||||
//
|
||||
// CHECKATTACK
|
||||
//
|
||||
|
||||
bool mutant_check_melee(edict_t *self)
|
||||
{
|
||||
return range_to(self, self->enemy) <= RANGE_MELEE && self->monsterinfo.melee_debounce_time <= level.time;
|
||||
}
|
||||
|
||||
bool mutant_check_jump(edict_t *self)
|
||||
{
|
||||
vec3_t v;
|
||||
float distance;
|
||||
|
||||
// Paril: no harm in letting them jump down if you're below them
|
||||
// if (self->absmin[2] > (self->enemy->absmin[2] + 0.75 * self->enemy->size[2]))
|
||||
// return false;
|
||||
|
||||
// don't jump if there's no way we can reach standing height
|
||||
if (self->absmin[2] + 125 < self->enemy->absmin[2])
|
||||
return false;
|
||||
|
||||
v[0] = self->s.origin[0] - self->enemy->s.origin[0];
|
||||
v[1] = self->s.origin[1] - self->enemy->s.origin[1];
|
||||
v[2] = 0;
|
||||
distance = v.length();
|
||||
|
||||
// if we're not trying to avoid a melee, then don't jump
|
||||
if (distance < 100 && self->monsterinfo.melee_debounce_time <= level.time)
|
||||
return false;
|
||||
// only use it to close distance gaps
|
||||
if (distance > 265)
|
||||
return false;
|
||||
|
||||
return self->monsterinfo.attack_finished < level.time && brandom();
|
||||
}
|
||||
|
||||
MONSTERINFO_CHECKATTACK(mutant_checkattack) (edict_t *self) -> bool
|
||||
{
|
||||
if (!self->enemy || self->enemy->health <= 0)
|
||||
return false;
|
||||
|
||||
if (mutant_check_melee(self))
|
||||
{
|
||||
self->monsterinfo.attack_state = AS_MELEE;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!self->spawnflags.has(SPAWNFLAG_MUTANT_NOJUMPING) && mutant_check_jump(self))
|
||||
{
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// PAIN
|
||||
//
|
||||
|
||||
mframe_t mutant_frames_pain1[] = {
|
||||
{ ai_move, 4 },
|
||||
{ ai_move, -3 },
|
||||
{ ai_move, -8 },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, 5 }
|
||||
};
|
||||
MMOVE_T(mutant_move_pain1) = { FRAME_pain101, FRAME_pain105, mutant_frames_pain1, mutant_run };
|
||||
|
||||
mframe_t mutant_frames_pain2[] = {
|
||||
{ ai_move, -24 },
|
||||
{ ai_move, 11 },
|
||||
{ ai_move, 5 },
|
||||
{ ai_move, -2 },
|
||||
{ ai_move, 6 },
|
||||
{ ai_move, 4 }
|
||||
};
|
||||
MMOVE_T(mutant_move_pain2) = { FRAME_pain201, FRAME_pain206, mutant_frames_pain2, mutant_run };
|
||||
|
||||
mframe_t mutant_frames_pain3[] = {
|
||||
{ ai_move, -22 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 1 },
|
||||
{ ai_move, 6 },
|
||||
{ ai_move, 3 },
|
||||
{ ai_move, 2 },
|
||||
{ ai_move },
|
||||
{ ai_move, 1 }
|
||||
};
|
||||
MMOVE_T(mutant_move_pain3) = { FRAME_pain301, FRAME_pain311, mutant_frames_pain3, mutant_run };
|
||||
|
||||
PAIN(mutant_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
float r;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
r = frandom();
|
||||
if (r < 0.33f)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else if (r < 0.66f)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (r < 0.33f)
|
||||
M_SetAnimation(self, &mutant_move_pain1);
|
||||
else if (r < 0.66f)
|
||||
M_SetAnimation(self, &mutant_move_pain2);
|
||||
else
|
||||
M_SetAnimation(self, &mutant_move_pain3);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(mutant_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// DEATH
|
||||
//
|
||||
|
||||
// FIXME: expanded dead box a bit, but rotation of dead body
|
||||
// means it'll never always fit unless you move the whole box based on angle
|
||||
void mutant_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { 0, -48, -24 };
|
||||
self->maxs = { 64, 16, -8 };
|
||||
monster_dead(self);
|
||||
}
|
||||
|
||||
void mutant_shrink(edict_t *self)
|
||||
{
|
||||
self->maxs[2] = 0;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t mutant_frames_death1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, mutant_shrink },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(mutant_move_death1) = { FRAME_death101, FRAME_death109, mutant_frames_death1, mutant_dead };
|
||||
|
||||
mframe_t mutant_frames_death2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, mutant_shrink },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(mutant_move_death2) = { FRAME_death201, FRAME_death210, mutant_frames_death2, mutant_dead };
|
||||
|
||||
DIE(mutant_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
ThrowGibs(self, damage, {
|
||||
{ 2, "models/objects/gibs/bone/tris.md2" },
|
||||
{ 4, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ 2, "models/monsters/mutant/gibs/hand.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ 2, "models/monsters/mutant/gibs/foot.md2", GIB_SKINNED },
|
||||
{ "models/monsters/mutant/gibs/chest.md2", GIB_SKINNED },
|
||||
{ "models/monsters/mutant/gibs/head.md2", GIB_SKINNED | GIB_HEAD }
|
||||
});
|
||||
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
|
||||
if (frandom() < 0.5f)
|
||||
M_SetAnimation(self, &mutant_move_death1);
|
||||
else
|
||||
M_SetAnimation(self, &mutant_move_death2);
|
||||
}
|
||||
|
||||
//================
|
||||
// ROGUE
|
||||
void mutant_jump_down(edict_t *self)
|
||||
{
|
||||
vec3_t forward, up;
|
||||
|
||||
AngleVectors(self->s.angles, forward, nullptr, up);
|
||||
self->velocity += (forward * 100);
|
||||
self->velocity += (up * 300);
|
||||
}
|
||||
|
||||
void mutant_jump_up(edict_t *self)
|
||||
{
|
||||
vec3_t forward, up;
|
||||
|
||||
AngleVectors(self->s.angles, forward, nullptr, up);
|
||||
self->velocity += (forward * 200);
|
||||
self->velocity += (up * 450);
|
||||
}
|
||||
|
||||
void mutant_jump_wait_land(edict_t *self)
|
||||
{
|
||||
if (!monster_jump_finished(self) && self->groundentity == nullptr)
|
||||
self->monsterinfo.nextframe = self->s.frame;
|
||||
else
|
||||
self->monsterinfo.nextframe = self->s.frame + 1;
|
||||
}
|
||||
|
||||
mframe_t mutant_frames_jump_up[] = {
|
||||
{ ai_move, -8 },
|
||||
{ ai_move, -8, mutant_jump_up },
|
||||
{ ai_move, 0, mutant_jump_wait_land },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(mutant_move_jump_up) = { FRAME_jump01, FRAME_jump05, mutant_frames_jump_up, mutant_run };
|
||||
|
||||
mframe_t mutant_frames_jump_down[] = {
|
||||
{ ai_move },
|
||||
{ ai_move, 0, mutant_jump_down },
|
||||
{ ai_move, 0, mutant_jump_wait_land },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(mutant_move_jump_down) = { FRAME_jump01, FRAME_jump05, mutant_frames_jump_down, mutant_run };
|
||||
|
||||
void mutant_jump_updown(edict_t *self, blocked_jump_result_t result)
|
||||
{
|
||||
if (!self->enemy)
|
||||
return;
|
||||
|
||||
if (result == blocked_jump_result_t::JUMP_JUMP_UP)
|
||||
M_SetAnimation(self, &mutant_move_jump_up);
|
||||
else
|
||||
M_SetAnimation(self, &mutant_move_jump_down);
|
||||
}
|
||||
|
||||
/*
|
||||
===
|
||||
Blocked
|
||||
===
|
||||
*/
|
||||
MONSTERINFO_BLOCKED(mutant_blocked) (edict_t *self, float dist) -> bool
|
||||
{
|
||||
if (auto result = blocked_checkjump(self, dist); result != blocked_jump_result_t::NO_JUMP)
|
||||
{
|
||||
if (result != blocked_jump_result_t::JUMP_TURN)
|
||||
mutant_jump_updown(self, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (blocked_checkplat(self, dist))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
// ROGUE
|
||||
//================
|
||||
|
||||
//
|
||||
// SPAWN
|
||||
//
|
||||
|
||||
/*QUAKED monster_mutant (1 .5 0) (-32 -32 -24) (32 32 32) Ambush Trigger_Spawn Sight NoJumping
|
||||
model="models/monsters/mutant/tris.md2"
|
||||
*/
|
||||
void SP_monster_mutant(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_swing = gi.soundindex("mutant/mutatck1.wav");
|
||||
sound_hit = gi.soundindex("mutant/mutatck2.wav");
|
||||
sound_hit2 = gi.soundindex("mutant/mutatck3.wav");
|
||||
sound_death = gi.soundindex("mutant/mutdeth1.wav");
|
||||
sound_idle = gi.soundindex("mutant/mutidle1.wav");
|
||||
sound_pain1 = gi.soundindex("mutant/mutpain1.wav");
|
||||
sound_pain2 = gi.soundindex("mutant/mutpain2.wav");
|
||||
sound_sight = gi.soundindex("mutant/mutsght1.wav");
|
||||
sound_search = gi.soundindex("mutant/mutsrch1.wav");
|
||||
sound_step1 = gi.soundindex("mutant/step1.wav");
|
||||
sound_step2 = gi.soundindex("mutant/step2.wav");
|
||||
sound_step3 = gi.soundindex("mutant/step3.wav");
|
||||
sound_thud = gi.soundindex("mutant/thud1.wav");
|
||||
|
||||
self->monsterinfo.aiflags |= AI_STINKY;
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/mutant/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/mutant/gibs/head.md2");
|
||||
gi.modelindex("models/monsters/mutant/gibs/chest.md2");
|
||||
gi.modelindex("models/monsters/mutant/gibs/hand.md2");
|
||||
gi.modelindex("models/monsters/mutant/gibs/foot.md2");
|
||||
|
||||
self->mins = { -18, -18, -24 };
|
||||
self->maxs = { 18, 18, 30 };
|
||||
|
||||
self->health = 300 * st.health_multiplier;
|
||||
self->gib_health = -120;
|
||||
self->mass = 300;
|
||||
|
||||
self->pain = mutant_pain;
|
||||
self->die = mutant_die;
|
||||
|
||||
self->monsterinfo.stand = mutant_stand;
|
||||
self->monsterinfo.walk = mutant_walk;
|
||||
self->monsterinfo.run = mutant_run;
|
||||
self->monsterinfo.dodge = nullptr;
|
||||
self->monsterinfo.attack = mutant_jump;
|
||||
self->monsterinfo.melee = mutant_melee;
|
||||
self->monsterinfo.sight = mutant_sight;
|
||||
self->monsterinfo.search = mutant_search;
|
||||
self->monsterinfo.idle = mutant_idle;
|
||||
self->monsterinfo.checkattack = mutant_checkattack;
|
||||
self->monsterinfo.blocked = mutant_blocked; // PGM
|
||||
self->monsterinfo.setskin = mutant_setskin;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &mutant_move_stand);
|
||||
|
||||
self->monsterinfo.combat_style = COMBAT_MELEE;
|
||||
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
self->monsterinfo.can_jump = !(self->spawnflags & SPAWNFLAG_MUTANT_NOJUMPING);
|
||||
self->monsterinfo.drop_height = 256;
|
||||
self->monsterinfo.jump_height = 68;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
167
rerelease/m_mutant.h
Normal file
167
rerelease/m_mutant.h
Normal file
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/mutant
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_attack01,
|
||||
FRAME_attack02,
|
||||
FRAME_attack03,
|
||||
FRAME_attack04,
|
||||
FRAME_attack05,
|
||||
FRAME_attack06,
|
||||
FRAME_attack07,
|
||||
FRAME_attack08,
|
||||
FRAME_attack09,
|
||||
FRAME_attack10,
|
||||
FRAME_attack11,
|
||||
FRAME_attack12,
|
||||
FRAME_attack13,
|
||||
FRAME_attack14,
|
||||
FRAME_attack15,
|
||||
FRAME_death101,
|
||||
FRAME_death102,
|
||||
FRAME_death103,
|
||||
FRAME_death104,
|
||||
FRAME_death105,
|
||||
FRAME_death106,
|
||||
FRAME_death107,
|
||||
FRAME_death108,
|
||||
FRAME_death109,
|
||||
FRAME_death201,
|
||||
FRAME_death202,
|
||||
FRAME_death203,
|
||||
FRAME_death204,
|
||||
FRAME_death205,
|
||||
FRAME_death206,
|
||||
FRAME_death207,
|
||||
FRAME_death208,
|
||||
FRAME_death209,
|
||||
FRAME_death210,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain104,
|
||||
FRAME_pain105,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain204,
|
||||
FRAME_pain205,
|
||||
FRAME_pain206,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_pain304,
|
||||
FRAME_pain305,
|
||||
FRAME_pain306,
|
||||
FRAME_pain307,
|
||||
FRAME_pain308,
|
||||
FRAME_pain309,
|
||||
FRAME_pain310,
|
||||
FRAME_pain311,
|
||||
FRAME_run03,
|
||||
FRAME_run04,
|
||||
FRAME_run05,
|
||||
FRAME_run06,
|
||||
FRAME_run07,
|
||||
FRAME_run08,
|
||||
FRAME_stand101,
|
||||
FRAME_stand102,
|
||||
FRAME_stand103,
|
||||
FRAME_stand104,
|
||||
FRAME_stand105,
|
||||
FRAME_stand106,
|
||||
FRAME_stand107,
|
||||
FRAME_stand108,
|
||||
FRAME_stand109,
|
||||
FRAME_stand110,
|
||||
FRAME_stand111,
|
||||
FRAME_stand112,
|
||||
FRAME_stand113,
|
||||
FRAME_stand114,
|
||||
FRAME_stand115,
|
||||
FRAME_stand116,
|
||||
FRAME_stand117,
|
||||
FRAME_stand118,
|
||||
FRAME_stand119,
|
||||
FRAME_stand120,
|
||||
FRAME_stand121,
|
||||
FRAME_stand122,
|
||||
FRAME_stand123,
|
||||
FRAME_stand124,
|
||||
FRAME_stand125,
|
||||
FRAME_stand126,
|
||||
FRAME_stand127,
|
||||
FRAME_stand128,
|
||||
FRAME_stand129,
|
||||
FRAME_stand130,
|
||||
FRAME_stand131,
|
||||
FRAME_stand132,
|
||||
FRAME_stand133,
|
||||
FRAME_stand134,
|
||||
FRAME_stand135,
|
||||
FRAME_stand136,
|
||||
FRAME_stand137,
|
||||
FRAME_stand138,
|
||||
FRAME_stand139,
|
||||
FRAME_stand140,
|
||||
FRAME_stand141,
|
||||
FRAME_stand142,
|
||||
FRAME_stand143,
|
||||
FRAME_stand144,
|
||||
FRAME_stand145,
|
||||
FRAME_stand146,
|
||||
FRAME_stand147,
|
||||
FRAME_stand148,
|
||||
FRAME_stand149,
|
||||
FRAME_stand150,
|
||||
FRAME_stand151,
|
||||
FRAME_stand152,
|
||||
FRAME_stand153,
|
||||
FRAME_stand154,
|
||||
FRAME_stand155,
|
||||
FRAME_stand156,
|
||||
FRAME_stand157,
|
||||
FRAME_stand158,
|
||||
FRAME_stand159,
|
||||
FRAME_stand160,
|
||||
FRAME_stand161,
|
||||
FRAME_stand162,
|
||||
FRAME_stand163,
|
||||
FRAME_stand164,
|
||||
FRAME_walk01,
|
||||
FRAME_walk02,
|
||||
FRAME_walk03,
|
||||
FRAME_walk04,
|
||||
FRAME_walk05,
|
||||
FRAME_walk06,
|
||||
FRAME_walk07,
|
||||
FRAME_walk08,
|
||||
FRAME_walk09,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_walk13,
|
||||
FRAME_walk14,
|
||||
FRAME_walk15,
|
||||
FRAME_walk16,
|
||||
FRAME_walk17,
|
||||
FRAME_walk18,
|
||||
FRAME_walk19,
|
||||
FRAME_walk20,
|
||||
FRAME_walk21,
|
||||
FRAME_walk22,
|
||||
FRAME_walk23,
|
||||
// ROGUE
|
||||
FRAME_jump01,
|
||||
FRAME_jump02,
|
||||
FRAME_jump03,
|
||||
FRAME_jump04,
|
||||
FRAME_jump05
|
||||
// ROGUE
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
965
rerelease/m_parasite.cpp
Normal file
965
rerelease/m_parasite.cpp
Normal file
@@ -0,0 +1,965 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
parasite
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_parasite.h"
|
||||
|
||||
constexpr float g_athena_parasite_miss_chance = 0.1f;
|
||||
constexpr float g_athena_parasite_proboscis_speed = 1250;
|
||||
constexpr float g_athena_parasite_proboscis_retract_modifier = 2.0f;
|
||||
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_die;
|
||||
static int sound_launch;
|
||||
static int sound_impact;
|
||||
static int sound_suck;
|
||||
static int sound_reelin;
|
||||
static int sound_sight;
|
||||
static int sound_tap;
|
||||
static int sound_scratch;
|
||||
static int sound_search;
|
||||
|
||||
void parasite_stand(edict_t *self);
|
||||
void parasite_start_run(edict_t *self);
|
||||
void parasite_run(edict_t *self);
|
||||
void parasite_walk(edict_t *self);
|
||||
void parasite_end_fidget(edict_t *self);
|
||||
void parasite_do_fidget(edict_t *self);
|
||||
void parasite_refidget(edict_t *self);
|
||||
|
||||
void parasite_launch(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_launch, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void parasite_reel_in(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_reelin, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_SIGHT(parasite_sight) (edict_t *self, edict_t *other) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void parasite_tap(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_tap, 0.75f, 2.75f, 0);
|
||||
}
|
||||
|
||||
void parasite_scratch(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_scratch, 0.75f, 2.75f, 0);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void parasite_search(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_search, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
mframe_t parasite_frames_start_fidget[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(parasite_move_start_fidget) = { FRAME_stand18, FRAME_stand21, parasite_frames_start_fidget, parasite_do_fidget };
|
||||
|
||||
mframe_t parasite_frames_fidget[] = {
|
||||
{ ai_stand, 0, parasite_scratch },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, parasite_scratch },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(parasite_move_fidget) = { FRAME_stand22, FRAME_stand27, parasite_frames_fidget, parasite_refidget };
|
||||
|
||||
mframe_t parasite_frames_end_fidget[] = {
|
||||
{ ai_stand, 0, parasite_scratch },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(parasite_move_end_fidget) = { FRAME_stand28, FRAME_stand35, parasite_frames_end_fidget, parasite_stand };
|
||||
|
||||
void parasite_end_fidget(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, ¶site_move_end_fidget);
|
||||
}
|
||||
|
||||
void parasite_do_fidget(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, ¶site_move_fidget);
|
||||
}
|
||||
|
||||
void parasite_refidget(edict_t *self)
|
||||
{
|
||||
if (frandom() <= 0.8f)
|
||||
M_SetAnimation(self, ¶site_move_fidget);
|
||||
else
|
||||
M_SetAnimation(self, ¶site_move_end_fidget);
|
||||
}
|
||||
|
||||
MONSTERINFO_IDLE(parasite_idle) (edict_t *self) -> void
|
||||
{
|
||||
if (self->enemy)
|
||||
return;
|
||||
|
||||
M_SetAnimation(self, ¶site_move_start_fidget);
|
||||
}
|
||||
|
||||
mframe_t parasite_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, parasite_tap },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, parasite_tap },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, parasite_tap },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, parasite_tap },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, parasite_tap },
|
||||
{ ai_stand },
|
||||
{ ai_stand, 0, parasite_tap }
|
||||
};
|
||||
MMOVE_T(parasite_move_stand) = { FRAME_stand01, FRAME_stand17, parasite_frames_stand, parasite_stand };
|
||||
|
||||
MONSTERINFO_STAND(parasite_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, ¶site_move_stand);
|
||||
}
|
||||
|
||||
mframe_t parasite_frames_run[] = {
|
||||
{ ai_run, 30 },
|
||||
{ ai_run, 30 },
|
||||
{ ai_run, 22, monster_footstep },
|
||||
{ ai_run, 19, monster_footstep },
|
||||
{ ai_run, 24 },
|
||||
{ ai_run, 28, monster_footstep },
|
||||
{ ai_run, 25, monster_footstep }
|
||||
};
|
||||
MMOVE_T(parasite_move_run) = { FRAME_run03, FRAME_run09, parasite_frames_run, nullptr };
|
||||
|
||||
mframe_t parasite_frames_start_run[] = {
|
||||
{ ai_run },
|
||||
{ ai_run, 30 },
|
||||
};
|
||||
MMOVE_T(parasite_move_start_run) = { FRAME_run01, FRAME_run02, parasite_frames_start_run, parasite_run };
|
||||
|
||||
#if 0
|
||||
mframe_t parasite_frames_stop_run[] = {
|
||||
{ ai_run, 20 },
|
||||
{ ai_run, 20 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 10 },
|
||||
{ ai_run },
|
||||
{ ai_run }
|
||||
};
|
||||
MMOVE_T(parasite_move_stop_run) = { FRAME_run10, FRAME_run15, parasite_frames_stop_run, nullptr };
|
||||
#endif
|
||||
|
||||
MONSTERINFO_RUN(parasite_start_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, ¶site_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, ¶site_move_start_run);
|
||||
}
|
||||
|
||||
static void proboscis_retract(edict_t *self);
|
||||
|
||||
void parasite_run(edict_t *self)
|
||||
{
|
||||
if (self->proboscus && self->proboscus->style != 2)
|
||||
proboscis_retract(self->proboscus);
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, ¶site_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, ¶site_move_run);
|
||||
}
|
||||
|
||||
mframe_t parasite_frames_walk[] = {
|
||||
{ ai_walk, 30 },
|
||||
{ ai_walk, 30 },
|
||||
{ ai_walk, 22, monster_footstep },
|
||||
{ ai_walk, 19, monster_footstep },
|
||||
{ ai_walk, 24 },
|
||||
{ ai_walk, 28, monster_footstep },
|
||||
{ ai_walk, 25, monster_footstep }
|
||||
};
|
||||
MMOVE_T(parasite_move_walk) = { FRAME_run03, FRAME_run09, parasite_frames_walk, parasite_walk };
|
||||
|
||||
mframe_t parasite_frames_start_walk[] = {
|
||||
{ ai_walk, 0 },
|
||||
{ ai_walk, 30, parasite_walk }
|
||||
};
|
||||
MMOVE_T(parasite_move_start_walk) = { FRAME_run01, FRAME_run02, parasite_frames_start_walk, nullptr };
|
||||
|
||||
#if 0
|
||||
mframe_t parasite_frames_stop_walk[] = {
|
||||
{ ai_walk, 20 },
|
||||
{ ai_walk, 20 },
|
||||
{ ai_walk, 12 },
|
||||
{ ai_walk, 10 },
|
||||
{ ai_walk },
|
||||
{ ai_walk }
|
||||
};
|
||||
MMOVE_T(parasite_move_stop_walk) = { FRAME_run10, FRAME_run15, parasite_frames_stop_walk, nullptr };
|
||||
#endif
|
||||
|
||||
MONSTERINFO_WALK(parasite_start_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, ¶site_move_start_walk);
|
||||
}
|
||||
|
||||
void parasite_walk(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, ¶site_move_walk);
|
||||
}
|
||||
|
||||
// hard reset on proboscis; like we never existed
|
||||
THINK(proboscis_reset) (edict_t *self) -> void
|
||||
{
|
||||
self->owner->proboscus = nullptr;
|
||||
G_FreeEdict(self->proboscus);
|
||||
G_FreeEdict(self);
|
||||
}
|
||||
|
||||
DIE(proboscis_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
if (mod.id == MOD_CRUSH)
|
||||
proboscis_reset(self);
|
||||
}
|
||||
|
||||
extern const mmove_t parasite_move_fire_proboscis;
|
||||
|
||||
static void parasite_break_wait(edict_t *self)
|
||||
{
|
||||
// prob exploded?
|
||||
if (self->proboscus && self->proboscus->style != 3)
|
||||
self->monsterinfo.nextframe = FRAME_break19;
|
||||
else if (brandom())
|
||||
{
|
||||
// don't get hurt
|
||||
parasite_reel_in(self);
|
||||
self->monsterinfo.nextframe = FRAME_break31;
|
||||
}
|
||||
}
|
||||
|
||||
static void proboscis_retract(edict_t *self)
|
||||
{
|
||||
// start retract animation
|
||||
if (self->owner->monsterinfo.active_move == ¶site_move_fire_proboscis)
|
||||
self->owner->monsterinfo.nextframe = FRAME_drain12;
|
||||
|
||||
// mark as retracting
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->solid = SOLID_NOT;
|
||||
// come back real hard
|
||||
if (self->style != 2)
|
||||
self->speed *= g_athena_parasite_proboscis_retract_modifier;
|
||||
self->style = 2;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
static void parasite_break_retract(edict_t *self)
|
||||
{
|
||||
if (self->proboscus)
|
||||
proboscis_retract(self->proboscus);
|
||||
}
|
||||
|
||||
static void parasite_break_sound(edict_t *self)
|
||||
{
|
||||
if (frandom() < 0.5f)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
}
|
||||
|
||||
void proboscis_segment_draw(edict_t *self);
|
||||
|
||||
static void parasite_charge_proboscis(edict_t *self, float dist)
|
||||
{
|
||||
if (self->s.frame >= FRAME_break01 && self->s.frame <= FRAME_break32)
|
||||
ai_move(self, dist);
|
||||
else
|
||||
ai_charge(self, dist);
|
||||
|
||||
if (self->proboscus)
|
||||
proboscis_segment_draw(self->proboscus->proboscus);
|
||||
}
|
||||
|
||||
static void parasite_break_noise(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
constexpr mframe_t parasite_frames_break[] = {
|
||||
{ parasite_charge_proboscis },
|
||||
{ parasite_charge_proboscis, -3, parasite_break_noise },
|
||||
{ parasite_charge_proboscis, 1 },
|
||||
{ parasite_charge_proboscis, 2 },
|
||||
{ parasite_charge_proboscis, -3 },
|
||||
{ parasite_charge_proboscis, 1 },
|
||||
{ parasite_charge_proboscis, 1 },
|
||||
{ parasite_charge_proboscis, 3 },
|
||||
{ parasite_charge_proboscis, 0, parasite_break_noise },
|
||||
{ parasite_charge_proboscis, -18 },
|
||||
{ parasite_charge_proboscis, 3 },
|
||||
{ parasite_charge_proboscis, 9 },
|
||||
{ parasite_charge_proboscis, 6 },
|
||||
{ parasite_charge_proboscis },
|
||||
{ parasite_charge_proboscis, -18 },
|
||||
{ parasite_charge_proboscis },
|
||||
{ parasite_charge_proboscis, 8, parasite_break_retract },
|
||||
{ parasite_charge_proboscis, 9 },
|
||||
{ parasite_charge_proboscis, 0, parasite_break_wait },
|
||||
{ parasite_charge_proboscis, -18, parasite_break_sound },
|
||||
{ parasite_charge_proboscis },
|
||||
{ parasite_charge_proboscis }, // airborne
|
||||
{ parasite_charge_proboscis }, // airborne
|
||||
{ parasite_charge_proboscis }, // slides
|
||||
{ parasite_charge_proboscis }, // slides
|
||||
{ parasite_charge_proboscis }, // slides
|
||||
{ parasite_charge_proboscis }, // slides
|
||||
{ parasite_charge_proboscis, 4 },
|
||||
{ parasite_charge_proboscis, 11 },
|
||||
{ parasite_charge_proboscis, -2 },
|
||||
{ parasite_charge_proboscis, -5 },
|
||||
{ parasite_charge_proboscis, 1 }
|
||||
};
|
||||
MMOVE_T(parasite_move_break) = { FRAME_break01, FRAME_break32, parasite_frames_break, parasite_start_run };
|
||||
|
||||
TOUCH(proboscis_touch) (edict_t *self, edict_t *other, const trace_t &tr, bool other_touching_self) -> void
|
||||
{
|
||||
// owner isn't trying to probe any more, don't touch anything
|
||||
if (self->owner->monsterinfo.active_move != ¶site_move_fire_proboscis)
|
||||
return;
|
||||
|
||||
vec3_t p;
|
||||
|
||||
// hit what we want to succ
|
||||
if ((other->svflags & SVF_PLAYER) || other == self->owner->enemy)
|
||||
{
|
||||
if (tr.startsolid)
|
||||
p = tr.endpos;
|
||||
else
|
||||
p = tr.endpos - ((self->s.origin - tr.endpos).normalized() * 12);
|
||||
|
||||
self->owner->monsterinfo.nextframe = FRAME_drain06;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->solid = SOLID_NOT;
|
||||
self->style = 1;
|
||||
// stick to this guy
|
||||
self->move_origin = p - other->s.origin;
|
||||
self->enemy = other;
|
||||
self->s.alpha = 0.35f;
|
||||
gi.sound(self, CHAN_WEAPON, sound_suck, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = tr.endpos + tr.plane.normal;
|
||||
// hit monster, don't suck but do small damage
|
||||
// and retract immediately
|
||||
if (other->svflags & (SVF_MONSTER | SVF_DEADMONSTER))
|
||||
proboscis_retract(self);
|
||||
else
|
||||
{
|
||||
// hit wall; stick to it and do break animation
|
||||
self->owner->monsterinfo.active_move = ¶site_move_break;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->solid = SOLID_NOT;
|
||||
self->style = 1;
|
||||
self->owner->s.angles[YAW] = self->s.angles[YAW];
|
||||
}
|
||||
}
|
||||
|
||||
if (other->takedamage)
|
||||
T_Damage(other, self, self->owner, tr.plane.normal, tr.endpos, tr.plane.normal, 5, 0, DAMAGE_NONE, MOD_UNKNOWN);
|
||||
|
||||
gi.positioned_sound(tr.endpos, self->owner, CHAN_AUTO, sound_impact, 1, ATTN_NORM, 0);
|
||||
|
||||
self->s.origin = p;
|
||||
self->nextthink = level.time + FRAME_TIME_S; // start doing stuff on next frame
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
// from break01
|
||||
constexpr vec3_t parasite_break_offsets[] = {
|
||||
{ 7.0f, 0, 7.0f },
|
||||
{ 6.3f, 14.5f, 4.0f },
|
||||
{ 8.5f, 0, 5.6f },
|
||||
{ 5.0f, -15.25f, 4.0f, },
|
||||
{ 9.5f, -1.8f, 5.9f },
|
||||
{ 6.2f, 14.f, 4.0f },
|
||||
{ 12.25f, 7.5f, 1.4f },
|
||||
{ 13.8f, 0, -2.4f },
|
||||
{ 13.8f, 0, -4.0f },
|
||||
{ 0.1f, 0, -0.7f },
|
||||
{ 5.0f, 0, 3.7f },
|
||||
{ 11.f, 0, 4.f },
|
||||
{ 13.5f, 0, -4.0f },
|
||||
{ 13.5f, 0, -4.0f },
|
||||
{ 0.2f, 0, -0.7f },
|
||||
{ 3.9f, 0, 3.6f },
|
||||
{ 8.5f, 0, 5.0f },
|
||||
{ 14.0f, 0, -4.f },
|
||||
{ 14.0f, 0, -4.f },
|
||||
{ 0.1f, 0, -0.5f }
|
||||
};
|
||||
|
||||
// from drain01
|
||||
constexpr vec3_t parasite_drain_offsets[] = {
|
||||
{ -1.7f, 0, 1.2f },
|
||||
{ -2.2f, 0, -0.6f },
|
||||
{ 7.7f, 0, 7.2f },
|
||||
{ 7.2f, 0, 5.7f },
|
||||
{ 6.2f, 0, 7.8f },
|
||||
{ 4.7f, 0, 6.7f },
|
||||
{ 5.0f, 0, 9.0f },
|
||||
{ 5.0f, 0, 7.0f },
|
||||
{ 5.0f, 0, 10.5f },
|
||||
{ 4.5f, 0, 9.7f },
|
||||
{ 1.5f, 0, 12.0f },
|
||||
{ 2.9f, 0, 11.0f },
|
||||
{ 2.1f, 0, 7.6f },
|
||||
};
|
||||
|
||||
vec3_t parasite_get_proboscis_start(edict_t *self)
|
||||
{
|
||||
vec3_t f, r, start;
|
||||
AngleVectors(self->s.angles, f, r, nullptr);
|
||||
vec3_t offset;
|
||||
if (self->s.frame >= FRAME_break01 && self->s.frame < FRAME_break01 + q_countof(parasite_break_offsets))
|
||||
offset = parasite_break_offsets[self->s.frame - FRAME_break01];
|
||||
else if (self->s.frame >= FRAME_drain01 && self->s.frame < FRAME_drain01 + q_countof(parasite_drain_offsets))
|
||||
offset = parasite_drain_offsets[self->s.frame - FRAME_drain01];
|
||||
else
|
||||
offset = { 8, 0, 6 };
|
||||
start = M_ProjectFlashSource(self, offset, f, r);
|
||||
return start;
|
||||
}
|
||||
|
||||
THINK(proboscis_think) (edict_t *self) -> void
|
||||
{
|
||||
self->nextthink = level.time + FRAME_TIME_S; // start doing stuff on next frame
|
||||
|
||||
// retracting; keep pulling until we hit the parasite
|
||||
if (self->style == 2)
|
||||
{
|
||||
vec3_t start = parasite_get_proboscis_start(self->owner);
|
||||
vec3_t dir = (self->s.origin - start);
|
||||
float dist = dir.normalize();
|
||||
|
||||
if (dist <= (self->speed * 2) * gi.frame_time_s)
|
||||
{
|
||||
// reached target; free self on next frame, let parasite know
|
||||
self->style = 3;
|
||||
self->think = proboscis_reset;
|
||||
self->s.origin = start;
|
||||
gi.linkentity(self);
|
||||
return;
|
||||
}
|
||||
|
||||
// pull us in
|
||||
self->s.origin -= dir * (self->speed * gi.frame_time_s);
|
||||
gi.linkentity(self);
|
||||
}
|
||||
// stuck on target; do damage, suck health
|
||||
// and check if target goes away
|
||||
else if (self->style == 1)
|
||||
{
|
||||
if (!self->enemy)
|
||||
{
|
||||
// stuck in wall
|
||||
}
|
||||
else if (!self->enemy->inuse || self->enemy->health <= 0 || !self->enemy->takedamage)
|
||||
{
|
||||
// target gone, retract early
|
||||
proboscis_retract(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
// update our position
|
||||
self->s.origin = self->enemy->s.origin + self->move_origin;
|
||||
|
||||
vec3_t start = parasite_get_proboscis_start(self->owner);
|
||||
|
||||
self->s.angles = vectoangles((self->s.origin - start).normalized());
|
||||
|
||||
// see if we got cut by the world
|
||||
trace_t tr = gi.traceline(start, self->s.origin, nullptr, MASK_SOLID);
|
||||
|
||||
if (tr.fraction != 1.0f)
|
||||
{
|
||||
// blocked, so retract
|
||||
proboscis_retract(self);
|
||||
self->s.origin = self->s.old_origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
// succ & drain
|
||||
if (self->timestamp <= level.time)
|
||||
{
|
||||
T_Damage(self->enemy, self, self->owner, tr.plane.normal, tr.endpos, tr.plane.normal, 2, 0, DAMAGE_NONE, MOD_UNKNOWN);
|
||||
self->owner->health = min(self->owner->max_health, self->owner->health + 2);
|
||||
self->owner->monsterinfo.setskin(self->owner);
|
||||
self->timestamp = level.time + 10_hz;
|
||||
}
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
}
|
||||
// flying
|
||||
else if (self->style == 0)
|
||||
{
|
||||
// owner gone away?
|
||||
if (!self->owner->enemy || !self->owner->enemy->inuse || self->owner->enemy->health <= 0)
|
||||
{
|
||||
proboscis_retract(self);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're well behind our target and missed by 2x velocity,
|
||||
// be smart enough to pull in automatically
|
||||
vec3_t to_target = (self->s.origin - self->owner->enemy->s.origin);
|
||||
float dist_to_target = to_target.normalize();
|
||||
|
||||
if (dist_to_target > (self->speed * 2) / 15.f)
|
||||
{
|
||||
vec3_t from_owner = (self->s.origin - self->owner->s.origin).normalized();
|
||||
float dot = to_target.dot(from_owner);
|
||||
|
||||
if (dot > 0.f)
|
||||
{
|
||||
proboscis_retract(self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRETHINK(proboscis_segment_draw) (edict_t *self) -> void
|
||||
{
|
||||
vec3_t start = parasite_get_proboscis_start(self->owner->owner);
|
||||
|
||||
self->s.origin = start;
|
||||
self->s.old_origin = self->owner->s.origin - ((self->owner->s.origin - start).normalized() * 8.f);
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
static void fire_proboscis(edict_t *self, vec3_t start, vec3_t dir, float speed)
|
||||
{
|
||||
edict_t *tip = G_Spawn();
|
||||
tip->s.angles = vectoangles(dir);
|
||||
tip->s.modelindex = gi.modelindex("models/monsters/parasite/tip/tris.md2");
|
||||
tip->movetype = MOVETYPE_FLYMISSILE;
|
||||
tip->owner = self;
|
||||
self->proboscus = tip;
|
||||
tip->clipmask = MASK_PROJECTILE & ~CONTENTS_DEADMONSTER;
|
||||
tip->s.origin = tip->s.old_origin = start;
|
||||
tip->speed = speed;
|
||||
tip->velocity = dir * speed;
|
||||
tip->solid = SOLID_BBOX;
|
||||
tip->takedamage = true;
|
||||
tip->flags |= FL_NO_DAMAGE_EFFECTS | FL_NO_KNOCKBACK;
|
||||
tip->die = proboscis_die;
|
||||
tip->touch = proboscis_touch;
|
||||
tip->think = proboscis_think;
|
||||
tip->nextthink = level.time + FRAME_TIME_S; // start doing stuff on next frame
|
||||
tip->svflags |= SVF_PROJECTILE;
|
||||
|
||||
edict_t *segment = G_Spawn();
|
||||
segment->s.modelindex = gi.modelindex("models/monsters/parasite/segment/tris.md2");
|
||||
segment->s.renderfx = RF_BEAM;
|
||||
segment->postthink = proboscis_segment_draw;
|
||||
|
||||
tip->proboscus = segment;
|
||||
segment->owner = tip;
|
||||
|
||||
trace_t tr = gi.traceline(tip->s.origin, tip->s.origin + (tip->velocity * gi.frame_time_s), self, tip->clipmask);
|
||||
if (tr.startsolid)
|
||||
{
|
||||
tr.plane.normal = -dir;
|
||||
tr.endpos = start;
|
||||
tip->touch(tip, tr.ent, tr, false);
|
||||
}
|
||||
else if (tr.fraction < 1.0f)
|
||||
tip->touch(tip, tr.ent, tr, false);
|
||||
|
||||
segment->s.origin = start;
|
||||
segment->s.old_origin = tip->s.origin + ((tip->s.origin - start).normalized() * 8.f);
|
||||
|
||||
gi.linkentity(tip);
|
||||
gi.linkentity(segment);
|
||||
}
|
||||
|
||||
static void parasite_fire_proboscis(edict_t *self)
|
||||
{
|
||||
if (self->proboscus && self->proboscus->style != 2)
|
||||
proboscis_reset(self->proboscus);
|
||||
|
||||
vec3_t start = parasite_get_proboscis_start(self);
|
||||
|
||||
vec3_t dir;
|
||||
PredictAim(self, self->enemy, start, g_athena_parasite_proboscis_speed, false, crandom_open() * g_athena_parasite_miss_chance, &dir, nullptr);
|
||||
|
||||
fire_proboscis(self, start, dir, g_athena_parasite_proboscis_speed);
|
||||
}
|
||||
|
||||
static void parasite_proboscis_wait(edict_t *self)
|
||||
{
|
||||
// loop frames while we wait
|
||||
if (self->s.frame == FRAME_drain04)
|
||||
self->monsterinfo.nextframe = FRAME_drain05;
|
||||
else
|
||||
self->monsterinfo.nextframe = FRAME_drain04;
|
||||
}
|
||||
|
||||
static void parasite_proboscis_pull_wait(edict_t *self)
|
||||
{
|
||||
// prob exploded?
|
||||
if (!self->proboscus || self->proboscus->style == 3)
|
||||
{
|
||||
self->monsterinfo.nextframe = FRAME_drain14;
|
||||
return;
|
||||
}
|
||||
|
||||
// being pulled in, so wait until we get destroyed
|
||||
if (self->s.frame == FRAME_drain12)
|
||||
self->monsterinfo.nextframe = FRAME_drain13;
|
||||
else
|
||||
self->monsterinfo.nextframe = FRAME_drain12;
|
||||
|
||||
if (self->proboscus->style != 2)
|
||||
proboscis_retract(self->proboscus);
|
||||
}
|
||||
|
||||
mframe_t parasite_frames_fire_proboscis[] = {
|
||||
{ parasite_charge_proboscis, 0, parasite_launch },
|
||||
{ parasite_charge_proboscis },
|
||||
{ parasite_charge_proboscis, 15, parasite_fire_proboscis }, // Target hits
|
||||
{ parasite_charge_proboscis, 0, parasite_proboscis_wait }, // drain
|
||||
{ parasite_charge_proboscis, 0, parasite_proboscis_wait }, // drain
|
||||
{ parasite_charge_proboscis, 0 }, // drain
|
||||
{ parasite_charge_proboscis, 0 }, // drain
|
||||
{ parasite_charge_proboscis, -2 }, // drain
|
||||
{ parasite_charge_proboscis, -2 }, // drain
|
||||
{ parasite_charge_proboscis, -3 }, // drain
|
||||
{ parasite_charge_proboscis, -2 }, // drain
|
||||
{ parasite_charge_proboscis, 0, parasite_proboscis_pull_wait }, // drain
|
||||
{ parasite_charge_proboscis, -1, parasite_proboscis_pull_wait }, // drain
|
||||
{ parasite_charge_proboscis, 0, parasite_reel_in }, // let go
|
||||
{ parasite_charge_proboscis, -2 },
|
||||
{ parasite_charge_proboscis, -2 },
|
||||
{ parasite_charge_proboscis, -3 },
|
||||
{ parasite_charge_proboscis }
|
||||
};
|
||||
MMOVE_T(parasite_move_fire_proboscis) = { FRAME_drain01, FRAME_drain18, parasite_frames_fire_proboscis, parasite_start_run };
|
||||
|
||||
MONSTERINFO_ATTACK(parasite_attack) (edict_t *self) -> void
|
||||
{
|
||||
if (!M_CheckClearShot(self, parasite_drain_offsets[0]))
|
||||
return;
|
||||
|
||||
if (self->proboscus && self->proboscus->style != 2)
|
||||
proboscis_retract(self->proboscus);
|
||||
|
||||
M_SetAnimation(self, ¶site_move_fire_proboscis);
|
||||
}
|
||||
|
||||
//================
|
||||
// ROGUE
|
||||
void parasite_jump_down(edict_t *self)
|
||||
{
|
||||
vec3_t forward, up;
|
||||
|
||||
AngleVectors(self->s.angles, forward, nullptr, up);
|
||||
self->velocity += (forward * 100);
|
||||
self->velocity += (up * 300);
|
||||
}
|
||||
|
||||
void parasite_jump_up(edict_t *self)
|
||||
{
|
||||
vec3_t forward, up;
|
||||
|
||||
AngleVectors(self->s.angles, forward, nullptr, up);
|
||||
self->velocity += (forward * 200);
|
||||
self->velocity += (up * 450);
|
||||
}
|
||||
|
||||
void parasite_jump_wait_land(edict_t *self)
|
||||
{
|
||||
if (self->groundentity == nullptr)
|
||||
{
|
||||
self->monsterinfo.nextframe = self->s.frame;
|
||||
|
||||
if (monster_jump_finished(self))
|
||||
self->monsterinfo.nextframe = self->s.frame + 1;
|
||||
}
|
||||
else
|
||||
self->monsterinfo.nextframe = self->s.frame + 1;
|
||||
}
|
||||
|
||||
mframe_t parasite_frames_jump_up[] = {
|
||||
{ ai_move, -8 },
|
||||
{ ai_move, -8 },
|
||||
{ ai_move, -8 },
|
||||
{ ai_move, -8, parasite_jump_up },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, parasite_jump_wait_land },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(parasite_move_jump_up) = { FRAME_jump01, FRAME_jump08, parasite_frames_jump_up, parasite_run };
|
||||
|
||||
mframe_t parasite_frames_jump_down[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, parasite_jump_down },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, parasite_jump_wait_land },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(parasite_move_jump_down) = { FRAME_jump01, FRAME_jump08, parasite_frames_jump_down, parasite_run };
|
||||
|
||||
void parasite_jump(edict_t *self, blocked_jump_result_t result)
|
||||
{
|
||||
if (!self->enemy)
|
||||
return;
|
||||
|
||||
if (result == blocked_jump_result_t::JUMP_JUMP_UP)
|
||||
M_SetAnimation(self, ¶site_move_jump_up);
|
||||
else
|
||||
M_SetAnimation(self, ¶site_move_jump_down);
|
||||
}
|
||||
|
||||
/*
|
||||
===
|
||||
Blocked
|
||||
===
|
||||
*/
|
||||
MONSTERINFO_BLOCKED(parasite_blocked) (edict_t *self, float dist) -> bool
|
||||
{
|
||||
if (auto result = blocked_checkjump(self, dist); result != blocked_jump_result_t::NO_JUMP)
|
||||
{
|
||||
if (result != blocked_jump_result_t::JUMP_TURN)
|
||||
parasite_jump(self, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (blocked_checkplat(self, dist))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
// ROGUE
|
||||
//================
|
||||
|
||||
/*
|
||||
===
|
||||
Death Stuff Starts
|
||||
===
|
||||
*/
|
||||
|
||||
void parasite_dead(edict_t *self)
|
||||
{
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, -8 };
|
||||
monster_dead(self);
|
||||
}
|
||||
|
||||
static void parasite_shrink(edict_t *self)
|
||||
{
|
||||
self->maxs[2] = 0;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t parasite_frames_death[] = {
|
||||
{ ai_move, 0, nullptr, FRAME_stand01 },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, parasite_shrink },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(parasite_move_death) = { FRAME_death101, FRAME_death107, parasite_frames_death, parasite_dead };
|
||||
|
||||
DIE(parasite_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
if (self->proboscus && self->proboscus->style != 2)
|
||||
proboscis_reset(self->proboscus);
|
||||
|
||||
// check for gib
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
ThrowGibs(self, damage, {
|
||||
{ 1, "models/objects/gibs/bone/tris.md2" },
|
||||
{ 3, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ "models/monsters/parasite/gibs/chest.md2", GIB_SKINNED },
|
||||
{ 2, "models/monsters/parasite/gibs/bleg.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ 2, "models/monsters/parasite/gibs/fleg.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/parasite/gibs/head.md2", GIB_SKINNED | GIB_HEAD }
|
||||
});
|
||||
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
M_SetAnimation(self, ¶site_move_death);
|
||||
}
|
||||
|
||||
/*
|
||||
===
|
||||
End Death Stuff
|
||||
===
|
||||
*/
|
||||
|
||||
mframe_t parasite_frames_pain1[] = {
|
||||
{ ai_move, 0, nullptr, FRAME_stand01 },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, [](edict_t *self) { self->monsterinfo.nextframe = FRAME_pain105; } },
|
||||
{ ai_move, 0, monster_footstep },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 6, monster_footstep },
|
||||
{ ai_move, 16 },
|
||||
{ ai_move, -6, monster_footstep },
|
||||
{ ai_move, -7 },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(parasite_move_pain1) = { FRAME_pain101, FRAME_pain111, parasite_frames_pain1, parasite_start_run };
|
||||
|
||||
PAIN(parasite_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
if (self->proboscus && self->proboscus->style != 2)
|
||||
proboscis_retract(self->proboscus);
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
if (frandom() < 0.5f)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
M_SetAnimation(self, ¶site_move_pain1);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(parasite_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
else
|
||||
self->s.skinnum = 0;
|
||||
}
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_PARASITE_NOJUMPING = 8_spawnflag;
|
||||
|
||||
/*QUAKED monster_parasite (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight NoJumping
|
||||
*/
|
||||
void SP_monster_parasite(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_pain1 = gi.soundindex("parasite/parpain1.wav");
|
||||
sound_pain2 = gi.soundindex("parasite/parpain2.wav");
|
||||
sound_die = gi.soundindex("parasite/pardeth1.wav");
|
||||
sound_launch = gi.soundindex("parasite/paratck1.wav");
|
||||
sound_impact = gi.soundindex("parasite/paratck2.wav");
|
||||
sound_suck = gi.soundindex("parasite/paratck3.wav");
|
||||
sound_reelin = gi.soundindex("parasite/paratck4.wav");
|
||||
sound_sight = gi.soundindex("parasite/parsght1.wav");
|
||||
sound_tap = gi.soundindex("parasite/paridle1.wav");
|
||||
sound_scratch = gi.soundindex("parasite/paridle2.wav");
|
||||
sound_search = gi.soundindex("parasite/parsrch1.wav");
|
||||
|
||||
gi.modelindex("models/monsters/parasite/tip/tris.md2");
|
||||
gi.modelindex("models/monsters/parasite/segment/tris.md2");
|
||||
|
||||
self->s.modelindex = gi.modelindex("models/monsters/parasite/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/parasite/gibs/head.md2");
|
||||
gi.modelindex("models/monsters/parasite/gibs/chest.md2");
|
||||
gi.modelindex("models/monsters/parasite/gibs/bleg.md2");
|
||||
gi.modelindex("models/monsters/parasite/gibs/fleg.md2");
|
||||
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, 24 };
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
|
||||
self->health = 175 * st.health_multiplier;
|
||||
self->gib_health = -50;
|
||||
self->mass = 250;
|
||||
|
||||
self->pain = parasite_pain;
|
||||
self->die = parasite_die;
|
||||
|
||||
self->monsterinfo.stand = parasite_stand;
|
||||
self->monsterinfo.walk = parasite_start_walk;
|
||||
self->monsterinfo.run = parasite_start_run;
|
||||
self->monsterinfo.attack = parasite_attack;
|
||||
self->monsterinfo.sight = parasite_sight;
|
||||
self->monsterinfo.idle = parasite_idle;
|
||||
self->monsterinfo.blocked = parasite_blocked; // PGM
|
||||
self->monsterinfo.setskin = parasite_setskin;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, ¶site_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
self->yaw_speed = 30;
|
||||
self->monsterinfo.can_jump = !self->spawnflags.has(SPAWNFLAG_PARASITE_NOJUMPING);
|
||||
self->monsterinfo.drop_height = 256;
|
||||
self->monsterinfo.jump_height = 68;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
139
rerelease/m_parasite.h
Normal file
139
rerelease/m_parasite.h
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/parasite
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_break01,
|
||||
FRAME_break02,
|
||||
FRAME_break03,
|
||||
FRAME_break04,
|
||||
FRAME_break05,
|
||||
FRAME_break06,
|
||||
FRAME_break07,
|
||||
FRAME_break08,
|
||||
FRAME_break09,
|
||||
FRAME_break10,
|
||||
FRAME_break11,
|
||||
FRAME_break12,
|
||||
FRAME_break13,
|
||||
FRAME_break14,
|
||||
FRAME_break15,
|
||||
FRAME_break16,
|
||||
FRAME_break17,
|
||||
FRAME_break18,
|
||||
FRAME_break19,
|
||||
FRAME_break20,
|
||||
FRAME_break21,
|
||||
FRAME_break22,
|
||||
FRAME_break23,
|
||||
FRAME_break24,
|
||||
FRAME_break25,
|
||||
FRAME_break26,
|
||||
FRAME_break27,
|
||||
FRAME_break28,
|
||||
FRAME_break29,
|
||||
FRAME_break30,
|
||||
FRAME_break31,
|
||||
FRAME_break32,
|
||||
FRAME_death101,
|
||||
FRAME_death102,
|
||||
FRAME_death103,
|
||||
FRAME_death104,
|
||||
FRAME_death105,
|
||||
FRAME_death106,
|
||||
FRAME_death107,
|
||||
FRAME_drain01,
|
||||
FRAME_drain02,
|
||||
FRAME_drain03,
|
||||
FRAME_drain04,
|
||||
FRAME_drain05,
|
||||
FRAME_drain06,
|
||||
FRAME_drain07,
|
||||
FRAME_drain08,
|
||||
FRAME_drain09,
|
||||
FRAME_drain10,
|
||||
FRAME_drain11,
|
||||
FRAME_drain12,
|
||||
FRAME_drain13,
|
||||
FRAME_drain14,
|
||||
FRAME_drain15,
|
||||
FRAME_drain16,
|
||||
FRAME_drain17,
|
||||
FRAME_drain18,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain104,
|
||||
FRAME_pain105,
|
||||
FRAME_pain106,
|
||||
FRAME_pain107,
|
||||
FRAME_pain108,
|
||||
FRAME_pain109,
|
||||
FRAME_pain110,
|
||||
FRAME_pain111,
|
||||
FRAME_run01,
|
||||
FRAME_run02,
|
||||
FRAME_run03,
|
||||
FRAME_run04,
|
||||
FRAME_run05,
|
||||
FRAME_run06,
|
||||
FRAME_run07,
|
||||
FRAME_run08,
|
||||
FRAME_run09,
|
||||
FRAME_run10,
|
||||
FRAME_run11,
|
||||
FRAME_run12,
|
||||
FRAME_run13,
|
||||
FRAME_run14,
|
||||
FRAME_run15,
|
||||
FRAME_stand01,
|
||||
FRAME_stand02,
|
||||
FRAME_stand03,
|
||||
FRAME_stand04,
|
||||
FRAME_stand05,
|
||||
FRAME_stand06,
|
||||
FRAME_stand07,
|
||||
FRAME_stand08,
|
||||
FRAME_stand09,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_stand18,
|
||||
FRAME_stand19,
|
||||
FRAME_stand20,
|
||||
FRAME_stand21,
|
||||
FRAME_stand22,
|
||||
FRAME_stand23,
|
||||
FRAME_stand24,
|
||||
FRAME_stand25,
|
||||
FRAME_stand26,
|
||||
FRAME_stand27,
|
||||
FRAME_stand28,
|
||||
FRAME_stand29,
|
||||
FRAME_stand30,
|
||||
FRAME_stand31,
|
||||
FRAME_stand32,
|
||||
FRAME_stand33,
|
||||
FRAME_stand34,
|
||||
FRAME_stand35,
|
||||
// ROGUE
|
||||
FRAME_jump01,
|
||||
FRAME_jump02,
|
||||
FRAME_jump03,
|
||||
FRAME_jump04,
|
||||
FRAME_jump05,
|
||||
FRAME_jump06,
|
||||
FRAME_jump07,
|
||||
FRAME_jump08
|
||||
// ROGUE
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
209
rerelease/m_player.h
Normal file
209
rerelease/m_player.h
Normal file
@@ -0,0 +1,209 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/player_x/frames
|
||||
|
||||
// This file generated by qdata - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_stand01,
|
||||
FRAME_stand02,
|
||||
FRAME_stand03,
|
||||
FRAME_stand04,
|
||||
FRAME_stand05,
|
||||
FRAME_stand06,
|
||||
FRAME_stand07,
|
||||
FRAME_stand08,
|
||||
FRAME_stand09,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_stand18,
|
||||
FRAME_stand19,
|
||||
FRAME_stand20,
|
||||
FRAME_stand21,
|
||||
FRAME_stand22,
|
||||
FRAME_stand23,
|
||||
FRAME_stand24,
|
||||
FRAME_stand25,
|
||||
FRAME_stand26,
|
||||
FRAME_stand27,
|
||||
FRAME_stand28,
|
||||
FRAME_stand29,
|
||||
FRAME_stand30,
|
||||
FRAME_stand31,
|
||||
FRAME_stand32,
|
||||
FRAME_stand33,
|
||||
FRAME_stand34,
|
||||
FRAME_stand35,
|
||||
FRAME_stand36,
|
||||
FRAME_stand37,
|
||||
FRAME_stand38,
|
||||
FRAME_stand39,
|
||||
FRAME_stand40,
|
||||
FRAME_run1,
|
||||
FRAME_run2,
|
||||
FRAME_run3,
|
||||
FRAME_run4,
|
||||
FRAME_run5,
|
||||
FRAME_run6,
|
||||
FRAME_attack1,
|
||||
FRAME_attack2,
|
||||
FRAME_attack3,
|
||||
FRAME_attack4,
|
||||
FRAME_attack5,
|
||||
FRAME_attack6,
|
||||
FRAME_attack7,
|
||||
FRAME_attack8,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain104,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain204,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_pain304,
|
||||
FRAME_jump1,
|
||||
FRAME_jump2,
|
||||
FRAME_jump3,
|
||||
FRAME_jump4,
|
||||
FRAME_jump5,
|
||||
FRAME_jump6,
|
||||
FRAME_flip01,
|
||||
FRAME_flip02,
|
||||
FRAME_flip03,
|
||||
FRAME_flip04,
|
||||
FRAME_flip05,
|
||||
FRAME_flip06,
|
||||
FRAME_flip07,
|
||||
FRAME_flip08,
|
||||
FRAME_flip09,
|
||||
FRAME_flip10,
|
||||
FRAME_flip11,
|
||||
FRAME_flip12,
|
||||
FRAME_salute01,
|
||||
FRAME_salute02,
|
||||
FRAME_salute03,
|
||||
FRAME_salute04,
|
||||
FRAME_salute05,
|
||||
FRAME_salute06,
|
||||
FRAME_salute07,
|
||||
FRAME_salute08,
|
||||
FRAME_salute09,
|
||||
FRAME_salute10,
|
||||
FRAME_salute11,
|
||||
FRAME_taunt01,
|
||||
FRAME_taunt02,
|
||||
FRAME_taunt03,
|
||||
FRAME_taunt04,
|
||||
FRAME_taunt05,
|
||||
FRAME_taunt06,
|
||||
FRAME_taunt07,
|
||||
FRAME_taunt08,
|
||||
FRAME_taunt09,
|
||||
FRAME_taunt10,
|
||||
FRAME_taunt11,
|
||||
FRAME_taunt12,
|
||||
FRAME_taunt13,
|
||||
FRAME_taunt14,
|
||||
FRAME_taunt15,
|
||||
FRAME_taunt16,
|
||||
FRAME_taunt17,
|
||||
FRAME_wave01,
|
||||
FRAME_wave02,
|
||||
FRAME_wave03,
|
||||
FRAME_wave04,
|
||||
FRAME_wave05,
|
||||
FRAME_wave06,
|
||||
FRAME_wave07,
|
||||
FRAME_wave08,
|
||||
FRAME_wave09,
|
||||
FRAME_wave10,
|
||||
FRAME_wave11,
|
||||
FRAME_point01,
|
||||
FRAME_point02,
|
||||
FRAME_point03,
|
||||
FRAME_point04,
|
||||
FRAME_point05,
|
||||
FRAME_point06,
|
||||
FRAME_point07,
|
||||
FRAME_point08,
|
||||
FRAME_point09,
|
||||
FRAME_point10,
|
||||
FRAME_point11,
|
||||
FRAME_point12,
|
||||
FRAME_crstnd01,
|
||||
FRAME_crstnd02,
|
||||
FRAME_crstnd03,
|
||||
FRAME_crstnd04,
|
||||
FRAME_crstnd05,
|
||||
FRAME_crstnd06,
|
||||
FRAME_crstnd07,
|
||||
FRAME_crstnd08,
|
||||
FRAME_crstnd09,
|
||||
FRAME_crstnd10,
|
||||
FRAME_crstnd11,
|
||||
FRAME_crstnd12,
|
||||
FRAME_crstnd13,
|
||||
FRAME_crstnd14,
|
||||
FRAME_crstnd15,
|
||||
FRAME_crstnd16,
|
||||
FRAME_crstnd17,
|
||||
FRAME_crstnd18,
|
||||
FRAME_crstnd19,
|
||||
FRAME_crwalk1,
|
||||
FRAME_crwalk2,
|
||||
FRAME_crwalk3,
|
||||
FRAME_crwalk4,
|
||||
FRAME_crwalk5,
|
||||
FRAME_crwalk6,
|
||||
FRAME_crattak1,
|
||||
FRAME_crattak2,
|
||||
FRAME_crattak3,
|
||||
FRAME_crattak4,
|
||||
FRAME_crattak5,
|
||||
FRAME_crattak6,
|
||||
FRAME_crattak7,
|
||||
FRAME_crattak8,
|
||||
FRAME_crattak9,
|
||||
FRAME_crpain1,
|
||||
FRAME_crpain2,
|
||||
FRAME_crpain3,
|
||||
FRAME_crpain4,
|
||||
FRAME_crdeath1,
|
||||
FRAME_crdeath2,
|
||||
FRAME_crdeath3,
|
||||
FRAME_crdeath4,
|
||||
FRAME_crdeath5,
|
||||
FRAME_death101,
|
||||
FRAME_death102,
|
||||
FRAME_death103,
|
||||
FRAME_death104,
|
||||
FRAME_death105,
|
||||
FRAME_death106,
|
||||
FRAME_death201,
|
||||
FRAME_death202,
|
||||
FRAME_death203,
|
||||
FRAME_death204,
|
||||
FRAME_death205,
|
||||
FRAME_death206,
|
||||
FRAME_death301,
|
||||
FRAME_death302,
|
||||
FRAME_death303,
|
||||
FRAME_death304,
|
||||
FRAME_death305,
|
||||
FRAME_death306,
|
||||
FRAME_death307,
|
||||
FRAME_death308
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
71
rerelease/m_rider.h
Normal file
71
rerelease/m_rider.h
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/boss3/rider
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_stand201,
|
||||
FRAME_stand202,
|
||||
FRAME_stand203,
|
||||
FRAME_stand204,
|
||||
FRAME_stand205,
|
||||
FRAME_stand206,
|
||||
FRAME_stand207,
|
||||
FRAME_stand208,
|
||||
FRAME_stand209,
|
||||
FRAME_stand210,
|
||||
FRAME_stand211,
|
||||
FRAME_stand212,
|
||||
FRAME_stand213,
|
||||
FRAME_stand214,
|
||||
FRAME_stand215,
|
||||
FRAME_stand216,
|
||||
FRAME_stand217,
|
||||
FRAME_stand218,
|
||||
FRAME_stand219,
|
||||
FRAME_stand220,
|
||||
FRAME_stand221,
|
||||
FRAME_stand222,
|
||||
FRAME_stand223,
|
||||
FRAME_stand224,
|
||||
FRAME_stand225,
|
||||
FRAME_stand226,
|
||||
FRAME_stand227,
|
||||
FRAME_stand228,
|
||||
FRAME_stand229,
|
||||
FRAME_stand230,
|
||||
FRAME_stand231,
|
||||
FRAME_stand232,
|
||||
FRAME_stand233,
|
||||
FRAME_stand234,
|
||||
FRAME_stand235,
|
||||
FRAME_stand236,
|
||||
FRAME_stand237,
|
||||
FRAME_stand238,
|
||||
FRAME_stand239,
|
||||
FRAME_stand240,
|
||||
FRAME_stand241,
|
||||
FRAME_stand242,
|
||||
FRAME_stand243,
|
||||
FRAME_stand244,
|
||||
FRAME_stand245,
|
||||
FRAME_stand246,
|
||||
FRAME_stand247,
|
||||
FRAME_stand248,
|
||||
FRAME_stand249,
|
||||
FRAME_stand250,
|
||||
FRAME_stand251,
|
||||
FRAME_stand252,
|
||||
FRAME_stand253,
|
||||
FRAME_stand254,
|
||||
FRAME_stand255,
|
||||
FRAME_stand256,
|
||||
FRAME_stand257,
|
||||
FRAME_stand258,
|
||||
FRAME_stand259,
|
||||
FRAME_stand260
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000;
|
||||
598
rerelease/m_shambler.cpp
Normal file
598
rerelease/m_shambler.cpp
Normal file
@@ -0,0 +1,598 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
SHAMBLER
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_shambler.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
static int sound_pain;
|
||||
static int sound_idle;
|
||||
static int sound_die;
|
||||
static int sound_sight;
|
||||
static int sound_windup;
|
||||
static int sound_melee1;
|
||||
static int sound_melee2;
|
||||
static int sound_smack;
|
||||
static int sound_boom;
|
||||
|
||||
//
|
||||
// misc
|
||||
//
|
||||
|
||||
MONSTERINFO_SIGHT(shambler_sight) (edict_t* self, edict_t* other) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
constexpr vec3_t lightning_left_hand[] = {
|
||||
{ 44, 36, 25 },
|
||||
{ 10, 44, 57 },
|
||||
{ -1, 40, 70 },
|
||||
{ -10, 34, 75 },
|
||||
{ 7.4f, 24, 89 }
|
||||
};
|
||||
|
||||
constexpr vec3_t lightning_right_hand[] = {
|
||||
{ 28, -38, 25 },
|
||||
{ 31, -7, 70 },
|
||||
{ 20, 0, 80 },
|
||||
{ 16, 1.2f, 81 },
|
||||
{ 27, -11, 83 }
|
||||
};
|
||||
|
||||
static void shambler_lightning_update(edict_t *self)
|
||||
{
|
||||
edict_t *lightning = self->beam;
|
||||
|
||||
if (self->s.frame >= FRAME_magic01 + q_countof(lightning_left_hand))
|
||||
{
|
||||
G_FreeEdict(lightning);
|
||||
self->beam = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
vec3_t f, r;
|
||||
AngleVectors(self->s.angles, f, r, nullptr);
|
||||
lightning->s.origin = M_ProjectFlashSource(self, lightning_left_hand[self->s.frame - FRAME_magic01], f, r);
|
||||
lightning->s.old_origin = M_ProjectFlashSource(self, lightning_right_hand[self->s.frame - FRAME_magic01], f, r);
|
||||
gi.linkentity(lightning);
|
||||
}
|
||||
|
||||
void shambler_windup(edict_t* self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_windup, 1, ATTN_NORM, 0);
|
||||
|
||||
edict_t *lightning = self->beam = G_Spawn();
|
||||
lightning->s.modelindex = gi.modelindex("models/proj/lightning/tris.md2");
|
||||
lightning->s.renderfx |= RF_BEAM;
|
||||
lightning->owner = self;
|
||||
shambler_lightning_update(self);
|
||||
}
|
||||
|
||||
MONSTERINFO_IDLE(shambler_idle) (edict_t* self) -> void
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
void shambler_maybe_idle(edict_t* self)
|
||||
{
|
||||
if (frandom() > 0.8)
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
//
|
||||
// stand
|
||||
//
|
||||
|
||||
mframe_t shambler_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(shambler_move_stand) = { FRAME_stand01, FRAME_stand17, shambler_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(shambler_stand) (edict_t* self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &shambler_move_stand);
|
||||
}
|
||||
|
||||
//
|
||||
// walk
|
||||
//
|
||||
|
||||
void shambler_walk(edict_t* self);
|
||||
|
||||
mframe_t shambler_frames_walk[] = {
|
||||
{ ai_walk, 10 }, // FIXME: add footsteps?
|
||||
{ ai_walk, 9 },
|
||||
{ ai_walk, 9 },
|
||||
{ ai_walk, 5 },
|
||||
{ ai_walk, 6 },
|
||||
{ ai_walk, 12 },
|
||||
{ ai_walk, 8 },
|
||||
{ ai_walk, 3 },
|
||||
{ ai_walk, 13 },
|
||||
{ ai_walk, 9 },
|
||||
{ ai_walk, 7, shambler_maybe_idle },
|
||||
{ ai_walk, 5 },
|
||||
};
|
||||
MMOVE_T(shambler_move_walk) = { FRAME_walk01, FRAME_walk12, shambler_frames_walk, nullptr };
|
||||
|
||||
MONSTERINFO_WALK(shambler_walk) (edict_t* self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &shambler_move_walk);
|
||||
}
|
||||
|
||||
//
|
||||
// run
|
||||
//
|
||||
|
||||
void shambler_run(edict_t* self);
|
||||
|
||||
mframe_t shambler_frames_run[] = {
|
||||
{ ai_run, 20 }, // FIXME: add footsteps?
|
||||
{ ai_run, 24 },
|
||||
{ ai_run, 20 },
|
||||
{ ai_run, 20 },
|
||||
{ ai_run, 24 },
|
||||
{ ai_run, 20, shambler_maybe_idle },
|
||||
};
|
||||
MMOVE_T(shambler_move_run) = { FRAME_run01, FRAME_run06, shambler_frames_run, nullptr };
|
||||
|
||||
MONSTERINFO_RUN(shambler_run) (edict_t* self) -> void
|
||||
{
|
||||
if (self->enemy && self->enemy->client)
|
||||
self->monsterinfo.aiflags |= AI_BRUTAL;
|
||||
else
|
||||
self->monsterinfo.aiflags &= ~AI_BRUTAL;
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
M_SetAnimation(self, &shambler_move_stand);
|
||||
return;
|
||||
}
|
||||
|
||||
M_SetAnimation(self, &shambler_move_run);
|
||||
}
|
||||
|
||||
//
|
||||
// pain
|
||||
//
|
||||
|
||||
// FIXME: needs halved explosion damage
|
||||
|
||||
mframe_t shambler_frames_pain[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
};
|
||||
MMOVE_T(shambler_move_pain) = { FRAME_pain01, FRAME_pain06, shambler_frames_pain, shambler_run };
|
||||
|
||||
PAIN(shambler_pain) (edict_t* self, edict_t* other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
if (level.time < self->timestamp)
|
||||
return;
|
||||
|
||||
self->timestamp = level.time + 1_ms;
|
||||
gi.sound(self, CHAN_AUTO, sound_pain, 1, ATTN_NORM, 0);
|
||||
|
||||
if (mod.id != MOD_CHAINFIST && damage <= 30 && frandom() > 0.2f)
|
||||
return;
|
||||
|
||||
// If hard or nightmare, don't go into pain while attacking
|
||||
if (skill->integer >= 2)
|
||||
{
|
||||
if ((self->s.frame >= FRAME_smash01) && (self->s.frame <= FRAME_smash12))
|
||||
return;
|
||||
|
||||
if ((self->s.frame >= FRAME_swingl01) && (self->s.frame <= FRAME_swingl09))
|
||||
return;
|
||||
|
||||
if ((self->s.frame >= FRAME_swingr01) && (self->s.frame <= FRAME_swingr09))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 2_sec;
|
||||
M_SetAnimation(self, &shambler_move_pain);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(shambler_setskin) (edict_t* self) -> void
|
||||
{
|
||||
// FIXME: create pain skin?
|
||||
//if (self->health < (self->max_health / 2))
|
||||
// self->s.skinnum |= 1;
|
||||
//else
|
||||
// self->s.skinnum &= ~1;
|
||||
}
|
||||
|
||||
//
|
||||
// attacks
|
||||
//
|
||||
|
||||
/*
|
||||
void() sham_magic3 =[ $magic3, sham_magic4 ] {
|
||||
ai_face();
|
||||
self.nextthink = self.nextthink + 0.2;
|
||||
local entity o;
|
||||
|
||||
self.effects = self.effects | EF_MUZZLEFLASH;
|
||||
ai_face();
|
||||
self.owner = spawn();
|
||||
o = self.owner;
|
||||
setmodel (o, "progs/s_light.mdl");
|
||||
setorigin (o, self.origin);
|
||||
o.angles = self.angles;
|
||||
o.nextthink = time + 0.7;
|
||||
o.think = SUB_Remove;
|
||||
};
|
||||
*/
|
||||
|
||||
void ShamblerSaveLoc(edict_t* self)
|
||||
{
|
||||
self->pos1 = self->enemy->s.origin; // save for aiming the shot
|
||||
self->pos1[2] += self->enemy->viewheight;
|
||||
self->monsterinfo.nextframe = FRAME_magic09;
|
||||
|
||||
gi.sound(self, CHAN_WEAPON, sound_boom, 1, ATTN_NORM, 0);
|
||||
shambler_lightning_update(self);
|
||||
}
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_SHAMBLER_PRECISE = 1_spawnflag;
|
||||
|
||||
vec3_t FindShamblerOffset(edict_t *self)
|
||||
{
|
||||
vec3_t offset = { 0, 0, 48.f };
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (M_CheckClearShot(self, offset))
|
||||
return offset;
|
||||
|
||||
offset.z -= 4.f;
|
||||
}
|
||||
|
||||
return { 0, 0, 48.f };
|
||||
}
|
||||
|
||||
void ShamblerCastLightning(edict_t* self)
|
||||
{
|
||||
if (!self->enemy)
|
||||
return;
|
||||
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t forward, right;
|
||||
vec3_t offset = FindShamblerOffset(self);
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, offset, forward, right);
|
||||
|
||||
// calc direction to where we targted
|
||||
PredictAim(self, self->enemy, start, 0, false, self->spawnflags.has(SPAWNFLAG_SHAMBLER_PRECISE) ? 0.f : 0.1f, &dir, nullptr);
|
||||
|
||||
vec3_t end = start + (dir * 8192);
|
||||
trace_t tr = gi.traceline(start, end, self, MASK_PROJECTILE | CONTENTS_SLIME | CONTENTS_LAVA);
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_LIGHTNING);
|
||||
gi.WriteEntity(self); // source entity
|
||||
gi.WriteEntity(world); // destination entity
|
||||
gi.WritePosition(start);
|
||||
gi.WritePosition(tr.endpos);
|
||||
gi.multicast(start, MULTICAST_PVS, false);
|
||||
|
||||
fire_bullet(self, start, dir, irandom(8, 12), 15, 0, 0, MOD_TESLA);
|
||||
}
|
||||
|
||||
mframe_t shambler_frames_magic[] = {
|
||||
{ ai_charge, 0, shambler_windup },
|
||||
{ ai_charge, 0, shambler_lightning_update },
|
||||
{ ai_charge, 0, shambler_lightning_update },
|
||||
{ ai_move, 0, shambler_lightning_update },
|
||||
{ ai_move, 0, shambler_lightning_update },
|
||||
{ ai_move, 0, ShamblerSaveLoc},
|
||||
{ ai_move },
|
||||
{ ai_charge },
|
||||
{ ai_move, 0, ShamblerCastLightning },
|
||||
{ ai_move, 0, ShamblerCastLightning },
|
||||
{ ai_move, 0, ShamblerCastLightning },
|
||||
{ ai_move },
|
||||
};
|
||||
|
||||
MMOVE_T(shambler_attack_magic) = { FRAME_magic01, FRAME_magic12, shambler_frames_magic, shambler_run };
|
||||
|
||||
MONSTERINFO_ATTACK(shambler_attack) (edict_t* self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &shambler_attack_magic);
|
||||
}
|
||||
|
||||
//
|
||||
// melee
|
||||
//
|
||||
|
||||
void shambler_melee1(edict_t* self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_melee1, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void shambler_melee2(edict_t* self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_melee2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void sham_swingl9(edict_t* self);
|
||||
void sham_swingr9(edict_t* self);
|
||||
|
||||
void sham_smash10(edict_t* self)
|
||||
{
|
||||
if (!self->enemy)
|
||||
return;
|
||||
|
||||
ai_charge(self, 0);
|
||||
|
||||
if (!CanDamage(self->enemy, self))
|
||||
return;
|
||||
|
||||
vec3_t aim = { MELEE_DISTANCE, self->mins[0], -4 };
|
||||
bool hit = fire_hit(self, aim, irandom(110, 120), 120); // Slower attack
|
||||
|
||||
if (hit)
|
||||
gi.sound(self, CHAN_WEAPON, sound_smack, 1, ATTN_NORM, 0);
|
||||
|
||||
// SpawnMeatSpray(self.origin + v_forward * 16, crandom() * 100 * v_right);
|
||||
// SpawnMeatSpray(self.origin + v_forward * 16, crandom() * 100 * v_right);
|
||||
};
|
||||
|
||||
void ShamClaw(edict_t* self)
|
||||
{
|
||||
if (!self->enemy)
|
||||
return;
|
||||
|
||||
ai_charge(self, 10);
|
||||
|
||||
if (!CanDamage(self->enemy, self))
|
||||
return;
|
||||
|
||||
vec3_t aim = { MELEE_DISTANCE, self->mins[0], -4 };
|
||||
bool hit = fire_hit(self, aim, irandom(70, 80), 80); // Slower attack
|
||||
|
||||
if (hit)
|
||||
gi.sound(self, CHAN_WEAPON, sound_smack, 1, ATTN_NORM, 0);
|
||||
|
||||
// 250 if left, -250 if right
|
||||
/*
|
||||
if (side)
|
||||
{
|
||||
makevectorsfixed(self.angles);
|
||||
SpawnMeatSpray(self.origin + v_forward * 16, side * v_right);
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
mframe_t shambler_frames_smash[] = {
|
||||
{ ai_charge, 2, shambler_melee1 },
|
||||
{ ai_charge, 6 },
|
||||
{ ai_charge, 6 },
|
||||
{ ai_charge, 5 },
|
||||
{ ai_charge, 4 },
|
||||
{ ai_charge, 1 },
|
||||
{ ai_charge, 0 },
|
||||
{ ai_charge, 0 },
|
||||
{ ai_charge, 0 },
|
||||
{ ai_charge, 0, sham_smash10 },
|
||||
{ ai_charge, 5 },
|
||||
{ ai_charge, 4 },
|
||||
};
|
||||
|
||||
MMOVE_T(shambler_attack_smash) = { FRAME_smash01, FRAME_smash12, shambler_frames_smash, shambler_run };
|
||||
|
||||
mframe_t shambler_frames_swingl[] = {
|
||||
{ ai_charge, 5, shambler_melee1 },
|
||||
{ ai_charge, 3 },
|
||||
{ ai_charge, 7 },
|
||||
{ ai_charge, 3 },
|
||||
{ ai_charge, 7 },
|
||||
{ ai_charge, 9 },
|
||||
{ ai_charge, 5, ShamClaw },
|
||||
{ ai_charge, 4 },
|
||||
{ ai_charge, 8, sham_swingl9 },
|
||||
};
|
||||
|
||||
MMOVE_T(shambler_attack_swingl) = { FRAME_swingl01, FRAME_swingl09, shambler_frames_swingl, shambler_run };
|
||||
|
||||
mframe_t shambler_frames_swingr[] = {
|
||||
{ ai_charge, 1, shambler_melee2 },
|
||||
{ ai_charge, 8 },
|
||||
{ ai_charge, 14 },
|
||||
{ ai_charge, 7 },
|
||||
{ ai_charge, 3 },
|
||||
{ ai_charge, 6 },
|
||||
{ ai_charge, 6, ShamClaw },
|
||||
{ ai_charge, 3 },
|
||||
{ ai_charge, 8, sham_swingr9 },
|
||||
};
|
||||
|
||||
MMOVE_T(shambler_attack_swingr) = { FRAME_swingr01, FRAME_swingr09, shambler_frames_swingr, shambler_run };
|
||||
|
||||
void sham_swingl9(edict_t* self)
|
||||
{
|
||||
ai_charge(self, 8);
|
||||
|
||||
if (brandom() && self->enemy && range_to(self, self->enemy) < MELEE_DISTANCE)
|
||||
M_SetAnimation(self, &shambler_attack_swingr);
|
||||
}
|
||||
|
||||
void sham_swingr9(edict_t* self)
|
||||
{
|
||||
ai_charge(self, 1);
|
||||
ai_charge(self, 10);
|
||||
|
||||
if (brandom() && self->enemy && range_to(self, self->enemy) < MELEE_DISTANCE)
|
||||
M_SetAnimation(self, &shambler_attack_swingl);
|
||||
}
|
||||
|
||||
MONSTERINFO_MELEE(shambler_melee) (edict_t* self) -> void
|
||||
{
|
||||
float chance = frandom();
|
||||
if (chance > 0.6 || self->health == 600)
|
||||
M_SetAnimation(self, &shambler_attack_smash);
|
||||
else if (chance > 0.3)
|
||||
M_SetAnimation(self, &shambler_attack_swingl);
|
||||
else
|
||||
M_SetAnimation(self, &shambler_attack_swingr);
|
||||
}
|
||||
|
||||
//
|
||||
// death
|
||||
//
|
||||
|
||||
void shambler_dead(edict_t* self)
|
||||
{
|
||||
self->mins = { -16, -16, -24 };
|
||||
self->maxs = { 16, 16, -0 };
|
||||
monster_dead(self);
|
||||
}
|
||||
|
||||
static void shambler_shrink(edict_t* self)
|
||||
{
|
||||
self->maxs[2] = 0;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t shambler_frames_death[] = {
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0, shambler_shrink },
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0 },
|
||||
{ ai_move, 0 }, // FIXME: thud?
|
||||
};
|
||||
MMOVE_T(shambler_move_death) = { FRAME_death01, FRAME_death11, shambler_frames_death, shambler_dead };
|
||||
|
||||
DIE(shambler_die) (edict_t* self, edict_t* inflictor, edict_t* attacker, int damage, const vec3_t& point, const mod_t &mod) -> void
|
||||
{
|
||||
if (self->beam)
|
||||
{
|
||||
G_FreeEdict(self->beam);
|
||||
self->beam = nullptr;
|
||||
}
|
||||
|
||||
if (self->beam2)
|
||||
{
|
||||
G_FreeEdict(self->beam2);
|
||||
self->beam2 = nullptr;
|
||||
}
|
||||
|
||||
// check for gib
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
// FIXME: better gibs for shambler, shambler head
|
||||
ThrowGibs(self, damage, {
|
||||
{ "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ "models/objects/gibs/chest/tris.md2" },
|
||||
{ "models/objects/gibs/head2/tris.md2", GIB_HEAD }
|
||||
});
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
|
||||
self->deadflag = true;
|
||||
self->takedamage = true;
|
||||
|
||||
M_SetAnimation(self, &shambler_move_death);
|
||||
}
|
||||
|
||||
void SP_monster_shambler(edict_t* self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
self->s.modelindex = gi.modelindex("models/monsters/shambler/tris.md2");
|
||||
self->mins = { -32, -32, -24 };
|
||||
self->maxs = { 32, 32, 64 };
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
|
||||
gi.modelindex("models/proj/lightning/tris.md2");
|
||||
sound_pain = gi.soundindex("shambler/shurt2.wav");
|
||||
sound_idle = gi.soundindex("shambler/sidle.wav");
|
||||
sound_die = gi.soundindex("shambler/sdeath.wav");
|
||||
sound_windup = gi.soundindex("shambler/sattck1.wav");
|
||||
sound_melee1 = gi.soundindex("shambler/melee1.wav");
|
||||
sound_melee2 = gi.soundindex("shambler/melee2.wav");
|
||||
sound_sight = gi.soundindex("shambler/ssight.wav");
|
||||
sound_smack = gi.soundindex("shambler/smack.wav");
|
||||
sound_boom = gi.soundindex("shambler/sboom.wav");
|
||||
|
||||
self->health = 600 * st.health_multiplier;
|
||||
self->gib_health = -60;
|
||||
|
||||
self->mass = 500;
|
||||
|
||||
self->pain = shambler_pain;
|
||||
self->die = shambler_die;
|
||||
self->monsterinfo.stand = shambler_stand;
|
||||
self->monsterinfo.walk = shambler_walk;
|
||||
self->monsterinfo.run = shambler_run;
|
||||
self->monsterinfo.dodge = nullptr;
|
||||
self->monsterinfo.attack = shambler_attack;
|
||||
self->monsterinfo.melee = shambler_melee;
|
||||
self->monsterinfo.sight = shambler_sight;
|
||||
self->monsterinfo.idle = shambler_idle;
|
||||
self->monsterinfo.blocked = nullptr;
|
||||
self->monsterinfo.setskin = shambler_setskin;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
if (self->spawnflags.has(SPAWNFLAG_SHAMBLER_PRECISE))
|
||||
self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
|
||||
|
||||
M_SetAnimation(self, &shambler_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
106
rerelease/m_shambler.h
Normal file
106
rerelease/m_shambler.h
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/tank
|
||||
|
||||
// This file generated by qdata - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_stand01,
|
||||
FRAME_stand02,
|
||||
FRAME_stand03,
|
||||
FRAME_stand04,
|
||||
FRAME_stand05,
|
||||
FRAME_stand06,
|
||||
FRAME_stand07,
|
||||
FRAME_stand08,
|
||||
FRAME_stand09,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_walk01,
|
||||
FRAME_walk02,
|
||||
FRAME_walk03,
|
||||
FRAME_walk04,
|
||||
FRAME_walk05,
|
||||
FRAME_walk06,
|
||||
FRAME_walk07,
|
||||
FRAME_walk08,
|
||||
FRAME_walk09,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_run01,
|
||||
FRAME_run02,
|
||||
FRAME_run03,
|
||||
FRAME_run04,
|
||||
FRAME_run05,
|
||||
FRAME_run06,
|
||||
FRAME_smash01,
|
||||
FRAME_smash02,
|
||||
FRAME_smash03,
|
||||
FRAME_smash04,
|
||||
FRAME_smash05,
|
||||
FRAME_smash06,
|
||||
FRAME_smash07,
|
||||
FRAME_smash08,
|
||||
FRAME_smash09,
|
||||
FRAME_smash10,
|
||||
FRAME_smash11,
|
||||
FRAME_smash12,
|
||||
FRAME_swingr01,
|
||||
FRAME_swingr02,
|
||||
FRAME_swingr03,
|
||||
FRAME_swingr04,
|
||||
FRAME_swingr05,
|
||||
FRAME_swingr06,
|
||||
FRAME_swingr07,
|
||||
FRAME_swingr08,
|
||||
FRAME_swingr09,
|
||||
FRAME_swingl01,
|
||||
FRAME_swingl02,
|
||||
FRAME_swingl03,
|
||||
FRAME_swingl04,
|
||||
FRAME_swingl05,
|
||||
FRAME_swingl06,
|
||||
FRAME_swingl07,
|
||||
FRAME_swingl08,
|
||||
FRAME_swingl09,
|
||||
FRAME_magic01,
|
||||
FRAME_magic02,
|
||||
FRAME_magic03,
|
||||
FRAME_magic04,
|
||||
FRAME_magic05,
|
||||
FRAME_magic06,
|
||||
FRAME_magic07,
|
||||
FRAME_magic08,
|
||||
FRAME_magic09,
|
||||
FRAME_magic10,
|
||||
FRAME_magic11,
|
||||
FRAME_magic12,
|
||||
FRAME_pain01,
|
||||
FRAME_pain02,
|
||||
FRAME_pain03,
|
||||
FRAME_pain04,
|
||||
FRAME_pain05,
|
||||
FRAME_pain06,
|
||||
FRAME_death01,
|
||||
FRAME_death02,
|
||||
FRAME_death03,
|
||||
FRAME_death04,
|
||||
FRAME_death05,
|
||||
FRAME_death06,
|
||||
FRAME_death07,
|
||||
FRAME_death08,
|
||||
FRAME_death09,
|
||||
FRAME_death10,
|
||||
FRAME_death11
|
||||
};
|
||||
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
2042
rerelease/m_soldier.cpp
Normal file
2042
rerelease/m_soldier.cpp
Normal file
File diff suppressed because it is too large
Load Diff
585
rerelease/m_soldier.h
Normal file
585
rerelease/m_soldier.h
Normal file
@@ -0,0 +1,585 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// E:\G Drive\md2f\quake2\baseq2\models/monsters/soldier
|
||||
|
||||
// This file generated by qdata - Do NOT Modify
|
||||
|
||||
enum {
|
||||
FRAME_attak101,
|
||||
FRAME_attak102,
|
||||
FRAME_attak103,
|
||||
FRAME_attak104,
|
||||
FRAME_attak105,
|
||||
FRAME_attak106,
|
||||
FRAME_attak107,
|
||||
FRAME_attak108,
|
||||
FRAME_attak109,
|
||||
FRAME_attak110,
|
||||
FRAME_attak111,
|
||||
FRAME_attak112,
|
||||
FRAME_attak201,
|
||||
FRAME_attak202,
|
||||
FRAME_attak203,
|
||||
FRAME_attak204,
|
||||
FRAME_attak205,
|
||||
FRAME_attak206,
|
||||
FRAME_attak207,
|
||||
FRAME_attak208,
|
||||
FRAME_attak209,
|
||||
FRAME_attak210,
|
||||
FRAME_attak211,
|
||||
FRAME_attak212,
|
||||
FRAME_attak213,
|
||||
FRAME_attak214,
|
||||
FRAME_attak215,
|
||||
FRAME_attak216,
|
||||
FRAME_attak217,
|
||||
FRAME_attak218,
|
||||
FRAME_attak301,
|
||||
FRAME_attak302,
|
||||
FRAME_attak303,
|
||||
FRAME_attak304,
|
||||
FRAME_attak305,
|
||||
FRAME_attak306,
|
||||
FRAME_attak307,
|
||||
FRAME_attak308,
|
||||
FRAME_attak309,
|
||||
FRAME_attak401,
|
||||
FRAME_attak402,
|
||||
FRAME_attak403,
|
||||
FRAME_attak404,
|
||||
FRAME_attak405,
|
||||
FRAME_attak406,
|
||||
FRAME_duck01,
|
||||
FRAME_duck02,
|
||||
FRAME_duck03,
|
||||
FRAME_duck04,
|
||||
FRAME_duck05,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain104,
|
||||
FRAME_pain105,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain204,
|
||||
FRAME_pain205,
|
||||
FRAME_pain206,
|
||||
FRAME_pain207,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_pain304,
|
||||
FRAME_pain305,
|
||||
FRAME_pain306,
|
||||
FRAME_pain307,
|
||||
FRAME_pain308,
|
||||
FRAME_pain309,
|
||||
FRAME_pain310,
|
||||
FRAME_pain311,
|
||||
FRAME_pain312,
|
||||
FRAME_pain313,
|
||||
FRAME_pain314,
|
||||
FRAME_pain315,
|
||||
FRAME_pain316,
|
||||
FRAME_pain317,
|
||||
FRAME_pain318,
|
||||
FRAME_pain401,
|
||||
FRAME_pain402,
|
||||
FRAME_pain403,
|
||||
FRAME_pain404,
|
||||
FRAME_pain405,
|
||||
FRAME_pain406,
|
||||
FRAME_pain407,
|
||||
FRAME_pain408,
|
||||
FRAME_pain409,
|
||||
FRAME_pain410,
|
||||
FRAME_pain411,
|
||||
FRAME_pain412,
|
||||
FRAME_pain413,
|
||||
FRAME_pain414,
|
||||
FRAME_pain415,
|
||||
FRAME_pain416,
|
||||
FRAME_pain417,
|
||||
FRAME_run01,
|
||||
FRAME_run02,
|
||||
FRAME_run03,
|
||||
FRAME_run04,
|
||||
FRAME_run05,
|
||||
FRAME_run06,
|
||||
FRAME_run07,
|
||||
FRAME_run08,
|
||||
FRAME_run09,
|
||||
FRAME_run10,
|
||||
FRAME_run11,
|
||||
FRAME_run12,
|
||||
FRAME_runs01,
|
||||
FRAME_runs02,
|
||||
FRAME_runs03,
|
||||
FRAME_runs04,
|
||||
FRAME_runs05,
|
||||
FRAME_runs06,
|
||||
FRAME_runs07,
|
||||
FRAME_runs08,
|
||||
FRAME_runs09,
|
||||
FRAME_runs10,
|
||||
FRAME_runs11,
|
||||
FRAME_runs12,
|
||||
FRAME_runs13,
|
||||
FRAME_runs14,
|
||||
FRAME_runs15,
|
||||
FRAME_runs16,
|
||||
FRAME_runs17,
|
||||
FRAME_runs18,
|
||||
FRAME_runt01,
|
||||
FRAME_runt02,
|
||||
FRAME_runt03,
|
||||
FRAME_runt04,
|
||||
FRAME_runt05,
|
||||
FRAME_runt06,
|
||||
FRAME_runt07,
|
||||
FRAME_runt08,
|
||||
FRAME_runt09,
|
||||
FRAME_runt10,
|
||||
FRAME_runt11,
|
||||
FRAME_runt12,
|
||||
FRAME_runt13,
|
||||
FRAME_runt14,
|
||||
FRAME_runt15,
|
||||
FRAME_runt16,
|
||||
FRAME_runt17,
|
||||
FRAME_runt18,
|
||||
FRAME_runt19,
|
||||
FRAME_stand101,
|
||||
FRAME_stand102,
|
||||
FRAME_stand103,
|
||||
FRAME_stand104,
|
||||
FRAME_stand105,
|
||||
FRAME_stand106,
|
||||
FRAME_stand107,
|
||||
FRAME_stand108,
|
||||
FRAME_stand109,
|
||||
FRAME_stand110,
|
||||
FRAME_stand111,
|
||||
FRAME_stand112,
|
||||
FRAME_stand113,
|
||||
FRAME_stand114,
|
||||
FRAME_stand115,
|
||||
FRAME_stand116,
|
||||
FRAME_stand117,
|
||||
FRAME_stand118,
|
||||
FRAME_stand119,
|
||||
FRAME_stand120,
|
||||
FRAME_stand121,
|
||||
FRAME_stand122,
|
||||
FRAME_stand123,
|
||||
FRAME_stand124,
|
||||
FRAME_stand125,
|
||||
FRAME_stand126,
|
||||
FRAME_stand127,
|
||||
FRAME_stand128,
|
||||
FRAME_stand129,
|
||||
FRAME_stand130,
|
||||
FRAME_stand301,
|
||||
FRAME_stand302,
|
||||
FRAME_stand303,
|
||||
FRAME_stand304,
|
||||
FRAME_stand305,
|
||||
FRAME_stand306,
|
||||
FRAME_stand307,
|
||||
FRAME_stand308,
|
||||
FRAME_stand309,
|
||||
FRAME_stand310,
|
||||
FRAME_stand311,
|
||||
FRAME_stand312,
|
||||
FRAME_stand313,
|
||||
FRAME_stand314,
|
||||
FRAME_stand315,
|
||||
FRAME_stand316,
|
||||
FRAME_stand317,
|
||||
FRAME_stand318,
|
||||
FRAME_stand319,
|
||||
FRAME_stand320,
|
||||
FRAME_stand321,
|
||||
FRAME_stand322,
|
||||
FRAME_stand323,
|
||||
FRAME_stand324,
|
||||
FRAME_stand325,
|
||||
FRAME_stand326,
|
||||
FRAME_stand327,
|
||||
FRAME_stand328,
|
||||
FRAME_stand329,
|
||||
FRAME_stand330,
|
||||
FRAME_stand331,
|
||||
FRAME_stand332,
|
||||
FRAME_stand333,
|
||||
FRAME_stand334,
|
||||
FRAME_stand335,
|
||||
FRAME_stand336,
|
||||
FRAME_stand337,
|
||||
FRAME_stand338,
|
||||
FRAME_stand339,
|
||||
FRAME_walk101,
|
||||
FRAME_walk102,
|
||||
FRAME_walk103,
|
||||
FRAME_walk104,
|
||||
FRAME_walk105,
|
||||
FRAME_walk106,
|
||||
FRAME_walk107,
|
||||
FRAME_walk108,
|
||||
FRAME_walk109,
|
||||
FRAME_walk110,
|
||||
FRAME_walk111,
|
||||
FRAME_walk112,
|
||||
FRAME_walk113,
|
||||
FRAME_walk114,
|
||||
FRAME_walk115,
|
||||
FRAME_walk116,
|
||||
FRAME_walk117,
|
||||
FRAME_walk118,
|
||||
FRAME_walk119,
|
||||
FRAME_walk120,
|
||||
FRAME_walk121,
|
||||
FRAME_walk122,
|
||||
FRAME_walk123,
|
||||
FRAME_walk124,
|
||||
FRAME_walk125,
|
||||
FRAME_walk126,
|
||||
FRAME_walk127,
|
||||
FRAME_walk128,
|
||||
FRAME_walk129,
|
||||
FRAME_walk130,
|
||||
FRAME_walk131,
|
||||
FRAME_walk132,
|
||||
FRAME_walk133,
|
||||
FRAME_walk201,
|
||||
FRAME_walk202,
|
||||
FRAME_walk203,
|
||||
FRAME_walk204,
|
||||
FRAME_walk205,
|
||||
FRAME_walk206,
|
||||
FRAME_walk207,
|
||||
FRAME_walk208,
|
||||
FRAME_walk209,
|
||||
FRAME_walk210,
|
||||
FRAME_walk211,
|
||||
FRAME_walk212,
|
||||
FRAME_walk213,
|
||||
FRAME_walk214,
|
||||
FRAME_walk215,
|
||||
FRAME_walk216,
|
||||
FRAME_walk217,
|
||||
FRAME_walk218,
|
||||
FRAME_walk219,
|
||||
FRAME_walk220,
|
||||
FRAME_walk221,
|
||||
FRAME_walk222,
|
||||
FRAME_walk223,
|
||||
FRAME_walk224,
|
||||
FRAME_death101,
|
||||
FRAME_death102,
|
||||
FRAME_death103,
|
||||
FRAME_death104,
|
||||
FRAME_death105,
|
||||
FRAME_death106,
|
||||
FRAME_death107,
|
||||
FRAME_death108,
|
||||
FRAME_death109,
|
||||
FRAME_death110,
|
||||
FRAME_death111,
|
||||
FRAME_death112,
|
||||
FRAME_death113,
|
||||
FRAME_death114,
|
||||
FRAME_death115,
|
||||
FRAME_death116,
|
||||
FRAME_death117,
|
||||
FRAME_death118,
|
||||
FRAME_death119,
|
||||
FRAME_death120,
|
||||
FRAME_death121,
|
||||
FRAME_death122,
|
||||
FRAME_death123,
|
||||
FRAME_death124,
|
||||
FRAME_death125,
|
||||
FRAME_death126,
|
||||
FRAME_death127,
|
||||
FRAME_death128,
|
||||
FRAME_death129,
|
||||
FRAME_death130,
|
||||
FRAME_death131,
|
||||
FRAME_death132,
|
||||
FRAME_death133,
|
||||
FRAME_death134,
|
||||
FRAME_death135,
|
||||
FRAME_death136,
|
||||
FRAME_death201,
|
||||
FRAME_death202,
|
||||
FRAME_death203,
|
||||
FRAME_death204,
|
||||
FRAME_death205,
|
||||
FRAME_death206,
|
||||
FRAME_death207,
|
||||
FRAME_death208,
|
||||
FRAME_death209,
|
||||
FRAME_death210,
|
||||
FRAME_death211,
|
||||
FRAME_death212,
|
||||
FRAME_death213,
|
||||
FRAME_death214,
|
||||
FRAME_death215,
|
||||
FRAME_death216,
|
||||
FRAME_death217,
|
||||
FRAME_death218,
|
||||
FRAME_death219,
|
||||
FRAME_death220,
|
||||
FRAME_death221,
|
||||
FRAME_death222,
|
||||
FRAME_death223,
|
||||
FRAME_death224,
|
||||
FRAME_death225,
|
||||
FRAME_death226,
|
||||
FRAME_death227,
|
||||
FRAME_death228,
|
||||
FRAME_death229,
|
||||
FRAME_death230,
|
||||
FRAME_death231,
|
||||
FRAME_death232,
|
||||
FRAME_death233,
|
||||
FRAME_death234,
|
||||
FRAME_death235,
|
||||
FRAME_death301,
|
||||
FRAME_death302,
|
||||
FRAME_death303,
|
||||
FRAME_death304,
|
||||
FRAME_death305,
|
||||
FRAME_death306,
|
||||
FRAME_death307,
|
||||
FRAME_death308,
|
||||
FRAME_death309,
|
||||
FRAME_death310,
|
||||
FRAME_death311,
|
||||
FRAME_death312,
|
||||
FRAME_death313,
|
||||
FRAME_death314,
|
||||
FRAME_death315,
|
||||
FRAME_death316,
|
||||
FRAME_death317,
|
||||
FRAME_death318,
|
||||
FRAME_death319,
|
||||
FRAME_death320,
|
||||
FRAME_death321,
|
||||
FRAME_death322,
|
||||
FRAME_death323,
|
||||
FRAME_death324,
|
||||
FRAME_death325,
|
||||
FRAME_death326,
|
||||
FRAME_death327,
|
||||
FRAME_death328,
|
||||
FRAME_death329,
|
||||
FRAME_death330,
|
||||
FRAME_death331,
|
||||
FRAME_death332,
|
||||
FRAME_death333,
|
||||
FRAME_death334,
|
||||
FRAME_death335,
|
||||
FRAME_death336,
|
||||
FRAME_death337,
|
||||
FRAME_death338,
|
||||
FRAME_death339,
|
||||
FRAME_death340,
|
||||
FRAME_death341,
|
||||
FRAME_death342,
|
||||
FRAME_death343,
|
||||
FRAME_death344,
|
||||
FRAME_death345,
|
||||
FRAME_death401,
|
||||
FRAME_death402,
|
||||
FRAME_death403,
|
||||
FRAME_death404,
|
||||
FRAME_death405,
|
||||
FRAME_death406,
|
||||
FRAME_death407,
|
||||
FRAME_death408,
|
||||
FRAME_death409,
|
||||
FRAME_death410,
|
||||
FRAME_death411,
|
||||
FRAME_death412,
|
||||
FRAME_death413,
|
||||
FRAME_death414,
|
||||
FRAME_death415,
|
||||
FRAME_death416,
|
||||
FRAME_death417,
|
||||
FRAME_death418,
|
||||
FRAME_death419,
|
||||
FRAME_death420,
|
||||
FRAME_death421,
|
||||
FRAME_death422,
|
||||
FRAME_death423,
|
||||
FRAME_death424,
|
||||
FRAME_death425,
|
||||
FRAME_death426,
|
||||
FRAME_death427,
|
||||
FRAME_death428,
|
||||
FRAME_death429,
|
||||
FRAME_death430,
|
||||
FRAME_death431,
|
||||
FRAME_death432,
|
||||
FRAME_death433,
|
||||
FRAME_death434,
|
||||
FRAME_death435,
|
||||
FRAME_death436,
|
||||
FRAME_death437,
|
||||
FRAME_death438,
|
||||
FRAME_death439,
|
||||
FRAME_death440,
|
||||
FRAME_death441,
|
||||
FRAME_death442,
|
||||
FRAME_death443,
|
||||
FRAME_death444,
|
||||
FRAME_death445,
|
||||
FRAME_death446,
|
||||
FRAME_death447,
|
||||
FRAME_death448,
|
||||
FRAME_death449,
|
||||
FRAME_death450,
|
||||
FRAME_death451,
|
||||
FRAME_death452,
|
||||
FRAME_death453,
|
||||
FRAME_death501,
|
||||
FRAME_death502,
|
||||
FRAME_death503,
|
||||
FRAME_death504,
|
||||
FRAME_death505,
|
||||
FRAME_death506,
|
||||
FRAME_death507,
|
||||
FRAME_death508,
|
||||
FRAME_death509,
|
||||
FRAME_death510,
|
||||
FRAME_death511,
|
||||
FRAME_death512,
|
||||
FRAME_death513,
|
||||
FRAME_death514,
|
||||
FRAME_death515,
|
||||
FRAME_death516,
|
||||
FRAME_death517,
|
||||
FRAME_death518,
|
||||
FRAME_death519,
|
||||
FRAME_death520,
|
||||
FRAME_death521,
|
||||
FRAME_death522,
|
||||
FRAME_death523,
|
||||
FRAME_death524,
|
||||
FRAME_death601,
|
||||
FRAME_death602,
|
||||
FRAME_death603,
|
||||
FRAME_death604,
|
||||
FRAME_death605,
|
||||
FRAME_death606,
|
||||
FRAME_death607,
|
||||
FRAME_death608,
|
||||
FRAME_death609,
|
||||
FRAME_death610,
|
||||
FRAME_stand401,
|
||||
FRAME_stand402,
|
||||
FRAME_stand403,
|
||||
FRAME_stand404,
|
||||
FRAME_stand405,
|
||||
FRAME_stand406,
|
||||
FRAME_stand407,
|
||||
FRAME_stand408,
|
||||
FRAME_stand409,
|
||||
FRAME_stand410,
|
||||
FRAME_stand411,
|
||||
FRAME_stand412,
|
||||
FRAME_stand413,
|
||||
FRAME_stand414,
|
||||
FRAME_stand415,
|
||||
FRAME_stand416,
|
||||
FRAME_stand417,
|
||||
FRAME_stand418,
|
||||
FRAME_stand419,
|
||||
FRAME_stand420,
|
||||
FRAME_stand421,
|
||||
FRAME_stand422,
|
||||
FRAME_stand423,
|
||||
FRAME_stand424,
|
||||
FRAME_stand425,
|
||||
FRAME_stand426,
|
||||
FRAME_stand427,
|
||||
FRAME_stand428,
|
||||
FRAME_stand429,
|
||||
FRAME_stand430,
|
||||
FRAME_stand431,
|
||||
FRAME_stand432,
|
||||
FRAME_stand433,
|
||||
FRAME_stand434,
|
||||
FRAME_stand435,
|
||||
FRAME_stand436,
|
||||
FRAME_stand437,
|
||||
FRAME_stand438,
|
||||
FRAME_stand439,
|
||||
FRAME_stand440,
|
||||
FRAME_stand441,
|
||||
FRAME_stand442,
|
||||
FRAME_stand443,
|
||||
FRAME_stand444,
|
||||
FRAME_stand445,
|
||||
FRAME_stand446,
|
||||
FRAME_stand447,
|
||||
FRAME_stand448,
|
||||
FRAME_stand449,
|
||||
FRAME_stand450,
|
||||
FRAME_stand451,
|
||||
FRAME_stand452,
|
||||
FRAME_stand201,
|
||||
FRAME_stand202,
|
||||
FRAME_stand203,
|
||||
FRAME_stand204,
|
||||
FRAME_stand205,
|
||||
FRAME_stand206,
|
||||
FRAME_stand207,
|
||||
FRAME_stand208,
|
||||
FRAME_stand209,
|
||||
FRAME_stand210,
|
||||
FRAME_stand211,
|
||||
FRAME_stand212,
|
||||
FRAME_stand213,
|
||||
FRAME_stand214,
|
||||
FRAME_stand215,
|
||||
FRAME_stand216,
|
||||
FRAME_stand217,
|
||||
FRAME_stand218,
|
||||
FRAME_stand219,
|
||||
FRAME_stand220,
|
||||
FRAME_stand221,
|
||||
FRAME_stand222,
|
||||
FRAME_stand223,
|
||||
FRAME_stand224,
|
||||
FRAME_stand225,
|
||||
FRAME_stand226,
|
||||
FRAME_stand227,
|
||||
FRAME_stand228,
|
||||
FRAME_stand229,
|
||||
FRAME_stand230,
|
||||
FRAME_stand231,
|
||||
FRAME_stand232,
|
||||
FRAME_stand233,
|
||||
FRAME_stand234,
|
||||
FRAME_stand235,
|
||||
FRAME_stand236,
|
||||
FRAME_stand237,
|
||||
FRAME_stand238,
|
||||
FRAME_stand239,
|
||||
FRAME_stand240,
|
||||
FRAME_attak501,
|
||||
FRAME_attak502,
|
||||
FRAME_attak503,
|
||||
FRAME_attak504,
|
||||
FRAME_attak505,
|
||||
FRAME_attak506,
|
||||
FRAME_attak507,
|
||||
FRAME_attak508
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.2f;
|
||||
731
rerelease/m_supertank.cpp
Normal file
731
rerelease/m_supertank.cpp
Normal file
@@ -0,0 +1,731 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
SUPERTANK
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_supertank.h"
|
||||
#include "m_flash.h"
|
||||
|
||||
constexpr spawnflags_t SPAWNFLAG_SUPERTANK_POWERSHIELD = 8_spawnflag;
|
||||
// n64
|
||||
constexpr spawnflags_t SPAWNFLAG_SUPERTANK_LONG_DEATH = 16_spawnflag;
|
||||
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_pain3;
|
||||
static int sound_death;
|
||||
static int sound_search1;
|
||||
static int sound_search2;
|
||||
|
||||
static int tread_sound;
|
||||
|
||||
void TreadSound(edict_t *self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, tread_sound, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
MONSTERINFO_SEARCH(supertank_search) (edict_t *self) -> void
|
||||
{
|
||||
if (frandom() < 0.5f)
|
||||
gi.sound(self, CHAN_VOICE, sound_search1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_search2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void supertank_dead(edict_t *self);
|
||||
void supertankRocket(edict_t *self);
|
||||
void supertankMachineGun(edict_t *self);
|
||||
void supertank_reattack1(edict_t *self);
|
||||
|
||||
//
|
||||
// stand
|
||||
//
|
||||
|
||||
mframe_t supertank_frames_stand[] = {
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand },
|
||||
{ ai_stand }
|
||||
};
|
||||
MMOVE_T(supertank_move_stand) = { FRAME_stand_1, FRAME_stand_60, supertank_frames_stand, nullptr };
|
||||
|
||||
MONSTERINFO_STAND(supertank_stand) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &supertank_move_stand);
|
||||
}
|
||||
|
||||
mframe_t supertank_frames_run[] = {
|
||||
{ ai_run, 12, TreadSound },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 },
|
||||
{ ai_run, 12 }
|
||||
};
|
||||
MMOVE_T(supertank_move_run) = { FRAME_forwrd_1, FRAME_forwrd_18, supertank_frames_run, nullptr };
|
||||
|
||||
//
|
||||
// walk
|
||||
//
|
||||
|
||||
mframe_t supertank_frames_forward[] = {
|
||||
{ ai_walk, 4, TreadSound },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 },
|
||||
{ ai_walk, 4 }
|
||||
};
|
||||
MMOVE_T(supertank_move_forward) = { FRAME_forwrd_1, FRAME_forwrd_18, supertank_frames_forward, nullptr };
|
||||
|
||||
void supertank_forward(edict_t *self)
|
||||
{
|
||||
M_SetAnimation(self, &supertank_move_forward);
|
||||
}
|
||||
|
||||
MONSTERINFO_WALK(supertank_walk) (edict_t *self) -> void
|
||||
{
|
||||
M_SetAnimation(self, &supertank_move_forward);
|
||||
}
|
||||
|
||||
MONSTERINFO_RUN(supertank_run) (edict_t *self) -> void
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
M_SetAnimation(self, &supertank_move_stand);
|
||||
else
|
||||
M_SetAnimation(self, &supertank_move_run);
|
||||
}
|
||||
|
||||
#if 0
|
||||
mframe_t supertank_frames_turn_right[] = {
|
||||
{ ai_move, 0, TreadSound },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(supertank_move_turn_right) = { FRAME_right_1, FRAME_right_18, supertank_frames_turn_right, supertank_run };
|
||||
|
||||
mframe_t supertank_frames_turn_left[] = {
|
||||
{ ai_move, 0, TreadSound },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(supertank_move_turn_left) = { FRAME_left_1, FRAME_left_18, supertank_frames_turn_left, supertank_run };
|
||||
#endif
|
||||
|
||||
mframe_t supertank_frames_pain3[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(supertank_move_pain3) = { FRAME_pain3_9, FRAME_pain3_12, supertank_frames_pain3, supertank_run };
|
||||
|
||||
mframe_t supertank_frames_pain2[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(supertank_move_pain2) = { FRAME_pain2_5, FRAME_pain2_8, supertank_frames_pain2, supertank_run };
|
||||
|
||||
mframe_t supertank_frames_pain1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(supertank_move_pain1) = { FRAME_pain1_1, FRAME_pain1_4, supertank_frames_pain1, supertank_run };
|
||||
|
||||
static void BossLoop(edict_t *self)
|
||||
{
|
||||
if (!(self->spawnflags & SPAWNFLAG_SUPERTANK_LONG_DEATH))
|
||||
return;
|
||||
|
||||
if (self->count)
|
||||
self->count--;
|
||||
else
|
||||
self->spawnflags &= ~SPAWNFLAG_SUPERTANK_LONG_DEATH;
|
||||
|
||||
self->monsterinfo.nextframe = FRAME_death_19;
|
||||
}
|
||||
|
||||
static void supertankGrenade(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t start;
|
||||
monster_muzzleflash_id_t flash_number;
|
||||
|
||||
if (!self->enemy || !self->enemy->inuse) // PGM
|
||||
return; // PGM
|
||||
|
||||
if (self->s.frame == FRAME_attak4_1)
|
||||
flash_number = MZ2_SUPERTANK_GRENADE_1;
|
||||
else
|
||||
flash_number = MZ2_SUPERTANK_GRENADE_2;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[flash_number], forward, right);
|
||||
|
||||
vec3_t aim_point;
|
||||
PredictAim(self, self->enemy, start, 0, false, crandom_open() * 0.1f, &forward, &aim_point);
|
||||
|
||||
for (float speed = 500.f; speed < 1000.f; speed += 100.f)
|
||||
{
|
||||
if (!M_CalculatePitchToFire(self, aim_point, start, forward, speed, 2.5f, true))
|
||||
continue;
|
||||
|
||||
monster_fire_grenade(self, start, forward, 50, speed, flash_number, 0.f, 0.f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mframe_t supertank_frames_death1[] = {
|
||||
{ ai_move, 0, BossExplode },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, BossLoop }
|
||||
};
|
||||
MMOVE_T(supertank_move_death) = { FRAME_death_1, FRAME_death_24, supertank_frames_death1, supertank_dead };
|
||||
|
||||
mframe_t supertank_frames_attack4[] = {
|
||||
{ ai_move, 0, supertankGrenade },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move, 0, supertankGrenade },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(supertank_move_attack4) = { FRAME_attak4_1, FRAME_attak4_6, supertank_frames_attack4, supertank_run };
|
||||
|
||||
mframe_t supertank_frames_attack2[] = {
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, supertankRocket },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, supertankRocket },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge, 0, supertankRocket },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_charge },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(supertank_move_attack2) = { FRAME_attak2_1, FRAME_attak2_27, supertank_frames_attack2, supertank_run };
|
||||
|
||||
mframe_t supertank_frames_attack1[] = {
|
||||
{ ai_charge, 0, supertankMachineGun },
|
||||
{ ai_charge, 0, supertankMachineGun },
|
||||
{ ai_charge, 0, supertankMachineGun },
|
||||
{ ai_charge, 0, supertankMachineGun },
|
||||
{ ai_charge, 0, supertankMachineGun },
|
||||
{ ai_charge, 0, supertankMachineGun },
|
||||
};
|
||||
MMOVE_T(supertank_move_attack1) = { FRAME_attak1_1, FRAME_attak1_6, supertank_frames_attack1, supertank_reattack1 };
|
||||
|
||||
mframe_t supertank_frames_end_attack1[] = {
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move },
|
||||
{ ai_move }
|
||||
};
|
||||
MMOVE_T(supertank_move_end_attack1) = { FRAME_attak1_7, FRAME_attak1_20, supertank_frames_end_attack1, supertank_run };
|
||||
|
||||
void supertank_reattack1(edict_t *self)
|
||||
{
|
||||
if (visible(self, self->enemy))
|
||||
{
|
||||
if (self->timestamp >= level.time || frandom() < 0.3f)
|
||||
M_SetAnimation(self, &supertank_move_attack1);
|
||||
else
|
||||
M_SetAnimation(self, &supertank_move_end_attack1);
|
||||
}
|
||||
else
|
||||
M_SetAnimation(self, &supertank_move_end_attack1);
|
||||
}
|
||||
|
||||
PAIN(supertank_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
|
||||
{
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
// Lessen the chance of him going into his pain frames
|
||||
if (mod.id != MOD_CHAINFIST)
|
||||
{
|
||||
if (damage <= 25)
|
||||
if (frandom() < 0.2f)
|
||||
return;
|
||||
|
||||
// Don't go into pain if he's firing his rockets
|
||||
if ((self->s.frame >= FRAME_attak2_1) && (self->s.frame <= FRAME_attak2_14))
|
||||
return;
|
||||
}
|
||||
|
||||
if (damage <= 10)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else if (damage <= 25)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain3, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
|
||||
self->pain_debounce_time = level.time + 3_sec;
|
||||
|
||||
if (!M_ShouldReactToPain(self, mod))
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (damage <= 10)
|
||||
M_SetAnimation(self, &supertank_move_pain1);
|
||||
else if (damage <= 25)
|
||||
M_SetAnimation(self, &supertank_move_pain2);
|
||||
else
|
||||
M_SetAnimation(self, &supertank_move_pain3);
|
||||
}
|
||||
|
||||
MONSTERINFO_SETSKIN(supertank_setskin) (edict_t *self) -> void
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum |= 1;
|
||||
else
|
||||
self->s.skinnum &= ~1;
|
||||
}
|
||||
|
||||
void supertankRocket(edict_t *self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
monster_muzzleflash_id_t flash_number;
|
||||
|
||||
if (!self->enemy || !self->enemy->inuse) // PGM
|
||||
return; // PGM
|
||||
|
||||
if (self->s.frame == FRAME_attak2_8)
|
||||
flash_number = MZ2_SUPERTANK_ROCKET_1;
|
||||
else if (self->s.frame == FRAME_attak2_11)
|
||||
flash_number = MZ2_SUPERTANK_ROCKET_2;
|
||||
else // (self->s.frame == FRAME_attak2_14)
|
||||
flash_number = MZ2_SUPERTANK_ROCKET_3;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[flash_number], forward, right);
|
||||
|
||||
if (self->spawnflags.has(SPAWNFLAG_SUPERTANK_POWERSHIELD))
|
||||
{
|
||||
vec = self->enemy->s.origin;
|
||||
vec[2] += self->enemy->viewheight;
|
||||
dir = vec - start;
|
||||
dir.normalize();
|
||||
monster_fire_heat(self, start, dir, 40, 500, flash_number, 0.075f);
|
||||
}
|
||||
else
|
||||
{
|
||||
PredictAim(self, self->enemy, start, 750, false, 0.f, &forward, nullptr);
|
||||
monster_fire_rocket(self, start, forward, 50, 750, flash_number);
|
||||
}
|
||||
}
|
||||
|
||||
void supertankMachineGun(edict_t *self)
|
||||
{
|
||||
vec3_t dir;
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
monster_muzzleflash_id_t flash_number;
|
||||
|
||||
if (!self->enemy || !self->enemy->inuse) // PGM
|
||||
return; // PGM
|
||||
|
||||
flash_number = static_cast<monster_muzzleflash_id_t>(MZ2_SUPERTANK_MACHINEGUN_1 + (self->s.frame - FRAME_attak1_1));
|
||||
|
||||
dir[0] = 0;
|
||||
dir[1] = self->s.angles[1];
|
||||
dir[2] = 0;
|
||||
|
||||
AngleVectors(dir, forward, right, nullptr);
|
||||
start = M_ProjectFlashSource(self, monster_flash_offset[flash_number], forward, right);
|
||||
PredictAim(self, self->enemy, start, 0, true, -0.1f, &forward, nullptr);
|
||||
monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD * 3, DEFAULT_BULLET_VSPREAD * 3, flash_number);
|
||||
}
|
||||
|
||||
MONSTERINFO_ATTACK(supertank_attack) (edict_t *self) -> void
|
||||
{
|
||||
vec3_t vec;
|
||||
float range;
|
||||
|
||||
vec = self->enemy->s.origin - self->s.origin;
|
||||
range = range_to(self, self->enemy);
|
||||
|
||||
// Attack 1 == Chaingun
|
||||
// Attack 2 == Rocket Launcher
|
||||
// Attack 3 == Grenade Launcher
|
||||
bool chaingun_good = M_CheckClearShot(self, monster_flash_offset[MZ2_SUPERTANK_MACHINEGUN_1]);
|
||||
bool rocket_good = M_CheckClearShot(self, monster_flash_offset[MZ2_SUPERTANK_ROCKET_1]);
|
||||
bool grenade_good = M_CheckClearShot(self, monster_flash_offset[MZ2_SUPERTANK_GRENADE_1]);
|
||||
|
||||
// fire rockets more often at distance
|
||||
if (chaingun_good && (!rocket_good || range <= 540 || frandom() < 0.3f))
|
||||
{
|
||||
// prefer grenade if the enemy is above us
|
||||
if (grenade_good && (range >= 350 || vec.z > 120.f || frandom() < 0.2f))
|
||||
M_SetAnimation(self, &supertank_move_attack4);
|
||||
else
|
||||
{
|
||||
M_SetAnimation(self, &supertank_move_attack1);
|
||||
self->timestamp = level.time + random_time(1500_ms, 2700_ms);
|
||||
}
|
||||
}
|
||||
else if (rocket_good)
|
||||
{
|
||||
// prefer grenade if the enemy is above us
|
||||
if (grenade_good && (vec.z > 120.f || frandom() < 0.2f))
|
||||
M_SetAnimation(self, &supertank_move_attack4);
|
||||
else
|
||||
M_SetAnimation(self, &supertank_move_attack2);
|
||||
}
|
||||
else if (grenade_good)
|
||||
M_SetAnimation(self, &supertank_move_attack4);
|
||||
}
|
||||
|
||||
//
|
||||
// death
|
||||
//
|
||||
|
||||
static void supertank_gib(edict_t *self)
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1_BIG);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.multicast(self->s.origin, MULTICAST_PHS, false);
|
||||
|
||||
self->s.sound = 0;
|
||||
self->s.skinnum /= 2;
|
||||
|
||||
ThrowGibs(self, 500, {
|
||||
{ 2, "models/objects/gibs/sm_meat/tris.md2" },
|
||||
{ 2, "models/objects/gibs/sm_metal/tris.md2", GIB_METALLIC },
|
||||
{ "models/monsters/boss1/gibs/cgun.md2", GIB_SKINNED | GIB_METALLIC },
|
||||
{ "models/monsters/boss1/gibs/chest.md2", GIB_SKINNED },
|
||||
{ "models/monsters/boss1/gibs/core.md2", GIB_SKINNED },
|
||||
{ "models/monsters/boss1/gibs/ltread.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss1/gibs/rgun.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss1/gibs/rtread.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss1/gibs/tube.md2", GIB_SKINNED | GIB_UPRIGHT },
|
||||
{ "models/monsters/boss1/gibs/head.md2", GIB_SKINNED | GIB_METALLIC | GIB_HEAD }
|
||||
});
|
||||
}
|
||||
|
||||
void supertank_dead(edict_t *self)
|
||||
{
|
||||
// no blowy on deady
|
||||
if (self->spawnflags.has(SPAWNFLAG_MONSTER_DEAD))
|
||||
{
|
||||
self->deadflag = false;
|
||||
self->takedamage = true;
|
||||
return;
|
||||
}
|
||||
|
||||
supertank_gib(self);
|
||||
}
|
||||
|
||||
DIE(supertank_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
|
||||
{
|
||||
if (self->spawnflags.has(SPAWNFLAG_MONSTER_DEAD))
|
||||
{
|
||||
// check for gib
|
||||
if (M_CheckGib(self, mod))
|
||||
{
|
||||
supertank_gib(self);
|
||||
self->deadflag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
|
||||
self->deadflag = true;
|
||||
self->takedamage = false;
|
||||
}
|
||||
|
||||
M_SetAnimation(self, &supertank_move_death);
|
||||
}
|
||||
|
||||
//===========
|
||||
// PGM
|
||||
MONSTERINFO_BLOCKED(supertank_blocked) (edict_t *self, float dist) -> bool
|
||||
{
|
||||
if (blocked_checkplat(self, dist))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
// PGM
|
||||
//===========
|
||||
|
||||
//
|
||||
// monster_supertank
|
||||
//
|
||||
|
||||
// RAFAEL (Powershield)
|
||||
|
||||
/*QUAKED monster_supertank (1 .5 0) (-64 -64 0) (64 64 72) Ambush Trigger_Spawn Sight Powershield LongDeath
|
||||
*/
|
||||
void SP_monster_supertank(edict_t *self)
|
||||
{
|
||||
if ( !M_AllowSpawn( self ) ) {
|
||||
G_FreeEdict( self );
|
||||
return;
|
||||
}
|
||||
|
||||
sound_pain1 = gi.soundindex("bosstank/btkpain1.wav");
|
||||
sound_pain2 = gi.soundindex("bosstank/btkpain2.wav");
|
||||
sound_pain3 = gi.soundindex("bosstank/btkpain3.wav");
|
||||
sound_death = gi.soundindex("bosstank/btkdeth1.wav");
|
||||
sound_search1 = gi.soundindex("bosstank/btkunqv1.wav");
|
||||
sound_search2 = gi.soundindex("bosstank/btkunqv2.wav");
|
||||
|
||||
tread_sound = gi.soundindex("bosstank/btkengn1.wav");
|
||||
|
||||
gi.soundindex("gunner/gunatck3.wav");
|
||||
gi.soundindex("infantry/infatck1.wav");
|
||||
gi.soundindex("tank/rocket.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/boss1/tris.md2");
|
||||
|
||||
gi.modelindex("models/monsters/boss1/gibs/cgun.md2");
|
||||
gi.modelindex("models/monsters/boss1/gibs/chest.md2");
|
||||
gi.modelindex("models/monsters/boss1/gibs/core.md2");
|
||||
gi.modelindex("models/monsters/boss1/gibs/head.md2");
|
||||
gi.modelindex("models/monsters/boss1/gibs/ltread.md2");
|
||||
gi.modelindex("models/monsters/boss1/gibs/rgun.md2");
|
||||
gi.modelindex("models/monsters/boss1/gibs/rtread.md2");
|
||||
gi.modelindex("models/monsters/boss1/gibs/tube.md2");
|
||||
|
||||
self->mins = { -64, -64, 0 };
|
||||
self->maxs = { 64, 64, 112 };
|
||||
|
||||
self->health = 1500 * st.health_multiplier;
|
||||
self->gib_health = -500;
|
||||
self->mass = 800;
|
||||
|
||||
self->pain = supertank_pain;
|
||||
self->die = supertank_die;
|
||||
self->monsterinfo.stand = supertank_stand;
|
||||
self->monsterinfo.walk = supertank_walk;
|
||||
self->monsterinfo.run = supertank_run;
|
||||
self->monsterinfo.dodge = nullptr;
|
||||
self->monsterinfo.attack = supertank_attack;
|
||||
self->monsterinfo.search = supertank_search;
|
||||
self->monsterinfo.melee = nullptr;
|
||||
self->monsterinfo.sight = nullptr;
|
||||
self->monsterinfo.blocked = supertank_blocked; // PGM
|
||||
self->monsterinfo.setskin = supertank_setskin;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
M_SetAnimation(self, &supertank_move_stand);
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
// RAFAEL
|
||||
if (self->spawnflags.has(SPAWNFLAG_SUPERTANK_POWERSHIELD))
|
||||
{
|
||||
if (!st.was_key_specified("power_armor_type"))
|
||||
self->monsterinfo.power_armor_type = IT_ITEM_POWER_SHIELD;
|
||||
if (!st.was_key_specified("power_armor_power"))
|
||||
self->monsterinfo.power_armor_power = 400;
|
||||
}
|
||||
// RAFAEL
|
||||
|
||||
walkmonster_start(self);
|
||||
|
||||
// PMM
|
||||
self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
|
||||
// pmm
|
||||
|
||||
// TODO
|
||||
if (level.is_n64)
|
||||
{
|
||||
self->spawnflags |= SPAWNFLAG_SUPERTANK_LONG_DEATH;
|
||||
self->count = 10;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// monster_boss5
|
||||
// RAFAEL
|
||||
//
|
||||
|
||||
/*QUAKED monster_boss5 (1 .5 0) (-64 -64 0) (64 64 72) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_boss5(edict_t *self)
|
||||
{
|
||||
self->spawnflags |= SPAWNFLAG_SUPERTANK_POWERSHIELD;
|
||||
SP_monster_supertank(self);
|
||||
gi.soundindex("weapons/railgr1a.wav");
|
||||
self->s.skinnum = 2;
|
||||
}
|
||||
265
rerelease/m_supertank.h
Normal file
265
rerelease/m_supertank.h
Normal file
@@ -0,0 +1,265 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/boss1/backup
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_attak1_1,
|
||||
FRAME_attak1_2,
|
||||
FRAME_attak1_3,
|
||||
FRAME_attak1_4,
|
||||
FRAME_attak1_5,
|
||||
FRAME_attak1_6,
|
||||
FRAME_attak1_7,
|
||||
FRAME_attak1_8,
|
||||
FRAME_attak1_9,
|
||||
FRAME_attak1_10,
|
||||
FRAME_attak1_11,
|
||||
FRAME_attak1_12,
|
||||
FRAME_attak1_13,
|
||||
FRAME_attak1_14,
|
||||
FRAME_attak1_15,
|
||||
FRAME_attak1_16,
|
||||
FRAME_attak1_17,
|
||||
FRAME_attak1_18,
|
||||
FRAME_attak1_19,
|
||||
FRAME_attak1_20,
|
||||
FRAME_attak2_1,
|
||||
FRAME_attak2_2,
|
||||
FRAME_attak2_3,
|
||||
FRAME_attak2_4,
|
||||
FRAME_attak2_5,
|
||||
FRAME_attak2_6,
|
||||
FRAME_attak2_7,
|
||||
FRAME_attak2_8,
|
||||
FRAME_attak2_9,
|
||||
FRAME_attak2_10,
|
||||
FRAME_attak2_11,
|
||||
FRAME_attak2_12,
|
||||
FRAME_attak2_13,
|
||||
FRAME_attak2_14,
|
||||
FRAME_attak2_15,
|
||||
FRAME_attak2_16,
|
||||
FRAME_attak2_17,
|
||||
FRAME_attak2_18,
|
||||
FRAME_attak2_19,
|
||||
FRAME_attak2_20,
|
||||
FRAME_attak2_21,
|
||||
FRAME_attak2_22,
|
||||
FRAME_attak2_23,
|
||||
FRAME_attak2_24,
|
||||
FRAME_attak2_25,
|
||||
FRAME_attak2_26,
|
||||
FRAME_attak2_27,
|
||||
FRAME_attak3_1,
|
||||
FRAME_attak3_2,
|
||||
FRAME_attak3_3,
|
||||
FRAME_attak3_4,
|
||||
FRAME_attak3_5,
|
||||
FRAME_attak3_6,
|
||||
FRAME_attak3_7,
|
||||
FRAME_attak3_8,
|
||||
FRAME_attak3_9,
|
||||
FRAME_attak3_10,
|
||||
FRAME_attak3_11,
|
||||
FRAME_attak3_12,
|
||||
FRAME_attak3_13,
|
||||
FRAME_attak3_14,
|
||||
FRAME_attak3_15,
|
||||
FRAME_attak3_16,
|
||||
FRAME_attak3_17,
|
||||
FRAME_attak3_18,
|
||||
FRAME_attak3_19,
|
||||
FRAME_attak3_20,
|
||||
FRAME_attak3_21,
|
||||
FRAME_attak3_22,
|
||||
FRAME_attak3_23,
|
||||
FRAME_attak3_24,
|
||||
FRAME_attak3_25,
|
||||
FRAME_attak3_26,
|
||||
FRAME_attak3_27,
|
||||
FRAME_attak4_1,
|
||||
FRAME_attak4_2,
|
||||
FRAME_attak4_3,
|
||||
FRAME_attak4_4,
|
||||
FRAME_attak4_5,
|
||||
FRAME_attak4_6,
|
||||
FRAME_backwd_1,
|
||||
FRAME_backwd_2,
|
||||
FRAME_backwd_3,
|
||||
FRAME_backwd_4,
|
||||
FRAME_backwd_5,
|
||||
FRAME_backwd_6,
|
||||
FRAME_backwd_7,
|
||||
FRAME_backwd_8,
|
||||
FRAME_backwd_9,
|
||||
FRAME_backwd_10,
|
||||
FRAME_backwd_11,
|
||||
FRAME_backwd_12,
|
||||
FRAME_backwd_13,
|
||||
FRAME_backwd_14,
|
||||
FRAME_backwd_15,
|
||||
FRAME_backwd_16,
|
||||
FRAME_backwd_17,
|
||||
FRAME_backwd_18,
|
||||
FRAME_death_1,
|
||||
FRAME_death_2,
|
||||
FRAME_death_3,
|
||||
FRAME_death_4,
|
||||
FRAME_death_5,
|
||||
FRAME_death_6,
|
||||
FRAME_death_7,
|
||||
FRAME_death_8,
|
||||
FRAME_death_9,
|
||||
FRAME_death_10,
|
||||
FRAME_death_11,
|
||||
FRAME_death_12,
|
||||
FRAME_death_13,
|
||||
FRAME_death_14,
|
||||
FRAME_death_15,
|
||||
FRAME_death_16,
|
||||
FRAME_death_17,
|
||||
FRAME_death_18,
|
||||
FRAME_death_19,
|
||||
FRAME_death_20,
|
||||
FRAME_death_21,
|
||||
FRAME_death_22,
|
||||
FRAME_death_23,
|
||||
FRAME_death_24,
|
||||
FRAME_death_31,
|
||||
FRAME_death_32,
|
||||
FRAME_death_33,
|
||||
FRAME_death_45,
|
||||
FRAME_death_46,
|
||||
FRAME_death_47,
|
||||
FRAME_forwrd_1,
|
||||
FRAME_forwrd_2,
|
||||
FRAME_forwrd_3,
|
||||
FRAME_forwrd_4,
|
||||
FRAME_forwrd_5,
|
||||
FRAME_forwrd_6,
|
||||
FRAME_forwrd_7,
|
||||
FRAME_forwrd_8,
|
||||
FRAME_forwrd_9,
|
||||
FRAME_forwrd_10,
|
||||
FRAME_forwrd_11,
|
||||
FRAME_forwrd_12,
|
||||
FRAME_forwrd_13,
|
||||
FRAME_forwrd_14,
|
||||
FRAME_forwrd_15,
|
||||
FRAME_forwrd_16,
|
||||
FRAME_forwrd_17,
|
||||
FRAME_forwrd_18,
|
||||
FRAME_left_1,
|
||||
FRAME_left_2,
|
||||
FRAME_left_3,
|
||||
FRAME_left_4,
|
||||
FRAME_left_5,
|
||||
FRAME_left_6,
|
||||
FRAME_left_7,
|
||||
FRAME_left_8,
|
||||
FRAME_left_9,
|
||||
FRAME_left_10,
|
||||
FRAME_left_11,
|
||||
FRAME_left_12,
|
||||
FRAME_left_13,
|
||||
FRAME_left_14,
|
||||
FRAME_left_15,
|
||||
FRAME_left_16,
|
||||
FRAME_left_17,
|
||||
FRAME_left_18,
|
||||
FRAME_pain1_1,
|
||||
FRAME_pain1_2,
|
||||
FRAME_pain1_3,
|
||||
FRAME_pain1_4,
|
||||
FRAME_pain2_5,
|
||||
FRAME_pain2_6,
|
||||
FRAME_pain2_7,
|
||||
FRAME_pain2_8,
|
||||
FRAME_pain3_9,
|
||||
FRAME_pain3_10,
|
||||
FRAME_pain3_11,
|
||||
FRAME_pain3_12,
|
||||
FRAME_right_1,
|
||||
FRAME_right_2,
|
||||
FRAME_right_3,
|
||||
FRAME_right_4,
|
||||
FRAME_right_5,
|
||||
FRAME_right_6,
|
||||
FRAME_right_7,
|
||||
FRAME_right_8,
|
||||
FRAME_right_9,
|
||||
FRAME_right_10,
|
||||
FRAME_right_11,
|
||||
FRAME_right_12,
|
||||
FRAME_right_13,
|
||||
FRAME_right_14,
|
||||
FRAME_right_15,
|
||||
FRAME_right_16,
|
||||
FRAME_right_17,
|
||||
FRAME_right_18,
|
||||
FRAME_stand_1,
|
||||
FRAME_stand_2,
|
||||
FRAME_stand_3,
|
||||
FRAME_stand_4,
|
||||
FRAME_stand_5,
|
||||
FRAME_stand_6,
|
||||
FRAME_stand_7,
|
||||
FRAME_stand_8,
|
||||
FRAME_stand_9,
|
||||
FRAME_stand_10,
|
||||
FRAME_stand_11,
|
||||
FRAME_stand_12,
|
||||
FRAME_stand_13,
|
||||
FRAME_stand_14,
|
||||
FRAME_stand_15,
|
||||
FRAME_stand_16,
|
||||
FRAME_stand_17,
|
||||
FRAME_stand_18,
|
||||
FRAME_stand_19,
|
||||
FRAME_stand_20,
|
||||
FRAME_stand_21,
|
||||
FRAME_stand_22,
|
||||
FRAME_stand_23,
|
||||
FRAME_stand_24,
|
||||
FRAME_stand_25,
|
||||
FRAME_stand_26,
|
||||
FRAME_stand_27,
|
||||
FRAME_stand_28,
|
||||
FRAME_stand_29,
|
||||
FRAME_stand_30,
|
||||
FRAME_stand_31,
|
||||
FRAME_stand_32,
|
||||
FRAME_stand_33,
|
||||
FRAME_stand_34,
|
||||
FRAME_stand_35,
|
||||
FRAME_stand_36,
|
||||
FRAME_stand_37,
|
||||
FRAME_stand_38,
|
||||
FRAME_stand_39,
|
||||
FRAME_stand_40,
|
||||
FRAME_stand_41,
|
||||
FRAME_stand_42,
|
||||
FRAME_stand_43,
|
||||
FRAME_stand_44,
|
||||
FRAME_stand_45,
|
||||
FRAME_stand_46,
|
||||
FRAME_stand_47,
|
||||
FRAME_stand_48,
|
||||
FRAME_stand_49,
|
||||
FRAME_stand_50,
|
||||
FRAME_stand_51,
|
||||
FRAME_stand_52,
|
||||
FRAME_stand_53,
|
||||
FRAME_stand_54,
|
||||
FRAME_stand_55,
|
||||
FRAME_stand_56,
|
||||
FRAME_stand_57,
|
||||
FRAME_stand_58,
|
||||
FRAME_stand_59,
|
||||
FRAME_stand_60
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
1162
rerelease/m_tank.cpp
Normal file
1162
rerelease/m_tank.cpp
Normal file
File diff suppressed because it is too large
Load Diff
305
rerelease/m_tank.h
Normal file
305
rerelease/m_tank.h
Normal file
@@ -0,0 +1,305 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
// G:\quake2\baseq2\models/monsters/tank
|
||||
|
||||
// This file generated by qdata - Do NOT Modify
|
||||
|
||||
enum
|
||||
{
|
||||
FRAME_stand01,
|
||||
FRAME_stand02,
|
||||
FRAME_stand03,
|
||||
FRAME_stand04,
|
||||
FRAME_stand05,
|
||||
FRAME_stand06,
|
||||
FRAME_stand07,
|
||||
FRAME_stand08,
|
||||
FRAME_stand09,
|
||||
FRAME_stand10,
|
||||
FRAME_stand11,
|
||||
FRAME_stand12,
|
||||
FRAME_stand13,
|
||||
FRAME_stand14,
|
||||
FRAME_stand15,
|
||||
FRAME_stand16,
|
||||
FRAME_stand17,
|
||||
FRAME_stand18,
|
||||
FRAME_stand19,
|
||||
FRAME_stand20,
|
||||
FRAME_stand21,
|
||||
FRAME_stand22,
|
||||
FRAME_stand23,
|
||||
FRAME_stand24,
|
||||
FRAME_stand25,
|
||||
FRAME_stand26,
|
||||
FRAME_stand27,
|
||||
FRAME_stand28,
|
||||
FRAME_stand29,
|
||||
FRAME_stand30,
|
||||
FRAME_walk01,
|
||||
FRAME_walk02,
|
||||
FRAME_walk03,
|
||||
FRAME_walk04,
|
||||
FRAME_walk05,
|
||||
FRAME_walk06,
|
||||
FRAME_walk07,
|
||||
FRAME_walk08,
|
||||
FRAME_walk09,
|
||||
FRAME_walk10,
|
||||
FRAME_walk11,
|
||||
FRAME_walk12,
|
||||
FRAME_walk13,
|
||||
FRAME_walk14,
|
||||
FRAME_walk15,
|
||||
FRAME_walk16,
|
||||
FRAME_walk17,
|
||||
FRAME_walk18,
|
||||
FRAME_walk19,
|
||||
FRAME_walk20,
|
||||
FRAME_walk21,
|
||||
FRAME_walk22,
|
||||
FRAME_walk23,
|
||||
FRAME_walk24,
|
||||
FRAME_walk25,
|
||||
FRAME_attak101,
|
||||
FRAME_attak102,
|
||||
FRAME_attak103,
|
||||
FRAME_attak104,
|
||||
FRAME_attak105,
|
||||
FRAME_attak106,
|
||||
FRAME_attak107,
|
||||
FRAME_attak108,
|
||||
FRAME_attak109,
|
||||
FRAME_attak110,
|
||||
FRAME_attak111,
|
||||
FRAME_attak112,
|
||||
FRAME_attak113,
|
||||
FRAME_attak114,
|
||||
FRAME_attak115,
|
||||
FRAME_attak116,
|
||||
FRAME_attak117,
|
||||
FRAME_attak118,
|
||||
FRAME_attak119,
|
||||
FRAME_attak120,
|
||||
FRAME_attak121,
|
||||
FRAME_attak122,
|
||||
FRAME_attak201,
|
||||
FRAME_attak202,
|
||||
FRAME_attak203,
|
||||
FRAME_attak204,
|
||||
FRAME_attak205,
|
||||
FRAME_attak206,
|
||||
FRAME_attak207,
|
||||
FRAME_attak208,
|
||||
FRAME_attak209,
|
||||
FRAME_attak210,
|
||||
FRAME_attak211,
|
||||
FRAME_attak212,
|
||||
FRAME_attak213,
|
||||
FRAME_attak214,
|
||||
FRAME_attak215,
|
||||
FRAME_attak216,
|
||||
FRAME_attak217,
|
||||
FRAME_attak218,
|
||||
FRAME_attak219,
|
||||
FRAME_attak220,
|
||||
FRAME_attak221,
|
||||
FRAME_attak222,
|
||||
FRAME_attak223,
|
||||
FRAME_attak224,
|
||||
FRAME_attak225,
|
||||
FRAME_attak226,
|
||||
FRAME_attak227,
|
||||
FRAME_attak228,
|
||||
FRAME_attak229,
|
||||
FRAME_attak230,
|
||||
FRAME_attak231,
|
||||
FRAME_attak232,
|
||||
FRAME_attak233,
|
||||
FRAME_attak234,
|
||||
FRAME_attak235,
|
||||
FRAME_attak236,
|
||||
FRAME_attak237,
|
||||
FRAME_attak238,
|
||||
FRAME_attak301,
|
||||
FRAME_attak302,
|
||||
FRAME_attak303,
|
||||
FRAME_attak304,
|
||||
FRAME_attak305,
|
||||
FRAME_attak306,
|
||||
FRAME_attak307,
|
||||
FRAME_attak308,
|
||||
FRAME_attak309,
|
||||
FRAME_attak310,
|
||||
FRAME_attak311,
|
||||
FRAME_attak312,
|
||||
FRAME_attak313,
|
||||
FRAME_attak314,
|
||||
FRAME_attak315,
|
||||
FRAME_attak316,
|
||||
FRAME_attak317,
|
||||
FRAME_attak318,
|
||||
FRAME_attak319,
|
||||
FRAME_attak320,
|
||||
FRAME_attak321,
|
||||
FRAME_attak322,
|
||||
FRAME_attak323,
|
||||
FRAME_attak324,
|
||||
FRAME_attak325,
|
||||
FRAME_attak326,
|
||||
FRAME_attak327,
|
||||
FRAME_attak328,
|
||||
FRAME_attak329,
|
||||
FRAME_attak330,
|
||||
FRAME_attak331,
|
||||
FRAME_attak332,
|
||||
FRAME_attak333,
|
||||
FRAME_attak334,
|
||||
FRAME_attak335,
|
||||
FRAME_attak336,
|
||||
FRAME_attak337,
|
||||
FRAME_attak338,
|
||||
FRAME_attak339,
|
||||
FRAME_attak340,
|
||||
FRAME_attak341,
|
||||
FRAME_attak342,
|
||||
FRAME_attak343,
|
||||
FRAME_attak344,
|
||||
FRAME_attak345,
|
||||
FRAME_attak346,
|
||||
FRAME_attak347,
|
||||
FRAME_attak348,
|
||||
FRAME_attak349,
|
||||
FRAME_attak350,
|
||||
FRAME_attak351,
|
||||
FRAME_attak352,
|
||||
FRAME_attak353,
|
||||
FRAME_attak401,
|
||||
FRAME_attak402,
|
||||
FRAME_attak403,
|
||||
FRAME_attak404,
|
||||
FRAME_attak405,
|
||||
FRAME_attak406,
|
||||
FRAME_attak407,
|
||||
FRAME_attak408,
|
||||
FRAME_attak409,
|
||||
FRAME_attak410,
|
||||
FRAME_attak411,
|
||||
FRAME_attak412,
|
||||
FRAME_attak413,
|
||||
FRAME_attak414,
|
||||
FRAME_attak415,
|
||||
FRAME_attak416,
|
||||
FRAME_attak417,
|
||||
FRAME_attak418,
|
||||
FRAME_attak419,
|
||||
FRAME_attak420,
|
||||
FRAME_attak421,
|
||||
FRAME_attak422,
|
||||
FRAME_attak423,
|
||||
FRAME_attak424,
|
||||
FRAME_attak425,
|
||||
FRAME_attak426,
|
||||
FRAME_attak427,
|
||||
FRAME_attak428,
|
||||
FRAME_attak429,
|
||||
FRAME_pain101,
|
||||
FRAME_pain102,
|
||||
FRAME_pain103,
|
||||
FRAME_pain104,
|
||||
FRAME_pain201,
|
||||
FRAME_pain202,
|
||||
FRAME_pain203,
|
||||
FRAME_pain204,
|
||||
FRAME_pain205,
|
||||
FRAME_pain301,
|
||||
FRAME_pain302,
|
||||
FRAME_pain303,
|
||||
FRAME_pain304,
|
||||
FRAME_pain305,
|
||||
FRAME_pain306,
|
||||
FRAME_pain307,
|
||||
FRAME_pain308,
|
||||
FRAME_pain309,
|
||||
FRAME_pain310,
|
||||
FRAME_pain311,
|
||||
FRAME_pain312,
|
||||
FRAME_pain313,
|
||||
FRAME_pain314,
|
||||
FRAME_pain315,
|
||||
FRAME_pain316,
|
||||
FRAME_death101,
|
||||
FRAME_death102,
|
||||
FRAME_death103,
|
||||
FRAME_death104,
|
||||
FRAME_death105,
|
||||
FRAME_death106,
|
||||
FRAME_death107,
|
||||
FRAME_death108,
|
||||
FRAME_death109,
|
||||
FRAME_death110,
|
||||
FRAME_death111,
|
||||
FRAME_death112,
|
||||
FRAME_death113,
|
||||
FRAME_death114,
|
||||
FRAME_death115,
|
||||
FRAME_death116,
|
||||
FRAME_death117,
|
||||
FRAME_death118,
|
||||
FRAME_death119,
|
||||
FRAME_death120,
|
||||
FRAME_death121,
|
||||
FRAME_death122,
|
||||
FRAME_death123,
|
||||
FRAME_death124,
|
||||
FRAME_death125,
|
||||
FRAME_death126,
|
||||
FRAME_death127,
|
||||
FRAME_death128,
|
||||
FRAME_death129,
|
||||
FRAME_death130,
|
||||
FRAME_death131,
|
||||
FRAME_death132,
|
||||
FRAME_recln101,
|
||||
FRAME_recln102,
|
||||
FRAME_recln103,
|
||||
FRAME_recln104,
|
||||
FRAME_recln105,
|
||||
FRAME_recln106,
|
||||
FRAME_recln107,
|
||||
FRAME_recln108,
|
||||
FRAME_recln109,
|
||||
FRAME_recln110,
|
||||
FRAME_recln111,
|
||||
FRAME_recln112,
|
||||
FRAME_recln113,
|
||||
FRAME_recln114,
|
||||
FRAME_recln115,
|
||||
FRAME_recln116,
|
||||
FRAME_recln117,
|
||||
FRAME_recln118,
|
||||
FRAME_recln119,
|
||||
FRAME_recln120,
|
||||
FRAME_recln121,
|
||||
FRAME_recln122,
|
||||
FRAME_recln123,
|
||||
FRAME_recln124,
|
||||
FRAME_recln125,
|
||||
FRAME_recln126,
|
||||
FRAME_recln127,
|
||||
FRAME_recln128,
|
||||
FRAME_recln129,
|
||||
FRAME_recln130,
|
||||
FRAME_recln131,
|
||||
FRAME_recln132,
|
||||
FRAME_recln133,
|
||||
FRAME_recln134,
|
||||
FRAME_recln135,
|
||||
FRAME_recln136,
|
||||
FRAME_recln137,
|
||||
FRAME_recln138,
|
||||
FRAME_recln139,
|
||||
FRAME_recln140
|
||||
};
|
||||
|
||||
constexpr float MODEL_SCALE = 1.000000f;
|
||||
3792
rerelease/p_client.cpp
Normal file
3792
rerelease/p_client.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1124
rerelease/p_hud.cpp
Normal file
1124
rerelease/p_hud.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1695
rerelease/p_move.cpp
Normal file
1695
rerelease/p_move.cpp
Normal file
File diff suppressed because it is too large
Load Diff
153
rerelease/p_trail.cpp
Normal file
153
rerelease/p_trail.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
#include "g_local.h"
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
PLAYER TRAIL
|
||||
|
||||
==============================================================================
|
||||
|
||||
This is a two-way list containing the a list of points of where
|
||||
the player has been recently. It is used by monsters for pursuit.
|
||||
|
||||
This is improved from vanilla; now, the list itself is stored in
|
||||
client data so it can be stored for multiple clients.
|
||||
|
||||
chain = next
|
||||
enemy = prev
|
||||
|
||||
The head node will always have a null "chain", the tail node
|
||||
will always have a null "enemy".
|
||||
*/
|
||||
|
||||
constexpr size_t TRAIL_LENGTH = 8;
|
||||
|
||||
// places a new entity at the head of the player trail.
|
||||
// the tail entity may be moved to the front if the length
|
||||
// is at the end.
|
||||
static edict_t *PlayerTrail_Spawn(edict_t *owner)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
for (edict_t *tail = owner->client->trail_tail; tail; tail = tail->chain)
|
||||
len++;
|
||||
|
||||
edict_t *trail;
|
||||
|
||||
// move the tail to the head
|
||||
if (len == TRAIL_LENGTH)
|
||||
{
|
||||
// unlink the old tail
|
||||
trail = owner->client->trail_tail;
|
||||
owner->client->trail_tail = trail->chain;
|
||||
owner->client->trail_tail->enemy = nullptr;
|
||||
trail->chain = trail->enemy = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// spawn a new head
|
||||
trail = G_Spawn();
|
||||
trail->classname = "player_trail";
|
||||
}
|
||||
|
||||
// link as new head
|
||||
if (owner->client->trail_head)
|
||||
owner->client->trail_head->chain = trail;
|
||||
trail->enemy = owner->client->trail_head;
|
||||
owner->client->trail_head = trail;
|
||||
|
||||
// if there's no tail, we become the tail too
|
||||
if (!owner->client->trail_tail)
|
||||
owner->client->trail_tail = trail;
|
||||
|
||||
return trail;
|
||||
}
|
||||
|
||||
// destroys all player trail entities in the map.
|
||||
// we don't want these to stay around across level loads.
|
||||
void PlayerTrail_Destroy(edict_t *player)
|
||||
{
|
||||
for (size_t i = 0; i < globals.num_edicts; i++)
|
||||
if (g_edicts[i].classname && strcmp(g_edicts[i].classname, "player_trail") == 0)
|
||||
if (!player || g_edicts[i].owner == player)
|
||||
G_FreeEdict(&g_edicts[i]);
|
||||
|
||||
if (player)
|
||||
player->client->trail_head = player->client->trail_tail = nullptr;
|
||||
else for (size_t i = 0; i < game.maxclients; i++)
|
||||
game.clients[i].trail_head = game.clients[i].trail_tail = nullptr;
|
||||
}
|
||||
|
||||
// check to see if we can add a new player trail spot
|
||||
// for this player.
|
||||
void PlayerTrail_Add(edict_t *player)
|
||||
{
|
||||
// if we can still see the head, we don't want a new one.
|
||||
if (player->client->trail_head && visible(player, player->client->trail_head))
|
||||
return;
|
||||
// don't spawn trails in intermission, if we're dead, if we're noclipping or not on ground yet
|
||||
else if (level.intermissiontime || player->health <= 0 || player->movetype == MOVETYPE_NOCLIP ||
|
||||
!player->groundentity)
|
||||
return;
|
||||
|
||||
edict_t *trail = PlayerTrail_Spawn(player);
|
||||
trail->s.origin = player->s.old_origin;
|
||||
trail->timestamp = level.time;
|
||||
trail->owner = player;
|
||||
}
|
||||
|
||||
// pick a trail node that matches the player
|
||||
// we're hunting that is visible to us.
|
||||
edict_t *PlayerTrail_Pick(edict_t *self, bool next)
|
||||
{
|
||||
// not player or doesn't have a trail yet
|
||||
if (!self->enemy->client || !self->enemy->client->trail_head)
|
||||
return nullptr;
|
||||
|
||||
// find which marker head that was dropped while we
|
||||
// were searching for this enemy
|
||||
edict_t *marker;
|
||||
|
||||
for (marker = self->enemy->client->trail_head; marker; marker = marker->enemy)
|
||||
{
|
||||
if (marker->timestamp <= self->monsterinfo.trail_time)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (next)
|
||||
{
|
||||
// find the marker we're closest to
|
||||
float closest_dist = std::numeric_limits<float>::infinity();
|
||||
edict_t *closest = nullptr;
|
||||
|
||||
for (edict_t *m2 = marker; m2; m2 = m2->enemy)
|
||||
{
|
||||
float len = (m2->s.origin - self->s.origin).lengthSquared();
|
||||
|
||||
if (len < closest_dist)
|
||||
{
|
||||
closest_dist = len;
|
||||
closest = m2;
|
||||
}
|
||||
}
|
||||
|
||||
// should never happen
|
||||
if (!closest)
|
||||
return nullptr;
|
||||
|
||||
// use the next one from the closest one
|
||||
marker = closest->chain;
|
||||
}
|
||||
else
|
||||
{
|
||||
// from that marker, find the first one we can see
|
||||
for (; marker && !visible(self, marker); marker = marker->enemy)
|
||||
continue;
|
||||
}
|
||||
|
||||
return marker;
|
||||
}
|
||||
1527
rerelease/p_view.cpp
Normal file
1527
rerelease/p_view.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user