Logo Search packages:      
Sourcecode: teeworlds version File versions  Download package

character.cpp

#include <new>
#include <engine/e_server_interface.h>
#include <engine/e_config.h>
#include <game/server/gamecontext.hpp>
#include <game/mapitems.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;
}


MACRO_ALLOC_POOL_ID_IMPL(CHARACTER, MAX_CLIENTS)

// player
CHARACTER::CHARACTER()
: ENTITY(NETOBJTYPE_CHARACTER)
{
      proximity_radius = phys_size;
}

void CHARACTER::reset()
{
      destroy();
}

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;

      reckoning_tick = 0;
      mem_zero(&sendcore, sizeof(sendcore));
      mem_zero(&reckoningcore, sizeof(reckoningcore));
      
      game.world.insert_entity(this);
      alive = true;
      player->force_balanced = false;

      game.controller->on_character_spawn(this);

      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()
{
      if(active_weapon != WEAPON_NINJA)
            return 0;
      
      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);

      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)
            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)
      {
            // 125ms is a magical limit of how fast a human can click
            reload_timer = 125 * server_tickspeed() / 1000;;
            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 hits = 0;
                  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_hammerhit(pos);
                        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;
                        hits++;
                  }
                  
                  // if we hit anything, we have to wait for the reload
                  if(hits)
                        reload_timer = server_tickspeed()/3;
                  
            } break;

            case WEAPON_GUN:
            {
                  PROJECTILE *proj = new PROJECTILE(WEAPON_GUN,
                        player->client_id,
                        projectile_startpos,
                        direction,
                        (int)(server_tickspeed()*tuning.gun_lifetime),
                        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),
                              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),
                        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, player->client_id);
                  game.create_sound(pos, SOUND_RIFLE_FIRE);
            } break;
            
            case WEAPON_NINJA:
            {
                  attack_tick = server_tick();
                  ninja.activationdir = direction;
                  ninja.currentmovetime = data->weapons.ninja.movetime * server_tickspeed() / 1000;

                  //reload_timer = data->weapons.ninja.base->firedelay * server_tickspeed() / 1000 + server_tick();
                  
                  // reset hit objects
                  numobjectshit = 0;

                  game.create_sound(pos, SOUND_NINJA_FIRE);
                  
            } break;
            
      }

      if(weapons[active_weapon].ammo > 0) // -1 == unlimited
            weapons[active_weapon].ammo--;
      attack_tick = server_tick();
      if(!reload_timer)
            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--;
      } */

      //if(active_weapon == WEAPON_NINJA)
      handle_ninja();


      // 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();
      }
      
      mem_copy(&latest_previnput, &latest_input, sizeof(latest_input));
}

void CHARACTER::tick()
{
      if(player->force_balanced)
      {
            char buf[128];
            str_format(buf, sizeof(buf), "You were moved to %s due to team balancing", game.controller->get_team_name(team));
            game.send_broadcast(buf, player->client_id);
            
            player->force_balanced = false;
      }

      //player_core core;
      //core.pos = pos;
      //core.jumped = jumped;
      core.input = input;
      core.tick(true);
      
      float phys_size = 28.0f;
      // handle death-tiles
      if(col_get((int)(pos.x+phys_size/2), (int)(pos.y-phys_size/2))&COLFLAG_DEATH ||
                  col_get((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2))&COLFLAG_DEATH ||
                  col_get((int)(pos.x-phys_size/2), (int)(pos.y-phys_size/2))&COLFLAG_DEATH ||
                  col_get((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2))&COLFLAG_DEATH)
      {
            die(player->client_id, WEAPON_WORLD);
      }

      // handle weapons
      handle_weapons();

      player_state = input.player_state;

      // Previnput
      previnput = input;
      return;
}

void CHARACTER::tick_defered()
{
      // advance the dummy
      {
            WORLD_CORE tempworld;
            reckoningcore.world = &tempworld;
            reckoningcore.tick(false);
            reckoningcore.move();
            reckoningcore.quantize();
      }
      
      //lastsentcore;
      /*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_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_HIT_NOHOOK) game.create_sound(pos, SOUND_HOOK_NOATTACH, 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;
      }
      
      // update the sendcore if needed
      {
            NETOBJ_CHARACTER predicted;
            NETOBJ_CHARACTER current;
            mem_zero(&predicted, sizeof(predicted));
            mem_zero(&current, sizeof(current));
            reckoningcore.write(&predicted);
            core.write(&current);

            // only allow dead reackoning for a top of 3 seconds
            if(reckoning_tick+server_tickspeed()*3 < server_tick() || mem_comp(&predicted, &current, sizeof(NETOBJ_CHARACTER)) != 0)
            {
                  reckoning_tick = server_tick();
                  sendcore = core;
                  reckoningcore = core;
            }
      }
}

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;
      */
      
      // this is for auto respawn after 3 secs
      player->die_tick = server_tick();
      
      alive = false;
      game.world.remove_entity(this);
      game.world.core.characters[player->client_id] = 0;
      game.create_death(pos, player->client_id);
      
      // we got to wait 0.5 secs before respawning
      player->respawn_tick = server_tick()+server_tickspeed()/2;
}

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);

      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.players[from])
            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 && game.players[from])
            {
                  CHARACTER *chr = game.players[from]->get_character();
                  if (chr)
                  {
                        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 snapping_client)
{
      if(networkclipped(snapping_client))
            return;
      
      NETOBJ_CHARACTER *character = (NETOBJ_CHARACTER *)snap_new_item(NETOBJTYPE_CHARACTER, player->client_id, sizeof(NETOBJ_CHARACTER));
      
      // write down the core
      if(game.world.paused)
      {
            // no dead reckoning when paused because the client doesn't know
            // how far to perform the reckoning
            character->tick = 0;
            core.write(character);
      }
      else
      {
            character->tick = reckoning_tick;
            sendcore.write(character);
      }
      
      // set emote
      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->direction = input.direction;

      if(player->client_id == snapping_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;
}

Generated by  Doxygen 1.6.0   Back to index