Index: src/engine/e_if_mods.h =================================================================== --- src/engine/e_if_mods.h (Revision 2013) +++ src/engine/e_if_mods.h (Arbeitskopie) @@ -164,5 +164,6 @@ */ void mods_message(int msg, int client_id); +int mods_slot_used(int id); #endif Index: src/engine/e_network_server.c =================================================================== --- src/engine/e_network_server.c (Revision 2013) +++ src/engine/e_network_server.c (Arbeitskopie) @@ -1,6 +1,7 @@ #include #include "e_network.h" #include "e_network_internal.h" +#include "e_if_mods.h" typedef struct { @@ -384,7 +385,7 @@ { for(i = 0; i < s->max_clients; i++) { - if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE) + if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE && !mods_slot_used(i)) { found = 1; conn_feed(&s->slots[i].conn, &s->recv.data, &addr); Index: src/engine/server/es_server.c =================================================================== --- src/engine/server/es_server.c (Revision 2013) +++ src/engine/server/es_server.c (Arbeitskopie) @@ -883,11 +883,14 @@ /* count the players */ int player_count = 0; + int dummy_count = 0; int i; for(i = 0; i < MAX_CLIENTS; i++) { if(clients[i].state != SRVCLIENT_STATE_EMPTY) player_count++; + else if(mods_slot_used(i)) + dummy_count++; } packer_reset(&p); @@ -923,9 +926,10 @@ str_format(buf, sizeof(buf), "%d", browseinfo_progression); packer_add_string(&p, buf, 4); - str_format(buf, sizeof(buf), "%d", player_count); packer_add_string(&p, buf, 3); /* num players */ - str_format(buf, sizeof(buf), "%d", netserver_max_clients(net)); packer_add_string(&p, buf, 3); /* max players */ + str_format(buf, sizeof(buf), "%d", player_count); packer_add_string(&p, buf, 3); /* num players */ + str_format(buf, sizeof(buf), "%d", netserver_max_clients(net)-dummy_count); packer_add_string(&p, buf, 3); /* max players */ + for(i = 0; i < MAX_CLIENTS; i++) { if(clients[i].state != SRVCLIENT_STATE_EMPTY) Index: src/game/collision.cpp =================================================================== --- src/game/collision.cpp (Revision 2013) +++ src/game/collision.cpp (Arbeitskopie) @@ -50,7 +50,10 @@ if(tiles[ny*width+nx].index > 128) return 0; - return tiles[ny*width+nx].index; + if(tiles[ny*width+nx].index == COLFLAG_SOLID || tiles[ny*width+nx].index == (COLFLAG_SOLID|COLFLAG_NOHOOK) || tiles[ny*width+nx].index == COLFLAG_DEATH) + return tiles[ny*width+nx].index; + else + return 0; } int col_is_solid(int x, int y) @@ -58,6 +61,15 @@ return col_get(x,y)&COLFLAG_SOLID; } +int col_get_index(int x, int y) +{ + int nx = x/32; + int ny = y/32; + if(y<0 || nx < 0 || nx >= width || ny >= height) + return 0; + + return tiles[ny*width+nx].index; +} // TODO: rewrite this smarter! int col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out_collision, vec2 *out_before_collision) Index: src/game/collision.hpp =================================================================== --- src/game/collision.hpp (Revision 2013) +++ src/game/collision.hpp (Arbeitskopie) @@ -17,5 +17,6 @@ int col_width(); int col_height(); int col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out_collision, vec2 *out_before_collision); +int col_get_index(int x, int y); #endif Index: src/game/server/entities/character.cpp =================================================================== --- src/game/server/entities/character.cpp (Revision 2013) +++ src/game/server/entities/character.cpp (Arbeitskopie) @@ -52,8 +52,16 @@ player_state = PLAYERSTATE_UNKNOWN; emote_stop = -1; last_action = -1; - active_weapon = WEAPON_GUN; - last_weapon = WEAPON_HAMMER; + if(!game.controller->is_ibot()) + { + active_weapon = WEAPON_GUN; + last_weapon = WEAPON_HAMMER; + } + else + { + active_weapon = WEAPON_RIFLE; + last_weapon = WEAPON_RIFLE; + } queued_weapon = -1; //clear(); @@ -96,7 +104,8 @@ if(active_weapon < 0 || active_weapon >= NUM_WEAPONS) active_weapon = 0; - game.create_sound(pos, SOUND_WEAPON_SWITCH); + if(!player->dummy) + game.create_sound(pos, SOUND_WEAPON_SWITCH); } bool CHARACTER::is_grounded() @@ -258,7 +267,11 @@ do_weaponswitch(); - vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); + vec2 direction; + if(player->dummy) + direction = normalize(fire_dir); + else + direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); bool fullauto = false; if(active_weapon == WEAPON_GRENADE || active_weapon == WEAPON_SHOTGUN || active_weapon == WEAPON_RIFLE) @@ -269,6 +282,7 @@ bool will_fire = false; if(count_input(latest_previnput.fire, latest_input.fire).presses) will_fire = true; if(fullauto && (latest_input.fire&1) && weapons[active_weapon].ammo) will_fire = true; + if(player->dummy && fire) will_fire = true; if(!will_fire) return; @@ -432,6 +446,9 @@ attack_tick = server_tick(); if(!reload_timer) reload_timer = data->weapons.id[active_weapon].firedelay * server_tickspeed() / 1000; + + if(player->dummy && fire) + fire = false; } int CHARACTER::handle_weapons() @@ -529,6 +546,9 @@ void CHARACTER::tick() { + if(player->dummy) + dummy_tick(); + if(player->force_balanced) { char buf[128]; @@ -655,6 +675,12 @@ void CHARACTER::die(int killer, int weapon) { + if(player->partner != -1 && game.controller->is_corp()) + { + if(killer != game.players[player->partner]->client_id && killer != player->client_id && game.players[killer]->dummy) + game.players[game.players[killer]->partner]->score++; + } + /*if (dead || team == -1) return;*/ int mode_special = game.controller->on_character_death(this, game.players[killer], weapon); @@ -696,11 +722,38 @@ bool CHARACTER::take_damage(vec2 force, int dmg, int from, int weapon) { + if(!game.players[from]->dummy && player->partner == -1 && player->dummy && game.players[from]->partner == -1 && weapon == WEAPON_HAMMER && game.controller->is_corp()) + { + CHARACTER *chr = game.players[from]->get_character(); + + player->partner = game.players[from]->client_id; + game.players[from]->partner = player->client_id; + die(player->client_id, WEAPON_WORLD); + chr->die(game.players[from]->client_id, WEAPON_WORLD); + game.players[from]->score++; + player->info_suicide = 0; + + char buf[128]; + str_format(buf, sizeof(buf), "%s is now your partner!", player->name); + game.send_broadcast(buf, game.players[from]->client_id); + + return false; + } + core.vel += force; + if(game.controller->is_corp() && (from == player->partner || player->partner == -1)) + return false; + if(game.controller->is_friendly_fire(player->client_id, from) && !config.sv_teamdamage) return false; + if(game.controller->is_ibot()) + { + die(from, weapon); + return false; + } + // player only inflicts half damage on self if(from == player->client_id) dmg = max(1, dmg/2); @@ -836,3 +889,332 @@ character->player_state = player_state; } + +void CHARACTER::dummy_tick() +{ + if(player->partner != -1 && game.controller->is_corp()) + { + //give aim + vec2 aim; + if(player->follow) + { + CHARACTER_CORE *p = game.world.core.characters[player->partner]; + CHARACTER *chr = game.players[player->partner]->get_character(); + if(chr) + aim = p->pos; + } + else if(player->move) + { + aim = pos; + for(int i = 0; i < MAX_CLIENTS; i++) + { + CHARACTER_CORE *p = game.world.core.characters[i]; + if(!p || p == game.world.core.characters[player->client_id] || game.players[i]->client_id == player->partner || game.players[i]->partner == -1) + continue; + aim = p->pos; + } + } + else + aim = pos; + + do_dummy_stuff(aim, 75, config.sv_bot_power); + + //if(distance(pos, aim) >= 75) + //{ + // //run direction + // if(aim.x > pos.x) + // input.direction = 1; + // else + // input.direction = -1; + + // //jump if you fall + // int count = 0; + // for(int i = 1; i < 16; i++) + // { + // if(col_get_index(pos.x, pos.y+i*64) == TILE_AIR) + // count++; + // } + // if(count >= 15) + // input.jump = -1; + // else + // input.jump = 0; + // count = 0; + + // //jump + // if(oldpos == core.pos) + // input.jump = -1; + // oldpos = core.pos; + + // //hook + // if(server_tick()%40 == 0) + // { + // vec2 hook_pos; + // vec2 a = aim - pos; + // int b; + // if(a.x > 0) + // b=3*64; + // else + // b=3*(-64); + + // for(int i = 1; i < 7; i++) + // { + // if(col_get_index(pos.x+b, pos.y-i*64) == TILE_SOLID) + // { + // hook_pos = vec2(pos.x+b, pos.y-i*64); + // count++; + // break; + // } + // else if(col_get_index(pos.x+b, pos.y-i*64) != TILE_AIR) + // return; + // } + // if(count > 0 && active_weapon != WEAPON_NINJA) + // { + // core.hook_pos = hook_pos; + // input.hook = 1; + // core.triggered_events |= COREEVENT_HOOK_ATTACH_GROUND; + // core.hook_state = HOOK_GRABBED; + // } + // count = 0; + // } + // if(server_tick()%25 == 0) + // input.hook = 0; + //} + //else + // input.direction = 0; + // + ////fire + //for(int i = 0; i < MAX_CLIENTS; i++) + //{ + // CHARACTER_CORE *p = game.world.core.characters[i]; + // if(!p || p == game.world.core.characters[player->client_id] || game.players[i]->client_id == player->partner || game.players[i]->partner == -1) + // continue; + + // + // if(distance(pos, p->pos) <= 800 && weapons[active_weapon].ammo != 0) + // { + // vec2 dir = p->pos - pos; + // input.target_x = dir.x; + // input.target_y = dir.y; + // fire_dir = dir; + + // weapons[WEAPON_GUN].ammo = 10; + // weapons[WEAPON_GRENADE].ammo = 10; + // weapons[WEAPON_RIFLE].ammo = 10; + // weapons[WEAPON_SHOTGUN].ammo = 10; + // + // if(active_weapon != WEAPON_NINJA) + // { + // if(distance(pos, p->pos) < 800 && distance(pos, p->pos) > 600) + // set_weapon(WEAPON_RIFLE); + // else if(distance(pos, p->pos) < 600 && distance(pos, p->pos) > 350) + // set_weapon(WEAPON_SHOTGUN); + // else if(distance(pos, p->pos) < 350 && distance(pos, p->pos) > 200) + // set_weapon(WEAPON_GUN); + // else if(distance(pos, p->pos) < 200 && distance(pos, p->pos) > 55) + // set_weapon(WEAPON_GRENADE); + // else if(distance(pos, p->pos) < 55 && distance(pos, p->pos) > 0) + // set_weapon(WEAPON_HAMMER); + // } + + // vec2 to = pos + fire_dir; + // CHARACTER *owner_char = game.get_player_char(player->client_id); + // if(!col_intersect_line(pos, to, 0x0, &to)) + // { + // fire = true; + // fire_weapon(); + // } + // } + //} + } + else if(game.controller->is_bot()) + { + if(!game.controller->is_ctf()) + { + vec2 near = vec2(999999,999999); + int near_id; + vec2 aim = pos; + for(int i = 0; i < MAX_CLIENTS; i++) + { + CHARACTER_CORE *p = game.world.core.characters[i]; + if(!p || p == game.world.core.characters[player->client_id]) + continue; + if(game.controller->is_teamplay() && game.players[i]->team == player->team) + continue; + + if(distance(pos,p->pos) < distance(pos,near)) + { + near = p->pos; + near_id = game.players[i]->client_id; + aim = near; + } + } + do_dummy_stuff(aim, 1, config.sv_bot_power); + } + } +} + +void CHARACTER::do_dummy_stuff(vec2 aim, int dist, int power) +{ + if(distance(pos, aim) >= dist) + { + //run direction + if(aim.x > pos.x) + input.direction = 1; + else + input.direction = -1; + + //jump if you fall + int count = 0; + for(int i = 1; i < 16; i++){ + if(col_get_index(pos.x, pos.y+i*64) == TILE_AIR) + count++;} + if(count >= 15) + input.jump = -1; + else + input.jump = 0; + count = 0; + + //jump + if(oldpos == core.pos) + input.jump = -1; + oldpos = core.pos; + + //hook + if(server_tick()%40 == 0) + { + vec2 hook_pos; + vec2 a = aim - pos; + int b; + if(a.x > 0) + b=3*64; + else + b=3*(-64); + + for(int i = 1; i < 7; i++) + { + if(col_get_index(pos.x+b, pos.y-i*64) == TILE_SOLID) + { + hook_pos = vec2(pos.x+b, pos.y-i*64); + count++; + break; + } + else if(col_get_index(pos.x+b, pos.y-i*64) != TILE_AIR) + return; + } + if(count > 0 && active_weapon != WEAPON_NINJA) + { + core.hook_pos = hook_pos; + input.hook = 1; + core.triggered_events |= COREEVENT_HOOK_ATTACH_GROUND; + core.hook_state = HOOK_GRABBED; + } + count = 0; + } + if(server_tick()%25 == 0) + input.hook = 0; + } + else + input.direction = 0; + + //fire + for(int i = 0; i < MAX_CLIENTS; i++) + { + CHARACTER_CORE *p = game.world.core.characters[i]; + if(!p || p == game.world.core.characters[player->client_id]) + continue; + if(game.controller->is_corp() && (game.players[i]->client_id == player->partner || game.players[i]->partner == -1)) + continue; + if(game.controller->is_bot() && game.controller->is_teamplay() && game.players[i]->team == player->team) + continue; + + + if(distance(pos, p->pos) <= 800 && weapons[active_weapon].ammo != 0) + { + vec2 dir = p->pos - pos; + input.target_x = dir.x; + input.target_y = dir.y; + fire_dir = dir; + + if(!game.controller->is_ibot()) + { + weapons[WEAPON_GUN].ammo = 10; + weapons[WEAPON_GRENADE].ammo = 10; + weapons[WEAPON_RIFLE].ammo = 10; + weapons[WEAPON_SHOTGUN].ammo = 10; + + if(active_weapon != WEAPON_NINJA) + { + if(power == 1) + { + //easy + if(distance(pos, p->pos) < 600 && distance(pos, p->pos) > 300) + set_weapon(WEAPON_GUN); + else if(distance(pos, p->pos) < 300 && distance(pos, p->pos) > 100) + set_weapon(WEAPON_SHOTGUN); + else if(distance(pos, p->pos) < 100 && distance(pos, p->pos) > 0) + set_weapon(WEAPON_HAMMER); + } + else if(power == 2) + { + //middle + if(distance(pos, p->pos) < 700 && distance(pos, p->pos) > 350) + set_weapon(WEAPON_GUN); + else if(distance(pos, p->pos) < 350 && distance(pos, p->pos) > 200) + set_weapon(WEAPON_SHOTGUN); + else if(distance(pos, p->pos) < 200 && distance(pos, p->pos) > 55) + set_weapon(WEAPON_GRENADE); + else if(distance(pos, p->pos) < 55 && distance(pos, p->pos) > 0) + set_weapon(WEAPON_HAMMER); + } + else if(power == 3) + { + //strong + if(distance(pos, p->pos) < 800 && distance(pos, p->pos) > 600) + set_weapon(WEAPON_RIFLE); + else if(distance(pos, p->pos) < 600 && distance(pos, p->pos) > 350) + set_weapon(WEAPON_SHOTGUN); + else if(distance(pos, p->pos) < 350 && distance(pos, p->pos) > 200) + set_weapon(WEAPON_GUN); + else if(distance(pos, p->pos) < 200 && distance(pos, p->pos) > 55) + set_weapon(WEAPON_GRENADE); + else if(distance(pos, p->pos) < 55 && distance(pos, p->pos) > 0) + set_weapon(WEAPON_HAMMER); + } + } + } + + vec2 to = pos + fire_dir; + if(!col_intersect_line(pos, to, 0x0, &to)) + { + if(game.controller->is_ibot() && distance(pos, p->pos) <= 500) + ftick++; + else if(game.controller->is_ibot()) + ftick = 0; + else if(!game.controller->is_ibot()) + { + fire = true; + fire_weapon(); + } + + if(game.controller->is_ibot()) + { + if(power == 1 && ftick >= 45) + { + fire = true; + fire_weapon(); + } + else if(power == 2 && ftick >= 30) + { + fire = true; + fire_weapon(); + } + else if(power == 3 && ftick >= 15) + { + fire = true; + fire_weapon(); + } + } + } + } + } +} \ No newline at end of file Index: src/game/server/entities/character.hpp =================================================================== --- src/game/server/entities/character.hpp (Revision 2013) +++ src/game/server/entities/character.hpp (Arbeitskopie) @@ -129,6 +129,13 @@ bool increase_health(int amount); bool increase_armor(int amount); + + vec2 oldpos; + bool fire; + vec2 fire_dir; + void dummy_tick(); + void do_dummy_stuff(vec2 aim, int dist, int power); + int ftick; }; #endif Index: src/game/server/entities/pickup.cpp =================================================================== --- src/game/server/entities/pickup.cpp (Revision 2013) +++ src/game/server/entities/pickup.cpp (Arbeitskopie) @@ -68,7 +68,7 @@ break; case POWERUP_WEAPON: - if(subtype >= 0 && subtype < NUM_WEAPONS) + if(subtype >= 0 && subtype < NUM_WEAPONS && !chr->player->dummy) { if(chr->weapons[subtype].ammo < data->weapons.id[subtype].maxammo || !chr->weapons[subtype].got) { Index: src/game/server/gamecontext.cpp =================================================================== --- src/game/server/gamecontext.cpp (Revision 2013) +++ src/game/server/gamecontext.cpp (Arbeitskopie) @@ -283,7 +283,7 @@ NETMSG_SV_VOTE_STATUS msg = {0}; for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i]) + if(players[i] && !players[i]->dummy) { msg.total++; if(players[i]->vote > 0) @@ -335,7 +335,7 @@ int total = 0, yes = 0, no = 0; for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i]) + if(players[i] && !players[i]->dummy) { total++; if(players[i]->vote > 0) Index: src/game/server/gamecontroller.cpp =================================================================== --- src/game/server/gamecontroller.cpp (Revision 2013) +++ src/game/server/gamecontroller.cpp (Arbeitskopie) @@ -33,6 +33,11 @@ num_spawn_points[0] = 0; num_spawn_points[1] = 0; num_spawn_points[2] = 0; + + dcount = 0; + corp = false; + bot = false; + ibot = false; } GAMECONTROLLER::~GAMECONTROLLER() @@ -99,9 +104,21 @@ } else { - evaluate_spawn_type(&eval, 0); - evaluate_spawn_type(&eval, 1); - evaluate_spawn_type(&eval, 2); + if(is_corp()) + { + if(player->partner == -1 && player->dummy) + evaluate_spawn_type(&eval, 1); + else if(player->partner == -1 && !player->dummy) + evaluate_spawn_type(&eval, 2); + else + evaluate_spawn_type(&eval, 0); + } + else + { + evaluate_spawn_type(&eval, 0); + evaluate_spawn_type(&eval, 1); + evaluate_spawn_type(&eval, 2); + } } *out_pos = eval.pos; @@ -145,7 +162,7 @@ subtype = WEAPON_NINJA; } - if(type != -1) + if(type != -1 && !is_ibot()) { PICKUP *pickup = new PICKUP(type, subtype); pickup->pos = pos; @@ -157,6 +174,24 @@ void GAMECONTROLLER::endround() { + if(is_corp()) + { + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(game.players[i]) + { + if(game.players[i]->dummy) + { + delete game.players[i]; + game.players[i] = 0; + } + else + game.players[i]->partner = -1; + } + } + dcount = 0; + } + if(warmup) // game can't end when we are running warmup return; @@ -309,16 +344,28 @@ int GAMECONTROLLER::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon) { // do scoreing - if(!killer) - return 0; - if(killer == victim->player) - victim->player->score--; // suicide + if(!killer->dummy || is_bot()) + { + if(!killer) + return 0; + if(killer == victim->player) + victim->player->score--; // suicide + else + { + if(is_teamplay() && victim->team == killer->team) + killer->score--; // teamkill + else + killer->score++; // normal kill + } + } else { - if(is_teamplay() && victim->team == killer->team) - killer->score--; // teamkill + if(!killer) + return 0; + if(killer == victim->player) + victim->player->info_suicide++; // suicide else - killer->score++; // normal kill + killer->info_kills++; // kills } return 0; } @@ -328,11 +375,26 @@ // default health chr->health = 10; - // give default weapons - chr->weapons[WEAPON_HAMMER].got = 1; - chr->weapons[WEAPON_HAMMER].ammo = -1; - chr->weapons[WEAPON_GUN].got = 1; - chr->weapons[WEAPON_GUN].ammo = 10; + if(!is_ibot()) + { + // give default weapons + chr->weapons[WEAPON_HAMMER].got = 1; + chr->weapons[WEAPON_HAMMER].ammo = -1; + chr->weapons[WEAPON_GUN].got = 1; + chr->weapons[WEAPON_GUN].ammo = 10; + + if(chr->player->dummy) + { + chr->weapons[WEAPON_SHOTGUN].got = 1; + chr->weapons[WEAPON_RIFLE].got = 1; + chr->weapons[WEAPON_GRENADE].got = 1; + } + } + else + { + chr->weapons[WEAPON_RIFLE].got = 1; + chr->weapons[WEAPON_RIFLE].ammo = -1; + } } void GAMECONTROLLER::do_warmup(int seconds) @@ -370,6 +432,16 @@ void GAMECONTROLLER::tick() { + //do info message every 4 minutes + if(server_tick()%(50*4*60) == 0 && is_corp()) + { + game.send_chat_target(-1, ""); + game.send_chat_target(-1, "Corp Bot Mod 0.x *dev*"); + game.send_chat_target(-1, "by Bobynator"); + game.send_chat_target(-1, "write /cmds to get all commands"); + game.send_chat_target(-1, ""); + } + // do warmup if(warmup) { @@ -471,12 +543,49 @@ server_setbrowseinfo(gametype, prog); } +void GAMECONTROLLER::make_corp(const char *new_gametype) +{ + corp = true; + gametype = new_gametype; +} +void GAMECONTROLLER::make_bot(const char *new_gametype) +{ + bot = true; + gametype = new_gametype; +} + +void GAMECONTROLLER::make_ibot(const char *new_gametype) +{ + ibot = true; + bot = true; + gametype = new_gametype; +} + +bool GAMECONTROLLER::is_corp() const +{ + return corp; +} + +bool GAMECONTROLLER::is_bot() const +{ + return bot; +} + +bool GAMECONTROLLER::is_ibot() const +{ + return ibot; +} bool GAMECONTROLLER::is_teamplay() const { return game_flags&GAMEFLAG_TEAMS; } +bool GAMECONTROLLER::is_ctf() const +{ + return game_flags&GAMEFLAG_FLAGS; +} + void GAMECONTROLLER::snap(int snapping_client) { NETOBJ_GAME *gameobj = (NETOBJ_GAME *)snap_new_item(NETOBJTYPE_GAME, 0, sizeof(NETOBJ_GAME)); @@ -630,6 +739,15 @@ topscore_count++; } } + + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(game.players[i]) + { + if(config.sv_scorelimit > 0 && topscore >= config.sv_scorelimit && is_corp()) + game.players[i]->my_bot_info(); + } + } // check score win condition if((config.sv_scorelimit > 0 && topscore >= config.sv_scorelimit) || Index: src/game/server/gamecontroller.hpp =================================================================== --- src/game/server/gamecontroller.hpp (Revision 2013) +++ src/game/server/gamecontroller.hpp (Arbeitskopie) @@ -131,6 +131,18 @@ int clampteam(int team); virtual void post_reset(); + + int dcount; + bool corp; + void make_corp(const char *gametype); + bool is_corp() const; + bool bot; + void make_bot(const char *gametype); + bool is_bot() const; + bool ibot; + void make_ibot(const char *gametype); + bool is_ibot() const; + bool is_ctf() const; }; #endif Index: src/game/server/gamemodes/mod.cpp =================================================================== --- src/game/server/gamemodes/mod.cpp (Revision 2013) +++ src/game/server/gamemodes/mod.cpp (Arbeitskopie) @@ -5,7 +5,7 @@ { // Exchange this to a string that identifies your game mode. // DM, TDM and CTF are reserved for teeworlds original modes. - gametype = "MOD"; + gametype = "mod"; //game_flags = GAMEFLAG_TEAMS; // GAMEFLAG_TEAMS makes it a two-team gamemode } Index: src/game/server/hooks.cpp =================================================================== --- src/game/server/hooks.cpp (Revision 2013) +++ src/game/server/hooks.cpp (Arbeitskopie) @@ -98,6 +98,30 @@ } } #endif + + if(game.controller->is_corp()) + { + int i = game.controller->dcount; + for(i = 0; i < 6; i++) + { + if(!game.players[i]) + { + game.controller->dcount++; + game.players[i] = new(i) PLAYER(i); + game.players[i]->dummy = true; + + char buf[512]; + const char *names[] = {"Boby", "Freddy", "Mandy", "Yannick", "Juri", "Zataru"}; + str_format(buf, sizeof(buf), "%s |bot",names[i]); + str_copy(game.players[i]->name, buf, MAX_NAME_LENGTH); + + const char *skins[] = {"cammostripes", "toptri", "twinbop", "redstripe", "bluestripe", "coala"}; + game.players[i]->dummy_skin = skins[i]; + + dbg_msg("game", "%s is entered", names[i]); + } + } + } } void mods_snap(int client_id) @@ -121,7 +145,11 @@ void mods_connected(int client_id) { - game.players[client_id] = new(client_id) PLAYER(client_id); + if(!game.players[client_id]) + game.players[client_id] = new(client_id) PLAYER(client_id); + else + game.players[client_id]->dummy_slot = true; + game.players[client_id]->dummy = false; //game.players[client_id].init(client_id); //game.players[client_id].client_id = client_id; @@ -144,6 +172,11 @@ { game.abort_vote_kick_on_disconnect(client_id); game.players[client_id]->on_disconnect(); + if(game.players[client_id]->dummy_slot) + { + game.players[client_id]->dummy_slot = false; + return; + } delete game.players[client_id]; game.players[client_id] = 0; @@ -195,7 +228,120 @@ p->last_chat = time_get(); - game.send_chat(client_id, team, msg->message); + char buf[512]; + if(!strcmp(msg->message, "/follow") && game.controller->is_corp()) + { + if(p->partner == -1){ + game.send_chat_target(client_id, "This command is only available if you got a Bot!"); + return;} + + game.players[p->partner]->follow = true; + game.players[p->partner]->move = false; + game.send_chat_target(client_id, "Your partner FOLLOWS you!"); + } + else if(!strcmp(msg->message, "/move") && game.controller->is_corp()) + { + if(p->partner == -1){ + game.send_chat_target(client_id, "This command is only available if you got a Bot!"); + return;} + + game.players[p->partner]->follow = false; + game.players[p->partner]->move = true; + game.send_chat_target(client_id, "Your partner MOVES around!"); + } + else if(!strcmp(msg->message, "/stay") && game.controller->is_corp()) + { + if(p->partner == -1){ + game.send_chat_target(client_id, "This command is only available if you got a Bot!"); + return;} + + game.players[p->partner]->follow = false; + game.players[p->partner]->move = false; + game.send_chat_target(client_id, "Your partner STAY!"); + } + else if(!strncmp(msg->message, "/set_name", 9) && game.controller->is_corp()) + { + if(p->partner == -1){ + game.send_chat_target(client_id, "This command is only available if you got a Bot!"); + return;} + + const char *name = msg->message; + name += 10; + + if(strlen(name) > 18){ + game.send_chat_target(client_id, "Name is to long!"); + return;} + + str_format(buf, sizeof(buf), "%s changed name to %s |bot", game.players[p->partner]->name, name); + game.send_chat_target(-1, buf); + str_format(buf, sizeof(buf), "%s |bot", name); + str_copy(game.players[p->partner]->name, buf, MAX_NAME_LENGTH); + } + else if((!strcmp(msg->message, "/info") || !strcmp(msg->message, "/about")) && game.controller->is_corp()) + { + game.send_chat_target(client_id, ""); + game.send_chat_target(client_id, "|____Corp_Bot_Mod____|"); + game.send_chat_target(client_id, ""); + game.send_chat_target(client_id, "Mod by Bobynator"); + game.send_chat_target(client_id, "Version: 0.x *dev*"); + game.send_chat_target(client_id, "write /cmds to get all chatcommands"); + } + else if((!strcmp(msg->message, "/cmds") || !strcmp(msg->message, "/cmdlist")) && game.controller->is_corp()) + { + game.send_chat_target(client_id, ""); + game.send_chat_target(client_id, "|______Commands______|"); + game.send_chat_target(client_id, ""); + game.send_chat_target(client_id, "/follow - your bot will follow you"); + game.send_chat_target(client_id, "/move - your bot will move around"); + game.send_chat_target(client_id, "/stay - your bot will stay"); + game.send_chat_target(client_id, "/set_name - change the name of your bot"); + game.send_chat_target(client_id, "/my_bot - get information about your bot"); + game.send_chat_target(client_id, "/home - back to bot selection"); + game.send_chat_target(client_id, "/info - get information about the mod"); + } + else if(!strcmp(msg->message, "/my_bot") && game.controller->is_corp()) + { + if(p->partner == -1){ + game.send_chat_target(client_id, "This command is only available if you got a Bot!"); + return;} + + game.send_chat_target(client_id, ""); + game.send_chat_target(client_id, "|_______My_Bot_______|"); + game.send_chat_target(client_id, ""); + str_format(buf, sizeof(buf), "name: %s", game.players[p->partner]->name); + game.send_chat_target(client_id, buf); + str_format(buf, sizeof(buf), "kills: %d", game.players[p->partner]->info_kills); + game.send_chat_target(client_id, buf); + str_format(buf, sizeof(buf), "selfkills: %d", game.players[p->partner]->info_suicide); + game.send_chat_target(client_id, buf); + if(game.players[p->partner]->follow) + game.send_chat_target(client_id, "state: follow"); + else if(game.players[p->partner]->move) + game.send_chat_target(client_id, "state: move"); + else + game.send_chat_target(client_id, "state: stay"); + } + else if(!strcmp(msg->message, "/home") && game.controller->is_corp()) + { + if(p->partner == -1){ + game.send_chat_target(client_id, "This command is only available if you got a Bot!"); + return;} + + p->dummy_reset(); + p->partner = -1; + CHARACTER *chr = p->get_character(); + if(chr) + chr->die(p->client_id, WEAPON_WORLD); + p->score = 0; + } + else if(!strncmp(msg->message, "/",1)) + { + game.send_chat_target(client_id, "Invalid command!"); + game.send_chat_target(client_id, "Use \"/cmdlist\" or \"/cmds\" to see all valid commands"); + return; + } + else + game.send_chat(client_id, team, msg->message); } else if(msgtype == NETMSGTYPE_CL_CALLVOTE) { @@ -499,6 +645,47 @@ dbg_msg("server", "forcing vote %s", console_arg_string(result, 0)); } +static void con_add_bot(void *result, void *user_data) +{ + int num = clamp(console_arg_int(result, 0), 0, (int)MAX_CLIENTS); + for(int i = 0; i < num; i++) + { + if(!game.players[i]) + { + game.players[i] = new(i) PLAYER(i); + game.players[i]->dummy = true; + str_copy(game.players[i]->name, "[Bot]", MAX_NAME_LENGTH); + game.players[i]->dummy_skin = "default"; + game.send_chat(-1, GAMECONTEXT::CHAT_ALL, "[Bot] entered the Game"); + } + else + { + num++; + if(num > MAX_CLIENTS) + num = MAX_CLIENTS; + } + } +} + +static void con_remove_bot(void *result, void *user_data) +{ + int num = clamp(console_arg_int(result, 0), 0, (int)MAX_CLIENTS); + for(int i = 0; i < num; i++) + { + if(game.players[i] && game.players[i]->dummy) + { + delete game.players[i]; + game.players[i] = 0; + game.send_chat(-1, GAMECONTEXT::CHAT_ALL, "[Bot] has left the Game"); + } + else + { + if(num < MAX_CLIENTS) + num++; + } + } +} + void mods_console_init() { MACRO_REGISTER_COMMAND("tune", "si", CFGFLAG_SERVER, con_tune_param, 0, ""); @@ -513,6 +700,10 @@ MACRO_REGISTER_COMMAND("addvote", "r", CFGFLAG_SERVER, con_addvote, 0, ""); MACRO_REGISTER_COMMAND("vote", "r", CFGFLAG_SERVER, con_vote, 0, ""); + + MACRO_REGISTER_COMMAND("add_bot", "i", CFGFLAG_SERVER, con_add_bot, 0, ""); + MACRO_REGISTER_COMMAND("remove_bot", "i", CFGFLAG_SERVER, con_remove_bot, 0, ""); + } void mods_init() @@ -537,6 +728,31 @@ game.controller = new GAMECONTROLLER_CTF; else if(strcmp(config.sv_gametype, "tdm") == 0) game.controller = new GAMECONTROLLER_TDM; + else if(strcmp(config.sv_gametype, "corp") == 0) + { + game.controller = new GAMECONTROLLER_DM; + game.controller->make_corp("CORP"); + } + else if(strcmp(config.sv_gametype, "botdm") == 0) + { + game.controller = new GAMECONTROLLER_DM; + game.controller->make_bot("botDM"); + } + else if(strcmp(config.sv_gametype, "bottdm") == 0) + { + game.controller = new GAMECONTROLLER_TDM; + game.controller->make_bot("botTDM"); + } + else if(strcmp(config.sv_gametype, "botidm") == 0) + { + game.controller = new GAMECONTROLLER_DM; + game.controller->make_ibot("botIDM"); + } + else if(strcmp(config.sv_gametype, "botitdm") == 0) + { + game.controller = new GAMECONTROLLER_TDM; + game.controller->make_ibot("botITDM"); + } else game.controller = new GAMECONTROLLER_DM; @@ -599,3 +815,7 @@ extern "C" const char *mods_net_version() { return GAME_NETVERSION; } extern "C" const char *mods_version() { return GAME_VERSION; } +extern "C" int mods_slot_used(int id) +{ +return (id >= 0 && id < MAX_CLIENTS && game.players[id]); +} Index: src/game/server/player.cpp =================================================================== --- src/game/server/player.cpp (Revision 2013) +++ src/game/server/player.cpp (Arbeitskopie) @@ -12,6 +12,15 @@ respawn_tick = server_tick(); character = 0; this->client_id = client_id; + dummy = false; + dummy_slot = false; + partner = -1; + follow = true; + move = false; + this->motd_sent = false; + this->motd_tick = server_tick() + server_tickspeed() * 3; + info_kills = 0; + info_suicide = 0; } PLAYER::~PLAYER() @@ -20,8 +29,35 @@ character = 0; } +void PLAYER::dummy_reset() +{ + if(game.controller->is_corp()) + { + CHARACTER *chr = game.players[partner]->get_character(); + if(chr) + chr->die(game.players[partner]->client_id, WEAPON_WORLD); + game.players[partner]->follow = true; + game.players[partner]->move = false; + game.players[partner]->info_suicide = 0; + game.players[partner]->info_kills = 0; + game.players[partner]->partner = -1; + partner = -1; + } +} + void PLAYER::tick() { + if(motd_tick <= server_tick() && !motd_sent) + { + char buf[128]; + if(game.controller->is_corp()) + str_format(buf, sizeof(buf), "Corp Bot Mod\nVersion: 0.x *dev*\nAbout: /info\nMod by Bobynator", server_clientname(client_id)); + else if(game.controller->is_bot()) + str_format(buf, sizeof(buf), "Train Bot Mod\nVersion: 0.x *dev*\nMod by Bobynator", server_clientname(client_id)); + game.send_broadcast(buf, client_id); + motd_sent = true; + } + server_setclientscore(client_id, score); // do latency stuff @@ -67,8 +103,12 @@ void PLAYER::snap(int snapping_client) { NETOBJ_CLIENT_INFO *client_info = (NETOBJ_CLIENT_INFO *)snap_new_item(NETOBJTYPE_CLIENT_INFO, client_id, sizeof(NETOBJ_CLIENT_INFO)); - str_to_ints(&client_info->name0, 6, server_clientname(client_id)); - str_to_ints(&client_info->skin0, 6, skin_name); + str_to_ints(&client_info->name0, 6, dummy?name:server_clientname(client_id)); + if(dummy) + str_to_ints(&client_info->skin0, 6, dummy_skin); + else + str_to_ints(&client_info->skin0, 6, skin_name); + client_info->use_custom_color = use_custom_color; client_info->color_body = color_body; client_info->color_feet = color_feet; @@ -88,6 +128,9 @@ void PLAYER::on_disconnect() { + if(partner != -1) + dummy_reset(); + kill_character(WEAPON_GAME); //game.controller->on_player_death(&game.players[client_id], 0, -1); @@ -160,6 +203,9 @@ dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), team); game.controller->on_player_info_change(game.players[client_id]); + + if(new_team == -1 && partner != -1) + dummy_reset(); } void PLAYER::try_respawn() @@ -181,3 +227,26 @@ game.create_playerspawn(spawnpos); } } + +void PLAYER::my_bot_info() +{ + if(!dummy && partner != -1) + { + char buf[512]; + game.send_chat_target(client_id, ""); + game.send_chat_target(client_id, "|_______My_Bot_______|"); + game.send_chat_target(client_id, ""); + str_format(buf, sizeof(buf), "name: %s", game.players[partner]->name); + game.send_chat_target(client_id, buf); + str_format(buf, sizeof(buf), "kills: %d", game.players[partner]->info_kills); + game.send_chat_target(client_id, buf); + str_format(buf, sizeof(buf), "selfkills: %d", game.players[partner]->info_suicide); + game.send_chat_target(client_id, buf); + if(game.players[partner]->follow) + game.send_chat_target(client_id, "state: follow"); + else if(game.players[partner]->move) + game.send_chat_target(client_id, "state: move"); + else + game.send_chat_target(client_id, "state: stay"); + } +} Index: src/game/server/player.hpp =================================================================== --- src/game/server/player.hpp (Revision 2013) +++ src/game/server/player.hpp (Arbeitskopie) @@ -70,6 +70,21 @@ void on_direct_input(NETOBJ_PLAYER_INPUT *new_input); void on_predicted_input(NETOBJ_PLAYER_INPUT *new_input); void on_disconnect(); + + bool dummy; + char name[MAX_NAME_LENGTH]; + bool dummy_slot; + + int partner; + bool follow; + bool move; + const char *dummy_skin; + bool motd_sent; + int motd_tick; + int info_kills; + int info_suicide; + void dummy_reset(); + void my_bot_info(); }; #endif Index: src/game/variables.hpp =================================================================== --- src/game/variables.hpp (Revision 2013) +++ src/game/variables.hpp (Arbeitskopie) @@ -65,6 +65,9 @@ MACRO_CONFIG_INT(sv_vote_scorelimit, 0, 0, 1, CFGFLAG_SERVER, "Allow voting to change score limit") MACRO_CONFIG_INT(sv_vote_timelimit, 0, 0, 1, CFGFLAG_SERVER, "Allow voting to change time limit") +/* corp */ +MACRO_CONFIG_INT(sv_bot_power, 2, 1, 3, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Power of the bots") + /* debug */ #ifdef CONF_DEBUG /* this one can crash the server if not used correctly */ MACRO_CONFIG_INT(dbg_dummies, 0, 0, 15, CFGFLAG_SERVER, "")