diff -Naur teeworlds-0.4.2-src/src/engine/e_if_server.h footspam/src/engine/e_if_server.h --- teeworlds-0.4.2-src/src/engine/e_if_server.h 2008-04-05 15:13:02.000000000 +0200 +++ footspam/src/engine/e_if_server.h 2008-08-07 17:53:33.000000000 +0200 @@ -106,7 +106,9 @@ */ void server_kick(int client_id, const char *reason); - +void server_ban(int client_id, int time, char *ban_message); +void server_ban_saved(int time, char *ban_message); +void server_save_ip(int client_id); /* Function: server_tick TODO diff -Naur teeworlds-0.4.2-src/src/engine/server/es_server.c footspam/src/engine/server/es_server.c --- teeworlds-0.4.2-src/src/engine/server/es_server.c 2008-04-05 15:13:02.000000000 +0200 +++ footspam/src/engine/server/es_server.c 2008-09-17 20:31:10.000000000 +0200 @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,10 @@ static int current_map_crc; static unsigned char *current_map_data = 0; static int current_map_size = 0; +int *banlist; +int *bantime; +int bans=0; +int saved_ip=0; void *snap_new_item(int type, int id, int size) { @@ -69,20 +74,20 @@ SRVCLIENT_STATE_CONNECTING, SRVCLIENT_STATE_READY, SRVCLIENT_STATE_INGAME, - + SRVCLIENT_SNAPRATE_INIT=0, SRVCLIENT_SNAPRATE_FULL, SRVCLIENT_SNAPRATE_RECOVER }; -typedef struct +typedef struct { int data[MAX_INPUT_SIZE]; int pred_tick; /* tick that the client predicted for the input */ int game_tick; /* the tick that was chosen for the input */ int64 timeleft; /* how much time in ms there were left before this should be applied */ } CLIENT_INPUT; - + /* */ typedef struct { @@ -90,14 +95,14 @@ int state; int latency; int snap_rate; - + int last_acked_snapshot; SNAPSTORAGE snapshots; - + CLIENT_INPUT latestinput; CLIENT_INPUT inputs[200]; /* TODO: handle input better */ int current_input; - + char name[MAX_NAME_LENGTH]; char clan[MAX_CLANNAME_LENGTH]; int score; @@ -107,6 +112,22 @@ static CLIENT clients[MAX_CLIENTS]; NETSERVER *net; +struct blacklist +{ + char *name; + struct blacklist *next; +}; +static struct blacklist *start, *end; + +static void str_lower(char *str) +{ + int i = 0; + for(;str[i];i++) + { + str[i] = tolower(str[i]); + } +} + static void snap_init_id() { int i; @@ -115,31 +136,31 @@ snap_ids[i].next = i+1; snap_ids[i].state = 0; } - + snap_ids[MAX_IDS-1].next = -1; snap_first_free_id = 0; snap_first_timed_id = -1; snap_last_timed_id = -1; snap_id_usage = 0; snap_id_inusage = 0; - + snap_id_inited = 1; } static void snap_remove_first_timeout() { int next_timed = snap_ids[snap_first_timed_id].next; - + /* add it to the free list */ snap_ids[snap_first_timed_id].next = snap_first_free_id; snap_ids[snap_first_timed_id].state = 0; snap_first_free_id = snap_first_timed_id; - + /* remove it from the timed list */ snap_first_timed_id = next_timed; if(snap_first_timed_id == -1) snap_last_timed_id = -1; - + snap_id_usage--; } @@ -148,12 +169,12 @@ int id; int64 now = time_get(); dbg_assert(snap_id_inited == 1, "requesting id too soon"); - - + + /* process timed ids */ while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout < now) snap_remove_first_timeout(); - + id = snap_first_free_id; dbg_assert(id != -1, "id error"); snap_first_free_id = snap_ids[snap_first_free_id].next; @@ -178,7 +199,7 @@ snap_ids[id].state = 2; snap_ids[id].timeout = time_get()+time_freq()*5; snap_ids[id].next = -1; - + if(snap_last_timed_id != -1) { snap_ids[snap_last_timed_id].next = id; @@ -222,10 +243,32 @@ void server_setclientname(int client_id, const char *name) { char nametry[MAX_NAME_LENGTH]; + char name_lower[MAX_NAME_LENGTH]; int i; + struct blacklist *act; + act = start; if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) return; - + if(!strcmp("", name)) + { + server_kick(client_id, "This name is not allowed here!"); + } + if(config.sv_name_blacklist) + { + strcpy(name_lower, name); + str_lower(name_lower); + while(1) + { + if(strstr(name_lower, (*act).name) != NULL) + { + server_ban(client_id, 30, "Forbidden name"); + return; + } + if(act == end) + break; + act = (*act).next; + } + } str_copy(nametry, name, MAX_NAME_LENGTH); if(server_try_setclientname(client_id, nametry)) { @@ -260,11 +303,80 @@ { if(client_id < 0 || client_id > MAX_CLIENTS) return; - + if(clients[client_id].state != SRVCLIENT_STATE_EMPTY) netserver_drop(net, client_id, reason); } +void server_save_ip(int client_id) +{ + if(clients[client_id].state != SRVCLIENT_STATE_EMPTY) + { + NETADDR4 addr; + netserver_client_addr(net, client_id, &addr); + saved_ip = (addr.ip[0]<<24)+(addr.ip[1]<<16)+(addr.ip[2]<<8)+addr.ip[3]; + } + else + saved_ip = 0; +} + +void server_ban_saved(int time, char *ban_message) +{ + int i; + NETADDR4 addr; + char reason[256]; + bans++; + if (bans==1) {banlist=malloc(sizeof(long));bantime=malloc(sizeof(long));} + else {banlist=realloc(banlist,sizeof(long)*bans);bantime=realloc(bantime,sizeof(long)*bans);} + banlist[bans-1]=saved_ip; + if(time > 0)bantime[bans-1]=server_tick()+server_tickspeed()*time *60; + else bantime[bans-1]=0; + + if(time > 0)str_format(reason, sizeof(reason), "Banned by console for %d minutes",time); + else str_format(reason, sizeof(reason), "Banned by console permanently."); + for(i = 0; i < MAX_CLIENTS; i++) + { + if(clients[i].state != SRVCLIENT_STATE_EMPTY) + { + netserver_client_addr(net, i, &addr); + if(saved_ip == (addr.ip[0]<<24)+(addr.ip[1]<<16)+(addr.ip[2]<<8)+addr.ip[3]) + { + if(strlen(ban_message) > 0) + server_kick(i, ban_message); + else + server_kick(i, reason); + } + } + } +} + +void server_ban(int client_id, int time, char *ban_message) { + NETADDR4 addr; + int clientip; + char reason[256]; + if (clients[client_id].authed != 1) + { + netserver_client_addr(net, client_id, &addr); + + clientip=(addr.ip[0]<<24)+(addr.ip[1]<<16)+(addr.ip[2]<<8)+addr.ip[3]; + dbg_msg("server", "the ip %d.%d.%d.%d is now banned",addr.ip[0],addr.ip[1],addr.ip[2],addr.ip[3]); + + bans++; + if (bans==1) {banlist=malloc(sizeof(long));bantime=malloc(sizeof(long));} + else {banlist=realloc(banlist,sizeof(long)*bans);bantime=realloc(bantime,sizeof(long)*bans);} + banlist[bans-1]=clientip; + if(time > 0)bantime[bans-1]=server_tick()+server_tickspeed()*time *60; + else bantime[bans-1]=0; + + if(time > 0)str_format(reason, sizeof(reason), "Banned by console for %d minutes",time); + else str_format(reason, sizeof(reason), "Banned by console permanently."); + if(strlen(ban_message) > 0) + server_kick(client_id, ban_message); + else + server_kick(client_id, reason); + } +} + int server_tick() { return current_tick; @@ -317,16 +429,16 @@ NETPACKET packet; if(!info) return -1; - + mem_zero(&packet, sizeof(NETPACKET)); - + packet.client_id = client_id; packet.data = info->data; packet.data_size = info->size; - if(info->flags&MSGFLAG_VITAL) + if(info->flags&MSGFLAG_VITAL) packet.flags = PACKETFLAG_VITAL; - + if(client_id == -1) { /* broadcast */ @@ -346,7 +458,7 @@ static void server_do_snap() { int i, k; - + { static PERFORMACE_INFO scope = {"presnap", 0}; perf_start(&scope); @@ -359,15 +471,15 @@ /* client must be ingame to recive snapshots */ if(clients[i].state != SRVCLIENT_STATE_INGAME) continue; - + /* this client is trying to recover, don't spam snapshots */ if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_RECOVER && (server_tick()%50) != 0) continue; - + /* this client is trying to recover, don't spam snapshots */ if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_INIT && (server_tick()%10) != 0) continue; - + { char data[MAX_SNAPSHOT_SIZE]; char deltadata[MAX_SNAPSHOT_SIZE]; @@ -400,14 +512,14 @@ /* remove old snapshos */ /* keep 1 seconds worth of snapshots */ snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED); - + /* save it the snapshot */ snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data, 0); - + /* find snapshot that we can preform delta against */ emptysnap.data_size = 0; emptysnap.num_items = 0; - + { deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot, 0); if(deltashot_size >= 0) @@ -419,7 +531,7 @@ clients[i].snap_rate = SRVCLIENT_SNAPRATE_RECOVER; } } - + for(k = 0; k < 200; k++) /* TODO: do this better */ { if(clients[i].inputs[k].game_tick == current_tick) @@ -429,7 +541,7 @@ break; } } - + /* create delta */ { static PERFORMACE_INFO scope = {"delta", 0}; @@ -438,7 +550,7 @@ perf_end(); } - + if(deltasize) { /* compress it */ @@ -449,17 +561,17 @@ int numpackets; int n, left; - { + { static PERFORMACE_INFO scope = {"compress", 0}; perf_start(&scope); - + { static PERFORMACE_INFO scope = {"int", 0}; perf_start(&scope); intsize = intpack_compress(deltadata, deltasize, intdata); perf_end(); } - + { static PERFORMACE_INFO scope = {"zero", 0}; perf_start(&scope); @@ -468,12 +580,12 @@ } perf_end(); } - + numpackets = (snapshot_size+max_size-1)/max_size; - - - + + + for(n = 0, left = snapshot_size; left; n++) { int chunk = left < max_size ? left : max_size; @@ -483,18 +595,18 @@ msg_pack_start_system(NETMSG_SNAPSINGLE, 0); else msg_pack_start_system(NETMSG_SNAP, 0); - + msg_pack_int(current_tick); msg_pack_int(current_tick-delta_tick); /* compressed with */ msg_pack_int(input_predtick); msg_pack_int((timeleft*1000)/time_freq()); - + if(numpackets != 1) { msg_pack_int(numpackets); msg_pack_int(n); } - + msg_pack_int(crc); msg_pack_int(chunk); msg_pack_raw(&compdata[n*max_size], chunk); @@ -512,7 +624,7 @@ msg_pack_end(); server_send_msg(i); } - + perf_end(); } } @@ -527,7 +639,7 @@ clients[cid].state = SRVCLIENT_STATE_CONNECTING; clients[cid].name[0] = 0; clients[cid].clan[0] = 0; - + /* reset input */ for(i = 0; i < 200; i++) { @@ -535,9 +647,9 @@ clients[cid].inputs[i].pred_tick = -1; } clients[cid].current_input = 0; - + mem_zero(&clients[cid].latestinput, sizeof(clients[cid].latestinput)); - + snapstorage_purge_all(&clients[cid].snapshots); clients[cid].last_acked_snapshot = -1; clients[cid].snap_rate = SRVCLIENT_SNAPRATE_INIT; @@ -584,16 +696,16 @@ { static volatile int reentry_guard = 0; int i; - + if(reentry_guard) return; reentry_guard++; - + for(i = 0; i < MAX_CLIENTS; i++) { if(clients[i].state != SRVCLIENT_STATE_EMPTY && clients[i].authed) server_send_rcon_line(i, line); } - + reentry_guard--; } @@ -602,6 +714,9 @@ int cid = packet->client_id; int sys; int msg = msg_unpack_start(packet->data, packet->data_size, &sys); + int i; + int clientip; + NETADDR4 addr; if(sys) { /* system message */ @@ -610,7 +725,7 @@ char version[64]; const char *password; str_copy(version, msg_unpack_string(), 64); - if(strcmp(version, mods_net_version()) != 0) + if(strcmp(version, "0.4 1bd7780b0f76307c") != 0) { /* OH FUCK! wrong version, drop him */ char reason[256]; @@ -618,18 +733,32 @@ netserver_drop(net, cid, reason); return; } - + str_copy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH); str_copy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH); password = msg_unpack_string(); - + if(config.password[0] != 0 && strcmp(config.password, password) != 0) { /* wrong password */ netserver_drop(net, cid, "wrong password"); return; } - + if(cid >= (config.sv_max_clients-config.sv_reserved_slots) && config.sv_reserved_slots_pass[0] != 0 && strcmp(config.sv_reserved_slots_pass, password) != 0) + { + /* wrong password */ + netserver_drop(net, cid, "Dropped due reserved slot"); + return; + } + + netserver_client_addr(net, cid, &addr); + clientip=(addr.ip[0]<<24)+(addr.ip[1]<<16)+(addr.ip[2]<<8)+addr.ip[3]; + for (i=0; i= current_map_size) { chunk_size = current_map_size-offset; @@ -646,7 +775,7 @@ chunk_size = 0; last = 1; } - + msg_pack_start_system(NETMSG_MAP_DATA, MSGFLAG_VITAL); msg_pack_int(last); msg_pack_int(current_map_size); @@ -654,7 +783,7 @@ msg_pack_raw(¤t_map_data[offset], chunk_size); msg_pack_end(); server_send_msg(cid); - + if(config.debug) dbg_msg("server", "sending chunk %d with size %d", chunk, chunk_size); } @@ -685,41 +814,41 @@ clients[cid].last_acked_snapshot = msg_unpack_int(); tick = msg_unpack_int(); size = msg_unpack_int(); - + /* check for errors */ if(msg_unpack_error() || size/4 > MAX_INPUT_SIZE) return; - + if(clients[cid].last_acked_snapshot > 0) clients[cid].snap_rate = SRVCLIENT_SNAPRATE_FULL; - + if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0, 0) >= 0) clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq()); input = &clients[cid].inputs[clients[cid].current_input]; input->timeleft = server_tick_start_time(tick)-time_get(); input->pred_tick = tick; - + if(tick <= server_tick()) tick = server_tick()+1; input->game_tick = tick; - + for(i = 0; i < size/4; i++) input->data[i] = msg_unpack_int(); - + mem_copy(clients[cid].latestinput.data, input->data, MAX_INPUT_SIZE*sizeof(int)); - + clients[cid].current_input++; clients[cid].current_input %= 200; - + /* call the mod with the fresh input data */ mods_client_direct_input(cid, clients[cid].latestinput.data); } else if(msg == NETMSG_RCON_CMD) { const char *cmd = msg_unpack_string(); - + if(msg_unpack_error() == 0 && clients[cid].authed) { dbg_msg("server", "cid=%d rcon='%s'", cid, cmd); @@ -731,7 +860,7 @@ const char *pw; msg_unpack_string(); /* login name, not used */ pw = msg_unpack_string(); - + if(msg_unpack_error() == 0) { if(config.sv_rcon_password[0] == 0) @@ -744,7 +873,7 @@ msg_pack_int(1); msg_pack_end(); server_send_msg(cid); - + clients[cid].authed = 1; server_send_rcon_line(cid, "Authentication successful. Remote console access granted."); dbg_msg("server", "cid=%d authed", cid); @@ -779,7 +908,7 @@ PACKER p; char buf[128]; - /* count the players */ + /* count the players */ int c = 0; int i; for(i = 0; i < MAX_CLIENTS; i++) @@ -787,7 +916,7 @@ if(clients[i].state != SRVCLIENT_STATE_EMPTY) c++; } - + packer_reset(&p); if(lan) packer_add_raw(&p, SERVERBROWSE_INFO_LAN, sizeof(SERVERBROWSE_INFO_LAN)); @@ -811,7 +940,7 @@ /* progression */ str_format(buf, sizeof(buf), "%d", browseinfo_progression); packer_add_string(&p, buf, 4); - + str_format(buf, sizeof(buf), "%d", c); 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 */ @@ -823,8 +952,8 @@ str_format(buf, sizeof(buf), "%d", clients[i].score); packer_add_string(&p, buf, 6); /* player score */ } } - - + + packet.client_id = -1; packet.address = *addr; packet.flags = PACKETFLAG_CONNLESS; @@ -841,7 +970,7 @@ NETPACKET packet; netserver_update(net); - + /* process packets */ while(netserver_recv(net, &packet)) { @@ -875,17 +1004,17 @@ df = datafile_load(buf); if(!df) return 0; - + /* reinit snapshot ids */ snap_timeout_ids(); - + /* get the crc of the map */ current_map_crc = datafile_crc(buf); dbg_msg("server", "%s crc is %08x", buf, current_map_crc); - + str_copy(current_map, mapname, sizeof(current_map)); map_set(df); - + /* load compelate map into memory for download */ { IOHANDLE file = io_open(buf, IOFLAG_READ); @@ -905,7 +1034,7 @@ net_init(); snap_init_id(); - + /* */ console_register_print_callback(server_send_rcon_line_authed); @@ -915,7 +1044,7 @@ dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); return -1; } - + /* start server */ if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, config.sv_port, &bindaddr) == 0) { @@ -926,18 +1055,49 @@ mem_zero(&bindaddr, sizeof(bindaddr)); bindaddr.port = config.sv_port; } - + if(config.sv_name_blacklist) + { + + FILE *name_file = fopen("name_blacklist", "r"); + if(name_file != NULL) + { + char tmp_names[MAX_NAME_LENGTH]; + if(fgets(tmp_names, MAX_NAME_LENGTH, name_file) != NULL) + { + tmp_names[strlen(tmp_names)-1] = 0; + start = malloc(sizeof(struct blacklist)); + end = start; + (*start).name = malloc(MAX_NAME_LENGTH); + strcpy((*start).name, tmp_names); + str_lower((*start).name); + } + while(fgets(tmp_names, MAX_NAME_LENGTH, name_file) != NULL) + { + tmp_names[strlen(tmp_names)-1] = 0; + (*end).next = malloc(sizeof(struct blacklist)); + end = (*end).next; + (*end).name = malloc(MAX_NAME_LENGTH); + strcpy((*end).name, tmp_names); + str_lower((*end).name); + } + fclose(name_file); + } + else + { + config.sv_name_blacklist = 0; + } + } net = netserver_open(bindaddr, config.sv_max_clients, 0); if(!net) { dbg_msg("server", "couldn't open socket. port might already be in use"); return -1; } - + netserver_set_callbacks(net, new_client_callback, del_client_callback, 0); - + dbg_msg("server", "server name is '%s'", config.sv_name); - + mods_init(); dbg_msg("server", "version %s", mods_net_version()); @@ -945,10 +1105,10 @@ { int64 reporttime = time_get(); int reportinterval = 3; - + lastheartbeat = 0; game_start_time = time_get(); - + if(config.debug) dbg_msg("server", "baseline memory usage %dk", mem_allocated()/1024); @@ -957,34 +1117,34 @@ static PERFORMACE_INFO rootscope = {"root", 0}; int64 t = time_get(); int new_ticks = 0; - + perf_start(&rootscope); - + /* load new map TODO: don't poll this */ if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload) { config.sv_map_reload = 0; - + /* load map */ if(server_load_map(config.sv_map)) { int c; - + /* new map loaded */ mods_shutdown(); - + for(c = 0; c < MAX_CLIENTS; c++) { if(clients[c].state == SRVCLIENT_STATE_EMPTY) continue; - + server_send_map(c); clients[c].state = SRVCLIENT_STATE_CONNECTING; clients[c].last_acked_snapshot = -1; clients[c].snap_rate = SRVCLIENT_SNAPRATE_RECOVER; snapstorage_purge_all(&clients[c].snapshots); } - + game_start_time = time_get(); current_tick = 0; mods_init(); @@ -995,19 +1155,19 @@ config_set_sv_map(&config, current_map); } } - + while(t > server_tick_start_time(current_tick+1)) { current_tick++; new_ticks++; - + /* apply new input */ { static PERFORMACE_INFO scope = {"input", 0}; int c, i; - + perf_start(&scope); - + for(c = 0; c < MAX_CLIENTS; c++) { if(clients[c].state == SRVCLIENT_STATE_EMPTY) @@ -1021,10 +1181,10 @@ } } } - + perf_end(); } - + /* progress game */ { static PERFORMACE_INFO scope = {"tick", 0}; @@ -1033,7 +1193,7 @@ perf_end(); } } - + /* snap game */ if(new_ticks) { @@ -1044,11 +1204,27 @@ server_do_snap(); perf_end(); } + if (bans>0) { + int i=0,j=0; + for (i=0; i server_tickspeed()) + { + dbg_msg("server","the IP=%d:%d:%d:%d is no longer banned",i,((banlist[i]>>24)&0xFF),((banlist[i]>>16)&0xFF),((banlist[i]>>8)&0xFF),(banlist[i]&0xFF)); + for (j=i; j 0)bantime[bans-1]=server_tick()+server_tickspeed()*console_arg_int(result, 1)*60; + else bantime[bans-1]=0; + + if(console_arg_int(result, 1) > 0)str_format(reason, sizeof(reason), "Banned by console for %d minutes",console_arg_int(result, 1)); + else str_format(reason, sizeof(reason), "Banned by console permanently."); + server_kick(console_arg_int(result, 0), reason); +} + +static void con_banlist(void *result, void *user_data) { + int i; + if (bans>0) { + for (i=0; i>24)&0xFF),((banlist[i]>>16)&0xFF),((banlist[i]>>8)&0xFF),(banlist[i]&0xFF),(bantime[i]-server_tick())/(server_tickspeed()*60)); + } + } else { + dbg_msg("server","there is no ban"); + } +} + +static void con_unban(void *result, void *user_data) { + int i=0; + int d=console_arg_int(result, 0); + + if (d<0 || d>=bans) dbg_msg("server","no such banid: %d, type banlist to see banids",d); + else + { + dbg_msg("server","the IP=%d:%d:%d:%d is no longer banned",d,((banlist[d]>>24)&0xFF),((banlist[d]>>16)&0xFF),((banlist[d]>>8)&0xFF),(banlist[d]&0xFF)); + for (i=d; i static TILE *tiles; +static int *dest[10]; static int width = 0; static int height = 0; +static int len[10]; int col_width() { return width; } int col_height() { return height; } @@ -19,6 +21,25 @@ width = layers_game_layer()->width; height = layers_game_layer()->height; tiles = (TILE *)map_get_data(layers_game_layer()->data); + mem_zero(&len, sizeof(len)); + for(int i=width*height-1;i>=0;i--) { + if(tiles[i].index==TILE_AIR) { + len[0]++; + } else if((tiles[i].index&1)!=0 && tiles[i].index>2 && tiles[i].index<21) { + len[tiles[i].index>>1]++; + } + } + for(int i=0;i<10;i++) { + dest[i] = new int[len[i]]; + len[i] = 0; + } + for(int i=width*height-1;i>=0;i--) { + if(tiles[i].index==TILE_AIR) { + dest[0][len[0]++] = i; + } else if((tiles[i].index&1)!=0 && tiles[i].index>2 && tiles[i].index<21) { + dest[tiles[i].index>>1][len[tiles[i].index>>1]++] = i; + } + } return 1; } @@ -30,12 +51,106 @@ if(nx < 0 || nx >= width || ny >= height) return 1; - if(y < 0) - return 0; // up == sky == free + if(y<0) { + return(tiles[nx].index==TILE_SOLID); + } return tiles[ny*width+nx].index == TILE_SOLID; } +int col_is_damage(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 == TILE_DAMAGE; +} + +int col_is_nohook(int x, int y) +{ + int nx = x/32; + int ny = y/32; + if(nx < 0 || nx >= width || ny >= height) + return 0; + + if(y<0) { + return(tiles[nx].index==TILE_NOHOOK); + } + + return tiles[ny*width+nx].index == TILE_NOHOOK; +} + +int col_is_teleport(int x, int y) +{ + int nx = x/32; + int ny = y/32; + if(y < 0 || nx < 0 || nx >= width || ny >= height) + return 0; + + int z = tiles[ny*width+nx].index-1; + if(z>2 && z<21 && (z&1)!=0) { + return(z>>1); + } + return(0); +} +int col_is_red(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 == TILE_RED; +} +int col_is_blue(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 == TILE_BLUE; +} +int col_is_goal_limit(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 == TILE_GOAL_LIMIT; +} +int col_is_fool(int x, int y, int team) +{ + int nx = x/32; + int ny = y/32; + if(y<0 || nx < 0 || nx >= width || ny >= height) + return 0; + + if(team==0)return tiles[ny*width+nx].index == TILE_FOOL_RED; + else if(team==1)return tiles[ny*width+nx].index == TILE_FOOL_BLUE; + else return 0; +} +vec2 teleport(int a) { + if(len[a]>0) { + int r = rand()%len[a]; + int x = (dest[a][r]%width)<<5; + int y = (dest[a][r]/width)<<5; + return(vec2((float)x+16.0,(float)y+16.0)); + } else { + return(vec2(0,0)); + } +} + +vec2 rand_point() { + int p = rand()%len[0]; + int x = (dest[0][p]%width)<<5; + int y = (dest[0][p]/width)<<5; + return(vec2((float)x+16.0,(float)y+16.0)); +} + // TODO: rewrite this smarter! bool col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out) { @@ -57,6 +172,21 @@ return false; } +bool col_intersect_nohook(vec2 pos0, vec2 pos1) +{ + float d = distance(pos0, pos1); + + for(float f = 0; f < d; f++) + { + float a = f/d; + vec2 pos = mix(pos0, pos1, a); + if(col_is_nohook((int)pos.x, (int)pos.y)) { + return true; + } + } + return false; +} + /* Simple collision rutines! */ diff -Naur teeworlds-0.4.2-src/src/game/g_collision.h footspam/src/game/g_collision.h --- teeworlds-0.4.2-src/src/game/g_collision.h 2008-04-05 15:13:02.000000000 +0200 +++ footspam/src/game/g_collision.h 2008-08-02 08:09:46.000000000 +0200 @@ -4,11 +4,20 @@ #include - int col_init(); int col_is_solid(int x, int y); +int col_is_damage(int x, int y); +int col_is_nohook(int x, int y); +int col_is_teleport(int x, int y); +int col_is_red(int x, int y); +int col_is_blue(int x, int y); +int col_is_fool(int x, int y,int team); +int col_is_goal_limit(int x, int y); int col_width(); +vec2 teleport(int z); int col_height(); +vec2 rand_point(); bool col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out); +bool col_intersect_nohook(vec2 pos0, vec2 pos1); #endif diff -Naur teeworlds-0.4.2-src/src/game/g_mapitems.h footspam/src/game/g_mapitems.h --- teeworlds-0.4.2-src/src/game/g_mapitems.h 2008-04-05 15:13:02.000000000 +0200 +++ footspam/src/game/g_mapitems.h 2008-08-02 08:09:46.000000000 +0200 @@ -43,8 +43,32 @@ TILE_AIR=0, TILE_SOLID, + TILE_DAMAGE, + TILE_D1, + TILE_T1, + TILE_D2, + TILE_T2, + TILE_D3, + TILE_T3, + TILE_D4, + TILE_T4, + TILE_D5, + TILE_T5, + TILE_D6, + TILE_T6, + TILE_D7, + TILE_T7, + TILE_D8, + TILE_T8, + TILE_D9, + TILE_T9, TILE_NOHOOK, - + TILE_RED, + TILE_BLUE, + TILE_FOOL_RED, + TILE_FOOL_BLUE, + TILE_GOAL_LIMIT, + TILEFLAG_VFLIP=1, TILEFLAG_HFLIP=2, TILEFLAG_OPAQUE=4, diff -Naur teeworlds-0.4.2-src/src/game/g_variables.h footspam/src/game/g_variables.h --- teeworlds-0.4.2-src/src/game/g_variables.h 2008-04-05 15:13:02.000000000 +0200 +++ footspam/src/game/g_variables.h 2008-08-22 21:38:19.000000000 +0200 @@ -57,3 +57,52 @@ MACRO_CONFIG_INT(sv_spamprotection, 1, 0, 1) MACRO_CONFIG_INT(sv_spectator_slots, 0, 0, 12) +MACRO_CONFIG_INT(sv_reserved_slots, 0, 0, 12) +MACRO_CONFIG_STR(sv_reserved_slots_pass, 32, "") +MACRO_CONFIG_INT(sv_player_keeptime, 0, 0, 1000) +MACRO_CONFIG_INT(sv_goalkeeper, 0, 0, 1) +MACRO_CONFIG_INT(sv_goalkeeper_changes, 0, 0, 1000) +MACRO_CONFIG_INT(sv_goalkeeper_changes_ban, 0, 0, 1000) +MACRO_CONFIG_INT(sv_goal_keeptime, 3, 0, 1000) +MACRO_CONFIG_INT(sv_realfoot, 0, 0, 1) +MACRO_CONFIG_INT(sv_teleport, 1, 0, 1) +MACRO_CONFIG_INT(sv_ball_respawn_time, 2, 0, 100) +MACRO_CONFIG_INT(sv_rolling, 0, 0, 1) +MACRO_CONFIG_INT(sv_allow_votes, 0, 0, 1) +MACRO_CONFIG_INT(sv_allow_gametype_votes, 0, 0, 1) +MACRO_CONFIG_INT(sv_nohook, 0, 0, 1) +MACRO_CONFIG_INT(sv_bounce_loss_y, 0, 0, 100000) +MACRO_CONFIG_INT(sv_bounce_loss_x, 0, 0, 100000) + +MACRO_CONFIG_INT(sv_suicide_score, 1, 0, 1) + +MACRO_CONFIG_INT(sv_grenadelauncher_startvelocity, 0, 0, 100000) +MACRO_CONFIG_INT(sv_explosions, 0, 0, 1) +MACRO_CONFIG_INT(sv_goalkeeper_jumping, 0, 0, 1) + +MACRO_CONFIG_INT(sv_teamchanges, 0, 0, 100) +MACRO_CONFIG_INT(sv_time_blocked, 180, 1, 1000) +MACRO_CONFIG_INT(sv_teamchangeskick, 0, 0, 100) +MACRO_CONFIG_INT(sv_teamchangesban, 0, 0, 100) +MACRO_CONFIG_INT(sv_messagesnum, 0, 0, 100) +MACRO_CONFIG_INT(sv_same_messages, 0, 0, 100) +MACRO_CONFIG_INT(sv_time_muted, 180, 1, 1000) +MACRO_CONFIG_INT(sv_messageskick, 0, 0, 100) +MACRO_CONFIG_INT(sv_messagesban, 0, 0, 100) +MACRO_CONFIG_STR(sv_startmessage, 1000, "") +MACRO_CONFIG_STR(sv_endroundmessage, 1000, "") +MACRO_CONFIG_INT(sv_handle_mapvotes, 0, 0, 1) +MACRO_CONFIG_INT(sv_kick_teamkiller, 0, 0, 100) +MACRO_CONFIG_INT(sv_name_blacklist, 0, 0, 1) +MACRO_CONFIG_INT(sv_all_vote, 0, 0, 1) +MACRO_CONFIG_INT(sv_votetime, 60, 0, 500) +MACRO_CONFIG_INT(sv_ban_time, 10, 0, 1000) +MACRO_CONFIG_INT(sv_team_balance, 0, 0, 100) +MACRO_CONFIG_INT(sv_allow_next_map, 0, 0, 1) +MACRO_CONFIG_INT(sv_allow_timelimit, 0, 0, 1) +MACRO_CONFIG_INT(sv_allow_scorelimit, 0, 0, 1) +MACRO_CONFIG_INT(sv_ban_teamkiller, 0, 0, 100) +MACRO_CONFIG_INT(sv_vote_pause, 0, 0, 1000) +MACRO_CONFIG_INT(sv_silent_mode, 0, 0, 1) +MACRO_CONFIG_INT(sv_balance_warning, 0, 0, 1000) + diff -Naur teeworlds-0.4.2-src/src/game/server/gs_common.h footspam/src/game/server/gs_common.h --- teeworlds-0.4.2-src/src/game/server/gs_common.h 2008-04-05 15:13:02.000000000 +0200 +++ footspam/src/game/server/gs_common.h 2008-08-22 21:38:19.000000000 +0200 @@ -15,7 +15,7 @@ // class event_handler { - static const int MAX_EVENTS = 128; + static const int MAX_EVENTS = 256; static const int MAX_DATASIZE = 128*64; int types[MAX_EVENTS]; // TODO: remove some of these arrays @@ -153,6 +153,9 @@ virtual void on_player_spawn(class player *p) {} virtual int on_player_death(class player *victim, class player *killer, int weapon); + virtual int on_player_goal(class player *goaler,int goalteam); + virtual int on_player_takeflag(class player *owner,int team){return 0;} + virtual int on_player_rmflag(int team){return 0;} virtual void on_player_info_change(class player *p); @@ -202,6 +205,7 @@ int bounce; float force; int start_tick; + int realstart_tick; projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner, int damage, int flags, float force, int sound_impact, int weapon); @@ -287,6 +291,10 @@ int health; int armor; + bool gotball; + bool goalkeeper; + int keepballtick; + // ninja struct @@ -298,8 +306,15 @@ } ninja; // + int goalchanges; int score; + int votedfor; int team; + int voted; + int votekick; + int lastplayer; + int lastplayertime; + int lastvote; int player_state; // if the client is chatting, accessing a menu or so bool spawning; @@ -352,6 +367,9 @@ virtual bool take_damage(vec2 force, int dmg, int from, int weapon); virtual void snap(int snaping_client); + + int last_message_tick; unsigned char message_num; bool muted; + bool teamchanging; unsigned char team_changes; int last_team_set; int teamkills; }; extern player *players; diff -Naur teeworlds-0.4.2-src/src/game/server/gs_game.cpp footspam/src/game/server/gs_game.cpp --- teeworlds-0.4.2-src/src/game/server/gs_game.cpp 2008-04-05 15:13:02.000000000 +0200 +++ footspam/src/game/server/gs_game.cpp 2008-08-09 08:15:07.000000000 +0200 @@ -90,7 +90,15 @@ { if(warmup) // game can't end when we are running warmup return; - + if(strlen(config.sv_endroundmessage) > 0) + { + NETMSG_SV_CHAT msg; + msg.team = 0; + msg.cid = -1; + msg.message = config.sv_endroundmessage; + msg.pack(MSGFLAG_VITAL); + server_send_msg(-1); + } world->paused = true; game_over_tick = server_tick(); sudden_death = 0; @@ -204,13 +212,32 @@ if(!killer) return 0; if(killer == victim) - victim->score--; // suicide + { + if(config.sv_suicide_score) + victim->score--; // suicide + } else { if(is_teamplay && victim->team == killer->team) killer->score--; // teamkill + else + killer->score++; // normal kill + } + return 0; +} + +int gameobject::on_player_goal(class player *goaler,int goalteam) +{ + // do scoreing + if(!goaler) + return 0; + else + { + if(is_teamplay && goaler->team == goalteam) + goaler->score--; // teamkill else - killer->score++; // normal kill + goaler->score++; // normal kill + teamscore[!goalteam]++; } return 0; } diff -Naur teeworlds-0.4.2-src/src/game/server/gs_game_ctf.cpp footspam/src/game/server/gs_game_ctf.cpp --- teeworlds-0.4.2-src/src/game/server/gs_game_ctf.cpp 2008-04-05 15:13:02.000000000 +0200 +++ footspam/src/game/server/gs_game_ctf.cpp 2008-08-02 08:09:46.000000000 +0200 @@ -47,9 +47,10 @@ if(f && f->carrying_player == victim) { create_sound_global(SOUND_CTF_DROP); - f->drop_tick = server_tick(); - f->carrying_player = 0; - f->vel = vec2(0,0); + //f->drop_tick = server_tick(); + //f->carrying_player = 0; + //f->vel = vec2(0,0); + f->reset(); if(killer && killer->team != victim->team) killer->score++; @@ -61,6 +62,21 @@ return had_flag; } +int gameobject_ctf::on_player_takeflag(class player *flagowner,int team) +{ + flag *f = flags[team]; + f->at_stand = 0; + f->carrying_player=flagowner; + return 0; +} + +int gameobject_ctf::on_player_rmflag(int team) +{ + flag *f = flags[team]; + f->reset(); + return 0; +} + void gameobject_ctf::tick() { gameobject::tick(); diff -Naur teeworlds-0.4.2-src/src/game/server/gs_game_ctf.h footspam/src/game/server/gs_game_ctf.h --- teeworlds-0.4.2-src/src/game/server/gs_game_ctf.h 2008-04-05 15:13:02.000000000 +0200 +++ footspam/src/game/server/gs_game_ctf.h 2008-08-02 08:09:46.000000000 +0200 @@ -13,6 +13,8 @@ virtual void on_player_spawn(class player *p); virtual int on_player_death(class player *victim, class player *killer, int weapon); + virtual int on_player_takeflag(class player *owner,int team); + virtual int on_player_rmflag(int team); }; // TODO: move to seperate file diff -Naur teeworlds-0.4.2-src/src/game/server/gs_server.cpp footspam/src/game/server/gs_server.cpp --- teeworlds-0.4.2-src/src/game/server/gs_server.cpp 2008-04-05 15:13:02.000000000 +0200 +++ footspam/src/game/server/gs_server.cpp 2008-09-12 09:56:08.000000000 +0200 @@ -24,8 +24,13 @@ void create_playerspawn(vec2 p); void create_death(vec2 p, int who); void create_sound(vec2 pos, int sound, int mask=-1); -class player *intersect_player(vec2 pos0, vec2 pos1, float radius, vec2 &new_pos, class entity *notthis = 0); +void auto_balancing(); +class player *intersect_player(vec2 pos0, vec2 pos1, float radius, vec2 &new_pos, class entity *notthis = 0,int realstart_tick=-1); class player *closest_player(vec2 pos, float radius, entity *notthis); +class player *closest_team_player(vec2 pos, float radius, entity *notthis,int playerteam); +int regn = 0,tele=0,strip=0;int ballonground=false;int respawnstarttick=-1;int passer=-1; +int start[6]; int goals=0;char vote_message[300]; +int timer_vote=-1;bool vote_called=false;int votetime=-1;char votetype[32];int votedfor=-1;static int votedtokick=-1; game_world *world; @@ -37,12 +42,146 @@ CHAT_BLUE=1 }; +static void send_display(char *message, int to) +{ + if(to == -1) + { + dbg_msg("chat", "*** %s", message); + } + NETMSG_SV_BROADCAST msg; + msg.message = message; + msg.pack(MSGFLAG_VITAL); + server_send_msg(to); +} + +void send_message(char *message, int to) +{ + if(to == -1) + { + dbg_msg("chat", "*** %s", message); + if(config.sv_silent_mode) + return; + } + NETMSG_SV_CHAT msg; + msg.team = 0; + msg.cid = -1; + msg.message = message; + msg.pack(MSGFLAG_VITAL); + server_send_msg(to); +} + +int get_team(int teamm) +{ + int i=0; + int result = 0; + for(;ivoted) + { + send_display("You already voted, ignoring vote", cid); + } + else + { + tmp->voted = true; + dbg_msg("game", "map-voting %s", text); + send_display("You voted for the map, thank you", cid); + } + return; + } + + if(config.sv_messagesnum && cid > -1 && cid < MAX_CLIENTS) + { + + + if(tmp->muted) + { + if(tmp->last_message_tick + config.sv_time_muted*SERVER_TICK_SPEED > server_tick()) + { + tmp->message_num++; + char buf[128]; + sprintf(buf, "You can speak in %i seconds", (config.sv_time_muted * server_tickspeed() + tmp->last_message_tick - server_tick())/server_tickspeed()); + send_display(buf, cid); + if(config.sv_messageskick && tmp->message_num > config.sv_messageskick) + { + char kicking[127]; + sprintf(kicking, "%s was kicked because of spamming.", server_clientname(cid)); + send_message(kicking, -1); + if(config.sv_messagesban) + { + char ban_message[255]; + sprintf(ban_message, "You were banned for %i minutes because of spamming", config.sv_messagesban); + server_ban(cid, config.sv_teamchangesban, ban_message); + } + else + server_kick(cid,"You were kicked because of spamming"); + } + return; + } + else + { + tmp->muted = false; + tmp->message_num = 0; + tmp->last_message_tick = server_tick(); + } + } + else + { + if(tmp->last_message_tick + 30*SERVER_TICK_SPEED > server_tick()) + { + tmp->message_num++; + if(config.sv_same_messages && strcmp(lastmessages[cid], text) == 0) + { + same_messages[cid]++; + } + else + { + strncpy(lastmessages[cid], text, 768); + same_messages[cid] = 0; + } + if(tmp->message_num >= config.sv_messagesnum || (config.sv_same_messages && config.sv_same_messages <= same_messages[cid])) + { + tmp->muted = true;same_messages[cid] = 0; + tmp->message_num = 0; + char muting[127]; + sprintf(muting, "%s was muted for %d seconds because of spamming.", server_clientname(cid), config.sv_time_muted); + send_message(muting, -1); + sprintf(muting, "You are muted for %d seconds\nbecause of spamming", config.sv_time_muted); + send_display(muting, cid); + return; + } + + } + else + { + tmp->last_message_tick = server_tick(); + tmp->message_num = 0; + } + } + } if(cid >= 0 && cid < MAX_CLIENTS) dbg_msg("chat", "%d:%d:%s: %s", cid, team, server_clientname(cid), text); else + { dbg_msg("chat", "*** %s", text); + if(config.sv_silent_mode) + return; + } if(team == CHAT_ALL) { @@ -68,7 +207,101 @@ } } } - +void resultvote() +{ + int players_serv=0,total=0,voteur=0; + for (int i=0; i < MAX_CLIENTS;i++) + { + if(players[i].client_id !=-1) + { + if(players[i].votedfor != -1) + {total+=players[i].votedfor;voteur++;} + players_serv++; + } + } + char buf[256]; + str_format(buf, sizeof(buf), "YES: %d | NO: %d (Time remaining : %d secondes)",total,voteur-total,votetime - (server_tick()-timer_vote)/(server_tickspeed())); + send_chat(-1,CHAT_ALL,buf); + if (voteur==players_serv || (server_tick()-timer_vote > server_tickspeed()*votetime && timer_vote != -1)) + { + vote_called=false; + int result=1; + char message[256]; + if((total > voteur / 2 && !config.sv_all_vote) || (total > players_serv / 2 && config.sv_all_vote))str_format(message, sizeof(message), "Vote passed"); + else {str_format(message, sizeof(message), "Vote failed");result=0;} + send_display(message, -1); + if(!strcmp(votetype,"kick") && votedtokick != -1) + { + if(result) + { + sprintf(message,"You are kicked by vote for %i minutes (YES: %d | NO: %d)", config.sv_ban_time,total,voteur-total); + server_ban_saved(config.sv_ban_time, message); + } + str_format(votetype, sizeof(votetype), "null"); + votedtokick = -1; + } + else if(!strcmp(votetype,"next_map")) + { + if(result) + { + gameobj->endround(); + } + str_format(votetype, sizeof(votetype), "null"); + votedtokick = -1; + } + else if(!strcmp(votetype,"timelimit")) + { + if(result) + { + if(votedtokick == -1) + config.sv_timelimit = 0; + else + config.sv_timelimit = votedtokick; + } + str_format(votetype, sizeof(votetype), "null"); + votedtokick = -1; + } + else if(!strcmp(votetype,"scorelimit")) + { + if(result) + { + if(votedtokick == -1) + config.sv_scorelimit = 0; + else + config.sv_scorelimit = votedtokick; + } + str_format(votetype, sizeof(votetype), "null"); + votedtokick = -1; + } + else if(!strcmp(votetype,"foot")) + { + if(result) + { + char fichier[32]; + str_format(fichier, sizeof(fichier), "foot.cfg"); + console_execute_file(fichier); + } + str_format(votetype, sizeof(votetype), "null"); + } + else if(!strcmp(votetype,"handball")) + { + if(result) + { + char fichier[32]; + str_format(fichier, sizeof(fichier), "handball.cfg"); + console_execute_file(fichier); + } + str_format(votetype, sizeof(votetype), "null"); + } + for (int i=0; i < MAX_CLIENTS;i++) + { + if(players[i].client_id !=-1) + { + players[i].votedfor=-1; + } + } + } +} void send_info(int who, int to_who) { @@ -80,7 +313,7 @@ msg.color_body = players[who].color_body; msg.color_feet =players[who].color_feet; msg.pack(MSGFLAG_VITAL); - + server_send_msg(to_who); } @@ -330,6 +563,7 @@ remove_entities(); reset_requested = false; + respawnstarttick=-1; } void game_world::remove_entities() @@ -355,6 +589,8 @@ if(!paused) { + auto_balancing(); + if(timer_vote != -1 && server_tick()-timer_vote > server_tickspeed()*votetime && vote_called)resultvote(); /* static PERFORMACE_INFO scopes[OBJTYPE_FLAG+1] = { @@ -377,10 +613,10 @@ {"powerup", 0}, {"flag", 0} }; - + static PERFORMACE_INFO tick_scope = {"tick", 0}; perf_start(&tick_scope);*/ - + // update all objects for(entity *ent = first_entity; ent; ent = ent->next_entity) { @@ -390,7 +626,7 @@ /*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG) perf_end();*/ } - + /* perf_end(); @@ -455,6 +691,7 @@ this->weapon = weapon; this->bounce = 0; this->start_tick = server_tick(); + this->realstart_tick = server_tick(); world->insert_entity(this); } @@ -482,28 +719,127 @@ curvature = tuning.gun_curvature; speed = tuning.gun_speed; } - + return calc_pos(pos, direction, curvature, speed, time); } void projectile::tick() { - float pt = (server_tick()-start_tick-1)/(float)server_tickspeed(); float ct = (server_tick()-start_tick)/(float)server_tickspeed(); vec2 prevpos = get_pos(pt); vec2 curpos = get_pos(ct); - + vec2 mypos = get_pos((server_tick()-start_tick-2)/(float)server_tickspeed()); + bool explode=false; lifespan--; - - int collide = col_intersect_line(prevpos, curpos, &curpos); - //int collide = col_check_point((int)curpos.x, (int)curpos.y); - - entity *targetplayer = (entity*)intersect_player(prevpos, curpos, 6.0f, curpos, powner); - if(targetplayer || collide || lifespan < 0) + + //int collide = col_intersect_line(prevpos, curpos, NULL); + int colnohook = col_intersect_nohook(prevpos,curpos); + int collide = col_check_point((int)curpos.x, (int)curpos.y); + //if (collide && realstart_tick==server_tick()-1 && !config.sv_realfoot)explode=true; + if(collide && config.sv_rolling) + { + static vec2 last_col; + static int same_pos = 0; + static int last_move = 10; + vec2 tmp; + tmp.x = curpos.x; + tmp.y = mypos.y; + int coll_x = col_intersect_line(mypos, tmp, NULL); + tmp.x = mypos.x; + tmp.y = curpos.y; + int coll_y = col_intersect_line(mypos, tmp, NULL); + float speed = (float)tuning.grenade_speed; + float curvature = (float)tuning.grenade_curvature; + + if(!(direction.x < 0.01 && direction.x > -0.01 && direction.y < 0.01 && direction.y > -0.01) && last_col.x + 1 >= pos.x && last_col.x - 1 <= pos.x && last_col.y + 1 >= pos.y && last_col.y - 1 <= pos.y && same_pos++ > 1) + { + if(same_pos > 50 || last_move > 64) + { + explode = true; + } + + if(!col_is_solid(pos.x+last_move, pos.y+last_move)) + { + pos.x = pos.x+last_move; + pos.y = pos.y+last_move; + } + else if(!col_is_solid(pos.x+last_move, pos.y-last_move)) + { + pos.x = pos.x+last_move; + pos.y = pos.y-last_move; + } + else if(!col_is_solid(pos.x-last_move, pos.y+last_move)) + { + pos.x = pos.x-last_move; + pos.y = pos.y+last_move; + } + else if(!col_is_solid(pos.x-last_move, pos.y-last_move)) + { + pos.x = pos.x-last_move; + pos.y = pos.y-last_move; + } + last_move += 5; + same_pos ++; + if(coll_x) + { + direction.x = -direction.x/(config.sv_bounce_loss_x+100)*100; + } + else + direction.x = direction.x/(config.sv_bounce_loss_x+100)*100; + if(coll_y) + { + direction.y = -(direction.y + 2*curvature/10000*speed*(server_tick()-start_tick)/(float)server_tickspeed())/(config.sv_bounce_loss_y+100)*100; + } + else + direction.y = (direction.y + 2*curvature/10000*(server_tick()-start_tick)/(float)server_tickspeed()*speed)/(config.sv_bounce_loss_y+100)*100; + } + else + { + + if(!(last_col.x + 1 >= pos.x && last_col.x - 1 <= pos.x && last_col.y + 1 >= pos.y && last_col.y - 1 <= pos.y)) + { + same_pos = 0; + last_col = pos; + last_move = 10; + } + int i = server_tick(); + for(; i >= start_tick-10; i--) + { + tmp = get_pos((i-start_tick)/(float)server_tickspeed()); + if(!col_is_solid(tmp.x, tmp.y)) + { + pos = tmp; + if(coll_x) + { + direction.x = -direction.x/(config.sv_bounce_loss_x+100)*100; + } + else + direction.x = direction.x/(config.sv_bounce_loss_x+100)*100; + if(coll_y) + { + direction.y = -(direction.y + 2*curvature/10000*speed*(i-start_tick)/(float)server_tickspeed())/(config.sv_bounce_loss_y+100)*100; + } + else + direction.y = (direction.y + 2*curvature/10000*(i-start_tick)/(float)server_tickspeed()*speed)/(config.sv_bounce_loss_y+100)*100; + break; + } + } + if(col_is_solid(pos.x, pos.y)) + { + explode = true; + } + } + realstart_tick = 0; + start_tick=server_tick(); + } + else if (collide){direction.x=0;direction.y=0;start_tick=server_tick();pos=mypos;} + + entity *targetplayer = (entity*)intersect_player(prevpos, curpos, 6.0f, curpos, powner,realstart_tick); + if(targetplayer || lifespan < 0 || explode) { - if (lifespan >= 0 || weapon == WEAPON_GRENADE) + if (config.sv_explosions && (lifespan >= 0 || weapon == WEAPON_GRENADE)) create_sound(curpos, sound_impact); if (flags & PROJECTILE_FLAGS_EXPLODE) @@ -514,6 +850,80 @@ } world->destroy_entity(this); + ballonground=false; + } + else if (col_is_red((int)curpos.x,(int)curpos.y) && owner != -1 && players[owner].team != -1) + { + if(goals++ == 10) + goals = 0; + players[owner].gotball=false; + players[owner].weapons[WEAPON_GRENADE].got = false; + players[owner].active_weapon = WEAPON_HAMMER; + players[owner].last_weapon = WEAPON_HAMMER; + world->destroy_entity(this); + gameobj->on_player_goal(get_player(owner), 0); + if (passer != -1 && players[passer].team==1 && players[passer].team == players[owner].team && passer != owner){ + char bufer[256]; + str_format(bufer, sizeof(bufer), "Blue team scored (%s)\nwith a pass from %s",server_clientname(owner),server_clientname(passer)); + if(!vote_called)send_display(bufer, -1);gameobj->on_player_goal(get_player(passer), 0);passer=-1;} + else + { + char buf[128]; + str_format(buf, sizeof(buf), "Blue team scored (%s)",server_clientname(owner)); + if(!vote_called)send_broadcast(buf, -1); + } + for (int i=0;i < MAX_CLIENTS ;i++) + { + if(players[i].client_id != -1) + { + if(goals == 0) + { + players[i].teamkills = 0; + players[i].goalchanges = 0; + } + players[i].respawn(); + players[i].gotball=false; + if(goals == 0) + players[i].teamkills = 0; + } + } + create_sound_global(SOUND_CTF_CAPTURE); + ballonground=false; + gameobj->on_player_rmflag(players[owner].team); + respawnstarttick=server_tick(); + //dbg_msg("Ball","Blue team scored %s",server_clientname(owner)); + } + else if (col_is_blue((int)curpos.x,(int)curpos.y) && owner != -1 && players[owner].team != -1) + { + players[owner].gotball=false; + players[owner].weapons[WEAPON_GRENADE].got = false; + players[owner].active_weapon = WEAPON_HAMMER; + players[owner].last_weapon = WEAPON_HAMMER; + world->destroy_entity(this); + gameobj->on_player_goal(get_player(owner), 1); + if (passer != -1 && players[passer].team==0 && players[passer].team == players[owner].team && passer != owner){ + char bufer[256]; + str_format(bufer, sizeof(bufer), "Red team scored (%s)\nwith a pass from %s",server_clientname(owner),server_clientname(passer)); + if(!vote_called)send_display(bufer, -1);gameobj->on_player_goal(get_player(passer), 1);passer=-1;} + else + { + char buf[128]; + str_format(buf, sizeof(buf), "Red team scored (%s)",server_clientname(owner)); + if(!vote_called)send_broadcast(buf, -1); + } + for (int i=0;i < MAX_CLIENTS ;i++) + { + if(players[i].client_id != -1) + { + players[i].respawn(); + players[i].gotball=false; + } + } + create_sound_global(SOUND_CTF_CAPTURE); + ballonground=false; + gameobj->on_player_rmflag(players[owner].team); + respawnstarttick=server_tick(); + //dbg_msg("Ball","Red team scored %s",server_clientname(owner)); } } @@ -530,7 +940,7 @@ void projectile::snap(int snapping_client) { float ct = (server_tick()-start_tick)/(float)server_tickspeed(); - + if(distance(players[snapping_client].pos, get_pos(ct)) > 1000.0f) return; @@ -551,7 +961,7 @@ dir = direction; bounces = 0; do_bounce(); - + world->insert_entity(this); } @@ -559,13 +969,13 @@ bool laser::hit_player(vec2 from, vec2 to) { vec2 at; - player *hit = intersect_player(pos, to, 0.0f, at, owner); + player *hit = intersect_player(pos, to, 0.0f, at, owner,-1); if(!hit) return false; this->from = from; pos = at; - energy = -1; + energy = -1; hit->take_damage(vec2(0,0), tuning.laser_damage, owner->client_id, WEAPON_RIFLE); return true; } @@ -573,16 +983,16 @@ void laser::do_bounce() { eval_tick = server_tick(); - + if(energy < 0) { //dbg_msg("laser", "%d removed", server_tick()); world->destroy_entity(this); return; } - + vec2 to = pos + dir*energy; - + if(col_intersect_line(pos, to, &to)) { if(!hit_player(pos, to)) @@ -592,17 +1002,17 @@ pos = to - dir*2; vec2 temp_pos = pos; vec2 temp_dir = dir*4.0f; - + move_point(&temp_pos, &temp_dir, 1.0f, 0); pos = temp_pos; dir = normalize(temp_dir); - + energy -= distance(from, pos) + tuning.laser_bounce_cost; bounces++; - + if(bounces > tuning.laser_bounce_num) energy = -1; - + create_sound(pos, SOUND_RIFLE_BOUNCE); } } @@ -615,10 +1025,10 @@ energy = -1; } } - + //dbg_msg("laser", "%d done %f %f %f %f", server_tick(), from.x, from.y, pos.x, pos.y); } - + void laser::reset() { world->destroy_entity(this); @@ -659,6 +1069,9 @@ void player::init() { + voted = false; + teamchanging = false; team_changes = 0; last_team_set = server_tick(); + last_message_tick = server_tick(); message_num = 0; muted = false; proximity_radius = phys_size; client_id = -1; team = -1; // -1 == spectator @@ -677,10 +1090,10 @@ { pos = vec2(0.0f, 0.0f); core.reset(); - + emote_type = 0; emote_stop = -1; - + //direction = vec2(0.0f, 1.0f); score = 0; dead = true; @@ -695,6 +1108,7 @@ mem_zero(&input, sizeof(input)); mem_zero(&previnput, sizeof(previnput)); num_inputs = 0; + gotball=false; last_action = -1; @@ -703,8 +1117,8 @@ attack_tick = 0; mem_zero(&ninja, sizeof(ninja)); - - active_weapon = WEAPON_GUN; + + active_weapon = WEAPON_HAMMER; last_weapon = WEAPON_HAMMER; queued_weapon = -1; } @@ -715,18 +1129,21 @@ { if(w == active_weapon) return; - + last_weapon = active_weapon; queued_weapon = -1; active_weapon = w; if(active_weapon < 0 || active_weapon >= NUM_WEAPONS) active_weapon = 0; - + create_sound(pos, SOUND_WEAPON_SWITCH); } void player::respawn() { + if(gotball) + keepballtick -= config.sv_goal_keeptime*server_tickspeed(); + fire_weapon(); spawning = true; } @@ -744,26 +1161,193 @@ else if(team == 1) return "blue team"; } - + return "spectators"; } +void auto_balancing() +{ + static int last_warning_tick = 0; + static int start_unbalance = 0; + if((config.sv_team_balance || config.sv_balance_warning) && gameobj->gametype != GAMETYPE_DM) + { + int team0 = get_team(0); + int team1 = get_team(1); + if(!start_unbalance) + start_unbalance = server_tick(); + if((config.sv_team_balance && (team0-team1 > config.sv_team_balance || team1-team0 > config.sv_team_balance)) || (!config.sv_team_balance && config.sv_balance_warning && (team0-team1 > config.sv_balance_warning || team1-team0 > config.sv_balance_warning))) + { + if(((config.sv_balance_warning && config.sv_team_balance && start_unbalance + config.sv_balance_warning*server_tickspeed() > server_tick()) || !config.sv_team_balance) && last_warning_tick + 20*server_tickspeed() < server_tick()) + { + send_display("Teams are unbalanced", -1); + last_warning_tick = server_tick(); + } + else if((config.sv_team_balance && config.sv_balance_warning && start_unbalance + config.sv_balance_warning*server_tickspeed() < server_tick()) || !config.sv_balance_warning) + { + send_display("Auto balancing teams...", -1); + while(team0-team1 > config.sv_team_balance) + { + int i = rand() % MAX_CLIENTS; + int steps = rand() % MAX_CLIENTS; + int now = 0; + while(1) + { + if(players[i].team == 0 && steps == now++) + { + int tmp_points = players[i].score; + players[i].team_changes--; + send_display("You have been assigned to the blue team", i); + players[i].set_team(1); + team0--; + players[i].score = tmp_points; + break; + } + if(++i == MAX_CLIENTS) + { + i = 0; + } + } + } + while(team1-team0 > config.sv_team_balance) + { + int i = rand() % MAX_CLIENTS; + int steps = rand() % MAX_CLIENTS; + int now = 0; + while(1) + { + if(players[i].team == 1 && steps == now++) + { + int tmp_points = players[i].score; + players[i].team_changes--; + send_display("You have been assigned to the red team", i); + players[i].set_team(0); + team1--; + players[i].score = tmp_points; + break; + } + if(++i == MAX_CLIENTS) + { + i = 0; + } + } + } + } + } + else + { + start_unbalance = 0; + last_warning_tick = 0; + } + } +} + void player::set_team(int new_team) { + if(config.sv_teamchanges) + { + if(teamchanging) + { + if(last_team_set + config.sv_time_blocked*SERVER_TICK_SPEED > server_tick()) + { + team_changes++; + char buf[128]; + sprintf(buf, "You can change team in %i seconds", (config.sv_time_blocked * server_tickspeed() + last_team_set - server_tick())/server_tickspeed()); + send_display(buf, client_id); + if(config.sv_teamchangeskick && team_changes > config.sv_teamchangeskick) + { + + char kicking[127]; + sprintf(kicking, "%s was kicked because of fast teamchanging.", server_clientname(client_id)); + send_message(kicking, -1); + if(config.sv_teamchangesban) + { + char ban_message[255]; + sprintf(ban_message, "You were banned for %i minutes because of fast teamchanging", config.sv_teamchangesban); + server_ban(client_id, config.sv_teamchangesban, ban_message); + } + else + server_kick(client_id,"You were kicked because of fast teamchanging"); + } + return; + } + else + { + teamchanging = false; + team_changes = 0; + last_team_set = server_tick(); + } + } + else + { + if(last_team_set + 30*SERVER_TICK_SPEED > server_tick()) + { + team_changes++; + if(config.sv_teamchanges && team_changes >= config.sv_teamchanges) + { + teamchanging = true; + team_changes = 0; + char blocking[127]; + sprintf(blocking, "%s can't change team for %d seconds", server_clientname(client_id), config.sv_time_blocked); + send_message(blocking, -1); + sprintf(blocking, "You can't change team for %d seconds", config.sv_time_blocked); + send_display(blocking, client_id); + return; + } + + } + else + { + last_team_set = server_tick(); + team_changes = 0; + } + } + } + if(config.sv_team_balance && !config.sv_balance_warning && gameobj->gametype != GAMETYPE_DM) + { + int team0 = get_team(0); + int team1 = get_team(1); + if(team == -1) + { + if(new_team == 1 && team1-team0 + 1 > config.sv_team_balance) + { + send_display("You can't join blue because of team-balance", client_id); + new_team = 0; + } + else if(new_team == 0 && team0-team1 + 1 > config.sv_team_balance) + { + send_display("You can't join red because of team-balance", client_id); + new_team = 1; + } + } + else if(new_team == 1 && team1-team0 + 2 > config.sv_team_balance) + { + send_display("You can't join blue because of team-balance", client_id); + return; + } + else if(new_team == 0 && team0-team1 + 2 > config.sv_team_balance) + { + send_display("You can't join red because of team-balance", client_id); + return; + } + } + teamkills = 0; // clamp the team new_team = gameobj->clampteam(new_team); if(team == new_team) return; - + char buf[512]; str_format(buf, sizeof(buf), "%s joined the %s", server_clientname(client_id), get_team_name(new_team)); - send_chat(-1, CHAT_ALL, buf); - + send_chat(-1, CHAT_ALL, buf); + + gotball=false; + teamkills --; die(client_id, -1); team = new_team; + goalkeeper=false; score = 0; dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), team); - + gameobj->on_player_info_change(&players[client_id]); // send all info to this client @@ -786,7 +1370,7 @@ die_pos = vec2(0,0); pos = vec2(100,100); } - + vec2 pos; bool got; int friendly_team; @@ -797,35 +1381,35 @@ static float evaluate_spawn(spawneval *eval, vec2 pos) { float score = 0.0f; - + for(int c = 0; c < MAX_CLIENTS; c++) { if(players[c].client_id == -1) continue; - + // don't count dead people if(!(players[c].flags&entity::FLAG_PHYSICS)) continue; - + // team mates are not as dangerous as enemies float scoremod = 1.0f; if(eval->friendly_team != -1 && players[c].team == eval->friendly_team) scoremod = 0.5f; - + float d = distance(pos, players[c].pos); if(d == 0) score += 1000000000.0f; else score += 1.0f/d; } - + // weight in the die posititon float d = distance(pos, eval->die_pos); if(d == 0) score += 1000000000.0f; else score += 1.0f/d; - + return score; } @@ -856,17 +1440,17 @@ void player::try_respawn() { vec2 spawnpos = vec2(100.0f, -60.0f); - + // get spawn point spawneval eval; eval.die_pos = die_pos; - + eval.pos = vec2(100, 100); - - if(gameobj->gametype == GAMETYPE_CTF) + + if(gameobj->gametype == GAMETYPE_CTF || gameobj->gametype == GAMETYPE_TDM) { eval.friendly_team = team; - + // try first try own team spawn, then normal spawn and then enemy evaluate_spawn_type(&eval, 1+(team&1)); if(!eval.got) @@ -878,14 +1462,12 @@ } else { - if(gameobj->gametype == GAMETYPE_TDM) - eval.friendly_team = team; - + evaluate_spawn_type(&eval, 0); evaluate_spawn_type(&eval, 1); evaluate_spawn_type(&eval, 2); } - + spawnpos = eval.pos; // check if the position is occupado @@ -897,7 +1479,7 @@ if(ents[i] != this) return; } - + spawning = false; pos = spawnpos; @@ -908,9 +1490,9 @@ health = 10; armor = 0; jumped = 0; - + mem_zero(&ninja, sizeof(ninja)); - + dead = false; set_flag(entity::FLAG_PHYSICS); player_state = PLAYERSTATE_PLAYING; @@ -923,13 +1505,14 @@ mem_zero(&weapons, sizeof(weapons)); weapons[WEAPON_HAMMER].got = true; weapons[WEAPON_HAMMER].ammo = -1; - weapons[WEAPON_GUN].got = true; + if(config.sv_realfoot && !goalkeeper)weapons[WEAPON_HAMMER].ammo = 0; + weapons[WEAPON_GUN].got = false; weapons[WEAPON_GUN].ammo = data->weapons[WEAPON_GUN].maxammo; /*weapons[WEAPON_RIFLE].got = true; weapons[WEAPON_RIFLE].ammo = -1;*/ - - active_weapon = WEAPON_GUN; + + active_weapon = WEAPON_HAMMER; last_weapon = WEAPON_HAMMER; queued_weapon = 0; @@ -966,7 +1549,7 @@ set_weapon(active_weapon); return 0; } - + // force ninja weapon set_weapon(WEAPON_NINJA); @@ -978,7 +1561,7 @@ ninja.activationdir = direction; ninja.currentmovetime = data->weapons[WEAPON_NINJA].movetime * server_tickspeed() / 1000; ninja.currentcooldown = data->weapons[WEAPON_NINJA].firedelay * server_tickspeed() / 1000 + server_tick(); - + // reset hit objects numobjectshit = 0; @@ -1058,7 +1641,7 @@ { if(reload_timer != 0) // make sure we have reloaded return; - + if(queued_weapon == -1) // check for a queued weapon return; @@ -1074,7 +1657,7 @@ int wanted_weapon = active_weapon; if(queued_weapon != -1) wanted_weapon = queued_weapon; - + // select weapon int next = count_input(latest_previnput.next_weapon, latest_input.next_weapon).presses; int prev = count_input(latest_previnput.prev_weapon, latest_input.prev_weapon).presses; @@ -1106,7 +1689,7 @@ // check for insane values if(wanted_weapon >= 0 && wanted_weapon < NUM_WEAPONS && wanted_weapon != active_weapon && weapons[wanted_weapon].got) queued_weapon = wanted_weapon; - + do_weaponswitch(); } @@ -1114,32 +1697,34 @@ { if(reload_timer != 0 || active_weapon == WEAPON_NINJA) return; - + do_weaponswitch(); - + vec2 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) + if(active_weapon == WEAPON_SHOTGUN || active_weapon == WEAPON_RIFLE) fullauto = true; // check if we gonna fire 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(fullauto && (latest_input.fire&1) && weapons[active_weapon].ammo || + (active_weapon == WEAPON_GRENADE && !goalkeeper && server_tick()-keepballtick > server_tickspeed()*config.sv_player_keeptime) + || (config.sv_goalkeeper && goalkeeper && active_weapon == WEAPON_GRENADE && (server_tick()-keepballtick > server_tickspeed()*config.sv_goal_keeptime))) + {will_fire = true;} if(!will_fire) return; - // check for ammo if(!weapons[active_weapon].ammo) { create_sound(pos, SOUND_WEAPON_NOAMMO); return; } - + vec2 projectile_startpos = pos+direction*phys_size*0.75f; - + switch(active_weapon) { case WEAPON_HAMMER: @@ -1147,32 +1732,32 @@ // reset objects hit numobjectshit = 0; create_sound(pos, SOUND_HAMMER_FIRE); - + int type = NETOBJTYPE_PLAYER_CHARACTER; entity *ents[64]; - int num = world->find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, ents, 64, &type, 1); + int num = world->find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, ents, 64, &type, 1); for (int i = 0; i < num; i++) { player *target = (player*)ents[i]; if (target == this) continue; - + // hit a player, give him damage and stuffs... vec2 fdir = normalize(ents[i]->pos - pos); // set his velocity to fast upward (for now) create_sound(pos, SOUND_HAMMER_HIT); - ents[i]->take_damage(vec2(0,-1.0f), data->weapons[active_weapon].meleedamage, client_id, active_weapon); + ents[i]->take_damage(vec2(0,-1.0f), 0, client_id, active_weapon); vec2 dir; if (length(target->pos - pos) > 0.0f) dir = normalize(target->pos - pos); else dir = vec2(0,-1); - + target->core.vel += normalize(dir + vec2(0,-1.1f)) * 10.0f; } - + } break; case WEAPON_GUN: @@ -1184,28 +1769,28 @@ (int)(server_tickspeed()*tuning.gun_lifetime), this, 1, 0, 0, -1, WEAPON_GUN); - + // pack the projectile and send it to the client directly NETOBJ_PROJECTILE p; proj->fill_info(&p); - + msg_pack_start(NETMSGTYPE_SV_EXTRA_PROJECTILE, 0); msg_pack_int(1); for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) msg_pack_int(((int *)&p)[i]); msg_pack_end(); server_send_msg(client_id); - + create_sound(pos, SOUND_GUN_FIRE); } break; - + case WEAPON_SHOTGUN: { int shotspread = 2; msg_pack_start(NETMSGTYPE_SV_EXTRA_PROJECTILE, 0); msg_pack_int(shotspread*2+1); - + for(int i = -shotspread; i <= shotspread; i++) { float spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f}; @@ -1220,23 +1805,31 @@ (int)(server_tickspeed()*tuning.shotgun_lifetime), this, 1, 0, 0, -1, WEAPON_SHOTGUN); - + // pack the projectile and send it to the client directly NETOBJ_PROJECTILE p; proj->fill_info(&p); - + for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) msg_pack_int(((int *)&p)[i]); } msg_pack_end(); - server_send_msg(client_id); - + server_send_msg(client_id); + create_sound(pos, SOUND_SHOTGUN_FIRE); } break; case WEAPON_GRENADE: { + if(col_is_solid(projectile_startpos.x, projectile_startpos.y)) + { + create_sound(pos, SOUND_GRENADE_FIRE); + reload_timer = 50; + return; + } + direction.x += core.vel.x*((float)config.sv_grenadelauncher_startvelocity/10000); + direction.y += core.vel.y*((float)config.sv_grenadelauncher_startvelocity/10000); projectile *proj = new projectile(WEAPON_GRENADE, client_id, projectile_startpos, @@ -1248,7 +1841,7 @@ // pack the projectile and send it to the client directly NETOBJ_PROJECTILE p; proj->fill_info(&p); - + msg_pack_start(NETMSGTYPE_SV_EXTRA_PROJECTILE, 0); msg_pack_int(1); for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) @@ -1257,14 +1850,22 @@ server_send_msg(client_id); create_sound(pos, SOUND_GRENADE_FIRE); + gotball=false; + weapons[WEAPON_GRENADE].got = false; + weapons[WEAPON_HAMMER].got = true; + active_weapon = WEAPON_HAMMER; + last_weapon = WEAPON_HAMMER; + ballonground=true; + gameobj->on_player_rmflag(team); + //dbg_msg("Ball","%s a tirer la balle",server_clientname(client_id)); } break; - + case WEAPON_RIFLE: { new laser(pos, direction, tuning.laser_reach, this); create_sound(pos, SOUND_RIFLE_FIRE); } break; - + } if(weapons[active_weapon].ammo > 0) // -1 == unlimited @@ -1295,7 +1896,7 @@ reload_timer--; return 0; } - + if (active_weapon == WEAPON_NINJA) { // don't update other weapons while ninja is active @@ -1326,7 +1927,7 @@ weapons[active_weapon].ammoregenstart = -1; } } - + return 0; } @@ -1344,7 +1945,6 @@ void player::tick() { server_setclientscore(client_id, score); - // grab latest input /* { @@ -1356,7 +1956,9 @@ mem_copy(&latest_input, input, sizeof(latest_input)); } }*/ - + + if(config.sv_goalkeeper_jumping && goalkeeper) + core.jumped = 0; // check if we have enough input // this is to prevent initial weird clicks if(num_inputs < 2) @@ -1364,7 +1966,7 @@ latest_previnput = latest_input; previnput = input; } - + // do latency stuff { CLIENT_INFO info; @@ -1422,10 +2024,137 @@ //core.jumped = jumped; core.input = input; core.tick(); + if (!dead && core.hooked_player >= 0) { + if (team == get_player(core.hooked_player)->team || get_player(core.hooked_player)->goalkeeper ) + { + core.hooked_player = -1; + core.hook_state = HOOK_RETRACTED; + core.triggered_events |= COREEVENT_HOOK_RETRACT; + core.hook_state = HOOK_RETRACTED; + core.hook_pos = core.pos; + core.hook_tick = 0; + } + } + if(config.sv_nohook) + { + core.hook_pos = core.pos; + core.hook_state = HOOK_IDLE; + } // handle weapons handle_weapons(); + if(regn && (server_tick()%regn)==0) { + if(health<10) { + health++; + } else if(armor<10) { + armor++; + } + } + if(col_is_damage((int)core.pos.x,core.pos.y)) { + die(client_id,-1); + if(gotball)gotball=false; + } + if(col_is_fool((int)core.pos.x,core.pos.y,!team) && (gotball || config.sv_goalkeeper)) { + respawn(); + if(gotball){ballonground=false;gameobj->on_player_rmflag(team);} + player* pplayer = closest_team_player(pos, 500.0f, 0,team); + if(pplayer && pplayer->client_id != -1 && gotball) + { + pplayer->weapons[WEAPON_GRENADE].got = true; + pplayer->weapons[WEAPON_GRENADE].ammo = 1; + pplayer->gotball=true; + pplayer->queued_weapon = -1; + pplayer->weapons[WEAPON_HAMMER].got = false; + pplayer->active_weapon = WEAPON_GRENADE; + pplayer->last_weapon = WEAPON_GRENADE; + gameobj->on_player_takeflag(pplayer, pplayer->team); + ballonground=false; + passer=-1; + keepballtick = server_tick(); + } + gotball=false; + } + if(col_is_fool((int)core.pos.x,core.pos.y,team) && config.sv_goalkeeper && !goalkeeper) { + respawn(); + if(gotball){ballonground=false;gameobj->on_player_rmflag(team);} + player* pplayer = closest_team_player(pos, 500.0f, 0,team); + if(pplayer && pplayer->client_id != -1 && pplayer->client_id != client_id && gotball) + { + pplayer->weapons[WEAPON_GRENADE].got = true; + pplayer->weapons[WEAPON_GRENADE].ammo = 1; + pplayer->gotball=true; + pplayer->queued_weapon = -1; + pplayer->weapons[WEAPON_HAMMER].got = false; + pplayer->active_weapon = WEAPON_GRENADE; + pplayer->last_weapon = WEAPON_GRENADE; + gameobj->on_player_takeflag(pplayer, pplayer->team); + ballonground=false; + passer=-1; + keepballtick = server_tick(); + } + gotball=false; + } + if(col_is_goal_limit((int)core.pos.x,core.pos.y) && goalkeeper && config.sv_goalkeeper) + { + respawn(); + if(gotball){ballonground=false;gameobj->on_player_rmflag(team);} + player* pplayer = closest_team_player(pos, 500.0f, 0,team); + if(pplayer && pplayer->client_id != -1 && gotball) + { + pplayer->weapons[WEAPON_GRENADE].got = true; + pplayer->weapons[WEAPON_GRENADE].ammo = 1; + pplayer->gotball=true; + pplayer->queued_weapon = -1; + pplayer->weapons[WEAPON_HAMMER].got = false; + pplayer->active_weapon = WEAPON_GRENADE; + pplayer->last_weapon = WEAPON_GRENADE; + gameobj->on_player_takeflag(pplayer, pplayer->team); + ballonground=false; + passer=-1; + keepballtick = server_tick(); + } + gotball=false; + } + int z = col_is_teleport((int)core.pos.x,core.pos.y); + if(tele && z) { + core.hooked_player = -1; + core.hook_state = HOOK_RETRACTED; + core.triggered_events |= COREEVENT_HOOK_RETRACT; + core.hook_state = HOOK_RETRACTED; + core.pos = teleport(z); + core.hook_pos = core.pos; + if(strip) { + active_weapon = WEAPON_HAMMER; + last_weapon = WEAPON_HAMMER; + if(z&1) { + weapons[0].got = true; + for(int i=1;i<5;i++) { + weapons[i].got = false; + } + } else { + for(int i=0;i<5;i++) { + if(start[i]) { + weapons[i].got = true; + weapons[i].ammo = data->weapons[i].maxammo; + } else { + weapons[i].got = false; + weapons[i].ammo = 0; + } + } + } + } + } + if((core.hook_state==HOOK_FLYING || core.hook_state==HOOK_GRABBED) && col_intersect_nohook(core.pos,core.hook_pos)) { + core.hooked_player = -1; + core.hook_state = HOOK_RETRACTED; + core.triggered_events |= COREEVENT_HOOK_RETRACT; + core.hook_state = HOOK_RETRACTED; + core.hook_pos = core.pos; + core.hook_tick = 0; + } + + player_state = input.player_state; // Previnput @@ -1440,16 +2169,16 @@ vec2 start_pos = core.pos; vec2 start_vel = core.vel; bool stuck_before = test_box(core.pos, vec2(28.0f, 28.0f)); - + core.move(); bool stuck_after_move = test_box(core.pos, vec2(28.0f, 28.0f)); core.quantize(); bool stuck_after_quant = test_box(core.pos, vec2(28.0f, 28.0f)); pos = core.pos; - + if(!stuck_before && (stuck_after_move || stuck_after_quant)) { - dbg_msg("player", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", + dbg_msg("player", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", stuck_before, stuck_after_move, stuck_after_quant, @@ -1461,7 +2190,7 @@ int events = core.triggered_events; int mask = cmask_all_except_one(client_id); - + if(events&COREEVENT_GROUND_JUMP) create_sound(pos, SOUND_PLAYER_JUMP, mask); if(events&COREEVENT_AIR_JUMP) { @@ -1473,14 +2202,14 @@ c->y = (int)pos.y; } } - + //if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos); if(events&COREEVENT_HOOK_ATTACH_PLAYER) create_sound(pos, SOUND_HOOK_ATTACH_PLAYER, cmask_all()); if(events&COREEVENT_HOOK_ATTACH_GROUND) create_sound(pos, SOUND_HOOK_ATTACH_GROUND, mask); //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); - + } - + if(team == -1) { pos.x = input.target_x; @@ -1492,7 +2221,16 @@ { if (dead || team == -1) return; - + if(gotball) + { + core.vel.x = 0; + core.vel.y = 0; + if(goalkeeper) + keepballtick -= server_tickspeed()*config.sv_goal_keeptime; + else + keepballtick -= server_tickspeed()*config.sv_player_keeptime; + } + fire_weapon(); int mode_special = gameobj->on_player_death(this, get_player(killer), weapon); dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d special=%d", @@ -1517,18 +2255,31 @@ die_tick = server_tick(); clear_flag(FLAG_PHYSICS); create_death(pos, client_id); + if(killer != client_id && players[killer].team != team && players[killer].teamkills > 0) + players[killer].teamkills --; + if(config.sv_kick_teamkiller && (killer == client_id || players[killer].team == team) && players[killer].teamkills++ >= config.sv_kick_teamkiller) + { + char kickmessage[256]; + sprintf(kickmessage, "%s was kicked because of teamkilling/selfkillng.", server_clientname(killer)); + send_message(kickmessage, -1); + if(config.sv_ban_teamkiller) + server_ban(killer, config.sv_ban_teamkiller, "You were banned because of teamkilling/selfkilling"); + else + server_kick(killer, "You were kicked because of teamkilling/selfkilling"); + } } bool player::take_damage(vec2 force, int dmg, int from, int weapon) { core.vel += force; - - if(gameobj->is_friendly_fire(client_id, from) && !config.sv_teamdamage) - return false; + + /*if(gameobj->is_friendly_fire(client_id, from) && !config.sv_teamdamage) + return false;*/ // player only inflicts half damage on self if(from == client_id) dmg = max(1, dmg/2); + if(weapon == 3)dmg=0; // CTF and TDM (TODO: check for FF) //if (gameobj->gametype != GAMETYPE_DM && from >= 0 && players[from].team == team) @@ -1557,7 +2308,7 @@ health--; dmg--; } - + if(dmg > armor) { dmg -= armor; @@ -1569,40 +2320,104 @@ dmg = 0; } } - + health -= dmg; } - + /*if (from == client_id && ballonground) + { + gotball=true; + weapons[WEAPON_GRENADE].got = true; + weapons[WEAPON_HAMMER].got = false; + weapons[WEAPON_GRENADE].ammo = 1; + active_weapon = WEAPON_GRENADE; + last_weapon = WEAPON_GRENADE; + queued_weapon = -1; + ballonground=false; + gameobj->on_player_takeflag(get_player(client_id), players[client_id].team); + //dbg_msg("Ball","%s s'est fait une auto pass",server_clientname(client_id)); + keepballtick=-1; + } + else */if (gotball) + { + gotball=false; + weapons[WEAPON_GRENADE].got = false; + weapons[WEAPON_HAMMER].got = true; + active_weapon = WEAPON_HAMMER; + last_weapon = WEAPON_HAMMER; + players[from].weapons[WEAPON_GRENADE].got = true; + players[from].weapons[WEAPON_HAMMER].got = false; + players[from].weapons[WEAPON_GRENADE].ammo = 2; + players[from].gotball=true; + players[from].active_weapon = WEAPON_GRENADE; + players[from].last_weapon = WEAPON_GRENADE; + //char buf[128]; + //str_format(buf, sizeof(buf), "%s took the ball", server_clientname(from)); + //send_chat(-1, CHAT_ALL,buf); + players[from].queued_weapon = -1; + ballonground=false; + gameobj->on_player_rmflag(players[client_id].team); + gameobj->on_player_takeflag(get_player(from), players[from].team); + reload_timer = data->weapons[active_weapon].firedelay *3* server_tickspeed() / 1000; + passer=-1; + //dbg_msg("Ball","%s a pris la balle de force a %s",server_clientname(from),server_clientname(client_id)); + } + else if (!gotball && weapon == 3 && ballonground) + { + gotball=true; + weapons[WEAPON_GRENADE].got = true; + weapons[WEAPON_HAMMER].got = false; + weapons[WEAPON_GRENADE].ammo = 1; + active_weapon = WEAPON_GRENADE; + last_weapon = WEAPON_GRENADE; + /*players[from].weapons[WEAPON_GRENADE].got = false; + players[from].weapons[WEAPON_HAMMER].got = true; + players[from].gotball = false; + players[from].last_weapon = WEAPON_HAMMER; + players[from].active_weapon = WEAPON_HAMMER;*/ + //char buf[128]; + //str_format(buf, sizeof(buf), "%s gave the ball to %s",server_clientname(from),server_clientname(client_id)); + //send_chat(-1, CHAT_ALL,buf); + queued_weapon = -1; + ballonground=false; + //gameobj->on_player_rmflag(players[from].team); + gameobj->on_player_takeflag(get_player(client_id), players[client_id].team); + if (players[client_id].team == players[from].team)passer=from; + //dbg_msg("Ball","%s a passer la balle a %s",server_clientname(from),server_clientname(client_id)); + } + keepballtick = server_tick(); damage_taken_tick = server_tick(); // do damage hit sound - if(from >= 0 && from != client_id) + if(config.sv_explosions && from >= 0 && from != client_id) create_sound(get_player(from)->pos, SOUND_HIT, cmask_one(from)); // check for death - if(health <= 0) + if(config.sv_explosions) { - die(from, weapon); - - // set attacker's face to happy (taunt!) - if (from >= 0 && from != client_id) + if(health <= 0) { - player *p = get_player(from); + die(from, weapon); - p->emote_type = EMOTE_HAPPY; - p->emote_stop = server_tick() + server_tickspeed(); - } + // set attacker's face to happy (taunt!) + if (from >= 0 && from != client_id) + { + player *p = get_player(from); - return false; - } + p->emote_type = EMOTE_HAPPY; + p->emote_stop = server_tick() + server_tickspeed(); + } - if (dmg > 2) - create_sound(pos, SOUND_PLAYER_PAIN_LONG); - else - create_sound(pos, SOUND_PLAYER_PAIN_SHORT); + return false; + } + + if (dmg > 2) + create_sound(pos, SOUND_PLAYER_PAIN_LONG); + else + create_sound(pos, SOUND_PLAYER_PAIN_SHORT); - emote_type = EMOTE_PAIN; - emote_stop = server_tick() + 500 * server_tickspeed() / 1000; + emote_type = EMOTE_PAIN; + emote_stop = server_tick() + 500 * server_tickspeed() / 1000; + } // spawn blood? return true; @@ -1649,7 +2464,7 @@ character->ammocount = 0; character->health = 0; character->armor = 0; - + character->weapon = active_weapon; character->attacktick = attack_tick; @@ -1710,15 +2525,24 @@ void powerup::tick() { // wait for respawn + int allowspawn=1; + for (int i=0;i < MAX_CLIENTS; i++) + { + if(players[i].client_id !=-1 && players[i].gotball == true) + allowspawn=0; + } if(spawntick > 0) { - if(server_tick() > spawntick) + if(server_tick() > spawntick && allowspawn &&(server_tick()-respawnstarttick > server_tickspeed()*config.sv_ball_respawn_time) && !ballonground) { // respawn spawntick = -1; + respawnstarttick=-1; if(type == POWERUP_WEAPON) create_sound(pos, SOUND_WEAPON_SPAWN); + if(!vote_called) + send_display("Ball respawned", -1); } else return; @@ -1754,8 +2578,21 @@ if(pplayer->weapons[subtype].ammo < data->weapons[subtype].maxammo || !pplayer->weapons[subtype].got) { pplayer->weapons[subtype].got = true; - pplayer->weapons[subtype].ammo = min(data->weapons[subtype].maxammo, pplayer->weapons[subtype].ammo + data->powerupinfo[type].amount); - respawntime = data->powerupinfo[type].respawntime; + pplayer->weapons[subtype].ammo = 1; + pplayer->gotball=true; + pplayer->queued_weapon = -1; + pplayer->weapons[WEAPON_HAMMER].got = false; + pplayer->active_weapon = WEAPON_GRENADE; + pplayer->last_weapon = WEAPON_GRENADE; + gameobj->on_player_takeflag(pplayer, pplayer->team); + ballonground=false; + respawntime = -1; + pplayer->keepballtick = server_tick(); + spawntick=1; + passer=-1; + if(!vote_called) + send_display("", -1); + //dbg_msg("Ball","%s a pris la balle au spawn",server_clientname(pplayer->client_id)); // TODO: data compiler should take care of stuff like this if(subtype == WEAPON_GRENADE) @@ -1795,7 +2632,7 @@ pplayer->emote_type = EMOTE_ANGRY; pplayer->emote_stop = server_tick() + 1200 * server_tickspeed() / 1000; - + break; } default: @@ -1852,18 +2689,21 @@ void create_explosion(vec2 p, int owner, int weapon, bool bnodamage) { // create the event - NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)events.create(NETEVENTTYPE_EXPLOSION, sizeof(NETEVENT_EXPLOSION)); - if(ev) + if(config.sv_explosions) { - ev->x = (int)p.x; - ev->y = (int)p.y; + NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)events.create(NETEVENTTYPE_EXPLOSION, sizeof(NETEVENT_EXPLOSION)); + if(ev) + { + ev->x = (int)p.x; + ev->y = (int)p.y; + } } if (!bnodamage) { // deal damage entity *ents[64]; - float radius = 128.0f; + float radius = 50.0f; float innerradius = 42.0f; int num = world->find_entities(p, radius, ents, 64); @@ -1877,7 +2717,7 @@ l = 1-clamp((l-innerradius)/(radius-innerradius), 0.0f, 1.0f); float dmg = 6 * l; if((int)dmg) - ents[i]->take_damage(forcedir*dmg*2, (int)dmg, owner, weapon); + ents[i]->take_damage(forcedir*dmg*0, (int)dmg, owner, weapon); } } } @@ -1944,18 +2784,18 @@ } // TODO: should be more general -player *intersect_player(vec2 pos0, vec2 pos1, float radius, vec2& new_pos, entity *notthis) +player *intersect_player(vec2 pos0, vec2 pos1, float radius, vec2& new_pos, entity *notthis, int realstart_tick) { // Find other players float closest_len = distance(pos0, pos1) * 100.0f; vec2 line_dir = normalize(pos1-pos0); player *closest = 0; - + for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i].client_id < 0 || (entity *)&players[i] == notthis) + if(players[i].client_id < 0 || ((entity *)&players[i] == notthis && (server_tick()-realstart_tick < server_tickspeed()/2 || realstart_tick ==-1))) continue; - + if(!(players[i].flags&entity::FLAG_PHYSICS)) continue; @@ -1971,7 +2811,7 @@ } } } - + return closest; } @@ -1981,12 +2821,12 @@ // Find other players float closest_range = radius*2; player *closest = 0; - + for(int i = 0; i < MAX_CLIENTS; i++) { if(players[i].client_id < 0 || (entity *)&players[i] == notthis) continue; - + if(!(players[i].flags&entity::FLAG_PHYSICS)) continue; @@ -2000,10 +2840,36 @@ } } } - + return closest; } +player *closest_team_player(vec2 pos, float radius, entity *notthis,int playerteam) +{ + // Find other players + float closest_range = radius*2; + player *closest = 0; + + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id < 0 || (entity *)&players[i] == notthis || players[i].team==playerteam) + continue; + + if(!(players[i].flags&entity::FLAG_PHYSICS)) + continue; + float len = distance(pos, players[i].pos); + if(len < player::phys_size+radius) + { + if(len < closest_range) + { + closest_range = len; + closest = &players[i]; + } + } + } + + return closest; +} // Server hooks void mods_tick() { @@ -2012,6 +2878,12 @@ if(world->paused) // make sure that the game object always updates gameobj->tick(); + if(config.sv_teleport) { + tele = tele?0:1; + char buf[128]; + tele?sprintf(buf,"Teleport tiles will teleport tee's."):sprintf(buf,"Teleport tiles will no longer teleport tee's."); + send_chat(-1,-2,buf); + config.sv_teleport = 0;} } void mods_snap(int client_id) @@ -2024,7 +2896,7 @@ { if(!world->paused) players[client_id].on_direct_input((NETOBJ_PLAYER_INPUT *)input); - + /* if(i->fire) { @@ -2044,7 +2916,7 @@ //players[client_id].previnput = players[client_id].input; players[client_id].input = *(NETOBJ_PLAYER_INPUT*)input; players[client_id].num_inputs++; - + if(players[client_id].input.target_x == 0 && players[client_id].input.target_y == 0) players[client_id].input.target_y = -1; } @@ -2060,8 +2932,23 @@ char buf[512]; str_format(buf, sizeof(buf), "%s entered and joined the %s", server_clientname(client_id), get_team_name(players[client_id].team)); - send_chat(-1, CHAT_ALL, buf); - + send_chat(-1, CHAT_ALL, buf); + players[client_id].gotball=false; + players[client_id].goalkeeper=false; + players[client_id].voted=false; + players[client_id].votekick=0; + players[client_id].votedfor=-1; + players[client_id].teamkills = 0; + players[client_id].goalchanges = 0; + if(strlen(config.sv_startmessage) > 0) + { + NETMSG_SV_CHAT msg; + msg.team = 0; + msg.cid = -1; + msg.message = config.sv_startmessage; + msg.pack(MSGFLAG_VITAL); + server_send_msg(client_id); + } dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), players[client_id].team); } @@ -2069,7 +2956,7 @@ { players[client_id].init(); players[client_id].client_id = client_id; - + // Check which team the player should be on if(config.sv_tournament_mode) players[client_id].team = -1; @@ -2088,7 +2975,6 @@ char buf[512]; str_format(buf, sizeof(buf), "%s has left the game", server_clientname(client_id)); send_chat(-1, CHAT_ALL, buf); - dbg_msg("game", "leave player='%d:%s'", client_id, server_clientname(client_id)); gameobj->on_player_death(&players[client_id], 0, -1); @@ -2105,7 +2991,7 @@ dbg_msg("server", "dropped weird message '%s' (%d), failed on '%s'", netmsg_get_name(msgtype), msgtype, netmsg_failed_on()); return; } - + if(msgtype == NETMSGTYPE_CL_SAY) { NETMSG_CL_SAY *msg = (NETMSG_CL_SAY *)rawmsg; @@ -2114,15 +3000,334 @@ team = players[client_id].team; else team = CHAT_ALL; - + if(config.sv_spamprotection && players[client_id].last_chat+time_freq() > time_get()) { // consider this as spam } else { + if(!stricmp(msg->message, ".info")) + { + char commands[768]="Foot mod version-0.7 from Rajh and mod from scosu (http://scosu.de). Available commands:\n"; + if(config.sv_handle_mapvotes) + { + strcat(commands, "Positive map-voting: /++\n"); + strcat(commands, "Negative map-voting: /--\n"); + } + strcat(commands, "Current vote: /currentvote\n"); + strcat(commands, "List of player-ids: /players\n"); + if(config.sv_allow_votes) + { + strcat(commands, "Kick player by name: /kick \n"); + strcat(commands, "Kick player by id: /kick_id \n"); + } + if(config.sv_allow_next_map) + { + strcat(commands, "Start vote for change to next map: /next_map\n"); + } + if(config.sv_allow_timelimit) + { + strcat(commands, "Start vote for set timelimit: /timelimit \n"); + } + if(config.sv_allow_scorelimit) + { + strcat(commands, "Start vote for set scorelimit: /scorelimit \n"); + } + if(config.sv_goalkeeper) + { + strcat(commands, "Become goalkeeper: /goalkeeper\n"); + } + if(config.sv_allow_gametype_votes) + { + strcat(commands,"Vote for changing to football: /vote foot, or handball: /vote hand"); + } + send_message(commands, client_id); + } + else if (!strnicmp(msg->message, "/kick ",6) && !vote_called && config.sv_allow_votes) + { + if(config.sv_vote_pause && players[client_id].lastvote != 0 && (server_tick() - players[client_id].lastvote)/server_tickspeed() < config.sv_vote_pause) + { + char novote[90]; + sprintf(novote, "You can start votings in %i seconds", config.sv_vote_pause-(server_tick() - players[client_id].lastvote)/server_tickspeed()); + send_display(novote, client_id); + return; + } + int playersnumber=0; + int playerstokick=-1; + char message[256]; + for (int i=0; i < MAX_CLIENTS;i++) + { + if(players[i].client_id !=-1) + { + str_format(message, sizeof(message), "/kick "); + strcat(message,server_clientname(i)); + if(!strncmp(msg->message, message,9)) + { + playersnumber++; + playerstokick=i; + } + } + } + if(playersnumber == 1 && playerstokick != -1) + { + votetime = config.sv_votetime; + str_format(votetype, sizeof(votetype), "kick"); + votedtokick=playerstokick; + str_format(vote_message, sizeof(vote_message), "Vote for: kick %s\nsay \"/yes\" or \"/no\" (%s)", server_clientname(playerstokick),server_clientname(client_id)); + send_display(vote_message,-1); + vote_called=true; + timer_vote = server_tick(); + players[client_id].votedfor = 1; + players[client_id].lastvote = server_tick(); + server_save_ip(votedtokick); + } + else if(playersnumber > 1) + { + send_display("Several players possible", client_id); + } + else + { + send_display("There is no player with this name", client_id); + } + } + else if (!strnicmp(msg->message, "/kick_id ",9) && !vote_called && config.sv_allow_votes) + { + if(config.sv_vote_pause && players[client_id].lastvote != 0 && (server_tick() - players[client_id].lastvote)/server_tickspeed() < config.sv_vote_pause) + { + char novote[90]; + sprintf(novote, "You can start votings in %i seconds", config.sv_vote_pause-(server_tick() - players[client_id].lastvote)/server_tickspeed()); + send_display(novote, client_id); + return; + } + votedtokick = atoi(&msg->message[9]); + if(votedtokick > 0 && votedtokick-- <= MAX_CLIENTS && players[votedtokick].client_id != -1) + { + votetime = config.sv_votetime; + str_format(votetype, sizeof(votetype), "kick"); + str_format(vote_message, sizeof(vote_message), "Vote for: kick %s\nsay \"/yes\" or \"/no\" (%s)",server_clientname(votedtokick), server_clientname(client_id)); + send_display(vote_message,-1); + vote_called=true; + timer_vote = server_tick(); + players[client_id].votedfor = 1; + players[client_id].lastvote = server_tick(); + server_save_ip(votedtokick); + } + else + { + votedtokick = -1; + send_display("That was no valid integer", client_id); + } + } + else if (!strnicmp(msg->message, "/next_map",9) && !vote_called && config.sv_allow_next_map) + { + if(config.sv_vote_pause && players[client_id].lastvote != 0 && (server_tick() - players[client_id].lastvote)/server_tickspeed() < config.sv_vote_pause) + { + char novote[90]; + sprintf(novote, "You can start votings in %i seconds", config.sv_vote_pause-(server_tick() - players[client_id].lastvote)/server_tickspeed()); + send_display(novote, client_id); + return; + } + votetime = config.sv_votetime; + str_format(votetype, sizeof(votetype), "next_map"); + str_format(vote_message, sizeof(vote_message), "Vote for: changing to next map\nsay \"/yes\" or \"/no\" (%s)",server_clientname(client_id)); + send_display(vote_message,-1); + vote_called=true; + timer_vote = server_tick(); + players[client_id].votedfor = 1; + players[client_id].lastvote = server_tick(); + } + else if (!strnicmp(msg->message, "/timelimit ",11) && !vote_called && config.sv_allow_timelimit) + { + if(config.sv_vote_pause && players[client_id].lastvote != 0 && (server_tick() - players[client_id].lastvote)/server_tickspeed() < config.sv_vote_pause) + { + char novote[90]; + sprintf(novote, "You can start votings in %i seconds", config.sv_vote_pause-(server_tick() - players[client_id].lastvote)/server_tickspeed()); + send_display(novote, client_id); + return; + } + votedtokick = atoi(&msg->message[11]); + if(votedtokick == -1 || (votedtokick>0 && votedtokick < 1000)) + { + votetime = config.sv_votetime; + str_format(votetype, sizeof(votetype), "timelimit"); + str_format(vote_message, sizeof(vote_message), "Vote for: set timelimit to %i\nsay \"/yes\" or \"/no\" (%s)",votedtokick, server_clientname(client_id)); + send_display(vote_message,-1); + vote_called=true; + timer_vote = server_tick(); + players[client_id].votedfor = 1; + players[client_id].lastvote = server_tick(); + } + else + { + votedtokick = -1; + send_display("That was no valid integer", client_id); + } + } + else if (!strnicmp(msg->message, "/scorelimit ",12) && !vote_called && config.sv_allow_scorelimit) + { + if(config.sv_vote_pause && players[client_id].lastvote != 0 && (server_tick() - players[client_id].lastvote)/server_tickspeed() < config.sv_vote_pause) + { + char novote[90]; + sprintf(novote, "You can start votings in %i seconds", config.sv_vote_pause-(server_tick() - players[client_id].lastvote)/server_tickspeed()); + send_display(novote, client_id); + return; + } + votedtokick = atoi(&msg->message[12]); + if(votedtokick == -1 || (votedtokick>0 && votedtokick < 1000)) + { + votetime = config.sv_votetime; + str_format(votetype, sizeof(votetype), "scorelimit"); + str_format(vote_message, sizeof(vote_message), "Vote for: set scorelimit to %i\nsay \"/yes\" or \"/no\" (%s)",votedtokick, server_clientname(client_id)); + send_display(vote_message,-1); + vote_called=true; + timer_vote = server_tick(); + players[client_id].votedfor = 1; + players[client_id].lastvote = server_tick(); + } + else + { + votedtokick = -1; + send_display("That was no valid integer", client_id); + } + } + else if (!strnicmp(msg->message, "/yes",3) && players[client_id].votedfor == -1 && vote_called) + { + char message[256]; + str_format(message, sizeof(message), "%s voted for yes",server_clientname(client_id)); + send_chat(-1,CHAT_ALL,message); + players[client_id].votedfor = 1; + //resultvote(); + } + else if (!strnicmp(msg->message, "/no",2) && players[client_id].votedfor == -1 && vote_called) + { + char buf[256]; + str_format(buf, sizeof(buf), "%s voted for no",server_clientname(client_id)); + send_message(buf, -1); + players[client_id].votedfor = 0; + //resultvote(); + } + else if (!strnicmp(msg->message, "/currentvote",12) && vote_called) + { + send_display(vote_message, client_id); + resultvote(); + } + else if (!strnicmp(msg->message, "/players",2)) + { + char all_players[512] = "Players:\n"; + int i = 0; + for(;i < MAX_CLIENTS;i++) + { + if(players[i].client_id != -1) + { + char player[5]; + sprintf(player, "%i", i+1); + strcat(all_players, player); + strcat(all_players, ": "); + strcat(all_players, server_clientname(i)); + strcat(all_players, "\n"); + } + } + send_message(all_players, client_id); + } + else if(!stricmp(msg->message, "/goalkeeper")) + { + if(config.sv_goalkeeper){ + if(players[client_id].goalkeeper) + { + char buf[128]; + str_format(buf, sizeof(buf), "%s is not a goalkeeper any more.",server_clientname(client_id)); + send_chat(-1,CHAT_ALL,buf); + players[client_id].goalkeeper=false; + players[client_id].goalchanges++; + players[client_id].respawn(); + } + else + { + int taken=false; + for(int i=0;i < MAX_CLIENTS;i++) + { + if(players[i].client_id != -1 && players[i].goalkeeper && players[i].team == players[client_id].team) + taken=true; + } + if (taken) + { + send_display("Sorry, there is already a goal keeper in your team.", client_id); + } + else + { + char buf[128]; + str_format(buf, sizeof(buf), "%s is now the goal keeper.",server_clientname(client_id)); + send_chat(-1,CHAT_ALL,buf); + players[client_id].goalkeeper=true; + players[client_id].goalchanges++; + players[client_id].respawn(); + } + } + if(config.sv_goalkeeper_changes && players[client_id].goalchanges > config.sv_goalkeeper_changes) + { + char ban[100]; + sprintf(ban, "%s was kicked because he/she became too often goalkeeper", server_clientname(client_id)); + send_message(ban, -1); + if(config.sv_goalkeeper_changes_ban) + { + server_ban(client_id, config.sv_goalkeeper_changes_ban, "You were banned because you became too often goalkeeper"); + } + else + { + server_kick(client_id, "You were kicked because you became too often goalkeeper"); + } + } + } + else{ + send_display("Goalkeeper disabled.", client_id); + } + } + else if(!stricmp(msg->message, "/help")) + { + send_message("The grenade is the ball. Take it and fire it (with the grenade launcher) into the enemy goal.\nIf you pass the ball to a friend your team will earn more points.", client_id); + } + else if (!strnicmp(msg->message, "/vote",5) && !vote_called && config.sv_allow_gametype_votes) + { + if(config.sv_vote_pause && players[client_id].lastvote != 0 && (server_tick() - players[client_id].lastvote)/server_tickspeed() < config.sv_vote_pause) + { + char novote[90]; + sprintf(novote, "You can start votings in %i seconds", config.sv_vote_pause-(server_tick() - players[client_id].lastvote)/server_tickspeed()); + send_display(novote, client_id); + return; + } + char message[256]; + if(!stricmp(msg->message, "/vote foot")) + { + votetime =config.sv_votetime; + str_format(votetype, sizeof(votetype), "foot"); + char buf[512]; + str_format(buf, sizeof(buf), "Vote for: changing mod to foot \nsay \"/yes\" or \"/no\" (%s)", server_clientname(client_id)); + send_display(buf,-1); + vote_called=true; + players[client_id].votedfor = 1; + timer_vote = server_tick(); + players[client_id].lastvote = server_tick(); + } + else + { + votetime =config.sv_votetime; + str_format(votetype, sizeof(votetype), "handball"); + char buf[512]; + str_format(buf, sizeof(buf), "Vote for: changing mod to hand\nsay \"/yes\" or \"/no\" (%s)", server_clientname(client_id)); + send_display(buf,-1); + vote_called=true; + players[client_id].votedfor = 1; + timer_vote = server_tick(); + players[client_id].lastvote = server_tick(); + } + } + else + { + players[client_id].last_chat = time_get(); + send_chat(client_id, team, msg->message); + } players[client_id].last_chat = time_get(); - send_chat(client_id, team, msg->message); } } else if (msgtype == NETMSGTYPE_CL_SETTEAM) @@ -2159,7 +3364,7 @@ // copy old name char oldname[MAX_NAME_LENGTH]; str_copy(oldname, server_clientname(client_id), MAX_NAME_LENGTH); - + server_setclientname(client_id, msg->name); if(msgtype == NETMSGTYPE_CL_CHANGEINFO && strcmp(oldname, server_clientname(client_id)) != 0) { @@ -2167,16 +3372,16 @@ str_format(chattext, sizeof(chattext), "%s changed name to %s", oldname, server_clientname(client_id)); send_chat(-1, CHAT_ALL, chattext); } - + // set skin str_copy(players[client_id].skin_name, msg->skin, sizeof(players[client_id].skin_name)); - + gameobj->on_player_info_change(&players[client_id]); - + if(msgtype == NETMSGTYPE_CL_STARTINFO) { // a client that connected! - + // send all info to this client for(int i = 0; i < MAX_CLIENTS; i++) { @@ -2186,13 +3391,13 @@ // send tuning parameters to client send_tuning_params(client_id); - + // NETMSG_SV_READY_TO_ENTER m; m.pack(MSGFLAG_VITAL); - server_send_msg(client_id); + server_send_msg(client_id); } - + send_info(client_id, -1); } else if (msgtype == NETMSGTYPE_CL_EMOTICON) @@ -2265,25 +3470,116 @@ { int client_id = clamp(console_arg_int(result, 0), 0, (int)MAX_CLIENTS); int team = clamp(console_arg_int(result, 1), -1, 1); - + dbg_msg("", "%d %d", client_id, team); - + + if(players[client_id].client_id != client_id) + return; + + players[client_id].set_team(team); +} +static void con_foot_mod(void *result, void *user_data) +{ + int enable = clamp(console_arg_int(result, 0), 0, 1); + + if(enable) { + char buf[128]; + str_format(buf, sizeof(buf), "Foot ball mod enabled. say /goalkeeper to be the goalkeeper"); + send_chat(-1,CHAT_ALL,buf); + config.sv_goalkeeper = 1; + config.sv_goal_keeptime = 3; + config.sv_player_keeptime = 0; + config.sv_realfoot = 1; + for(int i=0;i < MAX_CLIENTS;i++){ + if(players[i].client_id != -1) + players[i].goalkeeper=false; + } + } + else if(!enable) { + char buf[128]; + str_format(buf, sizeof(buf), "Foot ball mod disabled"); + send_chat(-1,CHAT_ALL,buf); + config.sv_goalkeeper = 0; + config.sv_player_keeptime = 1000; + config.sv_realfoot = 0; + for(int i=0;i < MAX_CLIENTS;i++){ + if(players[i].client_id != -1) + players[i].goalkeeper=false; + } + } +} +static void con_get_pos(void *result, void *user_data) +{ + int client_id = clamp(console_arg_int(result, 0), 0, (int)MAX_CLIENTS); + int team = clamp(console_arg_int(result, 1), -1, 1); + + dbg_msg("Getpos", "%d %d", players[console_arg_int(result, 0)].core.pos.x,players[console_arg_int(result, 0)].core.pos.x); + if(players[client_id].client_id != client_id) return; - + players[client_id].set_team(team); } +static void con_debugmod(void *result, void *user_data) +{ + dbg_msg("debugmod", "Respawnstarttick = %d server_tick()=%d ballonground = %d passer=%s", respawnstarttick, server_tick(),ballonground,server_clientname(passer)); +} + +static void con_vote(void *result, void *user_data) +{ + votetime =console_arg_int(result, 0); + char buf[512]; + str_format(buf, sizeof(buf), "||| Vote for: %s (say \"/yes\" or \"/no\") |||", console_arg_string(result, 1)); + send_chat(-1, CHAT_ALL, buf); + vote_called=true; + //if (console_arg_int(result, 0) == 1) + timer_vote = server_tick(); + //else world->timer_vote = -1; +} +static void con_kill_pl(void *result, void *user_data) +{ + players[console_arg_int(result, 0)].die(-1,-1); + char buf[512]; + str_format(buf, sizeof(buf), "%s Killed by admin", server_clientname(console_arg_int(result, 0))); + send_chat(-1, CHAT_ALL, buf); +} + +static void con_next_map(void *result, void *user_data) +{ + gameobj->endround(); +} + +static void con_cancel_vote(void *result, void *user_data) +{ + str_format(votetype, sizeof(votetype), "null"); + votedtokick = -1; + for (int i=0; i < MAX_CLIENTS;i++) + { + if(players[i].client_id !=-1) + { + players[i].votedfor=-1; + } + } + vote_called = false; + send_display("Vote canceled by admin", -1); +} + void mods_console_init() { MACRO_REGISTER_COMMAND("tune", "si", con_tune_param, 0); MACRO_REGISTER_COMMAND("tune_reset", "", con_tune_reset, 0); MACRO_REGISTER_COMMAND("tune_dump", "", con_tune_dump, 0); - + MACRO_REGISTER_COMMAND("debugmod", "", con_debugmod, 0); + MACRO_REGISTER_COMMAND("foot_mod", "i", con_foot_mod, 0); + MACRO_REGISTER_COMMAND("kill_pl", "i", con_kill_pl, 0); + MACRO_REGISTER_COMMAND("vote", "ir", con_vote, 0); MACRO_REGISTER_COMMAND("restart", "?i", con_restart, 0); MACRO_REGISTER_COMMAND("broadcast", "r", con_broadcast, 0); MACRO_REGISTER_COMMAND("say", "r", con_say, 0); MACRO_REGISTER_COMMAND("set_team", "ii", con_set_team, 0); + MACRO_REGISTER_COMMAND("next_map", "", con_next_map, 0); + MACRO_REGISTER_COMMAND("cancel_vote", "", con_cancel_vote, 0); } void mods_init() @@ -2312,11 +3608,11 @@ // create all entities from the game layer MAPITEM_LAYER_TILEMAP *tmap = layers_game_layer(); TILE *tiles = (TILE *)map_get_data(tmap->data); - + num_spawn_points[0] = 0; num_spawn_points[1] = 0; num_spawn_points[2] = 0; - + for(int y = 0; y < tmap->height; y++) { for(int x = 0; x < tmap->width; x++)