diff options
Diffstat (limited to 'src/game/client/components/players.cpp')
| -rw-r--r-- | src/game/client/components/players.cpp | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp new file mode 100644 index 00000000..3178b82f --- /dev/null +++ b/src/game/client/components/players.cpp @@ -0,0 +1,463 @@ + +extern "C" { + #include <engine/e_config.h> +} + +#include <engine/e_client_interface.h> +#include <game/generated/g_protocol.hpp> +#include <game/generated/gc_data.hpp> + +#include <game/gamecore.hpp> // get_angle +#include <game/client/animstate.hpp> +#include <game/client/gameclient.hpp> +#include <game/client/gc_client.hpp> +#include <game/client/gc_ui.hpp> +#include <game/client/gc_render.hpp> + +#include <game/client/components/flow.hpp> +#include <game/client/components/skins.hpp> +#include <game/client/components/effects.hpp> + +#include "players.hpp" + +void PLAYERS::render_hand(TEE_RENDER_INFO *info, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset) +{ + // for drawing hand + //const skin *s = skin_get(skin_id); + + float basesize = 10.0f; + //dir = normalize(hook_pos-pos); + + vec2 hand_pos = center_pos + dir; + float angle = get_angle(dir); + if (dir.x < 0) + angle -= angle_offset; + else + angle += angle_offset; + + vec2 dirx = dir; + vec2 diry(-dir.y,dir.x); + + if (dir.x < 0) + diry = -diry; + + hand_pos += dirx * post_rot_offset.x; + hand_pos += diry * post_rot_offset.y; + + //gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id); + gfx_texture_set(info->texture); + gfx_quads_begin(); + gfx_setcolor(info->color_body.r, info->color_body.g, info->color_body.b, info->color_body.a); + + // two passes + for (int i = 0; i < 2; i++) + { + bool outline = i == 0; + + select_sprite(outline?SPRITE_TEE_HAND_OUTLINE:SPRITE_TEE_HAND, 0, 0, 0); + gfx_quads_setrotation(angle); + gfx_quads_draw(hand_pos.x, hand_pos.y, 2*basesize, 2*basesize); + } + + gfx_quads_setrotation(0); + gfx_quads_end(); +} + +void PLAYERS::render_player( + const NETOBJ_CHARACTER *prev_char, + const NETOBJ_CHARACTER *player_char, + const NETOBJ_PLAYER_INFO *prev_info, + const NETOBJ_PLAYER_INFO *player_info + ) +{ + NETOBJ_CHARACTER prev; + NETOBJ_CHARACTER player; + prev = *prev_char; + player = *player_char; + + NETOBJ_PLAYER_INFO info = *player_info; + TEE_RENDER_INFO render_info = gameclient.clients[info.cid].render_info; + + // check for teamplay modes + bool is_teamplay = false; + if(gameclient.snap.gameobj) + is_teamplay = gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS != 0; + + // check for ninja + if (player.weapon == WEAPON_NINJA) + { + // change the skin for the player to the ninja + int skin = gameclient.skins->find("x_ninja"); + if(skin != -1) + { + if(is_teamplay) + render_info.texture = gameclient.skins->get(skin)->color_texture; + else + { + render_info.texture = gameclient.skins->get(skin)->org_texture; + render_info.color_body = vec4(1,1,1,1); + render_info.color_feet = vec4(1,1,1,1); + } + } + } + + // set size + render_info.size = 64.0f; + + float intratick = client_intratick(); + float ticktime = client_ticktime(); + + if(player.health < 0) // dont render dead players + return; + + //float angle = mix((float)prev.angle, (float)player.angle, intratick)/256.0f; + + // TODO: fix this good! + float mixspeed = 0.05f; + if(player.attacktick != prev.attacktick) + mixspeed = 0.1f; + + float angle = mix(gameclient.clients[info.cid].angle, player.angle/256.0f, mixspeed); + gameclient.clients[info.cid].angle = angle; + vec2 direction = get_direction((int)(angle*256.0f)); + + if(info.local && config.cl_predict) + { + if(!gameclient.snap.local_character || (gameclient.snap.local_character->health < 0) || (gameclient.snap.gameobj && gameclient.snap.gameobj->game_over)) + { + } + else + { + // apply predicted results + predicted_char.write(&player); + predicted_prev_char.write(&prev); + intratick = client_predintratick(); + } + } + + vec2 position = mix(vec2(prev.x, prev.y), vec2(player.x, player.y), intratick); + vec2 vel = mix(vec2(prev.vx/256.0f, prev.vy/256.0f), vec2(player.vx/256.0f, player.vy/256.0f), intratick); + + gameclient.flow->add(position, vel*100.0f, 10.0f); + + render_info.got_airjump = player.jumped&2?0:1; + + if(prev.health < 0) // Don't flicker from previous position + position = vec2(player.x, player.y); + + bool stationary = player.vx < 1 && player.vx > -1; + bool inair = col_check_point(player.x, player.y+16) == 0; + bool want_other_dir = (player.wanted_direction == -1 && vel.x > 0) || (player.wanted_direction == 1 && vel.x < 0); + + // evaluate animation + float walk_time = fmod(position.x, 100.0f)/100.0f; + ANIMSTATE state; + state.set(&data->animations[ANIM_BASE], 0); + + if(inair) + state.add(&data->animations[ANIM_INAIR], 0, 1.0f); // TODO: some sort of time here + else if(stationary) + state.add(&data->animations[ANIM_IDLE], 0, 1.0f); // TODO: some sort of time here + else if(!want_other_dir) + state.add(&data->animations[ANIM_WALK], walk_time, 1.0f); + + if (player.weapon == WEAPON_HAMMER) + { + float a = clamp((client_tick()-player.attacktick+ticktime)/10.0f, 0.0f, 1.0f); + state.add(&data->animations[ANIM_HAMMER_SWING], a, 1.0f); + } + if (player.weapon == WEAPON_NINJA) + { + float a = clamp((client_tick()-player.attacktick+ticktime)/40.0f, 0.0f, 1.0f); + state.add(&data->animations[ANIM_NINJA_SWING], a, 1.0f); + } + + // do skidding + if(!inair && want_other_dir && length(vel*50) > 500.0f) + { + static int64 skid_sound_time = 0; + if(time_get()-skid_sound_time > time_freq()/10) + { + snd_play_random(CHN_WORLD, SOUND_PLAYER_SKID, 0.25f, position); + skid_sound_time = time_get(); + } + + gameclient.effects->skidtrail( + position+vec2(-player.wanted_direction*6,12), + vec2(-player.wanted_direction*100*length(vel),-50) + ); + } + + // draw hook + if (prev.hook_state>0 && player.hook_state>0) + { + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + //gfx_quads_begin(); + + vec2 pos = position; + vec2 hook_pos; + + if(player_char->hooked_player != -1) + { + if(gameclient.snap.local_info && player_char->hooked_player == gameclient.snap.local_info->cid) + { + hook_pos = mix(vec2(predicted_prev_char.pos.x, predicted_prev_char.pos.y), + vec2(predicted_char.pos.x, predicted_char.pos.y), client_predintratick()); + } + else + hook_pos = mix(vec2(prev_char->hook_x, prev_char->hook_y), vec2(player_char->hook_x, player_char->hook_y), client_intratick()); + } + else + hook_pos = mix(vec2(prev.hook_x, prev.hook_y), vec2(player.hook_x, player.hook_y), intratick); + + float d = distance(pos, hook_pos); + vec2 dir = normalize(pos-hook_pos); + + gfx_quads_setrotation(get_angle(dir)+pi); + + // render head + select_sprite(SPRITE_HOOK_HEAD); + gfx_quads_draw(hook_pos.x, hook_pos.y, 24,16); + + // render chain + select_sprite(SPRITE_HOOK_CHAIN); + int i = 0; + for(float f = 24; f < d && i < 1024; f += 24, i++) + { + vec2 p = hook_pos + dir*f; + gfx_quads_draw(p.x, p.y,24,16); + } + + gfx_quads_setrotation(0); + gfx_quads_end(); + + render_hand(&render_info, position, normalize(hook_pos-pos), -pi/2, vec2(20, 0)); + } + + // draw gun + { + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + gfx_quads_setrotation(state.attach.angle*pi*2+angle); + + // normal weapons + int iw = clamp(player.weapon, 0, NUM_WEAPONS-1); + select_sprite(data->weapons.id[iw].sprite_body, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); + + vec2 dir = direction; + float recoil = 0.0f; + vec2 p; + if (player.weapon == WEAPON_HAMMER) + { + // Static position for hammer + p = position + vec2(state.attach.x, state.attach.y); + p.y += data->weapons.id[iw].offsety; + // if attack is under way, bash stuffs + if(direction.x < 0) + { + gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2); + p.x -= data->weapons.id[iw].offsetx; + } + else + { + gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); + } + draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); + } + else if (player.weapon == WEAPON_NINJA) + { + p = position; + p.y += data->weapons.id[iw].offsety; + + if(direction.x < 0) + { + gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2); + p.x -= data->weapons.id[iw].offsetx; + gameclient.effects->powerupshine(p+vec2(32,0), vec2(32,12)); + } + else + { + gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); + gameclient.effects->powerupshine(p-vec2(32,0), vec2(32,12)); + } + draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); + + // HADOKEN + if ((client_tick()-player.attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons.id[iw].num_sprite_muzzles) + { + int itex = rand() % data->weapons.id[iw].num_sprite_muzzles; + float alpha = 1.0f; + if (alpha > 0.0f && data->weapons.id[iw].sprite_muzzles[itex]) + { + vec2 dir = vec2(player_char->x,player_char->y) - vec2(prev_char->x, prev_char->y); + dir = normalize(dir); + float hadokenangle = get_angle(dir); + gfx_quads_setrotation(hadokenangle); + //float offsety = -data->weapons[iw].muzzleoffsety; + select_sprite(data->weapons.id[iw].sprite_muzzles[itex], 0); + vec2 diry(-dir.y,dir.x); + p = position; + float offsetx = data->weapons.id[iw].muzzleoffsetx; + p -= dir * offsetx; + draw_sprite(p.x, p.y, 160.0f); + } + } + } + else + { + // TODO: should be an animation + recoil = 0; + float a = (client_tick()-player.attacktick+intratick)/5.0f; + if(a < 1) + recoil = sinf(a*pi); + p = position + dir * data->weapons.id[iw].offsetx - dir*recoil*10.0f; + p.y += data->weapons.id[iw].offsety; + draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); + } + + if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN) + { + // check if we're firing stuff + if(data->weapons.id[iw].num_sprite_muzzles)//prev.attackticks) + { + float alpha = 0.0f; + int phase1tick = (client_tick() - player.attacktick); + if (phase1tick < (data->weapons.id[iw].muzzleduration + 3)) + { + float t = ((((float)phase1tick) + intratick)/(float)data->weapons.id[iw].muzzleduration); + alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t))); + } + + int itex = rand() % data->weapons.id[iw].num_sprite_muzzles; + if (alpha > 0.0f && data->weapons.id[iw].sprite_muzzles[itex]) + { + float offsety = -data->weapons.id[iw].muzzleoffsety; + select_sprite(data->weapons.id[iw].sprite_muzzles[itex], direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); + if(direction.x < 0) + offsety = -offsety; + + vec2 diry(-dir.y,dir.x); + vec2 muzzlepos = p + dir * data->weapons.id[iw].muzzleoffsetx + diry * offsety; + + draw_sprite(muzzlepos.x, muzzlepos.y, data->weapons.id[iw].visual_size); + } + } + } + gfx_quads_end(); + + switch (player.weapon) + { + case WEAPON_GUN: render_hand(&render_info, p, direction, -3*pi/4, vec2(-15, 4)); break; + case WEAPON_SHOTGUN: render_hand(&render_info, p, direction, -pi/2, vec2(-5, 4)); break; + case WEAPON_GRENADE: render_hand(&render_info, p, direction, -pi/2, vec2(-4, 7)); break; + } + + } + + // render the "shadow" tee + if(info.local && config.debug) + { + vec2 ghost_position = mix(vec2(prev_char->x, prev_char->y), vec2(player_char->x, player_char->y), client_intratick()); + TEE_RENDER_INFO ghost = render_info; + ghost.color_body.a = 0.5f; + ghost.color_feet.a = 0.5f; + render_tee(&state, &ghost, player.emote, direction, ghost_position); // render ghost + } + + render_info.size = 64.0f; // force some settings + render_info.color_body.a = 1.0f; + render_info.color_feet.a = 1.0f; + render_tee(&state, &render_info, player.emote, direction, position); + + if(player.player_state == PLAYERSTATE_CHATTING) + { + gfx_texture_set(data->images[IMAGE_EMOTICONS].id); + gfx_quads_begin(); + select_sprite(SPRITE_DOTDOT); + gfx_quads_draw(position.x + 24, position.y - 40, 64,64); + gfx_quads_end(); + } + + if (gameclient.clients[info.cid].emoticon_start != -1 && gameclient.clients[info.cid].emoticon_start + 2 * client_tickspeed() > client_tick()) + { + gfx_texture_set(data->images[IMAGE_EMOTICONS].id); + gfx_quads_begin(); + + int since_start = client_tick() - gameclient.clients[info.cid].emoticon_start; + int from_end = gameclient.clients[info.cid].emoticon_start + 2 * client_tickspeed() - client_tick(); + + float a = 1; + + if (from_end < client_tickspeed() / 5) + a = from_end / (client_tickspeed() / 5.0); + + float h = 1; + if (since_start < client_tickspeed() / 10) + h = since_start / (client_tickspeed() / 10.0); + + float wiggle = 0; + if (since_start < client_tickspeed() / 5) + wiggle = since_start / (client_tickspeed() / 5.0); + + float wiggle_angle = sin(5*wiggle); + + gfx_quads_setrotation(pi/6*wiggle_angle); + + gfx_setcolor(1.0f,1.0f,1.0f,a); + // client_datas::emoticon is an offset from the first emoticon + select_sprite(SPRITE_OOP + gameclient.clients[info.cid].emoticon); + gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h); + gfx_quads_end(); + } + + // render name plate + if(!info.local && config.cl_nameplates) + { + //gfx_text_color + float a = 1; + if(config.cl_nameplates_always == 0) + a = clamp(1-powf(distance(gameclient.local_target_pos, position)/200.0f,16.0f), 0.0f, 1.0f); + + const char *name = gameclient.clients[info.cid].name; + float tw = gfx_text_width(0, 28.0f, name, -1); + gfx_text_color(1,1,1,a); + gfx_text(0, position.x-tw/2.0f, position.y-60, 28.0f, name, -1); + + if(config.debug) // render client id when in debug aswell + { + char buf[128]; + str_format(buf, sizeof(buf),"%d", info.cid); + gfx_text(0, position.x, position.y-90, 28.0f, buf, -1); + } + + gfx_text_color(1,1,1,1); + } +} + +void PLAYERS::on_render() +{ + int num = snap_num_items(SNAP_CURRENT); + for(int i = 0; i < num; i++) + { + SNAP_ITEM item; + const void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == NETOBJTYPE_CHARACTER) + { + const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + const void *prev_info = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_INFO, item.id); + const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, item.id); + + if(prev && prev_info && info) + { + render_player( + (const NETOBJ_CHARACTER *)prev, + (const NETOBJ_CHARACTER *)data, + (const NETOBJ_PLAYER_INFO *)prev_info, + (const NETOBJ_PLAYER_INFO *)info + ); + } + } + } +} |