about summary refs log tree commit diff
path: root/src/game
diff options
context:
space:
mode:
Diffstat (limited to 'src/game')
-rw-r--r--src/game/client/gc_client.cpp2
-rw-r--r--src/game/client/gc_render.cpp4
-rw-r--r--src/game/client/gc_render.h1
-rw-r--r--src/game/client/gc_render_obj.cpp72
-rw-r--r--src/game/editor/ed_io.cpp2
-rw-r--r--src/game/g_game.cpp2
-rw-r--r--src/game/g_mapitems.h2
-rw-r--r--src/game/g_protocol.h10
-rw-r--r--src/game/g_tuning.h12
-rw-r--r--src/game/server/gs_common.h21
-rw-r--r--src/game/server/gs_game.cpp4
-rw-r--r--src/game/server/gs_server.cpp220
12 files changed, 317 insertions, 35 deletions
diff --git a/src/game/client/gc_client.cpp b/src/game/client/gc_client.cpp
index c092ca73..79b50317 100644
--- a/src/game/client/gc_client.cpp
+++ b/src/game/client/gc_client.cpp
@@ -1117,7 +1117,7 @@ void render_game()
 		for (int i = 0; i < local_character->weaponstage; i++)
 			gfx_quads_drawTL(x+local_character->ammocount * 12 -i*12, y+22, 11, 11);
 		select_sprite(data->weapons[local_character->weapon%data->num_weapons].sprite_proj);
-		for (int i = 0; i < local_character->ammocount; i++)
+		for (int i = 0; i < min(local_character->ammocount, 10); i++)
 			gfx_quads_drawTL(x+i*12,y+24,10,10);
 
 		gfx_quads_end();
diff --git a/src/game/client/gc_render.cpp b/src/game/client/gc_render.cpp
index 6297fc9c..285b6c1c 100644
--- a/src/game/client/gc_render.cpp
+++ b/src/game/client/gc_render.cpp
@@ -341,6 +341,10 @@ static void render_items()
 			if(prev)
 				render_powerup((const obj_powerup *)prev, (const obj_powerup *)data);
 		}
