#include #include #include #include #include "character.hpp" #include "laser.hpp" #include "projectile.hpp" struct INPUT_COUNT { int presses; int releases; }; static INPUT_COUNT count_input(int prev, int cur) { INPUT_COUNT c = {0,0}; prev &= INPUT_STATE_MASK; cur &= INPUT_STATE_MASK; int i = prev; while(i != cur) { i = (i+1)&INPUT_STATE_MASK; if(i&1) c.presses++; else c.releases++; } return c; } // player CHARACTER::CHARACTER() : ENTITY(NETOBJTYPE_CHARACTER) {} void CHARACTER::reset() { } bool CHARACTER::spawn(PLAYER *player, vec2 pos, int team) { player_state = PLAYERSTATE_UNKNOWN; emote_stop = -1; last_action = -1; active_weapon = WEAPON_GUN; last_weapon = WEAPON_HAMMER; queued_weapon = -1; //clear(); this->player = player; this->pos = pos; this->team = team; core.reset(); core.world = &game.world.core; core.pos = pos; game.world.core.characters[player->client_id] = &core; game.world.insert_entity(this); alive = true; return true; } void CHARACTER::destroy() { game.world.core.characters[player->client_id] = 0; alive = false; } void CHARACTER::set_weapon(int w) { 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; game.create_sound(pos, SOUND_WEAPON_SWITCH); } bool CHARACTER::is_grounded() { if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5))) return true; if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5))) return true; return false; } int CHARACTER::handle_ninja() { vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); if ((server_tick() - ninja.activationtick) > (data->weapons.ninja.duration * server_tickspeed() / 1000)) { // time's up, return weapons[WEAPON_NINJA].got = false; active_weapon = last_weapon; if(active_weapon == WEAPON_NINJA) active_weapon = WEAPON_GUN; set_weapon(active_weapon); return 0; } // force ninja weapon set_weapon(WEAPON_NINJA); // Check if it should activate if (count_input(latest_previnput.fire, latest_input.fire).presses && (server_tick() > ninja.currentcooldown)) { // ok then, activate ninja attack_tick = server_tick(); ninja.activationdir = direction; ninja.currentmovetime = data->weapons.ninja.movetime * server_tickspeed() / 1000; ninja.currentcooldown = data->weapons.ninja.base->firedelay * server_tickspeed() / 1000 + server_tick(); // reset hit objects numobjectshit = 0; game.create_sound(pos, SOUND_NINJA_FIRE); // release all hooks when ninja is activated //release_hooked(); //release_hooks(); } ninja.currentmovetime--; if (ninja.currentmovetime == 0) { // reset player velocity core.vel *= 0.2f; //return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON; } if (ninja.currentmovetime > 0) { // Set player velocity core.vel = ninja.activationdir * data->weapons.ninja.velocity; vec2 oldpos = pos; move_box(&core.pos, &core.vel, vec2(phys_size, phys_size), 0.0f); // reset velocity so the client doesn't predict stuff core.vel = vec2(0.0f,0.0f); if ((ninja.currentmovetime % 2) == 0) { //create_smoke(pos); } // check if we hit anything along the way { CHARACTER *ents[64]; vec2 dir = pos - oldpos; float radius = phys_size * 2.0f; //length(dir * 0.5f); vec2 center = oldpos + dir * 0.5f; int num = game.world.find_entities(center, radius, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER); for (int i = 0; i < num; i++) { // Check if entity is a player if (ents[i] == this) continue; // make sure we haven't hit this object before bool balreadyhit = false; for (int j = 0; j < numobjectshit; j++) { if (hitobjects[j] == ents[i]) balreadyhit = true; } if (balreadyhit) continue; // check so we are sufficiently close if (distance(ents[i]->pos, pos) > (phys_size * 2.0f)) continue; // hit a player, give him damage and stuffs... game.create_sound(ents[i]->pos, SOUND_NINJA_HIT); // set his velocity to fast upward (for now) if(numobjectshit < 10) hitobjects[numobjectshit++] = ents[i]; ents[i]->take_damage(vec2(0,10.0f), data->weapons.ninja.base->damage, player->client_id,WEAPON_NINJA); } } return 0; } return 0; } void CHARACTER::do_weaponswitch() { if(reload_timer != 0) // make sure we have reloaded return; if(queued_weapon == -1) // check for a queued weapon return; if(weapons[WEAPON_NINJA].got) // if we have ninja, no weapon selection is possible return; // switch weapon set_weapon(queued_weapon); } void CHARACTER::handle_weaponswitch() { 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; if(next < 128) // make sure we only try sane stuff { while(next) // next weapon selection { wanted_weapon = (wanted_weapon+1)%NUM_WEAPONS; if(weapons[wanted_weapon].got) next--; } } if(prev < 128) // make sure we only try sane stuff { while(prev) // prev weapon selection { wanted_weapon = (wanted_weapon-1)<0?NUM_WEAPONS-1:wanted_weapon-1; if(weapons[wanted_weapon].got) prev--; } } // direct weapon selection if(latest_input.wanted_weapon) wanted_weapon = input.wanted_weapon-1; // 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(); } void CHARACTER::fire_weapon() { 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) 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(!will_fire) return; // check for ammo if(!weapons[active_weapon].ammo) { game.create_sound(pos, SOUND_WEAPON_NOAMMO); return; } vec2 projectile_startpos = pos+direction*phys_size*0.75f; switch(active_weapon) { case WEAPON_HAMMER: { // reset objects hit numobjectshit = 0; game.create_sound(pos, SOUND_HAMMER_FIRE); CHARACTER *ents[64]; int num = game.world.find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER); for (int i = 0; i < num; i++) { CHARACTER *target = 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) game.create_sound(pos, SOUND_HAMMER_HIT); ents[i]->take_damage(vec2(0,-1.0f), data->weapons.hammer.base->damage, player->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: { PROJECTILE *proj = new PROJECTILE(WEAPON_GUN, player->client_id, projectile_startpos, direction, (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_EXTRAPROJECTILE, 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(player->client_id); game.create_sound(pos, SOUND_GUN_FIRE); } break; case WEAPON_SHOTGUN: { int shotspread = 2; msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 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}; float a = get_angle(direction); a += spreading[i+2]; float v = 1-(abs(i)/(float)shotspread); float speed = mix((float)tuning.shotgun_speeddiff, 1.0f, v); PROJECTILE *proj = new PROJECTILE(WEAPON_SHOTGUN, player->client_id, projectile_startpos, vec2(cosf(a), sinf(a))*speed, (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(player->client_id); game.create_sound(pos, SOUND_SHOTGUN_FIRE); } break; case WEAPON_GRENADE: { PROJECTILE *proj = new PROJECTILE(WEAPON_GRENADE, player->client_id, projectile_startpos, direction, (int)(server_tickspeed()*tuning.grenade_lifetime), this, 1, PROJECTILE::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_GRENADE_EXPLODE, WEAPON_GRENADE); // pack the projectile and send it to the client directly NETOBJ_PROJECTILE p; proj->fill_info(&p); msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 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(player->client_id); game.create_sound(pos, SOUND_GRENADE_FIRE); } break; case WEAPON_RIFLE: { new LASER(pos, direction, tuning.laser_reach, this); game.create_sound(pos, SOUND_RIFLE_FIRE); } break; } if(weapons[active_weapon].ammo > 0) // -1 == unlimited weapons[active_weapon].ammo--; attack_tick = server_tick(); reload_timer = data->weapons.id[active_weapon].firedelay * server_tickspeed() / 1000; } int CHARACTER::handle_weapons() { vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); /* if(config.dbg_stress) { for(int i = 0; i < NUM_WEAPONS; i++) { weapons[i].got = true; weapons[i].ammo = 10; } if(reload_timer) // twice as fast reload reload_timer--; } */ // check reload timer if(reload_timer) { reload_timer--; return 0; } if (active_weapon == WEAPON_NINJA) { // don't update other weapons while ninja is active return handle_ninja(); } // fire weapon, if wanted fire_weapon(); // ammo regen int ammoregentime = data->weapons.id[active_weapon].ammoregentime; if(ammoregentime) { // If equipped and not active, regen ammo? if (reload_timer <= 0) { if (weapons[active_weapon].ammoregenstart < 0) weapons[active_weapon].ammoregenstart = server_tick(); if ((server_tick() - weapons[active_weapon].ammoregenstart) >= ammoregentime * server_tickspeed() / 1000) { // Add some ammo weapons[active_weapon].ammo = min(weapons[active_weapon].ammo + 1, 10); weapons[active_weapon].ammoregenstart = -1; } } else { weapons[active_weapon].ammoregenstart = -1; } } return 0; } void CHARACTER::on_predicted_input(NETOBJ_PLAYER_INPUT *new_input) { // check for changes if(mem_comp(&input, new_input, sizeof(NETOBJ_PLAYER_INPUT)) != 0) last_action = server_tick(); // copy new input mem_copy(&input, new_input, sizeof(input)); num_inputs++; // or are not allowed to aim in the center if(input.target_x == 0 && input.target_y == 0) input.target_y = -1; } void CHARACTER::on_direct_input(NETOBJ_PLAYER_INPUT *new_input) { mem_copy(&latest_previnput, &latest_input, sizeof(latest_input)); mem_copy(&latest_input, new_input, sizeof(latest_input)); if(num_inputs > 2 && team != -1) { handle_weaponswitch(); fire_weapon(); } } void CHARACTER::tick() { //input = latest_input; // grab latest input /* { int size = 0; int *input = server_latestinput(client_id, &size); if(input) { mem_copy(&latest_previnput, &latest_input, sizeof(latest_input)); mem_copy(&latest_input, input, sizeof(latest_input)); } }*/ // check if we have enough input // this is to prevent initial weird clicks /* if(num_inputs < 2) { latest_previnput = latest_input; previnput = input; }*/ //game.world.core.players[player->client_id] = &core; // enable / disable physics /* if(team == -1 || dead) { game.world.core.players[client_id] = 0; //game.world.remove_entity(this); } else { game.world.core.players[client_id] = &core; //game.world._entity(this); } // spectator if(team == -1) return; if(spawning) try_respawn(); // TODO: rework the input to be more robust if(dead) { if(server_tick()-die_tick >= server_tickspeed()/2 && count_input(latest_previnput.fire, latest_input.fire).presses) die_tick = -1; if(server_tick()-die_tick >= server_tickspeed()*5) // auto respawn after 3 sec respawn(); //if((input.fire&1) && server_tick()-die_tick >= server_tickspeed()/2) // auto respawn after 0.5 sec //respawn(); return; } * */ //player_core core; //core.pos = pos; //core.jumped = jumped; core.input = input; core.tick(); // handle weapons handle_weapons(); player_state = input.player_state; // Previnput previnput = input; return; } void CHARACTER::tick_defered() { /*if(!dead) {*/ 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", stuck_before, stuck_after_move, stuck_after_quant, start_pos.x, start_pos.y, start_vel.x, start_vel.y, *((unsigned *)&start_pos.x), *((unsigned *)&start_pos.y), *((unsigned *)&start_vel.x), *((unsigned *)&start_vel.y)); } int events = core.triggered_events; int mask = cmask_all_except_one(player->client_id); if(events&COREEVENT_GROUND_JUMP) game.create_sound(pos, SOUND_PLAYER_JUMP, mask); if(events&COREEVENT_AIR_JUMP) { game.create_sound(pos, SOUND_PLAYER_AIRJUMP, mask); NETEVENT_COMMON *c = (NETEVENT_COMMON *)game.events.create(NETEVENTTYPE_AIRJUMP, sizeof(NETEVENT_COMMON), mask); if(c) { c->x = (int)pos.x; 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) game.create_sound(pos, SOUND_HOOK_ATTACH_PLAYER, cmask_all()); if(events&COREEVENT_HOOK_ATTACH_GROUND) game.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; pos.y = input.target_y; } } bool CHARACTER::increase_health(int amount) { if(health >= 10) return false; health = clamp(health+amount, 0, 10); return true; } bool CHARACTER::increase_armor(int amount) { if(armor >= 10) return false; armor = clamp(armor+amount, 0, 10); return true; } void CHARACTER::die(int killer, int weapon) { /*if (dead || team == -1) return;*/ int mode_special = game.controller->on_character_death(this, &game.players[killer], weapon); dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d special=%d", killer, server_clientname(killer), player->client_id, server_clientname(player->client_id), weapon, mode_special); // send the kill message NETMSG_SV_KILLMSG msg; msg.killer = killer; msg.victim = player->client_id; msg.weapon = weapon; msg.mode_special = mode_special; msg.pack(MSGFLAG_VITAL); server_send_msg(-1); // a nice sound game.create_sound(pos, SOUND_PLAYER_DIE); // set dead state // TODO: do stuff here /* die_pos = pos; dead = true; die_tick = server_tick(); */ alive = false; game.world.remove_entity(this); game.create_death(pos, player->client_id); } bool CHARACTER::take_damage(vec2 force, int dmg, int from, int weapon) { core.vel += force; if(game.controller->is_friendly_fire(player->client_id, from) && !config.sv_teamdamage) return false; // player only inflicts half damage on self if(from == player->client_id) dmg = max(1, dmg/2); // CTF and TDM (TODO: check for FF) //if (gameobj->gametype != GAMETYPE_DM && from >= 0 && players[from].team == team) //return false; damage_taken++; // create healthmod indicator if(server_tick() < damage_taken_tick+25) { // make sure that the damage indicators doesn't group together game.create_damageind(pos, damage_taken*0.25f, dmg); } else { damage_taken = 0; game.create_damageind(pos, 0, dmg); } if(dmg) { if(armor) { if(dmg > 1) { health--; dmg--; } if(dmg > armor) { dmg -= armor; armor = 0; } else { armor -= dmg; dmg = 0; } } health -= dmg; } damage_taken_tick = server_tick(); // do damage hit sound if(from >= 0 && from != player->client_id) game.create_sound(game.players[from].view_pos, SOUND_HIT, cmask_one(from)); // check for death if(health <= 0) { die(from, weapon); // set attacker's face to happy (taunt!) if (from >= 0 && from != player->client_id) { CHARACTER *chr = &game.players[from].character; chr->emote_type = EMOTE_HAPPY; chr->emote_stop = server_tick() + server_tickspeed(); } return false; } if (dmg > 2) game.create_sound(pos, SOUND_PLAYER_PAIN_LONG); else game.create_sound(pos, SOUND_PLAYER_PAIN_SHORT); emote_type = EMOTE_PAIN; emote_stop = server_tick() + 500 * server_tickspeed() / 1000; // spawn blood? return true; } void CHARACTER::snap(int snaping_client) { if(distance(game.players[snaping_client].view_pos, pos) > 1000.0f) return; NETOBJ_CHARACTER *character = (NETOBJ_CHARACTER *)snap_new_item(NETOBJTYPE_CHARACTER, player->client_id, sizeof(NETOBJ_CHARACTER)); core.write(character); // this is to make sure that players that are just standing still // isn't sent. this is because the physics keep bouncing between // 0-128 when just standing. // TODO: fix the physics so this isn't needed if(snaping_client != player->client_id && abs(character->vy) < 256.0f) character->vy = 0; if (emote_stop < server_tick()) { emote_type = EMOTE_NORMAL; emote_stop = -1; } character->emote = emote_type; character->ammocount = 0; character->health = 0; character->armor = 0; character->weapon = active_weapon; character->attacktick = attack_tick; character->wanted_direction = input.direction; /* if(input.left && !input.right) character->wanted_direction = -1; else if(!input.left && input.right) character->wanted_direction = 1;*/ if(player->client_id == snaping_client) { character->health = health; character->armor = armor; if(weapons[active_weapon].ammo > 0) character->ammocount = weapons[active_weapon].ammo; } if (character->emote == EMOTE_NORMAL) { if(250 - ((server_tick() - last_action)%(250)) < 5) character->emote = EMOTE_BLINK; } character->player_state = player_state; } PLAYER::PLAYER() { } void PLAYER::init(int client_id) { // clear everything mem_zero(this, sizeof(*this)); new(this) PLAYER(); this->client_id = client_id; } void PLAYER::tick() { server_setclientscore(client_id, score); // do latency stuff { CLIENT_INFO info; if(server_getclientinfo(client_id, &info)) { latency.accum += info.latency; latency.accum_max = max(latency.accum_max, info.latency); latency.accum_min = min(latency.accum_min, info.latency); } if(server_tick()%server_tickspeed() == 0) { latency.avg = latency.accum/server_tickspeed(); latency.max = latency.accum_max; latency.min = latency.accum_min; latency.accum = 0; latency.accum_min = 1000; latency.accum_max = 0; } } if(spawning && !get_character()) try_respawn(); if(get_character()) view_pos = get_character()->pos; } void PLAYER::snap(int snaping_client) { NETOBJ_PLAYER_INFO *info = (NETOBJ_PLAYER_INFO *)snap_new_item(NETOBJTYPE_PLAYER_INFO, client_id, sizeof(NETOBJ_PLAYER_INFO)); info->latency = latency.min; info->latency_flux = latency.max-latency.min; info->local = 0; info->cid = client_id; info->score = score; info->team = team; if(client_id == snaping_client) info->local = 1; } void PLAYER::on_disconnect() { kill_character(); //game.controller->on_player_death(&game.players[client_id], 0, -1); char buf[512]; str_format(buf, sizeof(buf), "%s has left the game", server_clientname(client_id)); game.send_chat(-1, CHAT_ALL, buf); dbg_msg("game", "leave player='%d:%s'", client_id, server_clientname(client_id)); // clear this whole structure init(-1); /*game.world.remove_entity(&game.players[client_id]); game.world.core.players[client_id] = 0x0; game.players[client_id].client_id = -1; */ } void PLAYER::on_predicted_input(NETOBJ_PLAYER_INPUT *new_input) { CHARACTER *chr = get_character(); if(chr) chr->on_predicted_input(new_input); } void PLAYER::on_direct_input(NETOBJ_PLAYER_INPUT *new_input) { CHARACTER *chr = get_character(); if(chr) chr->on_direct_input(new_input); if(!chr && team >= 0 && (new_input->fire&1)) { spawning = true; dbg_msg("", "I wanna spawn"); } } CHARACTER *PLAYER::get_character() { if(character.alive) return &character; return 0; } void PLAYER::kill_character() { CHARACTER *chr = get_character(); if(chr) chr->die(-1, -1); } void PLAYER::respawn() { spawning = true; } void PLAYER::set_team(int new_team) { // clamp the team new_team = game.controller->clampteam(new_team); if(team == new_team) return; char buf[512]; str_format(buf, sizeof(buf), "%s joined the %s", server_clientname(client_id), game.controller->get_team_name(new_team)); game.send_chat(-1, CHAT_ALL, buf); kill_character(); team = new_team; score = 0; dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), team); game.controller->on_player_info_change(&game.players[client_id]); // send all info to this client for(int i = 0; i < MAX_CLIENTS; i++) { if(game.players[i].client_id != -1) game.send_info(i, -1); } } vec2 spawn_points[3][64]; int num_spawn_points[3] = {0}; struct SPAWNEVAL { SPAWNEVAL() { got = false; friendly_team = -1; // die_pos = vec2(0,0); pos = vec2(100,100); } vec2 pos; bool got; int friendly_team; float score; // vec2 die_pos; }; static float evaluate_spawn(SPAWNEVAL *eval, vec2 pos) { float score = 0.0f; CHARACTER *c = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER); for(; c; c = (CHARACTER *)c->typenext()) { // team mates are not as dangerous as enemies float scoremod = 1.0f; if(eval->friendly_team != -1 && c->team == eval->friendly_team) scoremod = 0.5f; float d = distance(pos, 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; } static void evaluate_spawn_type(SPAWNEVAL *eval, int t) { // get spawn point /* int start, num; map_get_type(t, &start, &num); if(!num) return; */ for(int i = 0; i < num_spawn_points[t]; i++) { //num_spawn_points[t] //mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + i, NULL, NULL); vec2 p = spawn_points[t][i];// vec2((float)sp->x, (float)sp->y); float s = evaluate_spawn(eval, p); if(!eval->got || eval->score > s) { eval->got = true; eval->score = s; eval->pos = p; } } } 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(game.controller->gametype == GAMETYPE_CTF) { 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) { evaluate_spawn_type(&eval, 0); if(!eval.got) evaluate_spawn_type(&eval, 1+((team+1)&1)); } } else { if(game.controller->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 ENTITY *ents[2] = {0}; int num_ents = game.world.find_entities(spawnpos, 64, ents, 2, NETOBJTYPE_CHARACTER); if(num_ents == 0) { spawning = false; character.spawn(this, spawnpos, team); } /* pos = spawnpos; core.pos = pos; core.vel = vec2(0,0); core.hooked_player = -1; health = 10; armor = 0; jumped = 0; mem_zero(&ninja, sizeof(ninja)); dead = false; player_state = PLAYERSTATE_PLAYING; game.world.insert_entity(this); core.hook_state = HOOK_IDLE; mem_zero(&input, sizeof(input)); // init weapons mem_zero(&weapons, sizeof(weapons)); weapons[WEAPON_HAMMER].got = true; weapons[WEAPON_HAMMER].ammo = -1; weapons[WEAPON_GUN].got = true; weapons[WEAPON_GUN].ammo = 10; active_weapon = WEAPON_GUN; last_weapon = WEAPON_HAMMER; queued_weapon = 0; reload_timer = 0; // Create sound and spawn effects game.create_sound(pos, SOUND_PLAYER_SPAWN); game.create_playerspawn(pos); game.controller->on_player_spawn(player); */ }