/* ================ FireBullets Go to the trouble of combining multiple pellets into a single damage call. This version is used by Monsters. ================ */ void CBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker ) { static int tracerCount; int tracer; TraceResult tr; Vector vecRight = gpGlobals->v_right; Vector vecUp = gpGlobals->v_up; if ( pevAttacker == NULL ) pevAttacker = pev; // the default attacker is ourselves ClearMultiDamage(); gMultiDamage.type = DMG_BULLET | DMG_NEVERGIB; for (ULONG iShot = 1; iShot <= cShots; iShot++) { // get circular gaussian spread float x, y, z; do { x = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5); y = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5); z = x*x+y*y; } while (z > 1); Vector vecDir = vecDirShooting + x * vecSpread.x * vecRight + y * vecSpread.y * vecUp; Vector vecEnd; vecEnd = vecSrc + vecDir * flDistance; UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev)/*pentIgnore*/, &tr); tracer = 0; if (iTracerFreq != 0 && (tracerCount++ % iTracerFreq) == 0) { Vector vecTracerSrc; if ( IsPlayer() ) {// adjust tracer position for player vecTracerSrc = vecSrc + Vector ( 0 , 0 , -4 ) + gpGlobals->v_right * 2 + gpGlobals->v_forward * 16; } else { vecTracerSrc = vecSrc; } if ( iTracerFreq != 1 ) // guns that always trace also always decal tracer = 1; switch( iBulletType ) { case BULLET_MONSTER_MP5: case BULLET_MONSTER_9MM: case BULLET_MONSTER_12MM: default: MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc ); WRITE_BYTE( TE_TRACER ); WRITE_COORD( vecTracerSrc.x ); WRITE_COORD( vecTracerSrc.y ); WRITE_COORD( vecTracerSrc.z ); WRITE_COORD( tr.vecEndPos.x ); WRITE_COORD( tr.vecEndPos.y ); WRITE_COORD( tr.vecEndPos.z ); MESSAGE_END(); break; } } // do damage, paint decals if (tr.flFraction != 1.0) { CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); if ( iDamage ) { pEntity->TraceAttack(pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); DecalGunshot( &tr, iBulletType ); } else switch(iBulletType) { default: case BULLET_MONSTER_9MM: pEntity->TraceAttack(pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); DecalGunshot( &tr, iBulletType ); break; case BULLET_MONSTER_MP5: pEntity->TraceAttack(pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); DecalGunshot( &tr, iBulletType ); break; case BULLET_MONSTER_12MM: pEntity->TraceAttack(pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET); if ( !tracer ) { TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); DecalGunshot( &tr, iBulletType ); } break; case BULLET_NONE: // FIX pEntity->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); // only decal glass if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0) { UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) ); } break; } } // make bullet trails UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 ); } ApplyMultiDamage(pev, pevAttacker); }
/* ================ FireBullets Go to the trouble of combining multiple pellets into a single damage call. This version is used by Players, uses the random seed generator to sync client and server side shots. ================ */ Vector CBaseEntity::FireBulletsPlayer ( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker, int shared_rand ) { static int tracerCount; TraceResult tr; Vector vecRight = gpGlobals->v_right; Vector vecUp = gpGlobals->v_up; float x, y, z; if ( pevAttacker == NULL ) pevAttacker = pev; // the default attacker is ourselves ClearMultiDamage(); gMultiDamage.type = DMG_BULLET | DMG_NEVERGIB; for ( ULONG iShot = 1; iShot <= cShots; iShot++ ) { //Use player's random seed. // get circular gaussian spread x = UTIL_SharedRandomFloat( shared_rand + iShot, -0.5, 0.5 ) + UTIL_SharedRandomFloat( shared_rand + ( 1 + iShot ) , -0.5, 0.5 ); y = UTIL_SharedRandomFloat( shared_rand + ( 2 + iShot ), -0.5, 0.5 ) + UTIL_SharedRandomFloat( shared_rand + ( 3 + iShot ), -0.5, 0.5 ); z = x * x + y * y; Vector vecDir = vecDirShooting + x * vecSpread.x * vecRight + y * vecSpread.y * vecUp; Vector vecEnd; vecEnd = vecSrc + vecDir * flDistance; UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev)/*pentIgnore*/, &tr); // do damage, paint decals if (tr.flFraction != 1.0) { CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); if ( iDamage ) { pEntity->TraceAttack(pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); DecalGunshot( &tr, iBulletType ); } else switch(iBulletType) { default: case BULLET_PLAYER_9MM: pEntity->TraceAttack(pevAttacker, gSkillData.plrDmg9MM, vecDir, &tr, DMG_BULLET); break; case BULLET_PLAYER_MP5: pEntity->TraceAttack(pevAttacker, gSkillData.plrDmgMP5, vecDir, &tr, DMG_BULLET); break; case BULLET_PLAYER_BUCKSHOT: // make distance based! pEntity->TraceAttack(pevAttacker, gSkillData.plrDmgBuckshot, vecDir, &tr, DMG_BULLET); break; case BULLET_PLAYER_357: pEntity->TraceAttack(pevAttacker, gSkillData.plrDmg357, vecDir, &tr, DMG_BULLET); break; case BULLET_NONE: // FIX pEntity->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); // only decal glass if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0) { UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) ); } break; } } // make bullet trails UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 ); } ApplyMultiDamage(pev, pevAttacker); return Vector( x * vecSpread.x, y * vecSpread.y, 0.0 ); }
//========================================================= //========================================================= void CHornet::Spawn(void) { Precache(); pev->movetype = MOVETYPE_FLY; pev->solid = SOLID_BBOX; pev->takedamage = DAMAGE_YES; pev->flags |= FL_MONSTER; pev->health = 1;// weak! if (g_pGameRules->IsMultiplayer()) { // hornets don't live as long in multiplayer m_flStopAttack = gpGlobals->time + 3.5; } else { m_flStopAttack = gpGlobals->time + 5.0; } m_flFieldOfView = 0.9; // +- 25 degrees if (RANDOM_LONG(1, 5) <= 2) { m_iHornetType = HORNET_TYPE_RED; m_flFlySpeed = HORNET_RED_SPEED; } else { m_iHornetType = HORNET_TYPE_ORANGE; m_flFlySpeed = HORNET_ORANGE_SPEED; } SET_MODEL(ENT(pev), "models/hornet.mdl"); UTIL_SetSize(pev, Vector(-4, -4, -4), Vector(4, 4, 4)); SetTouch(&CHornet::DieTouch); SetThink(&CHornet::StartTrack); edict_t *pSoundEnt = pev->owner; if (!pSoundEnt) pSoundEnt = edict(); switch (RANDOM_LONG(0, 2)) { case 0: EMIT_SOUND(pSoundEnt, CHAN_WEAPON, "agrunt/ag_fire1.wav", 1, ATTN_NORM); break; case 1: EMIT_SOUND(pSoundEnt, CHAN_WEAPON, "agrunt/ag_fire2.wav", 1, ATTN_NORM); break; case 2: EMIT_SOUND(pSoundEnt, CHAN_WEAPON, "agrunt/ag_fire3.wav", 1, ATTN_NORM); break; } if (!FNullEnt(pev->owner) && (pev->owner->v.flags & FL_CLIENT)) { pev->dmg = gSkillData.plrDmgHornet; } else { // no real owner, or owner isn't a client. pev->dmg = gSkillData.monDmgHornet; } SetNextThink(0.1); ResetSequenceInfo(); }
//========================================================= // MakeMonster- this is the code that drops the monster //========================================================= CBaseMonster* CMonsterMaker::MakeMonster( void ) { edict_t *pent; entvars_t *pevCreate; // ALERT(at_console,"Making Monster NOW\n"); pent = CREATE_NAMED_ENTITY( m_iszMonsterClassname ); if ( FNullEnt( pent ) ) { ALERT ( at_debug, "NULL Ent in MonsterMaker!\n" ); return NULL; } pevCreate = VARS( pent ); pevCreate->origin = pev->origin; pevCreate->angles = pev->angles; SetBits( pevCreate->spawnflags, SF_MONSTER_FALL_TO_GROUND ); if (pev->spawnflags & SF_MONSTERMAKER_NO_WPN_DROP) SetBits( pevCreate->spawnflags, SF_MONSTER_NO_WPN_DROP); // Children hit monsterclip brushes if ( pev->spawnflags & SF_MONSTERMAKER_MONSTERCLIP ) SetBits( pevCreate->spawnflags, SF_MONSTER_HITMONSTERCLIP ); DispatchSpawn( ENT( pevCreate ) ); pevCreate->owner = edict(); //LRC - custom monster behaviour CBaseEntity *pEntity = CBaseEntity::Instance( pevCreate ); CBaseMonster *pMonst = NULL; if (pEntity && (pMonst = pEntity->MyMonsterPointer()) != NULL) { pMonst->m_iClass = this->m_iClass; pMonst->m_iPlayerReact = this->m_iPlayerReact; } if ( !FStringNull( pev->netname ) ) { // if I have a netname (overloaded), give the child monster that name as a targetname pevCreate->targetname = pev->netname; } m_cLiveChildren++;// count this monster m_cNumMonsters--; if ( m_cNumMonsters == 0 ) { // Disable this forever. Don't kill it because it still gets death notices SetThink( NULL ); SetUse( NULL ); } else if (m_fActive) { SetNextThink( m_flDelay ); SetThink(&CMonsterMaker:: MakerThink ); } return pMonst; }
//// HOST_SAY // String comes in as // say blah blah blah // or as // blah blah blah // void Host_Say( edict_t *pEntity, int teamonly ) { CBasePlayer *client; int j; char *p; char text[128]; char szTemp[256]; const char *cpSay = "say"; const char *cpSayTeam = "say_team"; const char *pcmd = CMD_ARGV(0); // We can get a raw string now, without the "say " prepended if ( CMD_ARGC() == 0 ) return; entvars_t *pev = &pEntity->v; CBasePlayer* player = GetClassPtr((CBasePlayer *)pev); //Not yet. if ( player->m_flNextChatTime > gpGlobals->time ) return; if ( !stricmp( pcmd, cpSay) || !stricmp( pcmd, cpSayTeam ) ) { if ( CMD_ARGC() >= 2 ) { p = (char *)CMD_ARGS(); } else { // say with a blank message, nothing to do return; } } else // Raw text, need to prepend argv[0] { if ( CMD_ARGC() >= 2 ) { sprintf( szTemp, "%s %s", ( char * )pcmd, (char *)CMD_ARGS() ); } else { // Just a one word command, use the first word...sigh sprintf( szTemp, "%s", ( char * )pcmd ); } p = szTemp; } // remove quotes if present if (*p == '"') { p++; p[strlen(p)-1] = 0; } char *pc = NULL; // make sure the text has content for ( pc = p; pc != NULL && *pc != 0; pc++ ) { if ( !isspace( *pc ) ) { pc = NULL; // we've found an alphanumeric character, so text is valid break; } } if ( pc != NULL ) return; // no character found, so say nothing // turn on color set 2 (color on, no sound) if ( teamonly ) sprintf( text, "%c(TEAM) %s: ", 2, STRING( pEntity->v.netname ) ); else sprintf( text, "%c%s: ", 2, STRING( pEntity->v.netname ) ); j = sizeof(text) - 2 - strlen(text); // -2 for /n and null terminator if ( (int)strlen(p) > j ) p[j] = 0; strcat( text, p ); strcat( text, "\n" ); player->m_flNextChatTime = gpGlobals->time + CHAT_INTERVAL; // loop through all players // Start with the first player. // This may return the world in single player if the client types something between levels or during spawn // so check it, or it will infinite loop client = NULL; while ( ((client = (CBasePlayer*)UTIL_FindEntityByClassname( client, "player" )) != NULL) && (!FNullEnt(client->edict())) ) { if ( !client->pev ) continue; if ( client->edict() == pEntity ) continue; if ( !(client->IsNetClient()) ) // Not a client ? (should never be true) continue; // can the receiver hear the sender? or has he muted him? if ( g_VoiceGameMgr.PlayerHasBlockedPlayer( client, player ) ) continue; if ( teamonly && g_pGameRules->PlayerRelationship(client, CBaseEntity::Instance(pEntity)) != GR_TEAMMATE ) continue; MESSAGE_BEGIN( MSG_ONE, gmsgSayText, NULL, client->pev ); WRITE_BYTE( ENTINDEX(pEntity) ); WRITE_STRING( text ); MESSAGE_END(); } // print to the sending client MESSAGE_BEGIN( MSG_ONE, gmsgSayText, NULL, &pEntity->v ); WRITE_BYTE( ENTINDEX(pEntity) ); WRITE_STRING( text ); MESSAGE_END(); // echo to server console g_engfuncs.pfnServerPrint( text ); char * temp; if ( teamonly ) temp = "say_team"; else temp = "say"; // team match? if ( g_teamplay ) { UTIL_LogPrintf( "\"%s<%i><%s><%s>\" %s \"%s\"\n", STRING( pEntity->v.netname ), GETPLAYERUSERID( pEntity ), GETPLAYERAUTHID( pEntity ), g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( pEntity ), "model" ), temp, p ); } else { UTIL_LogPrintf( "\"%s<%i><%s><%i>\" %s \"%s\"\n", STRING( pEntity->v.netname ), GETPLAYERUSERID( pEntity ), GETPLAYERAUTHID( pEntity ), GETPLAYERUSERID( pEntity ), temp, p ); } }
void NetworkMsg::Execute (void *p) { if (m_message == NETMSG_UNDEFINED) return; // no message or not for bot, return // some needed variables static uint8_t r, g, b; static uint8_t enabled; static int damageArmor, damageTaken, damageBits; static int killerIndex, victimIndex, playerIndex; static int index, numPlayers; static int state, id, clip; static Vector damageOrigin; static WeaponProperty weaponProp; // now starts of netmessage execution switch (m_message) { case NETMSG_VGUI: // this message is sent when a VGUI menu is displayed. if (m_state == 0) { switch (PTR_TO_INT (p)) { case GMENU_TEAM: m_bot->m_startAction = CMENU_TEAM; break; case GMENU_TERRORIST: case GMENU_COUNTER: m_bot->m_startAction = CMENU_CLASS; break; } } break; case NETMSG_SHOWMENU: // this message is sent when a text menu is displayed. if (m_state < 3) // ignore first 3 fields of message break; if (strcmp(PTR_TO_STR(p), "#Team_Select") == 0) // team select menu? m_bot->m_startAction = CMENU_TEAM; else if (strcmp (PTR_TO_STR (p), "#Team_Select_Spect") == 0) // team select menu? m_bot->m_startAction = CMENU_TEAM; else if (strcmp (PTR_TO_STR (p), "#IG_Team_Select_Spect") == 0) // team select menu? m_bot->m_startAction = CMENU_TEAM; else if (strcmp (PTR_TO_STR (p), "#IG_Team_Select") == 0) // team select menu? m_bot->m_startAction = CMENU_TEAM; else if (strcmp (PTR_TO_STR (p), "#IG_VIP_Team_Select") == 0) // team select menu? m_bot->m_startAction = CMENU_TEAM; else if (strcmp (PTR_TO_STR (p), "#IG_VIP_Team_Select_Spect") == 0) // team select menu? m_bot->m_startAction = CMENU_TEAM; else if (strcmp(PTR_TO_STR(p), "#Terrorist_Select") == 0) // T model select? m_bot->m_startAction = CMENU_CLASS; else if (strcmp (PTR_TO_STR (p), "#CT_Select") == 0) // CT model select menu? m_bot->m_startAction = CMENU_CLASS; break; case NETMSG_WLIST: // this message is sent when a client joins the game. All of the weapons are sent with the weapon ID and information about what ammo is used. switch (m_state) { case 0: strcpy (weaponProp.className, PTR_TO_STR (p)); break; case 1: weaponProp.ammo1 = PTR_TO_INT (p); // ammo index 1 break; case 2: weaponProp.ammo1Max = PTR_TO_INT(p); // max ammo 1 break; case 5: weaponProp.slotID = PTR_TO_INT (p); // slot for this weapon break; case 6: weaponProp.position = PTR_TO_INT (p); // position in slot break; case 7: weaponProp.id = PTR_TO_INT (p); // weapon ID break; case 8: weaponProp.flags = PTR_TO_INT (p); // flags for weapon (WTF???) g_weaponDefs[weaponProp.id] = weaponProp; // store away this weapon with it's ammo information... break; } break; case NETMSG_CURWEAPON: // this message is sent when a weapon is selected (either by the bot chosing a weapon or by the server auto assigning the bot a weapon). In CS it's also called when Ammo is increased/decreased switch (m_state) { case 0: state = PTR_TO_INT (p); // state of the current weapon (WTF???) break; case 1: id = PTR_TO_INT (p); // weapon ID of current weapon break; case 2: clip = PTR_TO_INT (p); // ammo currently in the clip for this weapon if (id <= 31) { if (state != 0) m_bot->m_currentWeapon = id; // ammo amount decreased ? must have fired a bullet... if (id == m_bot->m_currentWeapon && m_bot->m_ammoInClip[id] > clip) { // time fired with in burst firing time ? if (m_bot->m_timeLastFired + 1.0f > engine->GetTime ()) m_bot->m_burstShotsFired++; m_bot->m_timeLastFired = engine->GetTime (); // remember the last bullet time } m_bot->m_ammoInClip[id] = clip; } break; } break; case NETMSG_AMMOX: // this message is sent whenever ammo amounts are adjusted (up or down). NOTE: Logging reveals that CS uses it very unreliable! switch (m_state) { case 0: index = PTR_TO_INT (p); // ammo index (for type of ammo) break; case 1: m_bot->m_ammo[index] = PTR_TO_INT (p); // store it away break; } break; case NETMSG_AMMOPICK: // this message is sent when the bot picks up some ammo (AmmoX messages are also sent so this message is probably // not really necessary except it allows the HUD to draw pictures of ammo that have been picked up. The bots // don't really need pictures since they don't have any eyes anyway. switch (m_state) { case 0: index = PTR_TO_INT (p); break; case 1: m_bot->m_ammo[index] = PTR_TO_INT (p); break; } break; case NETMSG_DAMAGE: // this message gets sent when the bots are getting damaged. switch (m_state) { case 0: damageArmor = PTR_TO_INT (p); break; case 1: damageTaken = PTR_TO_INT (p); break; case 2: damageBits = PTR_TO_INT (p); if (damageArmor > 0 || damageTaken > 0) m_bot->TakeDamage (m_bot->pev->dmg_inflictor, damageTaken, damageArmor, damageBits); break; } break; case NETMSG_MONEY: // this message gets sent when the bots money amount changes if (m_state == 0) m_bot->m_moneyAmount = PTR_TO_INT (p); // amount of money break; case NETMSG_STATUSICON: switch (m_state) { case 0: enabled = PTR_TO_BYTE(p); break; case 1: if (strcmp(PTR_TO_STR(p), "defuser") == 0) m_bot->m_hasDefuser = (enabled != 0); else if (strcmp(PTR_TO_STR(p), "buyzone") == 0) { m_bot->m_inBuyZone = (enabled != 0); m_bot->EquipInBuyzone(0); } else if (strcmp(PTR_TO_STR(p), "vipsafety") == 0) m_bot->m_inVIPZone = (enabled != 0); else if (strcmp(PTR_TO_STR(p), "c4") == 0) m_bot->m_inBombZone = (enabled == 2); break; } break; case NETMSG_DEATH: // this message sends on death switch (m_state) { case 0: killerIndex = PTR_TO_INT (p); break; case 1: victimIndex = PTR_TO_INT (p); break; case 2: // SyPB Pro P.45 - Death Msg improve edict_t *victim = INDEXENT(victimIndex); if (FNullEnt(victim) || !IsValidPlayer(victim)) break; Bot *victimer = g_botManager->GetBot(victim); if (victimer != null) { victimer->GetCurrentTask()->data = -1; victimer->DeleteSearchNodes(); } /* // SyPB Pro P.40 - Death Msg improve if (killerIndex != victimIndex) { edict_t *killer = INDEXENT(killerIndex); edict_t *victim = INDEXENT(victimIndex); if (FNullEnt(killer) || FNullEnt(victim) || !IsValidPlayer(victim)) break; Bot *victimer = g_botManager->GetBot(victim); if (victimer != null) { victimer->GetCurrentTask()->data = -1; victimer->DeleteSearchNodes(); } if (!IsZombieEntity(victim) && IsAlive(victim)) break; for (int i = 0; i < engine->GetMaxClients(); i++) { Bot *bot = g_botManager->GetBot(i); if (bot == null || !IsAlive(bot->GetEntity())) continue; if (IsZombieEntity(bot->GetEntity())) continue; if (GetGameMod() == 0 && killer != bot->GetEntity() && bot->EntityIsVisible(GetEntityOrigin(victim)) && GetTeam(killer) == GetTeam(bot->GetEntity()) && GetTeam(killer) != GetTeam(victim)) { if (killer == g_hostEntity) bot->HandleChatterMessage("#Bot_NiceShotCommander"); else bot->HandleChatterMessage("#Bot_NiceShotPall"); break; } if (GetTeam(bot->GetEntity()) == GetTeam(victim) && IsVisible(GetEntityOrigin(killer), bot->GetEntity()) && FNullEnt(bot->m_enemy) && GetTeam(killer) != GetTeam(victim)) { // SyPB Pro P.30 - AMXX API if (bot->m_blockCheckEnemyTime > engine->GetTime()) continue; bot->m_actualReactionTime = 0.0f; bot->m_seeEnemyTime = engine->GetTime(); bot->m_enemy = killer; bot->SetLastEnemy(killer); } } Bot *bot = g_botManager->GetBot(killer); if (bot != null) bot->m_lastVictim = victim; else if (GetGameMod () == 0) { if (victimer != null) { if (GetTeam(killer) == GetTeam(victim)) victimer->m_voteKickIndex = killerIndex; victimer->m_notKilled = false; } } } */ break; } break; case NETMSG_SCREENFADE: // this message gets sent when the Screen fades (Flashbang) switch (m_state) { case 3: r = PTR_TO_BYTE (p); break; case 4: g = PTR_TO_BYTE (p); break; case 5: b = PTR_TO_BYTE (p); break; case 6: m_bot->TakeBlinded (Vector (r, g, b), PTR_TO_BYTE (p)); break; } break; case NETMSG_HLTV: // round restart in steam cs switch (m_state) { case 0: numPlayers = PTR_TO_INT(p); break; case 1: if (numPlayers == 0 && PTR_TO_INT(p) == 0) RoundInit(); break; } break; case NETMSG_RESETHUD: #if 0 if (m_bot != null) m_bot->NewRound (); #endif break; case NETMSG_TEXTMSG: if (m_state == 1) { if (FStrEq (PTR_TO_STR (p), "#CTs_Win") || FStrEq (PTR_TO_STR (p), "#Bomb_Defused") || FStrEq (PTR_TO_STR (p), "#Terrorists_Win") || FStrEq (PTR_TO_STR (p), "#Round_Draw") || FStrEq (PTR_TO_STR (p), "#All_Hostages_Rescued") || FStrEq (PTR_TO_STR (p), "#Target_Saved") || FStrEq (PTR_TO_STR (p), "#Hostages_Not_Rescued") || FStrEq (PTR_TO_STR (p), "#Terrorists_Not_Escaped") || FStrEq (PTR_TO_STR (p), "#VIP_Not_Escaped") || FStrEq (PTR_TO_STR (p), "#Escaping_Terrorists_Neutralized") || FStrEq (PTR_TO_STR (p), "#VIP_Assassinated") || FStrEq (PTR_TO_STR (p), "#VIP_Escaped") || FStrEq (PTR_TO_STR (p), "#Terrorists_Escaped") || FStrEq (PTR_TO_STR (p), "#CTs_PreventEscape") || FStrEq (PTR_TO_STR (p), "#Target_Bombed") || FStrEq (PTR_TO_STR (p), "#Game_Commencing") || FStrEq (PTR_TO_STR (p), "#Game_will_restart_in")) { g_roundEnded = true; if (FStrEq (PTR_TO_STR (p), "#Game_Commencing")) g_isCommencing = true; // SyPB Pro P.29 - msg setting if (GetGameMod() == 0) { if (FStrEq(PTR_TO_STR(p), "#CTs_Win")) g_botManager->SetLastWinner(TEAM_COUNTER); // update last winner for economics if (FStrEq(PTR_TO_STR(p), "#Terrorists_Win")) g_botManager->SetLastWinner(TEAM_TERRORIST); // update last winner for economics } g_waypoint->SetBombPosition (true); } else if (!g_bombPlanted && FStrEq (PTR_TO_STR (p), "#Bomb_Planted")) { g_bombPlanted = true; g_bombSayString = true; g_timeBombPlanted = engine->GetTime (); for (int i = 0; i < engine->GetMaxClients (); i++) { Bot *bot = g_botManager->GetBot (i); if (bot != null && IsAlive (bot->GetEntity ())) { bot->DeleteSearchNodes (); bot->ResetTasks (); if (engine->RandomInt (0, 100) < 85 && GetTeam (bot->GetEntity ()) == TEAM_COUNTER) bot->ChatterMessage (Chatter_WhereIsTheBomb); } } g_waypoint->SetBombPosition (); } else if (m_bot != null && FStrEq (PTR_TO_STR (p), "#Switch_To_BurstFire")) m_bot->m_weaponBurstMode = BURST_ENABLED; else if (m_bot != null && FStrEq (PTR_TO_STR (p), "#Switch_To_SemiAuto")) m_bot->m_weaponBurstMode = BURST_DISABLED; } break; //case NETMSG_SCOREINFO: // SyPB Pro P.45 - TESTTEST /* switch (m_state) { case 0: playerIndex = PTR_TO_INT(p); break; case 4: // SyPB Pro P.29 - msg set team if (playerIndex >= 0 && playerIndex <= engine->GetMaxClients()) GetTeam(ENT(playerIndex)); } */ // break; case NETMSG_BARTIME: if (m_state == 0) { // SyPB Pro P.34 - Base Change if (GetGameMod() == 0) { if (PTR_TO_INT(p) > 0) m_bot->m_hasProgressBar = true; // the progress bar on a hud else if (PTR_TO_INT(p) == 0) m_bot->m_hasProgressBar = false; // no progress bar or disappeared } else m_bot->m_hasProgressBar = false; } break; default: AddLogEntry (true, LOG_FATAL, "Network message handler error. Call to unrecognized message id (%d).\n", m_message); } m_state++; // and finally update network message state }
void CLeech::SwimThink(void) { TraceResult tr; float flLeftSide; float flRightSide; float targetSpeed; float targetYaw = 0; CBaseEntity *pTarget; if(FNullEnt(FIND_CLIENT_IN_PVS(edict()))) { pev->nextthink = gpGlobals->time + RANDOM_FLOAT(1, 1.5); pev->velocity = g_vecZero; return; } else pev->nextthink = gpGlobals->time + 0.1; targetSpeed = LEECH_SWIM_SPEED; if(m_waterTime < gpGlobals->time) RecalculateWaterlevel(); if(m_stateTime < gpGlobals->time) SwitchLeechState(); ClearConditions(bits_COND_CAN_MELEE_ATTACK1); switch(m_MonsterState) { case MONSTERSTATE_COMBAT: pTarget = m_hEnemy; if(!pTarget) SwitchLeechState(); else { // Chase the enemy's eyes m_height = pTarget->pev->origin.z + pTarget->pev->view_ofs.z - 5; // Clip to viable water area if(m_height < m_bottom) m_height = m_bottom; else if(m_height > m_top) m_height = m_top; Vector location = pTarget->pev->origin - pev->origin; location.z += (pTarget->pev->view_ofs.z); if(location.Length() < 40) SetConditions(bits_COND_CAN_MELEE_ATTACK1); // Turn towards target ent targetYaw = UTIL_VecToYaw(location); targetYaw = UTIL_AngleDiff(targetYaw, UTIL_AngleMod(pev->angles.y)); if(targetYaw < (-LEECH_TURN_RATE * 0.75)) targetYaw = (-LEECH_TURN_RATE * 0.75); else if(targetYaw > (LEECH_TURN_RATE * 0.75)) targetYaw = (LEECH_TURN_RATE * 0.75); else targetSpeed *= 2; } break; default: if(m_zTime < gpGlobals->time) { float newHeight = RANDOM_FLOAT(m_bottom, m_top); m_height = 0.5 * m_height + 0.5 * newHeight; m_zTime = gpGlobals->time + RANDOM_FLOAT(1, 4); } if(RANDOM_LONG(0, 100) < 10) targetYaw = RANDOM_LONG(-30, 30); pTarget = NULL; // oldorigin test if((pev->origin - pev->oldorigin).Length() < 1) { // If leech didn't move, there must be something blocking it, so try to turn m_sideTime = 0; } break; } m_obstacle = ObstacleDistance(pTarget); pev->oldorigin = pev->origin; if(m_obstacle < 0.1) m_obstacle = 0.1; // is the way ahead clear? if(m_obstacle == 1.0) { // if the leech is turning, stop the trend. if(m_flTurning != 0) { m_flTurning = 0; } m_fPathBlocked = FALSE; pev->speed = UTIL_Approach(targetSpeed, pev->speed, LEECH_SWIM_ACCEL * LEECH_FRAMETIME); pev->velocity = gpGlobals->v_forward * pev->speed; } else { m_obstacle = 1.0 / m_obstacle; // IF we get this far in the function, the leader's path is blocked! m_fPathBlocked = TRUE; if(m_flTurning == 0) // something in the way and leech is not already turning to avoid { Vector vecTest; // measure clearance on left and right to pick the best dir to turn vecTest = pev->origin + (gpGlobals->v_right * LEECH_SIZEX) + (gpGlobals->v_forward * LEECH_CHECK_DIST); UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr); flRightSide = tr.flFraction; vecTest = pev->origin + (gpGlobals->v_right * -LEECH_SIZEX) + (gpGlobals->v_forward * LEECH_CHECK_DIST); UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr); flLeftSide = tr.flFraction; // turn left, right or random depending on clearance ratio float delta = (flRightSide - flLeftSide); if(delta > 0.1 || (delta > -0.1 && RANDOM_LONG(0, 100) < 50)) m_flTurning = -LEECH_TURN_RATE; else m_flTurning = LEECH_TURN_RATE; } pev->speed = UTIL_Approach(-(LEECH_SWIM_SPEED * 0.5), pev->speed, LEECH_SWIM_DECEL * LEECH_FRAMETIME * m_obstacle); pev->velocity = gpGlobals->v_forward * pev->speed; } pev->ideal_yaw = m_flTurning + targetYaw; UpdateMotion(); }
// Add bot -> ARG1(team), ARG2(skill), ARG3(model), ARG4(name) int cGame::CreateBot(edict_t * pPlayer, const char *arg1, const char *arg2, const char *arg3, const char *arg4) { edict_t *BotEnt; cBot *pBot; char c_skin[BOT_SKIN_LEN + 1]; char c_name[BOT_NAME_LEN + 1]; // clear memset(c_skin, 0, sizeof(c_skin)); memset(c_name, 0, sizeof(c_name)); int skill; int i, j, length; if ((arg4 != NULL) && (*arg4 != 0)) { strncpy(c_name, arg4, BOT_NAME_LEN - 1); c_name[BOT_NAME_LEN] = 0; // make sure c_name is null terminated } else { if (NamesAvailable()) SelectName(c_name); else strcpy(c_name, "RealBot"); } skill = -2; // -2, not valid if ((arg2 != NULL) && (*arg2 != 0)) skill = atoi(arg2); // set to given skill // when not valid (-2), it has default skill if ((skill < -1) || (skill > 10)) skill = iDefaultBotSkill; // When skill is -1, random, we set it by boundries given if (skill == -1) skill = RANDOM_LONG(iRandomMinSkill, iRandomMaxSkill); // length of name length = strlen(c_name); // remove any illegal characters from name... for (i = 0; i < length; i++) { if ((c_name[i] <= ' ') || (c_name[i] > '~') || (c_name[i] == '"')) { for (j = i; j < length; j++) // shuffle chars left (and null) c_name[j] = c_name[j + 1]; length--; } } BotEnt = (*g_engfuncs.pfnCreateFakeClient) (c_name); if (FNullEnt(BotEnt)) { REALBOT_PRINT(NULL, "cGame::CreateBot", "Cannot create bot, server is full"); return GAME_MSG_FAIL_SERVERFULL; // failed } else { char ptr[128]; // allocate space for message from ClientConnect char *infobuffer; int clientIndex; int index; index = 0; while ((bots[index].bIsUsed) && (index < 32)) index++; if (index == 32) { return GAME_MSG_FAILURE; } // create the player entity by calling MOD's player function // (from LINK_ENTITY_TO_CLASS for player object) // FIX: Free data for bot, so we can fill in new if (BotEnt->pvPrivateData != NULL) FREE_PRIVATE(BotEnt); BotEnt->pvPrivateData = NULL; BotEnt->v.frags = 0; // END OF FIX: --- score resetted CALL_GAME_ENTITY(PLID, "player", VARS(BotEnt)); infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer) (BotEnt); clientIndex = ENTINDEX(BotEnt); (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "model", ""); (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "rate", "3500.000000"); (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "cl_updaterate", "20"); (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "cl_lw", "1"); (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "cl_lc", "1"); (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "tracker", "0"); (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "cl_dlmax", "128"); if (RANDOM_LONG(0, 100) < 50) { (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "lefthand", "1"); } else { (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "lefthand", "0"); } (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "friends", "0"); (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "dm", "0"); (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "ah", "1"); (*g_engfuncs.pfnSetClientKeyValue) (clientIndex, infobuffer, "_vgui_menus", "0"); MDLL_ClientConnect(BotEnt, c_name, "127.0.0.1", ptr); // Pieter van Dijk - use instead of DispatchSpawn() - Hip Hip Hurray! MDLL_ClientPutInServer(BotEnt); BotEnt->v.flags |= FL_THIRDPARTYBOT; // initialize all the variables for this bot... // Retrieve Pointer pBot = &bots[index]; // Set variables pBot->iIndex = index; pBot->bIsUsed = true; pBot->respawn_state = RESPAWN_IDLE; pBot->fCreateTime = gpGlobals->time; pBot->fKickTime = 0.0; pBot->name[0] = 0; // name not set by server yet pBot->bot_money = 0; strcpy(pBot->skin, c_skin); pBot->pEdict = BotEnt; pBot->bStarted = false; // hasn't joined game yet // CS Message IDLE.. pBot->start_action = MSG_CS_IDLE; pBot->SpawnInit(); pBot->bInitialize = false; // don't need to initialize yet BotEnt->v.idealpitch = BotEnt->v.v_angle.x; BotEnt->v.ideal_yaw = BotEnt->v.v_angle.y; BotEnt->v.pitch_speed = BOT_PITCH_SPEED; BotEnt->v.yaw_speed = BOT_YAW_SPEED; pBot->bot_skill = skill; // Personality related pBot->ipHostage = 0; pBot->ipBombspot = 0; pBot->ipRandom = 0; pBot->ipTurnSpeed = 20; pBot->ipReplyToRadio = 0; pBot->ipCreateRadio = 0; pBot->ipHelpTeammate = 0; pBot->ipWalkWithKnife = 0; pBot->ipDroppedBomb = 0; pBot->ipCampRate = 0; pBot->ipChatRate = 0; pBot->ipFearRate = 0; pBot->ipHearRate = 0; pBot->played_rounds = 0; // Buy-personality related pBot->ipFavoPriWeapon = -1; pBot->ipFavoSecWeapon = -1; pBot->ipBuyFlashBang = 0; pBot->ipBuyGrenade = 0; pBot->ipBuySmokeGren = 0; pBot->ipBuyDefuseKit = 0; pBot->ipSaveForWeapon = 0; pBot->ipBuyArmour = 0; // here we set team if ((arg1 != NULL) && (arg1[0] != 0)) { pBot->iTeam = atoi(arg1); // and class if ((arg3 != NULL) && (arg3[0] != 0)) { pBot->bot_class = atoi(arg3); } } // Parsing name into bot identity INI_PARSE_BOTS(c_name, pBot); // return success return GAME_MSG_SUCCESS; } } // CreateBot()
/* ============ TakeDamage The damage is coming from inflictor, but get mad at attacker This should be the only function that ever reduces health. bitsDamageType indicates the type of damage sustained, ie: DMG_SHOCK Time-based damage: only occurs while the monster is within the trigger_hurt. When a monster is poisoned via an arrow etc it takes all the poison damage at once. GLOBALS ASSUMED SET: g_iSkillLevel ============ */ int CBaseMonster :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) { float flTake; Vector vecDir; if (!pev->takedamage) return 0; if ( !IsAlive() ) { return DeadTakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); } if ( pev->deadflag == DEAD_NO ) { // no pain sound during death animation. PainSound();// "Ouch!" } //!!!LATER - make armor consideration here! flTake = flDamage; // set damage type sustained m_bitsDamageType |= bitsDamageType; // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). vecDir = Vector( 0, 0, 0 ); if (!FNullEnt( pevInflictor )) { CBaseEntity *pInflictor = CBaseEntity :: Instance( pevInflictor ); if (pInflictor) { vecDir = ( pInflictor->Center() - Vector ( 0, 0, 10 ) - Center() ).Normalize(); vecDir = g_vecAttackDir = vecDir.Normalize(); } } // add to the damage total for clients, which will be sent as a single // message at the end of the frame // todo: remove after combining shotgun blasts? if ( IsPlayer() ) { if ( pevInflictor ) pev->dmg_inflictor = ENT(pevInflictor); pev->dmg_take += flTake; // check for godmode or invincibility if ( pev->flags & FL_GODMODE ) { return 0; } } // HL: if this is a player, move him around! // NS: Don't move players if ( ( !FNullEnt( pevInflictor ) ) && (pev->movetype == MOVETYPE_WALK) && (!pevAttacker || pevAttacker->solid != SOLID_TRIGGER) && !IsPlayer()) { pev->velocity = pev->velocity + vecDir * -DamageForce( flDamage ); } // do the damage pev->health -= flTake; // HACKHACK Don't kill monsters in a script. Let them break their scripts first if ( m_MonsterState == MONSTERSTATE_SCRIPT ) { SetConditions( bits_COND_LIGHT_DAMAGE ); return 0; } if ( (int)(pev->health) <= 0 ) { g_pevLastInflictor = pevInflictor; // Removed gibbing, as death animations weren't playing with gibs off // if ( bitsDamageType & DMG_ALWAYSGIB ) // { // Killed( pevAttacker, GIB_ALWAYS ); // } // else if ( bitsDamageType & DMG_NEVERGIB ) // { Killed( pevAttacker, GIB_NEVER ); // } // else // { // Killed( pevAttacker, GIB_NORMAL ); // } // Trigger log message if needed // AvHPlayer* theDeadPlayer = dynamic_cast<AvHPlayer*>(this); // AvHPlayer* theAttackingPlayer = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(ENT(pevAttacker))); // const char* inWeaponName = STRING(pevInflictor->classname); // if(theDeadPlayer && theAttackingPlayer && inWeaponName) // { // theDeadPlayer->LogPlayerKilledPlayer(theAttackingPlayer, inWeaponName); // } g_pevLastInflictor = NULL; return 0; } // react to the damage (get mad) if ( (pev->flags & FL_MONSTER) && !FNullEnt(pevAttacker) ) { if ( pevAttacker->flags & (FL_MONSTER | FL_CLIENT) ) {// only if the attack was a monster or client! // enemy's last known position is somewhere down the vector that the attack came from. if (pevInflictor) { if (m_hEnemy == NULL || pevInflictor == m_hEnemy->pev || !HasConditions(bits_COND_SEE_ENEMY)) { m_vecEnemyLKP = pevInflictor->origin; } } else { m_vecEnemyLKP = pev->origin + ( g_vecAttackDir * 64 ); } MakeIdealYaw( m_vecEnemyLKP ); // add pain to the conditions // !!!HACKHACK - fudged for now. Do we want to have a virtual function to determine what is light and // heavy damage per monster class? if ( flDamage > 0 ) { SetConditions(bits_COND_LIGHT_DAMAGE); } if ( flDamage >= 20 ) { SetConditions(bits_COND_HEAVY_DAMAGE); } } } return 1; }