[blue]#include[/blue] "extdll.h"
[blue]#include[/blue] "util.h"
[blue]#include[/blue] "cbase.h"
[blue]#include[/blue] "monsters.h"
[blue]#include[/blue] "schedule.h"
[blue]#include[/blue] "decals.h"
The top 5 are necessary. The last is just for this guy - it allows you, obviously, to make decals.[green]//=========================
//--Monster's Anim Events--
//=========================[/green]
[blue]#define[/blue] PDRONE_FIRE_SPIKE ( 1 )
[blue]#define[/blue] PDRONE_MELEE_LEFT ( 3 )
[blue]#define[/blue] PDRONE_MELEE_RIGHT ( 8 )
[blue]#define[/blue] PDRONE_MELEE_BOTH ( 6 )
[green]//=========================
//--Monster's Extra Defs---
//=========================
//-Body Groups[/green]
[blue]#define[/blue] BODYGROUP_BODY 1
[blue]#define[/blue] BODYGROUP_SPIKES 2
[green]//-Spike Groups[/green]
[blue]#define[/blue] BODY_NO_SPIKES 0
[blue]#define[/blue] BODY_SIX_SPIKES 1
[blue]#define[/blue] BODY_FIVE_SPIKES 2
[blue]#define[/blue] BODY_FOUR_SPIKES 3
[blue]#define[/blue] BODY_THREE_SPIKES 4
[blue]#define[/blue] BODY_TWO_SPIKES 5
[blue]#define[/blue] BODY_ONE_SPIKES 6
The first 4 lines are animation events. In animation, there are certain frames that trigger code-side eventes. The numbers are the "names" of these events, while the PDRONE_ stuff is the thing we reference the numbers by, to make it easier for us to edit.[blue]class[/blue] CPitDrone : [blue]public[/blue] CBaseMonster
{
[blue]public:[/blue]
[blue]void[/blue] Spawn( [blue]void[/blue] );
[blue]void[/blue] Precache( [blue]void[/blue] );
[blue]int[/blue] iSpikes;
[blue]int[/blue] Classify ( [blue]void[/blue] );
[blue]void[/blue] SetYawSpeed( [blue]void[/blue] );
};
Class is obvious, and so is CPitDrone. Then it says that we're taking the base as CBaseMonster. Next, we say that our public definitions (for other classes to edit) are spawn and precache. Precache means that the model is preloaded, so it doesn't make the game slower. There's a limit to 512 precaches. iSpikes is the current amount of spikes loaded into him...WRITE NOTHING IN THE COMMENTS ABOUT THAT LINE. Classify makes him choose what faction he wants to join, and SetYawSpeed changes how fast he turns.LINK_ENTITY_TO_CLASS( monster_pitdrone, CPitDrone );
Now, put on your Frankenstein labcoat (not his Monster, you un-nerds!) and prepare to set him to life.
[blue]void[/blue] CPitDrone :: Spawn()
{
Precache( ); [green]// So the model loads[/green]
SET_MODEL(ENT(pev), "models/npcs/pit_drone.mdl"); [green]// So you can see him[/green]
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); [green]// Let's make his size human. If you're smart enough (or have lots of patience) you can get replace the VEC_ stuff with "Vector( x, y, z)".[/green]
pev->solid = SOLID_SLIDEBOX; [green]// They see me slidin', they hating. This actually tells the engine for it to be Solid. Snakes can GTHO.[/green]
pev->movetype = MOVETYPE_STEP; [green]// 'cause monsters walk - they don't drive (Nightmares will follow)[/green]
m_bloodColor = BLOOD_COLOR_GREEN; [green]// Green blood - just like this comment. Freaked out much? (The blood's actually yellow, though)[/green]
pev->health = 100; [green]// Health - let's keep it as an integer, as opposed to a changeable variable for now.[/green]
pev->view_ofs = Vector ( 0, 0, 20 ); [green]// Eyes' offset (He sees you doing stuff you shouldn't)[/green]
m_flFieldOfView = 0.5; [green]// How far he can see.[/green]
m_MonsterState = MONSTERSTATE_NONE; [green]// Afet he spawns, make him sit there like an idiot, doing nothing.[/green]
MonsterInit(); [green]// Starts the monsters AI[/green]
iSpikes = 6; [green]// Default, he's fully loaded with spikes. AGAIN, NO PUNS YOU SICKOS![/green]
}
I've commented everything so you can easily understand.
[blue]void[/blue] CPitDrone :: Precache()
{
PRECACHE_MODEL("models/pit_drone_spike.mdl"); [green]//Loads the model for the spike[/green]
PRECACHE_MODEL("models/npcs/pit_drone.mdl"); [green]//Loads the NPC model in the game[/green]
[green]//Bunch of pretty self-explanatory sounds[/green]
PRECACHE_SOUND("pitdrone/pit_drone_melee_attack1.wav" );
PRECACHE_SOUND("pitdrone/pit_drone_melee_attack2.wav" );
PRECACHE_SOUND("pitdrone/pit_drone_attack_spike1.wav" );
PRECACHE_SOUND("pitdrone/pit_drone_eat.wav");
PRECACHE_SOUND("pitdrone/pit_drone_die1.wav");
PRECACHE_SOUND("pitdrone/pit_drone_die2.wav");
PRECACHE_SOUND("pitdrone/pit_drone_die3.wav");
PRECACHE_SOUND("pitdrone/pit_drone_hunt3.wav");
}
Okay, now you have a monster, but he has to epic voice chose a side end epic voice. There's 13 original CLASS_es, but I've added some of my own for my mod. Here, I'll use an original one. Address this tutorial (revived from the VERC for a how to.
[blue]int[/blue] CPitDrone :: Classify ( [blue]void[/blue] )
{
[blue]return[/blue] CLASS_ALIEN_MONSTER;
}
Finally, make him turn, turn, turn.
[blue]void[/blue] CPitDrone::SetYawSpeed( [blue]void[/blue] ) {
pev->yaw_speed = 90;
}
here[/url].]$sequence "range" "range" fps 30 [red]ACT_RANGE_ATTACK1[/red] 1 [blue]{ event 1 11 } { event 1008 1 "pitdrone/pit_drone_attack_spike1.wav" }[/blue]
If you look at the QC again, you'll see some events. These highlighted in blue. The first event is called "1" and is played at frame 11. The next is called "1008"" and is played at frame 1. This plays a sound - in this case, "pitdrone/pit_drone_attack_spike1.wav". Since this isn't a QC tutorial, I won't talk about that anymore.[blue]void[/blue] HandleAnimEvent( MonsterEvent_t *pEvent );
andSchedule_t *GetSchedule( [blue]void[/blue] ); [green]// Handles some schedules[/green]
to the class's public defenitions. It SHOULD look like this:
[blue]class[/blue] CPitDrone : [blue]public[/blue] CBaseMonster
{
[blue]public:[/blue]
[blue]void[/blue] Spawn( [blue]void[/blue] );
[blue]void[/blue] Precache( [blue]void[/blue] );
[blue]int[/blue] iSpikes;
[blue]int[/blue] Classify ( [blue]void[/blue] );
[blue]void[/blue] SetYawSpeed( [blue]void[/blue] );
[blue]void[/blue] HandleAnimEvent( MonsterEvent_t *pEvent );
Schedule_t *GetSchedule( [blue]void[/blue] ); [green]// Handles some schedules[/green]
};
HandleAnimEvent, obviously, handles the animation events. But before we get to that, we have to start coding the projectile. At the top of the file, add this:
[blue]class[/blue] CPitDroneSpike : [blue]public[/blue] CBaseEntity
{
[blue]public:
void[/blue] Spawn( [blue]void[/blue] );
[blue]void[/blue] Touch( CBaseEntity *pOther );
Vector waterSpeed;
};
LINK_ENTITY_TO_CLASS( pitdrone_spike, CPitDroneSpike );
[blue]void[/blue] CPitDroneSpike :: Spawn( [blue]void[/blue] )
{
Precache();
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_FLY;
pev->classname = MAKE_STRING( "pitdrone_spike" );
SET_MODEL( ENT(pev), "models/npcs/pit_drone_spike.mdl");
}
[blue]void[/blue] CPitDroneSpike :: Touch ( CBaseEntity *pOther )
{
[blue]if[/blue] ( !pOther->pev->takedamage ) {
[green]// If the entity doesn't take damage[/green]
[blue]if[/blue] (UTIL_PointContents(pev->origin) == CONTENTS_WATER)
{
pev->velocity = waterSpeed; [green]// Go slower while in water[/green]
}
[blue]else[/blue] {
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_FLY;
pev->velocity = Vector( 0, 0, 0 );
UTIL_Sparks( pev->origin );
EMIT_SOUND( ENT(pev), CHAN_VOICE, "npcs/pitdrone/pit_drone_eat.wav", 1, ATTN_NORM );
}
}
[blue]else[/blue] {
[green]// If it does take damage[/green]
pOther->TakeDamage ( pev, pev, gSkillData.pitdroneSpikeDmg, DMG_GENERIC ); [green]// Give damage to whatever it is[/green]
UTIL_Remove( [blue]this[/blue] ); [green]// Remove it[/green]
}
}
Everything is explained in the comments. Start a new function in the end of the .cpp file.
[blue]void[/blue] CPitDrone :: HandleAnimEvent( MonsterEvent_t *pEvent ) {
Now we can start listing our animation events. This is where the earlier stated definitions come to place. Let's start a switch statement and use the anim events.
[blue]switch[/blue] ( pEvent->event ) {
[blue]case[/blue] PDRONE_RELOAD:
{
iSpikes = 6;
SetBodygroup( BODYGROUP_SPIKES, 1 );
[blue]break[/blue];
}
This starts a switch statement, meaning that it switches the output depending on the case. Here, we're saying that, depending on our event, we do different stuff, like the case with the PDRONE_RELOAD. It sets our loaded spikes to 6 and sets the body to match. break; means that the case has ended.[blue]case[/blue] PDRONE_FIRE_SPIKE:
{
[green]// Define the vectors - an offset and a direction[/green]
Vector vecspikeOffset;
Vector vecspikeDir;
[green]// This stores pev->angles into 3 vectors, v_forward, v_up, and v_right, so we can use them in offsets.[/green]
UTIL_MakeVectors ( pev->angles );
[green]// Move the origin to a relative offset[/green]
vecspikeOffset = ( gpGlobals->v_forward * 22 + gpGlobals->v_up * 40 );
[green]// Now make the origin absolute, by adding the monster's origin[/green]
vecspikeOffset = ( pev->origin + vecspikeOffset );
[green]// Setting the Direction, by taking the enemy's origin and view offset (so we hit him in his head, not his feet) and the spike offset, and then normalizing it, so 1 is the maximum.[/green]
vecspikeDir = ( ( m_hEnemy->pev->origin + m_hEnemy->pev->view_ofs ) - vecspikeOffset ).Normalize();
[green]// Randomizing the Direction up a bit, so he's not a perfect shot.[/green]
vecspikeDir.x += RANDOM_FLOAT( -0.01, 0.01 );
vecspikeDir.y += RANDOM_FLOAT( -0.01, 0.01 );
vecspikeDir.z += RANDOM_FLOAT( -0.01, 0.01 );
[green]// Create the spike, place it and turn it.[/green]
CPitDroneSpike *pSpike = (CPitDroneSpike *)CBaseMonster::Create( "pitdrone_spike", vecspikeOffset, pev->angles, edict() );
[green]// Actually setting the velocity. This is why we normalized it, so we can have different speeds.[/green]
pSpike->pev->velocity = vecspikeDir * 900;
[green]// Same for water velocity[/green]
pSpike->waterSpeed = vecspikeDir * 300;
[green]// Remember the pitdrone, so we can say who killed the enemy[/green]
pSpike->pev->owner = ENT(pev);
[green]// No friction FTW[/green]
pSpike->pev->friction = 0;
[green]// Set the angles to correspond to the velocity[/green]
pSpike->pev->angles = UTIL_VecToAngles(pSpike->pev->velocity);
[green]// Take a spike out[/green]
iSpikes--;
[green]// Set the body to match the spikes.[/green]
[blue]if[/blue] (iSpikes == 0) {
SetBodygroup( BODYGROUP_SPIKES, 0 );
}
[blue]else[/blue] {
SetBodygroup( BODYGROUP_SPIKES, GetBodygroup( BODYGROUP_SPIKES )+1 );
}
[blue]break[/blue];
}
I commented on everything, so you know what it does. REMEMBER, comments are in green and start with //.[blue]case[/blue] PDRONE_MELEE_LEFT:
{
[green]// Only gonna comment on this one, cuz the rest are basically the same.[/green]
[green]// This gets the enemy and attacks at the same time.[/green]
[green]// The parameters after CheckTraceHullAttack are distance, amount of damage, and type[/green]
CBaseEntity *pHurt = CheckTraceHullAttack( 85, 20, DMG_SLASH );
[green]// If you did hurt someone...[/green]
[blue]if[/blue] ( pHurt )
{
[green]// ...make him change his view angle a bit (only players)...[/green]
pHurt->pev->punchangle.y = 15;
pHurt->pev->punchangle.x = 8;
[green]// ... and push him back a bit.[/green]
pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_up * -100;
}
[blue]break[/blue];
}
[blue]case[/blue] PDRONE_MELEE_RIGHT:
{
CBaseEntity *pHurt = CheckTraceHullAttack( 85, 20, DMG_SLASH );
[blue]if[/blue] ( pHurt )
{
pHurt->pev->punchangle.y = -15;
pHurt->pev->punchangle.x = 8;
pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_up * -100;
}
[blue]break[/blue];
}
[blue]case[/blue] PDRONE_MELEE_BOTH:
{
CBaseEntity *pHurt = CheckTraceHullAttack( 85, 30, DMG_SLASH );
[blue]if[/blue] ( pHurt )
{
pHurt->pev->punchangle.x = 15;
pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_up * -100;
}
[blue]break[/blue];
}
I still commented on it. Remember, this is for all three of his melees - left, right, and both, because we changed up his angles a bit. Read the comments. Let's close off the switch statement now. Add this right afterwards.
[blue]default[/blue]:
CBaseMonster::HandleAnimEvent( pEvent );
[blue]break[/blue];
}
}
What that, basically, did was check to see if the base has any events to use, and closes the switch and function.Schedule_t *CPitDrone :: GetSchedule( [blue]void[/blue] )
{
[green]// Call another switch class, to check the monster's attitude[/green]
[blue]switch[/blue] ( m_MonsterState )
{
[green]// Manly monster needs to fight[/green]
[blue]case[/blue] MONSTERSTATE_COMBAT:
{
[blue]if[/blue] ( HasConditions( bits_COND_ENEMY_DEAD ) )
{
[green]// The enemy is dead - call base class, all code to handle dead enemies is centralized there.[/green]
[blue]return[/blue] CBaseMonster :: GetSchedule();
}
[green]// Can I attack melee style?[/green]
[blue]if[/blue] ( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) )
{
[green]// Randomize my melee attacks, so it's a bit different[/green]
[blue]switch[/blue] (RANDOM_LONG ( 0, 1 )) {
[blue]case[/blue] 0:
[blue]return[/blue] GetScheduleOfType ( SCHED_MELEE_ATTACK1 );
[blue]break[/blue];
[blue]case[/blue] 1:
[blue]return[/blue] GetScheduleOfType ( SCHED_MELEE_ATTACK2 );
[blue]break[/blue];
}
}
[green]// I can range attack! HELLZ YEAH![/green]
[blue]if[/blue] ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) )
{
[green]// TOO CLOSE! USE MELEE.[/green]
[blue]if[/blue] ((pev->origin - m_hEnemy->pev->origin).Length() <= 256)
{
return GetScheduleOfType ( SCHED_CHASE_ENEMY );
}
[blue]if[/blue] ((pev->origin - m_hEnemy->pev->origin).Length() <= 512)
{
[green]// Do I have spikes?[/green]
[blue]if[/blue] (pev->body != BODY_NO_SPIKES) {
[green]// Yes. Fire![/green]
[blue]return[/blue] GetScheduleOfType ( SCHED_RANGE_ATTACK1 );
}
[blue]else[/blue] {
[green]// No.[/green]
[blue]if[/blue] ((pev->origin - m_hEnemy->pev->origin).Length() <= 312) {
[green]// I'm close, I can go and attack.[/green]
[blue]if[/blue] ( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) ) {
[blue]switch[/blue] (RANDOM_LONG ( 0, 1 )) {
[blue]case[/blue] 0:
[blue]return[/blue] GetScheduleOfType ( SCHED_MELEE_ATTACK1 );
[blue]break[/blue];
[blue]case[/blue] 1:
[blue]return[/blue] GetScheduleOfType ( SCHED_MELEE_ATTACK2 );
[blue]break[/blue];
}
}
[green]// Lemme get a bit closer, so I can attack[/green]
[blue]else[/blue] {
[blue]return[/blue] GetScheduleOfType ( SCHED_CHASE_ENEMY );
}
}
[green]// He's too far to melee. I'll just reload[/green]
[blue]else[/blue] {
[blue]return[/blue] GetScheduleOfType ( SCHED_RELOAD );
}
}
}
[green]// Too far to either fire the spikes or melee, so lemme just get closer, so I'm more accurate.[/green]
[blue]else[/blue] {
[blue]return[/blue] GetScheduleOfType ( SCHED_CHASE_ENEMY );
}
}
[green]// If I can do nothing, just chase after him[/green]
[blue]return[/blue] GetScheduleOfType ( SCHED_CHASE_ENEMY );
[blue]break[/blue];
}
}
[green]// The base probably knows what to do [/green]
[blue]return[/blue] CBaseMonster :: GetSchedule();
}
I've commented on the reason I put what where, but, just so you know, return GetScheduleOfType( SCHED_WHATEVER ); Returns the schedule, so he can know what to do.[blue]virtual[/blue] BOOL CheckRangeAttack1( [blue]float[/blue] flDot, [blue]float[/blue] flDist );
[blue]virtual[/blue] BOOL CheckRangeAttack2( [blue]float[/blue] flDot, [blue]float[/blue] flDist );
[blue]virtual[/blue] BOOL CheckMeleeAttack1( [blue]float[/blue] flDot, [blue]float[/blue] flDist );
[blue]virtual[/blue] BOOL CheckMeleeAttack2( [blue]float[/blue] flDot, [blue]float[/blue] flDist );
That work pretty much the same. They check whether the monster is allowed to range or melee or not.BOOL CBaseMonster :: CheckMeleeAttack2 ( [blue]float[/blue] flDot, [blue]float[/blue] flDist ) {
[blue]if[/blue] ( flDist <= 64 && flDot >= 0.7 )
{
[blue]return[/blue] TRUE;
}
[blue]return[/blue] FALSE;
}
That basically checks if the flDist (Distance, obviously), and flDot (I believe that's how-in-sight he is).
Schedule_t* CBaseMonster :: GetScheduleOfType ( int Type )
{
[blue]switch[/blue] ( Type )
{
[blue]case[/blue] SCHED_CHASE_ENEMY:
{
[blue]return[/blue] &slChaseEnemy[ 0 ];
}
[blue]default[/blue]:
{
ALERT ( at_console, "GetScheduleOfType()nNo CASE for Schedule Type %d!n", Type );
[blue]return[/blue] &slIdleStand[ 0 ];
[blue]break[/blue];
}
}
[blue]return[/blue] NULL;
}
In the example, you get the Type from the GetSchedule, where the function is called from. You put it into a switch statement. If it's SCHED_CHASE_ENEMY, release the proper schedule, in this case, &slChaseEnemy[ 0 ]. Otherwise, send an alert at the console, and give the standing schedule. If even THAT doesn't work, return nothing at all (that should never happen).
...
void Precache( void );
[red]CUSTOM_SCHEDULES;[/red]
};
[pre]
Now, go on to the schedule names.
[pre]
enum
{
SCHED_GET_BEST_ITEM = LAST_TALKMONSTER_SCHEDULE + 1,
SCHED_COVER_AND_GET_ITEM,
LAST_HEVSCIENTIST_SCHEDULE, // MUST be last
};
That's an example from my HEV scientists (who can fight back and pick up weapons). LAST_COMMON_SCHEDULE can replace LAST_TALKMONSTER_SCHEDULE if you're using CBaseMonster instead of a Talking Monster. LAST_HEVSCIENIST_SCHEDULE should be renamed to fit your own monster, and the two schedules (SCHED_GET_BEST_ITEM and SCHED_COVER_AND_GET_ITEM) could be changed. The second can be removed or duplicated. The = LAST_TALKMONSTER_SCHEDULE + 1, and LAST_HEVSCIENTIST_SCHEDULE, NEED to be there, but the text depends on the monster.DEFINE_CUSTOM_SCHEDULES( CHEVScientist )
{
slGetBestItem,
slCoverAndGetItem,
};
IMPLEMENT_CUSTOM_SCHEDULES( CHEVScientist, CTalkMonster );
The CHEVScienists should be changed to match your class, and the sl-s should match something that resembles your SCHED_s. The IMPLEMENT_CUSTOM_SCHEDULES is necessary, and, again, the values should be substituted for your class and base respectively.Task_t tlHeal[] =
{
{ TASK_MOVE_TO_TARGET_RANGE,(float)50 }, // Move within 60 of target ent (client)
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_TARGET_CHASE }, // If you fail, catch up with that guy! (change this to put syringe away and then chase)
{ TASK_FACE_IDEAL, (float)0 },
{ TASK_SAY_HEAL, (float)0 },
{ TASK_PLAY_SEQUENCE_FACE_TARGET, (float)ACT_ARM }, // Whip out the needle
{ TASK_HEAL, (float)0 }, // Put it in the player
{ TASK_PLAY_SEQUENCE_FACE_TARGET, (float)ACT_DISARM }, // Put away the needle
};
Schedule_t slHeal[] =
{
{
tlHeal,
ARRAYSIZE ( tlHeal ),
0, // Don't interrupt or he'll end up running around with a needle all the time
0,
"Heal"
},
};
A LOT of stuff going on here. First of all, this is the heal schedule, and, again, replace everything you need to. From the top, we started the task list. This part is pretty obvious. We type up the different tasks we're gonna use. For example, the first one: TASK_MOVE_TO_TARGET_RANGE says the task - that he should get close to the target (the player), and (float)50defines the parameter - the distance. This is not always necessary, and you should use (float)0 when nothing else is necessary. You can set different schedules and different acts, too.enum
{
TASK_GET_PATH_TO_BEST_ITEM = LAST_TALKMONSTER_TASK + 1,
TASK_PICK_UP_ITEM,
LAST_HEVSCIENTIST_TASK, // MUST be last
};
To say what it does, use this in the class:
void StartTask( Task_t *pTask );
Now, you can create the function itself:
void CHEVScientist :: StartTask( Task_t *pTask )
{
switch ( pTask->iTask )
{
case TASK_RELOAD:
m_IdealActivity = ACT_RELOAD;
break;
default:
CTalkMonster::StartTask( pTask );
}
}
Basically, it takes the task through a switch, and either calls the baseclass if it's not found, or does what's said - calling an ACT_.You must log in to post a comment. You can login or register a new account.
Thanks to all the commenters!
I use this tutorial and is very good but i have only 3 "errors":
First, the pitdrone doesn´t fire.
Second, the melee attack left and right doesn´t make damage to the player.
and finally, when the pitdrones kill the player, using the attack "double", and i press a key to automatically restore the game i have this message error:
"ED_ALLOC = No edits yet."
**--**--**--**--**--**--**--**--**--**--**--**--**--**--**--**--**--**--**--
Ok, for the first, i dont need them for the moment, my new monster doesn´t fire, so i think if i need that in the future, i can use the bullchicken code, or whatever.
For the second, i dont see where is the error...if anyone can help me.
For the last one, ("the most important"), i read about the "edits", they are the "slots" for the monsters, all hl monsters have one...so, ¿you need to "define" a slot for the pitdrone? how do that? heeelp! i am so close to finish my mod...i can´t release them until i have coding at least three new npc´s....
So, thanks for all, I hope someone can help me.
default:
CBaseMonster::HandleAnimEvent( pEvent );
break;
}
} "
Right after what exactly??
Before I got to the reload part, everything compiled fine.
But, every time the I enter the pit drone's FOV, the game exits to the main menu, literally disconnecting me from the game.
The integers that represent the animation events were not the same as what was written in the .qc for the melee attacks and reload so you had to change it to this
#define PDRONE_FIRE_SPIKE ( 1 )
#define PDRONE_MELEE_LEFT ( 2 )
#define PDRONE_MELEE_RIGHT ( 4 )
#define PDRONE_MELEE_BOTH ( 6 )
#define PDRONE_RELOAD ( 7 )
the return NULL under GetScheduleOfType was causing crashes so i changed it to:
return CBaseMonster::GetScheduleOfType(Type);
i had to make a custom checkAmmo function and override checkRangeAttack1 and explicitly call them in getSchedule.
and thats about it. heres a video preview of the changes i made and the showcasing it working. I will share the gitHub Code below so you can reference it.
https://github.com/TylerMaster/ICE1/blob/BRANDON_SMITH_Ex1/wip_monster.cpp
https://github.com/TylerMaster/ICE1/blob/BRANDON_SMITH_Ex1/wip_monster.h