示例#1
0
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;
}
示例#2
0
bool test_box(vec2 pos, vec2 size)
{
	size *= 0.5f;
	if(col_check_point(pos.x-size.x, pos.y-size.y))
		return true;
	if(col_check_point(pos.x+size.x, pos.y-size.y))
		return true;
	if(col_check_point(pos.x-size.x, pos.y+size.y))
		return true;
	if(col_check_point(pos.x+size.x, pos.y+size.y))
		return true;

	return false;
}
示例#3
0
// TODO: OPT: rewrite this smarter!
void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces)
{
	if(bounces)
		*bounces = 0;

	vec2 pos = *inout_pos;
	vec2 vel = *inout_vel;
	if(col_check_point(pos + vel))
	{
		int affected = 0;
		if(col_check_point(pos.x + vel.x, pos.y))
		{
			inout_vel->x *= -elasticity;
			if(bounces)
				(*bounces)++;
			affected++;
		}

		if(col_check_point(pos.x, pos.y + vel.y))
		{
			inout_vel->y *= -elasticity;
			if(bounces)
				(*bounces)++;
			affected++;
		}

		if(affected == 0)
		{
			inout_vel->x *= -elasticity;
			inout_vel->y *= -elasticity;
		}
	}
	else if(col_check_platte(pos.x, pos.y + vel.y))
	{
			if(bounces)
				(*bounces)++;
			inout_vel->x *= -elasticity;
			inout_vel->y *= -elasticity;
	}
	else
	{
		*inout_pos = pos + vel;
	}
}
示例#4
0
void CHARACTER_CORE::tick(bool use_input)
{
	float phys_size = 28.0f;
	triggered_events = 0;

	// get ground state
	bool grounded = false;
	if(col_check_point(pos.x+phys_size/2, pos.y+phys_size/2+5))
		grounded = true;
	if(col_check_point(pos.x-phys_size/2, pos.y+phys_size/2+5))
		grounded = true;
	//platte
	if(col_check_platte(pos.x+phys_size/2, pos.y+phys_size/2+5))
		grounded = true;
	if(col_check_platte(pos.x-phys_size/2, pos.y+phys_size/2+5))
		grounded = true;

	vec2 target_direction = normalize(vec2(input.target_x, input.target_y));

	vel.y += world->tuning.gravity;

	float max_speed = grounded ? world->tuning.ground_control_speed : world->tuning.air_control_speed;
	float accel = grounded ? world->tuning.ground_control_accel : world->tuning.air_control_accel;
	float friction = grounded ? world->tuning.ground_friction : world->tuning.air_friction;

	// handle input
	if(use_input)
	{
		direction = input.direction;

		// setup angle
		float a = 0;
		if(input.target_x == 0)
			a = atan((float)input.target_y);
		else
			a = atan((float)input.target_y/(float)input.target_x);

		if(input.target_x < 0)
			a = a+pi;

		angle = (int)(a*256.0f);

		// handle jump
		if(input.jump)
		{
			if(!(jumped&1))
			{
				if(grounded)
				{
					triggered_events |= COREEVENT_GROUND_JUMP;
					vel.y = -world->tuning.ground_jump_impulse;
					jumped |= 1;
				}
				else if(!(jumped&2))
				{
					triggered_events |= COREEVENT_AIR_JUMP;
					vel.y = -world->tuning.air_jump_impulse;
					jumped |= 3;
				}
			}
		}
		else
			jumped &= ~1;

		// handle hook
		if(input.hook)
		{
			if(hook_state == HOOK_IDLE)
			{
				hook_state = HOOK_FLYING;
				hook_pos = pos+target_direction*phys_size*1.5f;
				hook_dir = target_direction;
				hooked_player = -1;
				hook_tick = 0;
				triggered_events |= COREEVENT_HOOK_LAUNCH;
			}
		}
		else
		{
			hooked_player = -1;
			hook_state = HOOK_IDLE;
			hook_pos = pos;
		}
	}

	// add the speed modification according to players wanted direction
	if(direction < 0)
		vel.x = saturated_add(-max_speed, max_speed, vel.x, -accel);
	if(direction > 0)
		vel.x = saturated_add(-max_speed, max_speed, vel.x, accel);
	if(direction == 0)
		vel.x *= friction;

	// handle jumping
	// 1 bit = to keep track if a jump has been made on this input
	// 2 bit = to keep track if a air-jump has been made
	if(grounded)
		jumped &= ~2;

	// do hook
	if(hook_state == HOOK_IDLE)
	{
		hooked_player = -1;
		hook_state = HOOK_IDLE;
		hook_pos = pos;
	}
	else if(hook_state >= HOOK_RETRACT_START && hook_state < HOOK_RETRACT_END)
	{
		hook_state++;
	}
	else if(hook_state == HOOK_RETRACT_END)
	{
		hook_state = HOOK_RETRACTED;
		triggered_events |= COREEVENT_HOOK_RETRACT;
		hook_state = HOOK_RETRACTED;
	}
	else if(hook_state == HOOK_FLYING)
	{
		vec2 new_pos = hook_pos+hook_dir*world->tuning.hook_fire_speed;
		if(distance(pos, new_pos) > world->tuning.hook_length)
		{
			hook_state = HOOK_RETRACT_START;
			new_pos = pos + normalize(new_pos-pos) * world->tuning.hook_length;
		}

		// make sure that the hook doesn't go though the ground
		bool going_to_hit_ground = false;
		bool going_to_retract = false;
		int hit = col_intersect_line(hook_pos, new_pos, &new_pos, 0);
		if(hit)
		{
			if(hit&COLFLAG_NOHOOK)
				going_to_retract = true;
			else
				going_to_hit_ground = true;
		}

		// Check against other players first
		if(world && world->tuning.player_hooking)
		{
			float dist = 0.0f;
			for(int i = 0; i < MAX_CLIENTS; i++)
			{
				CHARACTER_CORE *p = world->characters[i];
				if(!p || p == this)
					continue;

				vec2 closest_point = closest_point_on_line(hook_pos, new_pos, p->pos);
				if(distance(p->pos, closest_point) < phys_size+2.0f)
				{
					if (hooked_player == -1 || distance (hook_pos, p->pos) < dist)
					{
						triggered_events |= COREEVENT_HOOK_ATTACH_PLAYER;
						hook_state = HOOK_GRABBED;
						hooked_player = i;
						dist = distance (hook_pos, p->pos);
					}
				}
			}
		}

		if(hook_state == HOOK_FLYING)
		{
			// check against ground
			if(going_to_hit_ground)
			{
				triggered_events |= COREEVENT_HOOK_ATTACH_GROUND;
				hook_state = HOOK_GRABBED;
			}
			else if(going_to_retract)
			{
				triggered_events |= COREEVENT_HOOK_HIT_NOHOOK;
				hook_state = HOOK_RETRACT_START;
			}

			hook_pos = new_pos;
		}
	}

	if(hook_state == HOOK_GRABBED)
	{
		if(hooked_player != -1)
		{
			CHARACTER_CORE *p = world->characters[hooked_player];
			if(p)
				hook_pos = p->pos;
			else
			{
				// release hook
				hooked_player = -1;
				hook_state = HOOK_RETRACTED;
				hook_pos = pos;
			}

			// keep players hooked for a max of 1.5sec
			//if(server_tick() > hook_tick+(server_tickspeed()*3)/2)
				//release_hooked();
		}

		// don't do this hook rutine when we are hook to a player
		if(hooked_player == -1 && distance(hook_pos, pos) > 46.0f)
		{
			vec2 hookvel = normalize(hook_pos-pos)*world->tuning.hook_drag_accel;
			// the hook as more power to drag you up then down.
			// this makes it easier to get on top of an platform
			if(hookvel.y > 0)
				hookvel.y *= 0.3f;

			// the hook will boost it's power if the player wants to move
			// in that direction. otherwise it will dampen everything abit
			if((hookvel.x < 0 && direction < 0) || (hookvel.x > 0 && direction > 0))
				hookvel.x *= 0.95f;
			else
				hookvel.x *= 0.75f;

			vec2 new_vel = vel+hookvel;

			// check if we are under the legal limit for the hook
			if(length(new_vel) < world->tuning.hook_drag_speed || length(new_vel) < length(vel))
				vel = new_vel; // no problem. apply

		}

		// release hook (max hook time is 1.25
		hook_tick++;
		if(hooked_player != -1 && (hook_tick > SERVER_TICK_SPEED+SERVER_TICK_SPEED/5 || !world->characters[hooked_player]))
		{
			hooked_player = -1;
			hook_state = HOOK_RETRACTED;
			hook_pos = pos;
		}
	}

	if(world && world->tuning.player_collision)
	{
		for(int i = 0; i < MAX_CLIENTS; i++)
		{
			CHARACTER_CORE *p = world->characters[i];
			if(!p)
				continue;

			//player *p = (player*)ent;
			if(p == this) // || !(p->flags&FLAG_ALIVE)
				continue; // make sure that we don't nudge our self

			// handle player <-> player collision
			float d = distance(pos, p->pos);
			vec2 dir = normalize(pos - p->pos);
			if(d < phys_size*1.25f && d > 1.0f)
			{
				float a = (phys_size*1.45f - d);

				// make sure that we don't add excess force by checking the
				// direction against the current velocity
				vec2 veldir = normalize(vel);
				float v = 1-(dot(veldir, dir)+1)/2;
				vel = vel + dir*a*(v*0.75f);
				vel = vel * 0.85f;
			}

			// handle hook influence
			if(hooked_player == i)
			{
				if(d > phys_size*1.50f) // TODO: fix tweakable variable
				{
					float accel = world->tuning.hook_drag_accel * (d/world->tuning.hook_length);
					float drag_speed = world->tuning.hook_drag_speed;

					// add force to the hooked player
					p->vel.x = saturated_add(-drag_speed, drag_speed, p->vel.x, accel*dir.x*1.5f);
					p->vel.y = saturated_add(-drag_speed, drag_speed, p->vel.y, accel*dir.y*1.5f);

					// add a little bit force to the guy who has the grip
					vel.x = saturated_add(-drag_speed, drag_speed, vel.x, -accel*dir.x*0.25f);
					vel.y = saturated_add(-drag_speed, drag_speed, vel.y, -accel*dir.y*0.25f);
				}
			}
		}
	}

	// clamp the velocity to something sane
	if(length(vel) > 6000)
		vel = normalize(vel) * 6000;
}
示例#5
0
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 new_tick = gameclient.new_tick;

	// 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) {
			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();
	
	if(player.health < 0) // dont render dead players
		return;

	float angle = mix((float)prev.angle, (float)player.angle, intratick)/256.0f;
	
	//float angle = 0;
	
	// just use the direct input if it's local player we are rendering
	if(info.local && client_state() != CLIENTSTATE_DEMOPLAYBACK)
		angle = get_angle(gameclient.controls->mouse_pos);
	
	// use preditect players if needed
	if(info.local && config.cl_predict && client_state() != CLIENTSTATE_DEMOPLAYBACK)
	{
		if ( gameclient.snap.local_character && ( gameclient.snap.local_character->health >= 0 ) ) {
			// apply predicted results
			gameclient.predicted_char.write(&player);
			gameclient.predicted_prev_char.write(&prev);
			intratick = client_predintratick();
			new_tick = gameclient.new_predicted_tick;
		}
	}
	
	vec2 direction = get_direction((int)(angle*256.0f));
	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;
	
	
	// detect events
	if(new_tick)
	{
		// detect air jump
		if(!render_info.got_airjump && !(prev.jumped&2))
			gameclient.effects->air_jump(position);
	}

	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.direction == -1 && vel.x > 0) || (player.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 ct = (client_prevtick()-player.attacktick)/(float)SERVER_TICK_SPEED + client_ticktime();
		state.add(&data->animations[ANIM_HAMMER_SWING], clamp(ct*5.0f,0.0f,1.0f), 1.0f);
	}
	if (player.weapon == WEAPON_NINJA)
	{
		float ct = (client_prevtick()-player.attacktick)/(float)SERVER_TICK_SPEED + client_ticktime();
		state.add(&data->animations[ANIM_NINJA_SWING], clamp(ct*2.0f,0.0f,1.0f), 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)
		{
			gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_PLAYER_SKID, 0.25f, position);
			skid_sound_time = time_get();
		}
		
		gameclient.effects->skidtrail(
			position+vec2(-player.direction*6,12),
			vec2(-player.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(gameclient.predicted_prev_char.pos.x, gameclient.predicted_prev_char.pos.y),
					vec2(gameclient.predicted_char.pos.x, gameclient.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();
	}
}