+		else if(item.type == OBJTYPE_LASER)
+		{
+			render_laser((const obj_laser *)data);
+		}
 		else if(item.type == OBJTYPE_FLAG)
 		{
 			const void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
diff --git a/src/game/client/gc_render.h b/src/game/client/gc_render.h
index d7adeada..b62206f7 100644
--- a/src/game/client/gc_render.h
+++ b/src/game/client/gc_render.h
@@ -57,6 +57,7 @@ void render_tee(class animstate *anim, tee_render_info *info, int emote, vec2 di
 void render_flag(const struct obj_flag *prev, const struct obj_flag *current);
 void render_powerup(const struct obj_powerup *prev, const struct obj_powerup *current);
 void render_projectile(const struct obj_projectile *current, int itemid);
+void render_laser(const struct obj_laser *current);
 void render_player(
 	const struct obj_player_character *prev_char, const struct obj_player_character *player_char,
 	const struct obj_player_info *prev_info, const struct obj_player_info *player_info);
diff --git a/src/game/client/gc_render_obj.cpp b/src/game/client/gc_render_obj.cpp
index 00f54c89..6f836307 100644
--- a/src/game/client/gc_render_obj.cpp
+++ b/src/game/client/gc_render_obj.cpp
@@ -26,7 +26,7 @@ void render_projectile(const obj_projectile *current, int itemid)
 
 	// get positions
 	float gravity = -400;
-	if(current->type != WEAPON_ROCKET)
+	if(current->type != WEAPON_GRENADE)
 		gravity = -100;
 
 	float ct = (client_tick()-current->start_tick)/(float)SERVER_TICK_SPEED + client_ticktime()*1/(float)SERVER_TICK_SPEED;
@@ -41,7 +41,7 @@ void render_projectile(const obj_projectile *current, int itemid)
 	
 
 	// add particle for this projectile
-	if(current->type == WEAPON_ROCKET)
+	if(current->type == WEAPON_GRENADE)
 	{
 		effect_smoketrail(pos, vel*-1);
 		flow_add(pos, vel*1000*client_frametime(), 10.0f);
@@ -136,6 +136,72 @@ void render_flag(const obj_flag *prev, const obj_flag *current)
 }
 
 
+void render_laser(const struct obj_laser *current)
+{
+
+	vec2 pos = vec2(current->x, current->y);
+	vec2 from = vec2(current->from_x, current->from_y);
+	vec2 dir = normalize(pos-from);
+
+
+
+	float ticks = client_tick() + client_intratick() - current->eval_tick;
+	float ms = (ticks/50.0f) * 1000.0f;
+	float a =  ms / tuning.laser_bounce_delay;
+	a = clamp(a, 0.0f, 1.0f);
+	float ia = 1-a;
+	
+	
+	
+	vec2 out(dir.y, -dir.x);
+	
+	out = out * (4.0f*ia);
+
+	gfx_blend_normal();
+	gfx_texture_set(-1);
+	gfx_quads_begin();
+	
+	vec4 start_color(0.25f,0.25f,0.5f,1.0f);
+	vec4 end_color(0.85f,0.85f,1.0f,1.0f);
+	start_color = end_color;
+	
+	gfx_setcolorvertex(0, start_color.r, start_color.g, start_color.b, start_color.a);
+	gfx_setcolorvertex(1, start_color.r, start_color.g, start_color.b, start_color.a);
+	gfx_setcolorvertex(2, end_color.r, end_color.g, end_color.b, end_color.a);
+	gfx_setcolorvertex(3, end_color.r, end_color.g, end_color.b, end_color.a);
+	
+	from = mix(from, pos, a);
+	
+	gfx_quads_draw_freeform(
+			from.x-out.x, from.y-out.y,
+			from.x+out.x, from.y+out.y,
+			pos.x-out.x, pos.y-out.y,
+			pos.x+out.x, pos.y+out.y
+		);
+		
+	gfx_quads_end();
+	
+	// render head
+	{
+		gfx_blend_normal();
+		gfx_texture_set(data->images[IMAGE_PARTICLES].id);
+		gfx_quads_begin();
+
+		gfx_setcolor(end_color.r, end_color.g, end_color.b, end_color.a);
+	
+		int sprites[] = {SPRITE_PART_SPLAT01, SPRITE_PART_SPLAT02, SPRITE_PART_SPLAT03};
+		select_sprite(sprites[client_tick()%3]);
+		gfx_quads_setrotation(client_tick());
+		gfx_quads_draw(pos.x, pos.y, 32,32);
+		gfx_quads_end();
+	}
+	
+	gfx_blend_normal();	
+}
+
+
+
+
 
 static void render_hand(tee_render_info *info, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset)
 {
@@ -416,7 +482,7 @@ void render_player(
 		{
 			case WEAPON_GUN: render_hand(&client_datas[info.clientid].render_info, p, direction, -3*pi/4, vec2(-15, 4)); break;
 			case WEAPON_SHOTGUN: render_hand(&client_datas[info.clientid].render_info, p, direction, -pi/2, vec2(-5, 4)); break;
-			case WEAPON_ROCKET: render_hand(&client_datas[info.clientid].render_info, p, direction, -pi/2, vec2(-4, 7)); break;
+			case WEAPON_GRENADE: render_hand(&client_datas[info.clientid].render_info, p, direction, -pi/2, vec2(-4, 7)); break;
 		}
 
 	}
diff --git a/src/game/editor/ed_io.cpp b/src/game/editor/ed_io.cpp
index fe8ee19a..80c82548 100644
--- a/src/game/editor/ed_io.cpp
+++ b/src/game/editor/ed_io.cpp
@@ -176,7 +176,7 @@ void editor_load_old(DATAFILE *df)
 				else if(t == MAPRES_ITEM)
 				{
 					if(e->data[0] == ITEM_WEAPON_SHOTGUN) id = ENTITY_WEAPON_SHOTGUN;
-					else if(e->data[0] == ITEM_WEAPON_ROCKET) id = ENTITY_WEAPON_ROCKET;
+					else if(e->data[0] == ITEM_WEAPON_ROCKET) id = ENTITY_WEAPON_GRENADE;
 					else if(e->data[0] == ITEM_NINJA) id = ENTITY_POWERUP_NINJA;
 					else if(e->data[0] == ITEM_ARMOR) id = ENTITY_ARMOR_1;
 					else if(e->data[0] == ITEM_HEALTH) id = ENTITY_HEALTH_1;
diff --git a/src/game/g_game.cpp b/src/game/g_game.cpp
index 74b9aed4..25114cb7 100644
--- a/src/game/g_game.cpp
+++ b/src/game/g_game.cpp
@@ -43,8 +43,6 @@ bool tuning_params::get(const char *name, float *value)
 	return false;
 }
 
-
-
 // TODO: OPT: rewrite this smarter!
 void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces)
 {
diff --git a/src/game/g_mapitems.h b/src/game/g_mapitems.h
index 53561beb..a8278a44 100644
--- a/src/game/g_mapitems.h
+++ b/src/game/g_mapitems.h
@@ -36,7 +36,7 @@ enum
 	ENTITY_ARMOR_1,
 	ENTITY_HEALTH_1,
 	ENTITY_WEAPON_SHOTGUN,
-	ENTITY_WEAPON_ROCKET,
+	ENTITY_WEAPON_GRENADE,
 	ENTITY_POWERUP_NINJA,
 	NUM_ENTITIES,
 	
diff --git a/src/game/g_protocol.h b/src/game/g_protocol.h
index b0d84916..f7d26900 100644
--- a/src/game/g_protocol.h
+++ b/src/game/g_protocol.h
@@ -12,6 +12,7 @@ enum
 	OBJTYPE_PLAYER_INFO,
 	OBJTYPE_PLAYER_CHARACTER, // use this if you are searching for the player entity
 	OBJTYPE_PROJECTILE,
+	OBJTYPE_LASER,
 	OBJTYPE_POWERUP,
 	OBJTYPE_FLAG,
 	EVENT_EXPLOSION,
@@ -136,12 +137,19 @@ struct obj_game
 
 struct obj_projectile
 {
-	int type;
 	int x, y;
 	int vx, vy; // should be an angle instead
+	int type;
 	int start_tick;
 };
 
+struct obj_laser
+{
+	int x, y;
+	int from_x, from_y;
+	int eval_tick;
+};
+
 struct obj_powerup
 {
 	int x, y;
diff --git a/src/game/g_tuning.h b/src/game/g_tuning.h
index 9f7ffb80..e23b46bf 100644
--- a/src/game/g_tuning.h
+++ b/src/game/g_tuning.h
@@ -15,3 +15,15 @@ MACRO_TUNING_PARAM(gravity, 0.5f)
 MACRO_TUNING_PARAM(terminal_velocity, 1000.0f / ticks_per_second)
 
 /* weapon tuning */
+MACRO_TUNING_PARAM(gun_speed, 30.0f)
+
+MACRO_TUNING_PARAM(shotgun_speed_center, 40.0f)
+MACRO_TUNING_PARAM(shotgun_speed_wide, 30.0f)
+
+MACRO_TUNING_PARAM(grenade_speed, 15.0f)
+
+MACRO_TUNING_PARAM(laser_reach, 800.0f)
+MACRO_TUNING_PARAM(laser_bounce_delay, 150)
+MACRO_TUNING_PARAM(laser_bounce_num, 2)
+MACRO_TUNING_PARAM(laser_bounce_cost, 32)
+MACRO_TUNING_PARAM(laser_damage, 8)
diff --git a/src/game/server/gs_common.h b/src/game/server/gs_common.h
index 4519cf37..838b192f 100644
--- a/src/game/server/gs_common.h
+++ b/src/game/server/gs_common.h
@@ -213,6 +213,27 @@ public:
 	virtual void snap(int snapping_client);
 };
 
+class laser : public entity
+{
+	vec2 from;
+	vec2 dir;
+	float energy;
+	int bounces;
+	int eval_tick;
+	player *owner;
+	
+	bool hit_player(vec2 from, vec2 to);
+	void do_bounce();
+	
+public:
+	
+	laser(vec2 pos, vec2 direction, float start_energy, player *owner);
+	
+	virtual void reset();
+	virtual void tick();
+	virtual void snap(int snapping_client);
+};
+
 // player entity
 class player : public entity
 {
diff --git a/src/game/server/gs_game.cpp b/src/game/server/gs_game.cpp
index 14b5a2c7..9a9c233e 100644
--- a/src/game/server/gs_game.cpp
+++ b/src/game/server/gs_game.cpp
@@ -60,10 +60,10 @@ bool gameobject::on_entity(int index, vec2 pos)
 		type = POWERUP_WEAPON;
 		subtype = WEAPON_SHOTGUN;
 	}
-	else if(index == ENTITY_WEAPON_ROCKET)
+	else if(index == ENTITY_WEAPON_GRENADE)
 	{
 		type = POWERUP_WEAPON;
-		subtype = WEAPON_ROCKET;
+		subtype = WEAPON_GRENADE;
 	}
 	else if(index == ENTITY_POWERUP_NINJA)
 	{
diff --git a/src/game/server/gs_server.cpp b/src/game/server/gs_server.cpp
index 0416c808..c21af02c 100644
--- a/src/game/server/gs_server.cpp
+++ b/src/game/server/gs_server.cpp
@@ -406,7 +406,7 @@ void projectile::reset()
 void projectile::tick()
 {
 	float gravity = -400;
-	if(type != WEAPON_ROCKET)
+	if(type != WEAPON_GRENADE)
 		gravity = -100;
 	
 	float pt = (server_tick()-start_tick-1)/(float)server_tickspeed();
@@ -423,7 +423,7 @@ void projectile::tick()
 	
 	if(targetplayer || collide || lifespan < 0)
 	{
-		if (lifespan >= 0 || weapon == WEAPON_ROCKET)
+		if (lifespan >= 0 || weapon == WEAPON_GRENADE)
 			create_sound(pos, sound_impact);
 
 		if (flags & PROJECTILE_FLAGS_EXPLODE)
@@ -454,6 +454,110 @@ void projectile::snap(int snapping_client)
 	proj->type = type;
 }
 
+
+//////////////////////////////////////////////////
+// laser
+//////////////////////////////////////////////////
+laser::laser(vec2 pos, vec2 direction, float start_energy, player *owner)
+: entity(OBJTYPE_LASER)
+{
+	this->pos = pos;
+	this->owner = owner;
+	energy = start_energy;
+	dir = direction;
+	bounces = 0;
+	do_bounce();
+	
+	world->insert_entity(this);
+}
+
+
+bool laser::hit_player(vec2 from, vec2 to)
+{
+	vec2 at;
+	player *hit = intersect_player(pos, to, at, owner);
+	if(!hit)
+		return false;
+
+	this->from = from;
+	pos = at;
+	energy = -1;		
+	hit->take_damage(vec2(0,0), tuning.laser_damage, owner->client_id, WEAPON_LASER);
+	return true;
+}
+
+void laser::do_bounce()
+{
+	eval_tick = server_tick();
+	
+	if(energy < 0)
+	{
+		//dbg_msg("laser", "%d removed", server_tick());
+		world->destroy_entity(this);
+		return;
+	}
+	
+	vec2 to = pos + dir*energy;
+	
+	if(col_intersect_line(pos, to, &to))
+	{
+		if(!hit_player(pos, to))
+		{
+			// intersected
+			from = pos;
+			pos = to - dir*2;
+			vec2 temp_pos = pos;
+			vec2 temp_dir = dir*4.0f;
+			
+			move_point(&temp_pos, &temp_dir, 1.0f, 0);
+			dir = normalize(temp_dir);
+			
+			energy -= distance(from, pos) + tuning.laser_bounce_cost;
+			bounces++;
+			
+			if(bounces > tuning.laser_bounce_num)
+				energy = -1;
+		}
+	}
+	else
+	{
+		if(!hit_player(pos, to))
+		{
+			from = pos;
+			pos = to;
+			energy = -1;
+		}
+	}
+		
+	//dbg_msg("laser", "%d done %f %f %f %f", server_tick(), from.x, from.y, pos.x, pos.y);
+}
+	
+void laser::reset()
+{
+	world->destroy_entity(this);
+}
+
+void laser::tick()
+{
+	if(server_tick() > eval_tick+(server_tickspeed()*tuning.laser_bounce_delay)/1000.0f)
+		do_bounce();
+
+}
+
+void laser::snap(int snapping_client)
+{
+	if(distance(players[snapping_client].pos, pos) > 1000.0f)
+		return;
+
+	obj_laser *obj = (obj_laser *)snap_new_item(OBJTYPE_LASER, id, sizeof(obj_laser));
+	obj->x = (int)pos.x;
+	obj->y = (int)pos.y;
+	obj->from_x = (int)from.x;
+	obj->from_y = (int)from.y;
+	obj->eval_tick = eval_tick;
+}
+
+
 //////////////////////////////////////////////////
 // player
 //////////////////////////////////////////////////
@@ -714,8 +818,9 @@ void player::try_respawn()
 	weapons[WEAPON_GUN].got = true;
 	weapons[WEAPON_GUN].ammo = data->weapons[WEAPON_GUN].maxammo;
 
-	//weapons[WEAPON_SNIPER].got = true;
-	//weapons[WEAPON_SNIPER].ammo = data->weapons[WEAPON_SNIPER].maxammo;
+	weapons[WEAPON_LASER].got = true;
+	weapons[WEAPON_LASER].ammo = 100000; //data->weapons[WEAPON_LASER].maxammo;
+	
 	active_weapon = WEAPON_GUN;
 	last_weapon = WEAPON_HAMMER;
 	wanted_weapon = WEAPON_GUN;
@@ -909,7 +1014,7 @@ int player::handle_weapons()
 	if(reload_timer == 0)
 	{
 		bool fullauto = false;
-		if(active_weapon == WEAPON_ROCKET || active_weapon == WEAPON_SHOTGUN)
+		if(active_weapon == WEAPON_GRENADE || active_weapon == WEAPON_SHOTGUN)
 			fullauto = true;
 		
 		if(count_input(latest_previnput.fire, latest_input.fire).presses || ((fullauto && latest_input.fire&1) && weapons[active_weapon].ammo))
@@ -932,23 +1037,23 @@ int player::handle_weapons()
 						new projectile(WEAPON_GUN,
 							client_id,
 							pos+vec2(0,0),
-							direction*30.0f,
+							direction*tuning.gun_speed,
 							server_tickspeed(),
 							this,
 							1, 0, 0, -1, WEAPON_GUN);
 						create_sound(pos, SOUND_GUN_FIRE);
 						break;
 					}
-					case WEAPON_ROCKET:
+					case WEAPON_GRENADE:
 					{
-						new projectile(WEAPON_ROCKET,
+						new projectile(WEAPON_GRENADE,
 							client_id,
 							pos+vec2(0,0),
-							direction*15.0f,
+							direction*tuning.grenade_speed,
 							100,
 							this,
-							1, projectile::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_ROCKET_EXPLODE, WEAPON_ROCKET);
-						create_sound(pos, SOUND_ROCKET_FIRE);
+							1, projectile::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_GRENADE_EXPLODE, WEAPON_GRENADE);
+						create_sound(pos, SOUND_GRENADE_FIRE);
 						break;
 					}
 					case WEAPON_SHOTGUN:
@@ -960,11 +1065,11 @@ int player::handle_weapons()
 							float a = get_angle(direction);
 							float v = 1.0f-fabs(i/(float)shotspread);
 							a += spreading[i+2];
+							float speed = mix((float)tuning.shotgun_speed_wide, (float)tuning.shotgun_speed_center, v);
 							new projectile(WEAPON_SHOTGUN,
 								client_id,
 								pos+vec2(0,0),
-								vec2(cosf(a), sinf(a))*(28.0f + 12.0f*v),
-								//vec2(cosf(a), sinf(a))*20.0f,
+								vec2(cosf(a), sinf(a))*speed,
 								(int)(server_tickspeed()*0.25f),
 								this,
 								1, 0, 0, -1, WEAPON_SHOTGUN);
@@ -973,16 +1078,10 @@ int player::handle_weapons()
 						break;
 					}
 					
-					case WEAPON_SNIPER:
+					case WEAPON_LASER:
 					{
-						new projectile(WEAPON_SNIPER,
-							client_id,
-							pos+vec2(0,0),
-							direction*300.0f,
-							100,
-							this,
-							1, 0, 0, -1, WEAPON_SNIPER);
-						create_sound(pos, SOUND_SNIPER_FIRE);						
+						new laser(pos, direction, tuning.laser_reach, this);
+						create_sound(pos, SOUND_LASER_FIRE);
 						break;
 					}
 					
@@ -1476,8 +1575,8 @@ void powerup::tick()
 					respawntime = data->powerupinfo[type].respawntime;
 
 					// TODO: data compiler should take care of stuff like this
-					if(subtype == WEAPON_ROCKET)
-						create_sound(pos, SOUND_PICKUP_ROCKET);
+					if(subtype == WEAPON_GRENADE)
+						create_sound(pos, SOUND_PICKUP_GRENADE);
 					else if(subtype == WEAPON_SHOTGUN)
 						create_sound(pos, SOUND_PICKUP_SHOTGUN);
 
@@ -1656,10 +1755,82 @@ void create_sound_global(int sound, int target)
 	server_send_msg(target);
 }
 
+float closest_point_on_line(vec2 line_point0, vec2 line_point1, vec2 target_point)
+{
+	vec2 c = target_point - line_point0;
+	vec2 v = (line_point1 - line_point0);
+	v = normalize(v);
+	float d = length(line_point0-line_point1);
+	float t = dot(v, c);
+
+	if (t < 0) return 0;
+	if (t > d) return 1;
+
+	return t;
+}
+
 // TODO: should be more general
+player *intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity *notthis)
+{
+	// Find other players
+	float closest_time = distance(pos0, pos1) * 100.0f;
+	vec2 line_dir = normalize(pos1-pos0);
+	player *closest = 0;
+		
+	for(int i = 0; i < MAX_CLIENTS; i++)
+	{
+		if(players[i].client_id < 0 || (entity *)&players[i] == notthis)
+			continue;
+			
+		if(!(players[i].flags&entity::FLAG_PHYSICS))
+			continue;
+
+		float t = closest_point_on_line(pos0, pos1, players[i].pos);
+		vec2 intersect_pos = pos0 + line_dir*t;
+		float len = distance(players[i].pos, intersect_pos);
+		if(len < player::phys_size)
+		{
+			if(t < closest_time)
+			{
+				new_pos = intersect_pos;
+				closest_time = t;
+				closest = &players[i];
+			}
+		}
+	}
+	
+	return closest;
+	
+	/*
+	entity *ents[64];
+	vec2 dir = pos1 - pos0;
+	float radius = length(dir * 0.5f);
+	vec2 center = pos0 + dir * 0.5f;
+	const int types[] = {OBJTYPE_PLAYER_CHARACTER};
+	int num = world->find_entities(center, radius, ents, 64, types, 1);
+	for (int i = 0; i < num; i++)
+	{
+		// Check if entity is a player
+		if (ents[i] != notthis)
+		{
+			new_pos = ents[i]->pos;
+			return (player*)ents[i];
+		}
+	}
+
+	return 0;*/
+}
+
+
+
+// TODO: should be more general
+
+	/*
 player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis)
 {
 	// Find other players
+	
+	
 	entity *ents[64];
 	vec2 dir = pos1 - pos0;
 	float radius = length(dir * 0.5f);
@@ -1678,6 +1849,7 @@ player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis)
 
 	return 0;
 }
+*/
 
 // Server hooks
 void mods_tick()