From 90d60e8f1469e880a99c385b218ff472fc200b10 Mon Sep 17 00:00:00 2001 From: Mattia Basaglia Date: Sun, 19 Mar 2017 14:10:06 +0000 Subject: [PATCH] Allow targeted bot spawns and remove bots on death --- qcsrc/server/bot/default/bot.qc | 30 +++++++-- qcsrc/server/bot/default/bot.qh | 4 ++ qcsrc/server/mutators/events.qh | 9 +++ .../mutators/mutator/gamemode_singleplayer.qc | 67 ++++++++++++++++--- 4 files changed, 95 insertions(+), 15 deletions(-) diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc index 67498d895..5514c7c5d 100644 --- a/qcsrc/server/bot/default/bot.qc +++ b/qcsrc/server/bot/default/bot.qc @@ -60,6 +60,12 @@ void bot_spawn_setup(entity bot) PutClientInServer(bot); } +void bot_remove(entity bot) +{ + currentbots = currentbots - 1; + dropclient(bot); +} + void bot_think(entity this) { if (this.bot_nextthink > time) @@ -484,12 +490,28 @@ void bot_removefromlargestteam() }); if(!bcount) return; // no bots to remove - currentbots = currentbots - 1; - dropclient(best); + bot_remove(best); } void bot_removenewest() { + entity best = NULL; + MUTATOR_CALLHOOK(Bot_SelectRemove, best); + best = M_ARGV(0, entity); + if ( best ) + { + if ( !IS_BOT_CLIENT(best) ) + { + LOG_WARN("Mutator selected a non-bot as a bot to remove\n"); + best = NULL; + } + else + { + bot_remove(best); + return; + } + } + if(teamplay) { bot_removefromlargestteam(); @@ -497,7 +519,6 @@ void bot_removenewest() } float besttime = 0; - entity best = NULL; int bcount = 0; FOREACH_CLIENT(it.isbot, @@ -520,8 +541,7 @@ void bot_removenewest() if(!bcount) return; // no bots to remove - currentbots = currentbots - 1; - dropclient(best); + bot_remove(best); } void autoskill(float factor) diff --git a/qcsrc/server/bot/default/bot.qh b/qcsrc/server/bot/default/bot.qh index f49b4f7bf..3c5e6231c 100644 --- a/qcsrc/server/bot/default/bot.qh +++ b/qcsrc/server/bot/default/bot.qh @@ -88,6 +88,10 @@ float bot_fixcount(); * Sets up an entity created with spawnclient() to be a bot */ void bot_spawn_setup(entity bot); +/** + * Removes the bot + */ +void bot_remove(entity bot); void bot_think(entity this); void bot_setnameandstuff(entity this); diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index e43f6a455..74e169cae 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -774,6 +774,15 @@ enum { /**/ MUTATOR_HOOKABLE(Bot_FixCount, EV_Bot_FixCount); +/* + * Called when a bot needs to be removed + */ +#define EV_Bot_SelectRemove(i, o) \ + /** bot to remove */ i(entity, MUTATOR_ARGV_0_entity) \ + /**/ o(entity, MUTATOR_ARGV_0_entity) \ + /**/ +MUTATOR_HOOKABLE(Bot_SelectRemove, EV_Bot_SelectRemove); + #define EV_ClientCommand_Spectate(i, o) \ /** player */ i(entity, MUTATOR_ARGV_0_entity) \ /**/ diff --git a/qcsrc/server/mutators/mutator/gamemode_singleplayer.qc b/qcsrc/server/mutators/mutator/gamemode_singleplayer.qc index 9e36f15c4..e75b5e316 100644 --- a/qcsrc/server/mutators/mutator/gamemode_singleplayer.qc +++ b/qcsrc/server/mutators/mutator/gamemode_singleplayer.qc @@ -7,6 +7,9 @@ const int SP_TEAM_ENEMY = NUM_TEAM_2; .string weapon_name; .int sp_spawn_team; .entity sp_spawn_spot; +.bool can_respawn; + +int sp_bot_number; /*QUAKED spawnfunc_tdm_team (0 .5 .8) (-16 -16 -24) (16 16 32) Team declaration for TDM gameplay, this allows you to decide what team names and control point models are used in your map. @@ -45,6 +48,7 @@ spawnfunc(info_player_singleplayer) void sp_spawn_bot(entity spawn_point) { + sp_bot_number++; entity bot = spawnclient(); if (bot) { @@ -60,7 +64,13 @@ void sp_spawn_bot(entity spawn_point) void sp_enemy_spawn_think(entity this) { sp_spawn_bot(this); - setthink(this, SUB_Remove); + setthink(this, func_null); +} + + +void sp_enemy_spawn_use(entity this, entity actor, entity trigger) +{ + sp_spawn_bot(this); } spawnfunc(info_player_singleplayer_enemy) @@ -70,7 +80,9 @@ spawnfunc(info_player_singleplayer_enemy) this.team = SP_TEAM_ENEMY; this.sp_spawn_team = SP_TEAM_ENEMY; spawnfunc_info_player_deathmatch(this); - setthink(this, sp_enemy_spawn_think); + this.use = sp_enemy_spawn_use; + if ( this.targetname == "" ) + setthink(this, sp_enemy_spawn_think); } void sp_delayed_init(entity this) @@ -78,11 +90,18 @@ void sp_delayed_init(entity this) // if no teams are found, spawn defaults if(find(NULL, classname, "sp_team") == NULL) { - LOG_TRACE("No \"sp_team\" entities found on this map, creating them anyway."); - sp_spawn_team_entity("Player", SP_TEAM_PLAYER); sp_spawn_team_entity("Enemy", SP_TEAM_ENEMY); } + + sp_bot_number = 0; +} + +void sp_remove_bot(entity bot) +{ + sp_bot_number--; + bot.sp_spawn_spot = NULL; + bot_clear(bot); } MUTATOR_HOOKFUNCTION(sp, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) @@ -150,8 +169,8 @@ MUTATOR_HOOKFUNCTION(sp, Spawn_Score) vector spawn_score = M_ARGV(2, vector); if ( IS_BOT_CLIENT(player) ) { - if ( spawn_spot.sp_spawn_team != SP_TEAM_ENEMY || - (player.sp_spawn_spot && player.sp_spawn_spot !=spawn_spot) ) + if ( spawn_spot.sp_spawn_team != SP_TEAM_ENEMY || + (player.sp_spawn_spot && player.sp_spawn_spot != spawn_spot) ) spawn_score.x = -1; } else if ( spawn_spot.sp_spawn_team != SP_TEAM_PLAYER ) @@ -169,9 +188,37 @@ MUTATOR_HOOKFUNCTION(sp, HideTeamNagger) MUTATOR_HOOKFUNCTION(sp, Bot_FixCount) { - int count = 0; - for ( entity e = NULL; (e = findfloat(e, sp_spawn_team, SP_TEAM_ENEMY)); ) - count++; - M_ARGV(2, int) = count; + M_ARGV(2, int) = sp_bot_number; return true; } + +MUTATOR_HOOKFUNCTION(sp, PlayerDies) +{ + entity target = M_ARGV(2, entity); + if ( IS_BOT_CLIENT(target) ) + { + if ( target.sp_spawn_spot && !target.sp_spawn_spot.can_respawn ) + { + sp_remove_bot(target); + } + } +} + +MUTATOR_HOOKFUNCTION(sp, Bot_SelectRemove) +{ + FOREACH_CLIENT(it.isbot, + { + if ( !it.sp_spawn_spot ) + { + M_ARGV(0, entity) = it; + return; + } + }); +} + +MUTATOR_HOOKFUNCTION(sp, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + if ( IS_BOT_CLIENT(player) && !player.sp_spawn_spot ) + bot_remove(player); +} -- 2.39.2