diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-08-14 18:25:44 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-08-14 18:25:44 +0000 |
| commit | a420eb543f8206730aebb80e60a625f7204694e4 (patch) | |
| tree | 235971d38c917c1b97c512743db64c2fd23ffadc /src/game/server/entities/character.cpp | |
| parent | 817f431377c7f1545621ff597c018b133651e991 (diff) | |
| download | zcatch-a420eb543f8206730aebb80e60a625f7204694e4.tar.gz zcatch-a420eb543f8206730aebb80e60a625f7204694e4.zip | |
moved alot of stuff to their own cpp/hpp files
Diffstat (limited to 'src/game/server/entities/character.cpp')
| -rw-r--r-- | src/game/server/entities/character.cpp | 1131 |
1 files changed, 1131 insertions, 0 deletions
diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp new file mode 100644 index 00000000..160e080a --- /dev/null +++ b/src/game/server/entities/character.cpp @@ -0,0 +1,1131 @@ +#include <new> +#include <engine/e_server_interface.h> +#include <engine/e_config.h> +#include <game/server/gs_common.hpp> + +#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); + */ +} |